I want to create opencv Mat from FTexture2DRHIRef BackBuffer when it ready to present.
In project settings i set frame buffer format to 8bit RGBA.
I get ID3D12Resource from BackBuffer and it descripton.
Descripton has D3D12_TEXTURE_LAYOUT_UNKNOWN layout. I think i need D3D12_TEXTURE_LAYOUT_ROW_MAJOR. But i cant even create ID3D12Resource with this layout
ID3D12Resource* texture2D_copy;
D3D12_RESOURCE_DESC textureDesc = {};
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
textureDesc.Alignment = 0;
textureDesc.Width = 1024;
textureDesc.Height = 1024;
textureDesc.DepthOrArraySize = 1;
textureDesc.MipLevels = 1;
textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
textureDesc.SampleDesc.Count = 1;
textureDesc.SampleDesc.Quality = 0;
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12_HEAP_PROPERTIES heapProperties = {};
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
HRESULT Result = m_device->CreateCommittedResource(
&heapProperties,
D3D12_HEAP_FLAG_NONE,
&textureDesc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(&texture2D_copy)
);
check(SUCCEEDED(Result));
With D3D12_TEXTURE_LAYOUT_UNKNOWN all ok.
Now opencv image and screen:
c++ code:
void AScreenCaptureActor::BeginPlay()
{
Super::BeginPlay();
gameWindow = GEngine->GameViewport->GetWindow().Get();
FSlateApplication::Get().GetRenderer()->OnBackBufferReadyToPresent().AddUObject(this, &AScreenCaptureActor::OnBackBufferReady_RenderThread);
}
void AScreenCaptureActor::getExternalMemory(DX12Texture& d3d12Texture)
{
cudaExternalMemoryHandleDesc memDesc{};
memset(&memDesc, 0, sizeof(memDesc));
memDesc.type = cudaExternalMemoryHandleTypeD3D12Resource;
memDesc.handle.win32.handle = d3d12Texture.handle;
memDesc.size = d3d12Texture.memSize;
memDesc.flags = cudaExternalMemoryDedicated;
cudaError_t cudaError = cudaImportExternalMemory(&m_externalMemory, &memDesc);
}
void AScreenCaptureActor::mapMemory(DX12Texture& d3d12Texture)
{
void* pData = nullptr;
cudaExternalMemoryBufferDesc buffDesc{};
memset(&buffDesc, 0, sizeof(buffDesc));
buffDesc.offset = 0;
buffDesc.size = d3d12Texture.memSize;
cudaExternalMemoryGetMappedBuffer(&pData, m_externalMemory, &buffDesc);
D3D12_RESOURCE_DESC Desc = d3d12Texture.resource->GetDesc();
int height = static_cast<int>(Desc.Height);
int width = static_cast<int>(Desc.Width);
cv::cuda::GpuMat mat = cv::cuda::GpuMat(height, width, CV_8UC4, (char*)pData);
auto step = mat.step;
cv::Mat rgba;
mat.download(rgba);
cv::Mat rgb;
cv::cvtColor(rgba, rgb, cv::COLOR_BGRA2BGR);
cv::imshow("rgb", rgb);
cv::waitKey(1);
/*
for (int i = 0; i < 2; i++)
{
int w = 2048;
int h = 256;
cv::cuda::GpuMat device_mat = cv::cuda::GpuMat(h, w, CV_8UC4, (char*)pData + i * 4 * w * h * sizeof(char), 2048*4);
cv::Mat host_rgba;
device_mat.download(host_rgba);
cv::Mat host_rgb;
cv::cvtColor(host_rgba, host_rgb, cv::COLOR_BGRA2BGR);
cv::imshow("patch_" + std::to_string(i), host_rgb);
}
*/
cv::waitKey(1);
cudaFree(pData);
}
void AScreenCaptureActor::OnBackBufferReady_RenderThread(SWindow& SlateWindow, const FTexture2DRHIRef& BackBuffer)
{
if (gameWindow == &SlateWindow)
{
FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList();
FRHITexture2D* CachedTexture = BackBuffer->GetTexture2D();
int32 Width = BackBuffer->GetSizeX();
int32 Height = BackBuffer->GetSizeY();
static HANDLE SharedHandle = nullptr;
if (GDynamicRHI)
{
ID3D12Resource* NativeResource = reinterpret_cast<ID3D12Resource*>(BackBuffer->GetNativeResource());
if (NativeResource)
{
ID3D12Device* m_device = reinterpret_cast<ID3D12Device*>(GDynamicRHI->RHIGetNativeDevice());
if (SharedHandle == nullptr)
{
SECURITY_ATTRIBUTES windowsSecurityAttributes;
LPCWSTR name = nullptr;
HRESULT Result = m_device->CreateSharedHandle(
NativeResource,
&windowsSecurityAttributes,
GENERIC_ALL,
name,
&SharedHandle);
}
D3D12_RESOURCE_DESC Desc = NativeResource->GetDesc();
// Desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
D3D12_RESOURCE_ALLOCATION_INFO allocInfo = m_device->GetResourceAllocationInfo(0, 1, &Desc);
DX12Texture Texture{ SharedHandle, NativeResource, allocInfo.SizeInBytes };
FRHITexture2D* TextureRHI = BackBuffer->GetTexture2D();
getExternalMemory(Texture);
mapMemory(Texture);
}
}
/*
FReadSurfaceDataFlags ReadDataFlags;
ReadDataFlags.SetLinearToGamma(false);
ReadDataFlags.SetOutputStencil(false);
TArray<FColor> PixelData;
RHICmdList.ReadSurfaceData(
BackBuffer,
FIntRect(0, 0, Width, Height),
PixelData,
FReadSurfaceDataFlags(RCM_UNorm)
);
//cv::Mat Image(Height, Width, CV_8UC3);
cv::Mat Image(Height, Width, CV_8UC4, PixelData.GetData());
// Convert from RGBA to BGR (OpenCV uses BGR by default)
cv::cvtColor(Image, Image, cv::COLOR_RGBA2RGB);
// Display the image
cv::imshow("Unreal Engine Screenshot", Image);
cv::waitKey(1);
*/
}
}