Hello! I'm implementing some features and i need to copy backbuffer to rendertarget (and next resize/blur it, but it doesn't matter, i just need a copy of backbuffer). Of course I'm looking for a solution to do that with GPU/GPU Ram only (without extra coping texture data to RAM and the usage of CPU or blocking game thread). I've found event that is fired from rendering pipeline and succesfully subscribed that:
It seems that I'm able to get the access to backbuffer here and copy it to created render target with copy texture command, but that doesn't work (back buffer contains data that i need, but i cannot copy that to my rendertarget):
CachedFrameBuffer is blank. If it makes sense I create CachedFrameBuffer from editor and tried different resolutions (including back buffer resolution), formats and etc. Moreover, I tried to create it manually and this doesn't affect too.
After investigation of engine's sources I find more progressive way to do that:
The approach is quite obvious: bind render target and render back buffer texture to it. But it doesn't work, my render target still is blank.
Maybe I'm missing something during operating with RHI texture objects instead of Unreal's UTextureRenderTarget2D*?
Also I tried to wrap RHI commands with ENQUEUE_RENDER_COMMAND macros,(btw should i do this for code that is run in rendering thread in BackBufferReady callback? I think not, anyway that doesn't help, render target still is blank).
So, please help me or share the info where I should dig in engine to achieve that I need.
Thank you!
Code:
FDelegateHandle OnBackBufferReadyToPresent = FSlateApplication::Get().GetRenderer()->OnBackBufferReadyToPresent().AddUObject(this, &UCachedFrameBufferComponent::OnBackBufferReady_RenderThread);
Code:
void UCachedFrameBufferComponent::OnBackBufferReady_RenderThread(SWindow& SlateWindow, const FTexture2DRHIRef& BackBuffer) { ensure(IsInRenderingThread()); FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList(); FRHITexture2D* CachedTexture = CachedFrameBuffer->Resource->TextureRHI->GetTexture2D(); FRHICopyTextureInfo CopyInfo; RHICmdList.CopyTexture(CachedTexture, BackBuffer, CopyInfo); }
Code:
UTextureRenderTarget2D* CachedFrameBuffer = UKismetRenderingLibrary::CreateRenderTarget2D(GetOwner()->GetWorld(), ViewportSize.X * ScreenPercentage, ViewportSize.Y * ScreenPercentage, ETextureRenderTargetFormat::RTF_RGBA8);
Code:
void UCachedFrameBufferComponent::CopyBackBuffer(const FTexture2DRHIRef& BackBuffer, const FTexture2DRHIRef& CachedFrame) { IRendererModule* RendererModule = &FModuleManager::GetModuleChecked<IRendererModule>("Renderer"); FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList(); if (BackBuffer->GetFormat() == CachedFrame->GetFormat() && BackBuffer->GetSizeXY() == CachedFrame->GetSizeXY()) { RHICmdList.CopyToResolveTarget(BackBuffer, CachedFrame, FResolveParams{}); } else // Texture format mismatch, use a shader to do the copy. { // #todo-renderpasses there's no explicit resolve here? Do we need one? FRHIRenderPassInfo RPInfo(CachedFrame, ERenderTargetActions::Load_Store); RHICmdList.BeginRenderPass(RPInfo, TEXT("CopyBackbuffer")); { RHICmdList.SetViewport(0, 0, 0.0f, CachedFrame->GetSizeX(), CachedFrame->GetSizeY(), 1.0f); FGraphicsPipelineStateInitializer GraphicsPSOInit; RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI(); GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI(); GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI(); TShaderMap<FGlobalShaderType>* ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel); TShaderMapRef<FScreenVS> VertexShader(ShaderMap); TShaderMapRef<FScreenPS> PixelShader(ShaderMap); GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI; GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader); GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader); GraphicsPSOInit.PrimitiveType = PT_TriangleList; SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit); if (CachedFrame->GetSizeX() != BackBuffer->GetSizeX() || CachedFrame->GetSizeY() != BackBuffer->GetSizeY()) { PixelShader->SetParameters(RHICmdList, TStaticSamplerState<SF_Bilinear>::GetRHI(), BackBuffer); } else { PixelShader->SetParameters(RHICmdList, TStaticSamplerState<SF_Point>::GetRHI(), BackBuffer); } RendererModule->DrawRectangle( RHICmdList, 0, 0, // Dest X, Y CachedFrame->GetSizeX(), // Dest Width CachedFrame->GetSizeY(), // Dest Height 0, 0, // Source U, V 1, 1, // Source USize, VSize CachedFrame->GetSizeXY(), // Target buffer size FIntPoint(1, 1), // Source texture size *VertexShader, EDRF_Default); } RHICmdList.EndRenderPass(); } }
Maybe I'm missing something during operating with RHI texture objects instead of Unreal's UTextureRenderTarget2D*?
Code:
FRHITexture2D* CachedTexture = CachedFrameBuffer->Resource->TextureRHI->GetTexture2D();
So, please help me or share the info where I should dig in engine to achieve that I need.
Thank you!
Comment