Skip to content

Commit 2a4ab6d

Browse files
committed
Fix random crash on viewport change, potential crash even if the viewport size is not changed.
1 parent 91cc640 commit 2a4ab6d

File tree

4 files changed

+107
-7
lines changed

4 files changed

+107
-7
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Changelog
2+
3+
## 1.0.2
4+
5+
### Fixes
6+
7+
- Random crash on viewport change, potential crash even if the viewport size is not changed.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include "HAL/CriticalSection.h"
2+
#include "Misc/ScopeLock.h"
3+
#include "Memory.h"
4+
5+
template <typename T>
6+
class BH_DoubleAsyncBuffer
7+
{
8+
T* CurrentBuffer;
9+
T* BackBuffer;
10+
int32 CurrentSize;
11+
int32 BackBufferSize;
12+
13+
FCriticalSection CriticalSection;
14+
15+
public:
16+
BH_DoubleAsyncBuffer()
17+
: CurrentSize(0), BackBufferSize(0)
18+
{
19+
CurrentBuffer = new T[0];
20+
BackBuffer = new T[0];
21+
}
22+
23+
~BH_DoubleAsyncBuffer()
24+
{
25+
delete[] CurrentBuffer;
26+
delete[] BackBuffer;
27+
}
28+
29+
void MemCopyFrom(const T* Data, int32 Size)
30+
{
31+
FScopeLock Lock(&CriticalSection);
32+
if (Size > BackBufferSize)
33+
{
34+
delete[] BackBuffer;
35+
BackBufferSize = Size;
36+
BackBuffer = new T[BackBufferSize];
37+
}
38+
FMemory::Memcpy(BackBuffer, Data, Size);
39+
}
40+
41+
void SwapBuffers()
42+
{
43+
FScopeLock Lock(&CriticalSection);
44+
T* TempBuffer = CurrentBuffer;
45+
CurrentBuffer = BackBuffer;
46+
BackBuffer = TempBuffer;
47+
48+
int32 TempSize = CurrentSize;
49+
CurrentSize = BackBufferSize;
50+
BackBufferSize = TempSize;
51+
}
52+
53+
const T* GetCurrentBuffer() const
54+
{
55+
return CurrentBuffer;
56+
}
57+
58+
int32 GetCurrentSize() const
59+
{
60+
return CurrentSize;
61+
}
62+
};

Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ bool ConvertRAWSurfaceDataToFLinearColor(EPixelFormat Format, uint32 Width, uint
2020
#endif
2121

2222
UBH_GameRecorder::UBH_GameRecorder(const FObjectInitializer& ObjectInitializer)
23-
: Super(ObjectInitializer), bIsRecording(false), bCopyTextureStarted(false), StagingTexture(nullptr), RawDataWidth(0), RawDataHeight(0)
23+
: Super(ObjectInitializer), bIsRecording(false), bCopyTextureStarted(false), StagingTexture(nullptr), BackTextureWidth(0), BackTextureHeight(0)
2424
{
2525
FrameBuffer = ObjectInitializer.CreateDefaultSubobject<UBH_FrameBuffer>(this, TEXT("FrameBuffer"));
2626
}
@@ -162,6 +162,17 @@ void UBH_GameRecorder::Tick(float DeltaTime)
162162
{
163163
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this]()
164164
{
165+
FScopeLock Lock(&AsyncTextureProcessingLock);
166+
167+
BackTextureLock.Lock();
168+
TextureBuffer.SwapBuffers(); // swap back with current
169+
170+
const uint8 *RawData = reinterpret_cast<const uint8*>(TextureBuffer.GetCurrentBuffer());
171+
int32 RawDataWidth = BackTextureWidth;
172+
int32 RawDataHeight = BackTextureHeight;
173+
174+
BackTextureLock.Unlock();
175+
165176
// Make sure that PendingPixels is of the correct size
166177
int32 NumPixels = RawDataWidth * RawDataHeight;
167178

@@ -175,7 +186,8 @@ void UBH_GameRecorder::Tick(float DeltaTime)
175186

176187

177188
// Convert raw surface data to linear color
178-
ConvertRAWSurfaceDataToFLinearColor(StagingTextureFormat, RawDataWidth, RawDataHeight, reinterpret_cast<uint8*>(RawData), Pitch, PendingLinearPixels.GetData(), FReadSurfaceDataFlags({}));
189+
ConvertRAWSurfaceDataToFLinearColor(StagingTextureFormat, RawDataWidth, RawDataHeight, const_cast<uint8*>(RawData),
190+
Pitch, PendingLinearPixels.GetData(), FReadSurfaceDataFlags({}));
179191

180192
// Convert linear color to FColor
181193
for (int32 i = 0; i < NumPixels; ++i)
@@ -323,7 +335,22 @@ void UBH_GameRecorder::ReadPixels()
323335
FRHICopyTextureInfo CopyInfo;
324336
RHICmdList.CopyTexture(GEngine->GameViewport->Viewport->GetRenderTargetTexture(), StagingTexture, CopyInfo);
325337

326-
RHICmdList.MapStagingSurface(StagingTexture, this->RawData, this->RawDataWidth, this->RawDataHeight);
338+
void* RawDataTemp = nullptr;
339+
int32 RawDataWidthTemp = 0;
340+
int32 RawDataHeightTemp = 0;
341+
342+
RHICmdList.MapStagingSurface(StagingTexture, RawDataTemp, RawDataWidthTemp, RawDataHeightTemp);
343+
344+
// copy the memory, because RawDataTemp is temporary
345+
BackTextureLock.Lock();
346+
347+
TextureBuffer.MemCopyFrom(
348+
reinterpret_cast<uint8*>(RawDataTemp),
349+
RawDataWidthTemp * RawDataHeightTemp * GPixelFormats[StagingTextureFormat].BlockBytes);
350+
BackTextureWidth = RawDataWidthTemp;
351+
BackTextureHeight = RawDataHeightTemp;
352+
353+
BackTextureLock.Unlock();
327354

328355
RHICmdList.UnmapStagingSurface(StagingTexture);
329356
}

Source/BetaHubBugReporter/Private/BH_GameRecorder.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#pragma once
22

33
#include "CoreMinimal.h"
4+
#include "Tickable.h"
5+
#include "HAL/CriticalSection.h"
46
#include "BH_VideoEncoder.h"
57
#include "BH_FrameBuffer.h"
6-
#include "Tickable.h"
78
#include "UObject/NoExportTypes.h"
89
#include "BH_SceneCaptureActor.h"
10+
#include "BH_DoubleAsyncBuffer.h"
911
#include "BH_GameRecorder.generated.h"
1012

1113
UCLASS()
@@ -64,9 +66,11 @@ class BETAHUBBUGREPORTER_API UBH_GameRecorder : public UObject, public FTickable
6466
int32 FrameHeight;
6567
FDateTime LastCaptureTime;
6668

67-
void *RawData;
68-
int32 RawDataWidth;
69-
int32 RawDataHeight;
69+
BH_DoubleAsyncBuffer<uint8> TextureBuffer;
70+
int32 BackTextureWidth;
71+
int32 BackTextureHeight;
72+
FCriticalSection BackTextureLock;
73+
FCriticalSection AsyncTextureProcessingLock;
7074

7175
void CaptureFrame();
7276
void CaptureRenderTargetFrame();

0 commit comments

Comments
 (0)