Skip to content

Commit d42a85d

Browse files
committed
Fix crashes on shipping builds
1 parent ca747c8 commit d42a85d

File tree

6 files changed

+240
-97
lines changed

6 files changed

+240
-97
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Fixes
66

77
- Compilation errors on shipping builds
8+
- Crashes on shipping builds
89

910
## 1.0.2
1011

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#pragma once
2+
3+
#include "CoreMinimal.h"
4+
#include "Misc/ScopeLock.h"
5+
#include "HAL/CriticalSection.h"
6+
#include "Memory.h"
7+
8+
template <typename T>
9+
class BH_AsyncPool
10+
{
11+
template <typename T>
12+
struct FBH_AsyncElement
13+
{
14+
bool bInUse;
15+
T* Pointer;
16+
17+
FBH_AsyncElement(T* InPointer)
18+
: bInUse(false)
19+
, Pointer(InPointer)
20+
{
21+
}
22+
23+
bool IsInUse() const
24+
{
25+
return bInUse;
26+
}
27+
28+
void SetInUse(bool InUse)
29+
{
30+
bInUse = InUse;
31+
}
32+
33+
T* GetPointer()
34+
{
35+
return Pointer;
36+
}
37+
};
38+
39+
TArray<FBH_AsyncElement<T>> Pool;
40+
FCriticalSection CriticalSection;
41+
42+
public:
43+
44+
BH_AsyncPool(int32 Size = 4)
45+
{
46+
for (int32 i = 0; i < Size; i++)
47+
{
48+
Pool.Add(FBH_AsyncElement<T>(new T()));
49+
}
50+
}
51+
52+
~BH_AsyncPool()
53+
{
54+
for (FBH_AsyncElement<T> Element : Pool)
55+
{
56+
delete Element.GetPointer();
57+
}
58+
}
59+
60+
T* GetElement()
61+
{
62+
FScopeLock Lock(&CriticalSection);
63+
for (FBH_AsyncElement<T> &Element : Pool)
64+
{
65+
if (!Element.IsInUse())
66+
{
67+
Element.SetInUse(true);
68+
return Element.GetPointer();
69+
}
70+
}
71+
return nullptr;
72+
}
73+
74+
void ReleaseElement(T* Pointer)
75+
{
76+
FScopeLock Lock(&CriticalSection);
77+
for (FBH_AsyncElement<T> &Element : Pool)
78+
{
79+
if (Element.GetPointer() == Pointer)
80+
{
81+
Element.SetInUse(false);
82+
return;
83+
}
84+
}
85+
}
86+
};
87+
88+
template <typename T>
89+
class BH_AsyncQueue
90+
{
91+
TArray<T*> Queue;
92+
FCriticalSection CriticalSection;
93+
94+
public:
95+
96+
BH_AsyncQueue()
97+
{
98+
}
99+
100+
~BH_AsyncQueue()
101+
{
102+
for (T* Element : Queue)
103+
{
104+
delete Element;
105+
}
106+
}
107+
108+
void Enqueue(T* Element)
109+
{
110+
FScopeLock Lock(&CriticalSection);
111+
Queue.Add(Element);
112+
}
113+
114+
T* Dequeue()
115+
{
116+
FScopeLock Lock(&CriticalSection);
117+
if (Queue.Num() > 0)
118+
{
119+
T* Element = Queue[0];
120+
Queue.RemoveAt(0);
121+
return Element;
122+
}
123+
return nullptr;
124+
}
125+
};

Source/BetaHubBugReporter/Private/BH_DoubleAsyncBuffer.h

Lines changed: 0 additions & 62 deletions
This file was deleted.

Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ 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), BackTextureWidth(0), BackTextureHeight(0)
23+
: Super(ObjectInitializer)
24+
, bIsRecording(false)
25+
, bCopyTextureStarted(false)
26+
, StagingTexture(nullptr)
27+
, RawFrameBufferPool(3)
2428
{
2529
FrameBuffer = ObjectInitializer.CreateDefaultSubobject<UBH_FrameBuffer>(this, TEXT("FrameBuffer"));
2630
}
@@ -162,32 +166,33 @@ void UBH_GameRecorder::Tick(float DeltaTime)
162166
{
163167
AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this]()
164168
{
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;
169+
BH_RawFrameBuffer<uint8>* TextureBuffer = RawFrameBufferQueue.Dequeue();
170+
if (!TextureBuffer)
171+
{
172+
// no texture buffer in the queue, nothing to process
173+
return;
174+
}
173175

174-
BackTextureLock.Unlock();
175-
176176
// Make sure that PendingPixels is of the correct size
177-
int32 NumPixels = RawDataWidth * RawDataHeight;
177+
int32 NumPixels = TextureBuffer->GetWidth() * TextureBuffer->GetHeight();
178178

179179
if (PendingLinearPixels.Num() != NumPixels)
180180
{
181181
PendingLinearPixels.SetNumUninitialized(NumPixels);
182182
PendingPixels.SetNumUninitialized(NumPixels);
183183
}
184184

185-
uint32 Pitch = GPixelFormats[StagingTextureFormat].BlockBytes * RawDataWidth;
185+
uint32 Pitch = GPixelFormats[StagingTextureFormat].BlockBytes * TextureBuffer->GetWidth();
186186

187187

188188
// Convert raw surface data to linear color
189-
ConvertRAWSurfaceDataToFLinearColor(StagingTextureFormat, RawDataWidth, RawDataHeight, const_cast<uint8*>(RawData),
190-
Pitch, PendingLinearPixels.GetData(), FReadSurfaceDataFlags({}));
189+
ConvertRAWSurfaceDataToFLinearColor(
190+
StagingTextureFormat,
191+
TextureBuffer->GetWidth(), TextureBuffer->GetHeight(),
192+
TextureBuffer->GetData(),
193+
Pitch,
194+
PendingLinearPixels.GetData(),
195+
FReadSurfaceDataFlags({}));
191196

192197
// Convert linear color to FColor
193198
for (int32 i = 0; i < NumPixels; ++i)
@@ -196,7 +201,7 @@ void UBH_GameRecorder::Tick(float DeltaTime)
196201
}
197202

198203
// Resize image to frame
199-
ResizeImageToFrame(PendingPixels, RawDataWidth, RawDataHeight, FrameWidth, FrameHeight, ResizedPixels);
204+
ResizeImageToFrame(PendingPixels, TextureBuffer->GetWidth(), TextureBuffer->GetHeight(), FrameWidth, FrameHeight, ResizedPixels);
200205

201206
// Set frame data on the game thread
202207
AsyncTask(ENamedThreads::GameThread, [this]()
@@ -206,6 +211,7 @@ void UBH_GameRecorder::Tick(float DeltaTime)
206211

207212
// only when I complete this, allow another read pixels
208213
bCopyTextureStarted = false;
214+
RawFrameBufferPool.ReleaseElement(TextureBuffer);
209215
});
210216
}
211217
}
@@ -332,25 +338,31 @@ void UBH_GameRecorder::ReadPixels()
332338
FViewport* Viewport = GEngine->GameViewport->Viewport;
333339
if (!Viewport) return;
334340

341+
BH_RawFrameBuffer<uint8>* TextureBuffer = RawFrameBufferPool.GetElement();
342+
if (!TextureBuffer)
343+
{
344+
// no texture buffer available at this time
345+
// UE_LOG(LogTemp, Error, TEXT("No texture buffer available."));
346+
return;
347+
}
348+
335349
FRHICopyTextureInfo CopyInfo;
336350
RHICmdList.CopyTexture(GEngine->GameViewport->Viewport->GetRenderTargetTexture(), StagingTexture, CopyInfo);
337351

338-
void* RawDataTemp = nullptr;
339-
int32 RawDataWidthTemp = 0;
340-
int32 RawDataHeightTemp = 0;
341-
342-
RHICmdList.MapStagingSurface(StagingTexture, RawDataTemp, RawDataWidthTemp, RawDataHeightTemp);
352+
void* RawData = nullptr;
353+
int32 RawDataWidth = 0;
354+
int32 RawDataHeight = 0;
343355

344-
// copy the memory, because RawDataTemp is temporary
345-
BackTextureLock.Lock();
356+
RHICmdList.MapStagingSurface(StagingTexture, RawData, RawDataWidth, RawDataHeight);
346357

347-
TextureBuffer.MemCopyFrom(
348-
reinterpret_cast<uint8*>(RawDataTemp),
349-
RawDataWidthTemp * RawDataHeightTemp * GPixelFormats[StagingTextureFormat].BlockBytes);
350-
BackTextureWidth = RawDataWidthTemp;
351-
BackTextureHeight = RawDataHeightTemp;
358+
TextureBuffer->CopyFrom(
359+
reinterpret_cast<uint8*>(RawData),
360+
RawDataWidth,
361+
RawDataHeight,
362+
GPixelFormats[StagingTextureFormat].BlockBytes);
352363

353-
BackTextureLock.Unlock();
364+
// async queue for processing
365+
RawFrameBufferQueue.Enqueue(TextureBuffer);
354366

355367
RHICmdList.UnmapStagingSurface(StagingTexture);
356368
}

Source/BetaHubBugReporter/Private/BH_GameRecorder.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
#include "BH_FrameBuffer.h"
88
#include "UObject/NoExportTypes.h"
99
#include "BH_SceneCaptureActor.h"
10-
#include "BH_DoubleAsyncBuffer.h"
10+
#include "BH_Async.h"
11+
#include "BH_RawFrameBuffer.h"
1112
#include "BH_GameRecorder.generated.h"
1213

1314
UCLASS()
@@ -66,11 +67,8 @@ class BETAHUBBUGREPORTER_API UBH_GameRecorder : public UObject, public FTickable
6667
int32 FrameHeight;
6768
FDateTime LastCaptureTime;
6869

69-
BH_DoubleAsyncBuffer<uint8> TextureBuffer;
70-
int32 BackTextureWidth;
71-
int32 BackTextureHeight;
72-
FCriticalSection BackTextureLock;
73-
FCriticalSection AsyncTextureProcessingLock;
70+
BH_AsyncQueue<BH_RawFrameBuffer<uint8>> RawFrameBufferQueue;
71+
BH_AsyncPool<BH_RawFrameBuffer<uint8>> RawFrameBufferPool;
7472

7573
void CaptureFrame();
7674
void CaptureRenderTargetFrame();

0 commit comments

Comments
 (0)