// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. #include "lib/Api.Image/pch.h" #include "inc/ImageConverter.h" #include "inc/ImageConversionHelpers.h" #include "inc/D3DDeviceCache.h" using namespace Microsoft::WRL; using namespace _winml; void ImageConverter::SyncD3D11ToD3D12(_In_ D3DDeviceCache& device_cache, _In_ ID3D11Texture2D* pD3D11Texture) { assert(pD3D11Texture != nullptr); ComPtr spTextureDevice; pD3D11Texture->GetDevice(&spTextureDevice); if (spTextureDevice.Get() == device_cache.GetD3D11Device()) { // If the texture is on D3DDeviceCache's device, we sync using D3DDeviceCache's fences device_cache.GPUSyncD3D11ToD3D12(); } else { // Otherwise, sync using our own cached fences ComPtr spD3D11DeviceFence = FetchOrCreateFenceOnDevice(device_cache, spTextureDevice.Get()); device_cache.SyncD3D11DeviceToConverter(spD3D11DeviceFence.Get()); } } void ImageConverter::SyncD3D12ToD3D11(_In_ D3DDeviceCache& device_cache, _In_ ID3D11Texture2D* spTexture) { assert(spTexture != nullptr); ComPtr spTextureDevice; spTexture->GetDevice(&spTextureDevice); if (spTextureDevice.Get() == device_cache.GetD3D11Device()) { // If the texture is on D3DDeviceCache's device, we sync using D3DDeviceCache's fences device_cache.GPUSyncD3D12ToD3D11(); } else { // Otherwise, sync using our own cached fences ComPtr spD3D11DeviceFence = FetchOrCreateFenceOnDevice(device_cache, spTextureDevice.Get()); device_cache.SyncConverterToD3D11Device(spD3D11DeviceFence.Get()); } } ComPtr ImageConverter::FetchOrCreateFenceOnDevice( _In_ D3DDeviceCache& device_cache, _In_ ID3D11Device* pD3D11Device ) { assert(pD3D11Device != nullptr); ComPtr fence; UINT comPtrSize = static_cast(sizeof(fence.GetAddressOf())); if (FAILED(pD3D11Device->GetPrivateData(device_cache.GetFenceGuid(), &comPtrSize, fence.GetAddressOf())) || fence.Get() == nullptr) { // There's no fence on the device, so create a new one ComPtr spD3D11Device5; WINML_THROW_IF_FAILED(pD3D11Device->QueryInterface(IID_PPV_ARGS(&spD3D11Device5))); WINML_THROW_IF_FAILED(spD3D11Device5->OpenSharedFence(device_cache.GetConverterFenceHandle(), IID_PPV_ARGS(&fence)) ); // Store the fence on the device WINML_THROW_IF_FAILED(spD3D11Device5->SetPrivateDataInterface(device_cache.GetFenceGuid(), fence.Get())); } return fence; } void ImageConverter::ResetCommandList(_In_ D3DDeviceCache& device_cache) { if (!command_list_) { assert(command_allocator_ == nullptr); WINML_THROW_IF_FAILED(device_cache.GetD3D12Device()->CreateCommandAllocator( device_cache.GetCommandQueue()->GetDesc().Type, IID_PPV_ARGS(command_allocator_.ReleaseAndGetAddressOf()) )); WINML_THROW_IF_FAILED(device_cache.GetD3D12Device()->CreateCommandList( 0, device_cache.GetCommandQueue()->GetDesc().Type, command_allocator_.Get(), pipeline_state_.Get(), IID_PPV_ARGS(command_list_.ReleaseAndGetAddressOf()) )); } else { command_list_->Reset(command_allocator_.Get(), pipeline_state_.Get()); } } void ImageConverter::ResetAllocator() { WINML_THROW_IF_FAILED(command_allocator_->Reset()); } ComPtr ImageConverter::CreateTextureFromUnsupportedColorFormat( const wm::IVideoFrame& videoFrame, const wgi::BitmapBounds& inputBounds, const wgi::BitmapBounds& outputBounds, wgdx::DirectXPixelFormat newFormat ) { assert(videoFrame != nullptr); // Make sure we create the new video frame on the same device. We don't want the VideoFrame pipeline to implicitly share the texture between // 2 devices since we will need to do it ourselves anyway. auto device = _winmli::GetDeviceFromDirect3DSurface(videoFrame.Direct3DSurface()); auto spNewVideoFrame = wm::VideoFrame::CreateAsDirect3D11SurfaceBacked(newFormat, outputBounds.Width, outputBounds.Height, device); videoFrame.as().CopyToAsync(spNewVideoFrame, inputBounds, outputBounds).get(); using namespace Windows::Graphics::DirectX::Direct3D11; auto spDxgiInterfaceAccess = spNewVideoFrame.Direct3DSurface().as(); ComPtr d3d11Texture; WINML_THROW_IF_FAILED(spDxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&d3d11Texture))); return d3d11Texture; } void ImageConverter::CopyTextureIntoTexture( _In_ ID3D11Texture2D* pTextureFrom, _In_ const wgi::BitmapBounds& inputBounds, _Inout_ ID3D11Texture2D* pTextureTo ) { assert(pTextureFrom != nullptr); assert(pTextureTo != nullptr); D3D11_TEXTURE2D_DESC textureFromDesc, textureToDesc; pTextureFrom->GetDesc(&textureFromDesc); pTextureTo->GetDesc(&textureToDesc); assert(inputBounds.Width <= textureFromDesc.Width && inputBounds.Width <= textureToDesc.Width); assert(inputBounds.Height <= textureFromDesc.Height && inputBounds.Height <= textureToDesc.Height); ComPtr spDeviceFrom, spDeviceTo; pTextureFrom->GetDevice(&spDeviceFrom); pTextureTo->GetDevice(&spDeviceTo); assert(spDeviceFrom.Get() == spDeviceTo.Get()); ComPtr spDeviceContext; spDeviceFrom->GetImmediateContext(&spDeviceContext); if (textureFromDesc.Width != textureToDesc.Width || textureFromDesc.Height != textureToDesc.Height) { // We can't copy the whole resource, so we have to use the slower CopySubresource() function D3D11_BOX cropBox = CD3D11_BOX( inputBounds.X, inputBounds.Y, 0, inputBounds.X + inputBounds.Width, inputBounds.Y + inputBounds.Height, 1 ); spDeviceContext->CopySubresourceRegion(pTextureTo, 0, 0, 0, 0, pTextureFrom, 0, &cropBox); } else { // Use the faster CopyResource() function since both textures have the same dimensions spDeviceContext->CopyResource(pTextureTo, pTextureFrom); } }