aboutsummaryrefslogtreecommitdiffstats
path: root/support/hololens/ServoApp/Common/CameraResources.cpp
blob: 4391a55c74e8bdc625e9b949e3ece9b19431abee (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include "pch.h"

#include "CameraResources.h"
#include "Common/DirectXHelper.h"
#include "DeviceResources.h"

using namespace DirectX;
using namespace Microsoft::WRL;
using namespace winrt::Windows::Foundation::Numerics;
using namespace winrt::Windows::Graphics::DirectX::Direct3D11;
using namespace winrt::Windows::Graphics::Holographic;
using namespace winrt::Windows::Perception::Spatial;

DX::CameraResources::CameraResources(HolographicCamera const &camera)
    : m_holographicCamera(camera), m_isStereo(camera.IsStereo()),
      m_d3dRenderTargetSize(camera.RenderTargetSize()) {
  m_d3dViewport = CD3D11_VIEWPORT(0.f, 0.f, m_d3dRenderTargetSize.Width,
                                  m_d3dRenderTargetSize.Height);
};

// Updates resources associated with a holographic camera's swap chain.
// The app does not access the swap chain directly, but it does create
// resource views for the back buffer.
void DX::CameraResources::CreateResourcesForBackBuffer(
    DX::DeviceResources *pDeviceResources,
    HolographicCameraRenderingParameters const &cameraParameters) {
  ID3D11Device *device = pDeviceResources->GetD3DDevice();

  // Get the WinRT object representing the holographic camera's back buffer.
  IDirect3DSurface surface = cameraParameters.Direct3D11BackBuffer();

  // Get the holographic camera's back buffer.
  // Holographic apps do not create a swap chain themselves; instead, buffers
  // are owned by the system. The Direct3D back buffer resources are provided to
  // the app using WinRT interop APIs.
  ComPtr<ID3D11Texture2D> cameraBackBuffer;
  winrt::check_hresult(surface
                           .as<::Windows::Graphics::DirectX::Direct3D11::
                                   IDirect3DDxgiInterfaceAccess>()
                           ->GetInterface(IID_PPV_ARGS(&cameraBackBuffer)));

  // Determine if the back buffer has changed. If so, ensure that the render
  // target view is for the current back buffer.
  if (m_d3dBackBuffer.Get() != cameraBackBuffer.Get()) {
    // This can change every frame as the system moves to the next buffer in the
    // swap chain. This mode of operation will occur when certain rendering
    // modes are activated.
    m_d3dBackBuffer = cameraBackBuffer;

    // Create a render target view of the back buffer.
    // Creating this resource is inexpensive, and is better than keeping track
    // of the back buffers in order to pre-allocate render target views for each
    // one.
    winrt::check_hresult(device->CreateRenderTargetView(
        m_d3dBackBuffer.Get(), nullptr, &m_d3dRenderTargetView));

    // Get the DXGI format for the back buffer.
    // This information can be accessed by the app using
    // CameraResources::GetBackBufferDXGIFormat().
    D3D11_TEXTURE2D_DESC backBufferDesc;
    m_d3dBackBuffer->GetDesc(&backBufferDesc);
    m_dxgiFormat = backBufferDesc.Format;

    // Check for render target size changes.
    winrt::Windows::Foundation::Size currentSize =
        m_holographicCamera.RenderTargetSize();
    if (m_d3dRenderTargetSize != currentSize) {
      // Set render target size.
      m_d3dRenderTargetSize = currentSize;

      // A new depth stencil view is also needed.
      m_d3dDepthStencilView.Reset();
    }
  }

  // Refresh depth stencil resources, if needed.
  if (m_d3dDepthStencilView == nullptr) {
    // Create a depth stencil view for use with 3D rendering if needed.
    CD3D11_TEXTURE2D_DESC depthStencilDesc(
        DXGI_FORMAT_R16_TYPELESS,
        static_cast<UINT>(m_d3dRenderTargetSize.Width),
        static_cast<UINT>(m_d3dRenderTargetSize.Height),
        m_isStereo ? 2 : 1, // Create two textures when rendering in stereo.
        1,                  // Use a single mipmap level.
        D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE);

    winrt::check_hresult(device->CreateTexture2D(&depthStencilDesc, nullptr,
                                                 &m_d3dDepthStencil));

    CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(
        m_isStereo ? D3D11_DSV_DIMENSION_TEXTURE2DARRAY
                   : D3D11_DSV_DIMENSION_TEXTURE2D,
        DXGI_FORMAT_D16_UNORM);
    winrt::check_hresult(device->CreateDepthStencilView(
        m_d3dDepthStencil.Get(), &depthStencilViewDesc,
        &m_d3dDepthStencilView));
  }

  // Create the constant buffer, if needed.
  if (m_viewProjectionConstantBuffer == nullptr) {
    // Create a constant buffer to store view and projection matrices for the
    // camera.
    CD3D11_BUFFER_DESC constantBufferDesc(sizeof(ViewProjectionConstantBuffer),
                                          D3D11_BIND_CONSTANT_BUFFER);
    winrt::check_hresult(device->CreateBuffer(&constantBufferDesc, nullptr,
                                              &m_viewProjectionConstantBuffer));
  }
}

// Releases resources associated with a back buffer.
void DX::CameraResources::ReleaseResourcesForBackBuffer(
    DX::DeviceResources *pDeviceResources) {
  ID3D11DeviceContext *context = pDeviceResources->GetD3DDeviceContext();

  // Release camera-specific resources.
  m_d3dBackBuffer.Reset();
  m_d3dDepthStencil.Reset();
  m_d3dRenderTargetView.Reset();
  m_d3dDepthStencilView.Reset();
  m_viewProjectionConstantBuffer.Reset();

  // Ensure system references to the back buffer are released by clearing the
  // render target from the graphics pipeline state, and then flushing the
  // Direct3D context.
  ID3D11RenderTargetView *nullViews[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = {
      nullptr};
  context->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
  context->Flush();
}

// Updates the view/projection constant buffer for a holographic camera.
void DX::CameraResources::UpdateViewProjectionBuffer(
    std::shared_ptr<DX::DeviceResources> deviceResources,
    HolographicCameraPose const &cameraPose,
    SpatialCoordinateSystem const &coordinateSystem) {
  // The system changes the viewport on a per-frame basis for system
  // optimizations.
  auto viewport = cameraPose.Viewport();
  m_d3dViewport =
      CD3D11_VIEWPORT(viewport.X, viewport.Y, viewport.Width, viewport.Height);

  // The projection transform for each frame is provided by the
  // HolographicCameraPose.
  HolographicStereoTransform cameraProjectionTransform =
      cameraPose.ProjectionTransform();

  // Get a container object with the view and projection matrices for the given
  // pose in the given coordinate system.
  auto viewTransformContainer =
      cameraPose.TryGetViewTransform(coordinateSystem);

  // If TryGetViewTransform returns a null pointer, that means the pose and
  // coordinate system cannot be understood relative to one another; content
  // cannot be rendered in this coordinate system for the duration of the
  // current frame. This usually means that positional tracking is not active
  // for the current frame, in which case it is possible to use a
  // SpatialLocatorAttachedFrameOfReference to render content that is not
  // world-locked instead.
  DX::ViewProjectionConstantBuffer viewProjectionConstantBufferData;
  bool viewTransformAcquired = viewTransformContainer != nullptr;
  if (viewTransformAcquired) {
    // Otherwise, the set of view transforms can be retrieved.
    HolographicStereoTransform viewCoordinateSystemTransform =
        viewTransformContainer.Value();

    // Update the view matrices. Holographic cameras (such as Microsoft
    // HoloLens) are constantly moving relative to the world. The view matrices
    // need to be updated every frame.
    XMStoreFloat4x4(
        &viewProjectionConstantBufferData.viewProjection[0],
        XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Left) *
                          XMLoadFloat4x4(&cameraProjectionTransform.Left)));
    XMStoreFloat4x4(
        &viewProjectionConstantBufferData.viewProjection[1],
        XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Right) *
                          XMLoadFloat4x4(&cameraProjectionTransform.Right)));
  }

  // Use the D3D device context to update Direct3D device-based resources.
  ID3D11DeviceContext *context = deviceResources->GetD3DDeviceContext();

  // Loading is asynchronous. Resources must be created before they can be
  // updated.
  if (context == nullptr || m_viewProjectionConstantBuffer == nullptr ||
      !viewTransformAcquired) {
    m_framePending = false;
  } else {
    // Update the view and projection matrices.
    context->UpdateSubresource(m_viewProjectionConstantBuffer.Get(), 0, nullptr,
                               &viewProjectionConstantBufferData, 0, 0);

    m_framePending = true;
  }
}

// Gets the view-projection constant buffer for the HolographicCamera and
// attaches it to the shader pipeline.
bool DX::CameraResources::AttachViewProjectionBuffer(
    std::shared_ptr<DX::DeviceResources> &deviceResources) {
  // This method uses Direct3D device-based resources.
  ID3D11DeviceContext *context = deviceResources->GetD3DDeviceContext();

  // Loading is asynchronous. Resources must be created before they can be
  // updated. Cameras can also be added asynchronously, in which case they must
  // be initialized before they can be used.
  if (context == nullptr || m_viewProjectionConstantBuffer == nullptr ||
      m_framePending == false) {
    return false;
  }

  // Set the viewport for this camera.
  context->RSSetViewports(1, &m_d3dViewport);

  // Send the constant buffer to the vertex shader.
  context->VSSetConstantBuffers(1, 1,
                                m_viewProjectionConstantBuffer.GetAddressOf());

  // The template includes a pass-through geometry shader that is used by
  // default on systems that don't support the D3D11_FEATURE_D3D11_OPTIONS3::
  // VPAndRTArrayIndexFromAnyShaderFeedingRasterizer extension. The shader
  // will be enabled at run-time on systems that require it.
  // If your app will also use the geometry shader for other tasks and those
  // tasks require the view/projection matrix, uncomment the following line
  // of code to send the constant buffer to the geometry shader as well.
  /*context->GSSetConstantBuffers(
  1,
  1,
  m_viewProjectionConstantBuffer.GetAddressOf()
  );*/

  m_framePending = false;

  return true;
}