MixedReality-HolographicRem.../common/CameraResourcesD3D11Hologra...

322 строки
16 KiB
C++

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
#include <pch.h>
#include <CameraResourcesD3D11Holographic.h>
#include <DeviceResourcesD3D11Holographic.h>
#include <DirectXHelper.h>
#include <windows.graphics.directx.direct3d11.interop.h>
#include <winrt/Windows.Perception.Spatial.h>
using namespace DirectX;
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;
namespace DXHelper
{
CameraResourcesD3D11Holographic::CameraResourcesD3D11Holographic(HolographicCamera const& camera, DXGI_FORMAT renderTargetViewFormat)
: m_holographicCamera(camera)
, m_isStereo(camera.IsStereo())
, m_isOpaque(camera.Display().IsOpaque())
, m_d3dRenderTargetSize(camera.RenderTargetSize())
, m_dxgiFormat(DXGI_FORMAT_UNKNOWN)
, m_renderTargetViewFormat(renderTargetViewFormat)
{
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 CameraResourcesD3D11Holographic::CreateResourcesForBackBuffer(
DeviceResourcesD3D11Holographic* 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.
winrt::com_ptr<ID3D11Texture2D> cameraBackBuffer;
winrt::check_hresult(surface.as<::Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>()->GetInterface(
IID_PPV_ARGS(cameraBackBuffer.put())));
// 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;
// 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;
// Note, for Holographic Remoting you should explicitly specify the format as DXGI_FORMAT_B8G8R8A8_UNORM. This ensures that the
// video data coming from the remote side is displayed as is without any automatic format conversion.
D3D11_RENDER_TARGET_VIEW_DESC viewDesc = {};
viewDesc.ViewDimension = backBufferDesc.ArraySize > 1 ? D3D11_RTV_DIMENSION_TEXTURE2DARRAY : D3D11_RTV_DIMENSION_TEXTURE2D;
viewDesc.Format = m_renderTargetViewFormat;
if (backBufferDesc.ArraySize > 1)
{
viewDesc.Texture2DArray.ArraySize = backBufferDesc.ArraySize;
}
// 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.
m_d3dRenderTargetView = nullptr;
winrt::check_hresult(device->CreateRenderTargetView(m_d3dBackBuffer.get(), &viewDesc, m_d3dRenderTargetView.put()));
// 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_d3dDepthStencil = nullptr;
m_d3dDepthStencilView = nullptr;
}
}
// 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);
// Allow sharing by default for easier interop with future D3D12 components for processing the remote or local frame.
// This is optional, but without the flag any D3D12 components need to perform an additional copy.
depthStencilDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
m_d3dDepthStencil = nullptr;
winrt::check_hresult(device->CreateTexture2D(&depthStencilDesc, nullptr, m_d3dDepthStencil.put()));
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.put()));
}
// 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);
constantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
constantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
winrt::check_hresult(device->CreateBuffer(&constantBufferDesc, nullptr, m_viewProjectionConstantBuffer.put()));
}
}
// Releases resources associated with a back buffer.
void CameraResourcesD3D11Holographic::ReleaseResourcesForBackBuffer(DeviceResourcesD3D11Holographic* pDeviceResources)
{
// Release camera-specific resources.
m_d3dBackBuffer = nullptr;
m_d3dDepthStencil = nullptr;
m_d3dRenderTargetView = nullptr;
m_d3dDepthStencilView = nullptr;
m_viewProjectionConstantBuffer = nullptr;
pDeviceResources->UseD3DDeviceContext([](auto context) {
// 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 CameraResourcesD3D11Holographic::UpdateViewProjectionBuffer(
const std::shared_ptr<DeviceResourcesD3D11Holographic>& 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.
m_cameraProjectionTransform = cameraPose.ProjectionTransform();
ViewProjectionConstantBuffer viewProjectionConstantBufferData = {};
bool viewTransformAcquired = false;
// MakeDropCmd-StripStart
if (m_freezeCamera && m_frozenCoordinateSystem)
{
// If the camera is frozen and we have a coordinate system from the previous frame, try to translate it
// to the current coordinate system, and override the view transform on the holographic camera.
auto coordinateTransformContainer = coordinateSystem.TryGetTransformTo(m_frozenCoordinateSystem);
viewTransformAcquired = coordinateTransformContainer != nullptr;
if (viewTransformAcquired)
{
cameraPose.OverrideViewTransform(m_frozenCoordinateSystem, m_frozenViewTransform);
auto coordinateTransform = coordinateTransformContainer.Value();
XMStoreFloat4x4(
&viewProjectionConstantBufferData.viewProjection[0],
XMMatrixTranspose(
XMLoadFloat4x4(&coordinateTransform) * XMLoadFloat4x4(&m_frozenViewTransform.Left) *
XMLoadFloat4x4(&m_cameraProjectionTransform.Left)));
XMStoreFloat4x4(
&viewProjectionConstantBufferData.viewProjection[1],
XMMatrixTranspose(
XMLoadFloat4x4(&coordinateTransform) * XMLoadFloat4x4(&m_frozenViewTransform.Right) *
XMLoadFloat4x4(&m_cameraProjectionTransform.Right)));
}
}
else
// MakeDropCmd-StripEnd
{
// 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.
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(&m_cameraProjectionTransform.Left)));
XMStoreFloat4x4(
&viewProjectionConstantBufferData.viewProjection[1],
XMMatrixTranspose(
XMLoadFloat4x4(&viewCoordinateSystemTransform.Right) * XMLoadFloat4x4(&m_cameraProjectionTransform.Right)));
// MakeDropCmd-StripStart
if (m_freezeCamera)
{
// If camera freezing is requested, store the used coordinate system and matrices to reuse in the next frame.
m_frozenCoordinateSystem = coordinateSystem;
m_frozenViewTransform = viewCoordinateSystemTransform;
}
else
{
m_frozenCoordinateSystem = nullptr;
}
// MakeDropCmd-StripEnd
}
}
// Use the D3D device context to update Direct3D device-based resources.
deviceResources->UseD3DDeviceContext([&](auto context) {
// 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.
D3D11_MAPPED_SUBRESOURCE resource;
context->Map(m_viewProjectionConstantBuffer.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
memcpy(resource.pData, &viewProjectionConstantBufferData, sizeof(viewProjectionConstantBufferData));
context->Unmap(m_viewProjectionConstantBuffer.get(), 0);
m_framePending = true;
}
});
}
// Gets the view-projection constant buffer for the HolographicCamera and attaches it
// to the shader pipeline.
bool CameraResourcesD3D11Holographic::AttachViewProjectionBuffer(std::shared_ptr<DeviceResourcesD3D11Holographic>& deviceResources)
{
// This method uses Direct3D device-based resources.
return deviceResources->UseD3DDeviceContext([&](auto context) {
// 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.
ID3D11Buffer* viewProjectionConstantBuffers[1] = {m_viewProjectionConstantBuffer.get()};
context->VSSetConstantBuffers(1, 1, viewProjectionConstantBuffers);
// 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;
});
}
winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface CameraResourcesD3D11Holographic::GetDepthStencilTextureInteropObject()
{
// Direct3D interop APIs are used to provide the buffer to the WinRT API.
winrt::com_ptr<IDXGIResource1> depthStencilResource;
winrt::check_bool(m_d3dDepthStencil.try_as(depthStencilResource));
winrt::com_ptr<IDXGISurface2> depthDxgiSurface;
winrt::check_hresult(depthStencilResource->CreateSubresourceSurface(0, depthDxgiSurface.put()));
winrt::com_ptr<::IInspectable> inspectableSurface;
winrt::check_hresult(CreateDirect3D11SurfaceFromDXGISurface(
depthDxgiSurface.get(), reinterpret_cast<IInspectable**>(winrt::put_abi(inspectableSurface))));
return inspectableSurface.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>();
}
} // namespace DXHelper