UE4 Capture Current Viewport
1、自定义类捕获当前窗口视图数据
1 #pragma once 2 3 // Copyright 2021 sev, All Rights Reserved. 4 5 #pragma once 6 7 8 ////////////////////////////////////////////////////////////////////////// 9 // class capture viewport 10 ////////////////////////////////////////////////////////////////////////// 11 DECLARE_MULTICAST_DELEGATE_OneParam(FOnViewportToPresent, uint8*); 12 DECLARE_DELEGATE(FOnViewportToBuffer); 13 14 class FCaptureViewport : public FRHICustomPresent 15 { 16 public: 17 FCaptureViewport(FRHIViewport* InViewport) 18 : m_ViewportRHI(nullptr) 19 , m_IsInitialized(false) 20 { 21 m_SurfaceDataWhole.Empty(); 22 m_SurfaceDataStereo.Empty(); 23 } 24 25 bool InitViewport(); 26 void UpdateViewport(); 27 bool FinishRendering(); 28 29 FRHIViewport* GetViewportRHI(); 30 FOnViewportToPresent OnViewportTOPresentEvent; 31 FOnViewportToBuffer OnViewportToBufferEvent; 32 public: 33 // Called when viewport is resized. 34 virtual void OnBackBufferResize(); 35 36 // Called from render thread to see if a native present will be requested for this frame. 37 // @return true if native Present will be requested for this frame; false otherwise. Must 38 // match value subsequently returned by Present for this frame. 39 virtual bool NeedsNativePresent(); 40 41 // Called from RHI thread to perform custom present. 42 // @param InOutSyncInterval - in out param, indicates if vsync is on (>0) or off (==0). 43 // @return true if native Present should be also be performed; false otherwise. If it returns 44 // true, then InOutSyncInterval could be modified to switch between VSync/NoVSync for the normal 45 // Present. Must match value previously returned by NeedsNormalPresent for this frame. 46 virtual bool Present(int32& InOutSyncInterval); 47 48 public: 49 mutable FCriticalSection m_Sync; 50 51 TArray<FColor> m_SurfaceDataWhole; 52 TArray<FColor> m_SurfaceDataStereo; 53 54 FRHIViewport* m_ViewportRHI; 55 FTexture2DRHIRef m_SceneColor; 56 uint8* m_OutputData; 57 58 int32 m_ViewportX; 59 int32 m_ViewportY; 60 uint32 m_LolStride; 61 62 bool m_IsInitialized; 63 bool m_IsSaveImage; 64 bool m_IsStopPreset; 65 };
.cpp
1 // Copyright 2021 sev, All Rights Reserved. 2 3 #include "CaptureViewport.h" 4 5 #include "Engine/GameEngine.h" 6 #include "RHI\Public\DynamicRHI.h" 7 #include "Slate\SceneViewport.h" 8 #include "IImageWrapper.h" 9 #include "IImageWrapperModule.h" 10 #include "Misc\FileHelper.h" 11 12 bool gIsOutImage = false; 13 EImmediateFlushType::Type gFlushType = EImmediateFlushType::WaitForOutstandingTasksOnly; 14 15 FSceneViewport* FindCurViewport() 16 { 17 FSceneViewport* SceneViewport = nullptr; 18 if (!GIsEditor) 19 { 20 UGameEngine* GameEngine = Cast<UGameEngine>(GEngine); 21 SceneViewport = GameEngine->SceneViewport.Get(); 22 } 23 else 24 { 25 if (GEngine->GameViewport) 26 SceneViewport = GEngine->GameViewport->GetGameViewport(); 27 } 28 29 return SceneViewport; 30 } 31 32 void ReadBackbuffer(const FViewportRHIRef ViewportRHI, FIntPoint OutRect, TArray<FColor>* OutPixels, FTexture2DRHIRef& Tex2DRef) 33 { 34 if (!ViewportRHI) 35 return; 36 37 FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList(); 38 39 //SCOPED_DRAW_EVENT(RHICmdList, HXReadBackbuffer); 40 41 { 42 FTexture2DRHIRef pSceneColor; 43 if (!GIsEditor) 44 { 45 //FViewportRHIRef ViewportRHI = pVP->GetViewportRHI(); 46 pSceneColor = RHICmdList.GetViewportBackBuffer(ViewportRHI); 47 } 48 else { 49 FSceneViewport* pVP = FindCurViewport(); 50 if (pVP) 51 pSceneColor = pVP->GetRenderTargetTexture(); 52 } 53 54 if (!IsValidRef(pSceneColor)) 55 { 56 return; 57 } 58 59 Tex2DRef = pSceneColor; 60 61 /*RHICmdList.ReadSurfaceData( 62 pSceneColor, 63 FIntRect(0, 0, OutRect.X, OutRect.Y), 64 *OutPixels, 65 FReadSurfaceDataFlags());*/ 66 67 QUICK_SCOPE_CYCLE_COUNTER(HXReadBackBuffer); 68 RHICmdList.ImmediateFlush(gFlushType); 69 GDynamicRHI->RHIReadSurfaceData(pSceneColor, FIntRect(0, 0, OutRect.X, OutRect.Y), *OutPixels, FReadSurfaceDataFlags()); 70 71 } 72 } 73 74 75 void PrintLog(FString FunName, double LastTime) 76 { 77 if (true) 78 return; 79 80 double CurrentTime = FPlatformTime::Seconds(); 81 CurrentTime -= LastTime; 82 83 FString OutFormat = FString::Printf(TEXT("%s * * "), *FunName); 84 OutFormat.Append(FString::SanitizeFloat(CurrentTime * 1000.0f)); 85 86 UE_LOG(LogTemp, Warning, TEXT("%s"), *OutFormat); 87 } 88 89 90 ////////////////////////////////////////////////////////////////////////// 91 // 92 ////////////////////////////////////////////////////////////////////////// 93 void FCaptureViewport::OnBackBufferResize() 94 { 95 96 } 97 98 bool FCaptureViewport::NeedsNativePresent() 99 { 100 return true; 101 } 102 103 bool FCaptureViewport::Present(int32& InOutSyncInterval) 104 { 105 FString RHIName = GDynamicRHI->GetName(); 106 if (RHIName == TEXT("D3D11")) 107 { 108 check(IsInRenderingThread()); 109 } 110 111 bool bNeedNativePresent = true; 112 { 113 FScopeLock lock(&m_Sync); 114 UpdateViewport(); 115 } 116 117 //FinishRendering(); 118 return bNeedNativePresent; 119 } 120 121 bool FCaptureViewport::InitViewport() 122 { 123 check(IsInGameThread()); 124 if (m_IsInitialized) 125 return true; 126 127 m_ViewportRHI = GetViewportRHI(); 128 if (m_ViewportRHI == nullptr) 129 return false; 130 131 if (m_ViewportX <= 0 || m_ViewportY <= 0) 132 return false; 133 134 auto oldCustomPresent = m_ViewportRHI->GetCustomPresent(); 135 if (oldCustomPresent != this) 136 { 137 m_ViewportRHI->SetCustomPresent(this); 138 } 139 140 m_IsInitialized = true; 141 m_IsStopPreset = true; 142 m_OutputData = nullptr; 143 return m_IsInitialized; 144 } 145 146 bool FCaptureViewport::FinishRendering() 147 { 148 /*if (m_SurfaceDataWhole.Num() >= m_ViewportX*m_ViewportY) 149 return true; 150 151 return false;*/ 152 153 return true; 154 } 155 156 void FCaptureViewport::UpdateViewport() 157 { 158 if (!m_IsStopPreset) 159 return; 160 161 FSceneViewport* pVP = FindCurViewport(); 162 if (pVP == nullptr) 163 return; 164 165 if (m_SurfaceDataStereo.Num() > 0) 166 m_SurfaceDataStereo.Empty(); 167 168 m_ViewportX = pVP->GetSizeXY().X; 169 m_ViewportY = pVP->GetSizeXY().Y; 170 171 double DelatTime = FPlatformTime::Seconds(); 172 173 FIntPoint OutPoint(m_ViewportX, m_ViewportY); 174 ReadBackbuffer(m_ViewportRHI, OutPoint, &m_SurfaceDataWhole, m_SceneColor); 175 OnViewportToBufferEvent.ExecuteIfBound(); 176 177 // 178 #ifdef RHIVIEWPORT 179 FRHICommandListImmediate& RHICmdList = GRHICommandList.GetImmediateCommandList(); 180 181 /*uint8* TextureData = (uint8*)GDynamicRHI->RHILockTexture2D(m_SceneColor->GetTexture2D(), 0, EResourceLockMode::RLM_ReadOnly, m_LolStride, false); 182 OnViewportTOPresentEvent.Broadcast(TextureData); 183 GDynamicRHI->RHIUnlockTexture2D(m_SceneColor->GetTexture2D(), 0, false);*/ 184 185 uint8* TextureData = (uint8*)RHICmdList.LockTexture2D(m_SceneColor->GetTexture2D(), 0, EResourceLockMode::RLM_ReadOnly, m_LolStride, false); 186 187 int32 SizeY = m_SceneColor->GetSizeY(); 188 int32 SizeX = m_SceneColor->GetSizeX(); 189 OnViewportTOPresentEvent.Broadcast(TextureData); 190 191 RHICmdList.UnlockTexture2D(m_SceneColor->GetTexture2D(), 0, false); 192 #endif 193 // test take-time 194 PrintLog(TEXT("ReadBackBuffer"), DelatTime); 195 if (gIsOutImage && m_SurfaceDataWhole.Num() > 0) 196 { 197 DelatTime = FPlatformTime::Seconds(); 198 //OnGetFrameData((const char*)m_SurfaceDataWhole.GetData(), m_ViewportX, m_ViewportY, m_ViewportX * sizeof(FColor)); 199 PrintLog(TEXT("OnGetFrameData"), DelatTime); 200 201 } 202 203 /*m_SurfaceDataStereo.AddUninitialized(m_SurfaceDataWhole.Num() * 2); 204 FMemory::Memcpy(m_SurfaceDataStereo.GetData(), m_SurfaceDataWhole.GetData(), sizeof(FColor)*m_SurfaceDataWhole.Num()); 205 FMemory::Memcpy(&m_SurfaceDataStereo[m_SurfaceDataWhole.Num()], m_SurfaceDataWhole.GetData(), sizeof(FColor)*m_SurfaceDataWhole.Num()); 206 207 if (gIsOutImage && m_SurfaceDataStereo.Num() > 0) 208 { 209 OnGetFrameData((const char*)m_SurfaceDataStereo.GetData(), m_ViewportX, m_ViewportY*2, m_ViewportX * sizeof(FColor)); 210 }*/ 211 212 // save data to image 213 /*if (!m_IsSaveImage) 214 return; 215 216 TArray<FColor> SurfaceData; 217 218 SurfaceData.AddUninitialized(m_SurfaceDataWhole.Num()); 219 220 FMemory::Memcpy(SurfaceData.GetData(), m_SurfaceDataWhole.GetData(), sizeof(FColor)*m_SurfaceDataWhole.Num()); 221 222 for (FColor& Color : SurfaceData) 223 { 224 Color.A = 255; 225 } 226 227 IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper")); 228 TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG); 229 ImageWrapper->SetRaw(SurfaceData.GetData(), SurfaceData.GetAllocatedSize(), m_ViewportX, m_ViewportY, ERGBFormat::BGRA, 8); 230 const TArray64<uint8>& PNGData = ImageWrapper->GetCompressed(100); 231 FString FileName = TEXT("D:\\CubeCapture.png");//FString::Printf(TEXT("D:\\SteroCap_%s.png"), *CaptureCounter); 232 233 FFileHelper::SaveArrayToFile(PNGData, *FileName); 234 235 ImageWrapper.Reset();*/ 236 } 237 238 239 240 FRHIViewport* FCaptureViewport::GetViewportRHI() 241 { 242 FRHIViewport* ViewportRHI = nullptr; 243 FSceneViewport* Viewport = FindCurViewport(); 244 245 if (!Viewport) 246 return nullptr; 247 248 //m_ViewportX = Viewport->GetSizeXY().X; 249 //m_ViewportY = Viewport->GetSizeXY().Y; 250 251 FSlateRenderer* Renderer = FSlateApplication::Get().GetRenderer(); 252 TSharedPtr<SWindow> Window; 253 if (!GIsEditor) 254 { 255 UGameEngine* GameEngine = Cast<UGameEngine>(GEngine); 256 Window = FSlateApplication::Get().FindWidgetWindow(GameEngine->GameViewportWidget.ToSharedRef()); 257 } 258 else 259 { 260 Window = FSlateApplication::Get().FindWidgetWindow(Viewport->GetViewportWidget().Pin().ToSharedRef()); 261 } 262 263 // If the window is not valid then we are likely in a loading movie and the viewport is not attached to the window. 264 // We'll have to wait until safe 265 if (Window.IsValid()) 266 { 267 void* ViewportResource = Renderer->GetViewportResource(*Window); 268 if (ViewportResource) 269 { 270 ViewportRHI = *((FViewportRHIRef*)ViewportResource); 271 //ViewportRHI = Viewport->GetViewportRHI().GetReference(); 272 } 273 } 274 275 return ViewportRHI; 276 }
可在暴露给蓝图的类中调用:
1 { 2 if (m_CaptureViewport == nullptr) 3 { 4 m_CaptureViewport = new FCaptureViewport(nullptr); 5 m_CaptureViewport->InitViewport(); 6 m_CaptureViewport->m_IsSaveImage = false; 7 8 } 9 10 11 m_CaptureViewport->OnViewportToBufferEvent.BindStatic(&URenderCineCameraLibrary::UpdateViewport_RenderThread); 12 } 13 14 // 15 void URenderCineCameraLibrary::UpdateViewport_RenderThread() 16 { 17 // do something with m_CaptureViewport->m_SurfaceDataWhole 18 }