Skip to main content
Version: 0.9.0

D3D11 Rendering

Source: qar-streaming-c/examples/d3d11_render_sender.c. Conceptual background: Rendering Streams.

D3D11 Rendering

Overview​

  • Obtain a session as in earlier tutorials (rejoin or onboard)
  • Create the app volume the stream is displayed in
  • Wait for a peer (e.g. a Visualizer) to request a render stream
  • Create a D3D11 device, chain it into the render sender with QarStreamParamsD3D11, and clear each colour view of the sender's shared textures directly on the GPU - no CPU copy

Prerequisites​

  • Complete the CPU Rendering with a Visualizer tutorial; this one differs only in the graphics backend
  • Windows with a D3D11.1-capable GPU
  • A running QarOS hub (for the first run, read the pairing code off its onboarding screen), with a peer that requests a render stream from this application once it is running

Build and Run​

 cmake --build --preset x64-windows-debug --target d3d11_render_sender
 ./build/x64-windows/Debug/d3d11_render_sender.exe
 <path-to-qar-streaming-c.dll> [runtime-dir] [pairing-code]

Library, Runtime, Session, and App Volume​

Argument parsing and session setup match the CPU Rendering with a Visualizer tutorial. A render sender is displayed in an app volume, so one is created before the sender.

  volume_init.common_name = "d3d11-volume.examples.qaros";
  volume_init.display_name = "D3D11 Tutorial Volume";
  volume_init.pose = qar_pose_default();
  volume_init.pose.position.z = -1.0f;
  volume_init.size = qar_app_volume_size_default();
 
  QarResult volume_result = qar_app_volumes_get_or_create(session, &volume_init, &volume_id);
  log_result("qar_app_volumes_get_or_create", volume_result);
  if(qar_result_is_error(volume_result))
  {
  teardown(runtime, session);
  return 6;
  }

Create the D3D11 Device​

The sender needs the ID3D11Device1 / ID3D11DeviceContext1 interfaces, so the device is created as usual and queried for them.

 static bool
 {
  const D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_1 };
  ID3D11Device* device = NULL;
  ID3D11DeviceContext* context = NULL;
  HRESULT hr = D3D11CreateDevice(
  NULL,
  D3D_DRIVER_TYPE_HARDWARE,
  NULL,
  D3D11_CREATE_DEVICE_BGRA_SUPPORT,
  levels,
  1,
  D3D11_SDK_VERSION,
  &device,
  NULL,
  &context
  );
  if(FAILED(hr))
  {
  fprintf(stderr, "D3D11CreateDevice failed (0x%08lX)\n", (unsigned long)hr);
  return false;
  }
 
  out_d3d->device = NULL;
  out_d3d->context = NULL;
  HRESULT device_hr =
  ID3D11Device_QueryInterface(device, &IID_ID3D11Device1, (void**)&out_d3d->device);
  HRESULT context_hr = ID3D11DeviceContext_QueryInterface(
  context, &IID_ID3D11DeviceContext1, (void**)&out_d3d->context
  );
  ID3D11DeviceContext_Release(context);
  ID3D11Device_Release(device);
  if(FAILED(device_hr) || FAILED(context_hr))
  {
  fprintf(stderr, "D3D11.1 interfaces not available\n");
  release_d3d11(out_d3d);
  return false;
  }
  return true;
 }

Create the D3D11 Render Sender​

Select QAR_GRAPHICS_API_D3D11 and chain QarStreamParamsD3D11 into QarRenderSenderInit::header.next. The bind flags decide how the shared textures can be used; keep acquire_keyed_mutex_sync true so the library acquires and releases each texture's keyed mutex around the frame cycle.

  d3d11_params.d3d11_device = d3d.device;
  d3d11_params.d3d11_context = d3d.context;
  d3d11_params.color_bind_flags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
  d3d11_params.depth_bind_flags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
  d3d11_params.acquire_keyed_mutex_sync = true;
 
  sender_init.peer_id = request_state.target_peer_id;
  sender_init.app_volume_id = &volume_id;
  sender_init.header.next = &d3d11_params.header;
 
  QarRenderSender* sender = NULL;
  QarResult sender_result = qar_render_sender_create(session, &sender_init, NULL, &sender);
  log_result("qar_render_sender_create", sender_result);
  if(qar_result_is_error(sender_result) || sender == NULL)
  {
  release_d3d11(&d3d);
  teardown(runtime, session);
  return 9;
  }

Render and Submit Frames​

Inside each begin-frame / show-frame cycle, qar_render_sender_frame_d3d11() hands out the shared textures and the views laid out on them. Each colour view is a pixel rectangle on one texture; here it is cleared to an animated colour with ID3D11DeviceContext1::ClearView. A real renderer draws its scene into the same rectangle, using the view poses from the frame info.

  const size_t frame_count = 300;
  for(size_t frame_index = 0; frame_index < frame_count; ++frame_index)
  {
  QarRenderFrameInfo* frame_info = NULL;
  QarResult begin_result = qar_render_sender_begin_frame(sender, NULL, &frame_info);
  if(qar_result_is_error(begin_result) || frame_info == NULL)
  {
  log_result("qar_render_sender_begin_frame", begin_result);
  break;
  }
 
  QarResult frame_result = qar_render_sender_frame_d3d11(sender, &frame);
  if(qar_result_is_success(frame_result))
  {
  clear_color_views(d3d.device, d3d.context, &frame, frame_index);
  }
  else
  {
  log_result("qar_render_sender_frame_d3d11", frame_result);
  }
 
  /* Show closes the frame cycle that begin-frame opened. */
  show.rendered_near_far.far_plane = 10.0f;
  QarResult show_result = qar_render_sender_show_frame(sender, &show);
  if(qar_result_is_error(show_result))
  {
  log_result("qar_render_sender_show_frame", show_result);
  break;
  }
  }
 

The clear itself:

 /** \brief Clear every colour view of the frame to an animated colour. */
 static void
  ID3D11Device1* device,
  ID3D11DeviceContext1* context,
  const QarVideoFrameD3D11* frame,
  size_t frame_index
 )
 {
  const float t = (float)(frame_index % 120) / 120.0f;
  for(size_t i = 0; i < frame->texture_views_count; ++i)
  {
  const QarVideoFrameView* view = &frame->texture_views[i];
  || view->texture_index >= frame->textures_count)
  {
  continue;
  }
 
  ID3D11Texture2D* texture = frame->textures[view->texture_index].texture;
  ID3D11RenderTargetView* rtv = NULL;
  HRESULT hr =
  ID3D11Device1_CreateRenderTargetView(device, (ID3D11Resource*)texture, NULL, &rtv);
  if(FAILED(hr))
  {
  fprintf(stderr, "CreateRenderTargetView failed (0x%08lX)\n", (unsigned long)hr);
  continue;
  }
 
  /* Left eye red-ish, right eye blue-ish, both pulsing. */
  const bool right = view->eye == QAR_VIDEO_FRAME_VIEW_EYE_RIGHT;
  const float color[4] = { right ? 0.1f : t, 0.2f, right ? t : 0.1f, 1.f };
  const D3D11_RECT rect = {
  (LONG)view->start_x, (LONG)view->start_y, (LONG)view->end_x, (LONG)view->end_y
  };
  ID3D11DeviceContext1_ClearView(context, (ID3D11View*)rtv, color, &rect, 1);
  ID3D11RenderTargetView_Release(rtv);
  }
 }

Generated via doxygen2docusaurus 2.2.1 by Doxygen 1.9.8.