Skip to content

Commit a65eb64

Browse files
committed
Make occlusion culling work in UE 4.26.
By using an explicit struct to shuttle the necessary data, rather than trying to copy an engine data structure.
1 parent 40acc2b commit a65eb64

File tree

3 files changed

+88
-37
lines changed

3 files changed

+88
-37
lines changed

CHANGES.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Change Log
22

3+
### ? - ?
4+
5+
##### Fixes :wrench:
6+
7+
- Fixed a bug that caused a crash in Unreal Engine 4.26 when enabling the experimental tileset occlusion culling feature.
8+
39
### v1.16.1 - 2022-08-01
410

511
##### Fixes :wrench:

Source/CesiumRuntime/Private/CesiumViewExtension.cpp

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,20 @@ TileOcclusionState CesiumViewExtension::getPrimitiveOcclusionState(
2323

2424
for (const SceneViewOcclusionResults& viewOcclusionResults :
2525
_currentOcclusionResults.occlusionResultsByView) {
26-
const FPrimitiveOcclusionHistory* pHistory =
27-
viewOcclusionResults.PrimitiveOcclusionHistorySet.Find(
28-
FPrimitiveOcclusionHistoryKey(id, 0));
26+
const PrimitiveOcclusionResult* pOcclusionResult =
27+
viewOcclusionResults.PrimitiveOcclusionResults.Find(id);
2928

30-
if (pHistory && pHistory->LastConsideredTime >= frameTimeCutoff) {
31-
if (!pHistory->OcclusionStateWasDefiniteLastFrame) {
29+
if (pOcclusionResult &&
30+
pOcclusionResult->LastConsideredTime >= frameTimeCutoff) {
31+
if (!pOcclusionResult->OcclusionStateWasDefiniteLastFrame) {
3232
return TileOcclusionState::OcclusionUnavailable;
3333
}
3434

3535
if (previouslyOccluded) {
36-
if (pHistory->LastPixelsPercentage > 0.01f) {
36+
if (pOcclusionResult->LastPixelsPercentage > 0.01f) {
3737
return TileOcclusionState::NotOccluded;
3838
}
39-
} else if (!pHistory->WasOccludedLastFrame) {
39+
} else if (!pOcclusionResult->WasOccludedLastFrame) {
4040
return TileOcclusionState::NotOccluded;
4141
}
4242

@@ -71,9 +71,9 @@ void CesiumViewExtension::BeginRenderViewFamily(
7171
// Recycle the current occlusion results.
7272
for (SceneViewOcclusionResults& occlusionResults :
7373
_currentOcclusionResults.occlusionResultsByView) {
74-
occlusionResults.PrimitiveOcclusionHistorySet.Reset();
75-
_recycledOcclusionHistorySets.Enqueue(
76-
std::move(occlusionResults.PrimitiveOcclusionHistorySet));
74+
occlusionResults.PrimitiveOcclusionResults.Reset();
75+
_recycledOcclusionResultSets.Enqueue(
76+
std::move(occlusionResults.PrimitiveOcclusionResults));
7777
}
7878
_currentOcclusionResults = {};
7979

@@ -121,19 +121,21 @@ void CesiumViewExtension::PostRenderViewFamily_RenderThread(
121121
// Do we actually need the view?
122122
occlusionResults.pView = pView;
123123

124-
if (!_recycledOcclusionHistorySets.IsEmpty()) {
124+
if (!_recycledOcclusionResultSets.IsEmpty()) {
125125
// Recycle a previously allocated occlusion history set, if one is
126126
// available.
127-
occlusionResults.PrimitiveOcclusionHistorySet =
128-
std::move(*_recycledOcclusionHistorySets.Peek());
129-
_recycledOcclusionHistorySets.Pop();
130-
occlusionResults.PrimitiveOcclusionHistorySet.Append(
131-
pViewState->PrimitiveOcclusionHistorySet);
127+
occlusionResults.PrimitiveOcclusionResults =
128+
std::move(*_recycledOcclusionResultSets.Peek());
129+
_recycledOcclusionResultSets.Pop();
132130
} else {
133131
// If no previously-allocated set exists, just allocate a new one. It
134132
// will be recycled later.
135-
occlusionResults.PrimitiveOcclusionHistorySet =
136-
pViewState->PrimitiveOcclusionHistorySet;
133+
}
134+
135+
occlusionResults.PrimitiveOcclusionResults.Reserve(
136+
pViewState->PrimitiveOcclusionHistorySet.Num());
137+
for (const auto& element : pViewState->PrimitiveOcclusionHistorySet) {
138+
occlusionResults.PrimitiveOcclusionResults.Emplace(element);
137139
}
138140

139141
// Unreal will not execute occlusion queries that get frustum culled in a
@@ -147,7 +149,7 @@ void CesiumViewExtension::PostRenderViewFamily_RenderThread(
147149
if (pView->bIsViewInfo && pScene != nullptr) {
148150
const FViewInfo* pViewInfo = static_cast<const FViewInfo*>(pView);
149151
const FSceneBitArray& visibility = pViewInfo->PrimitiveVisibilityMap;
150-
auto& occlusion = occlusionResults.PrimitiveOcclusionHistorySet;
152+
auto& occlusion = occlusionResults.PrimitiveOcclusionResults;
151153

152154
const uint32 PrimitiveCount = pScene->Primitives.Num();
153155
for (uint32 i = 0; i < PrimitiveCount; ++i) {
@@ -159,21 +161,18 @@ void CesiumViewExtension::PostRenderViewFamily_RenderThread(
159161
if (pSceneInfo == nullptr)
160162
continue;
161163

162-
const FPrimitiveOcclusionHistory* pHistory =
163-
occlusion.Find(FPrimitiveOcclusionHistoryKey(
164-
pSceneInfo->PrimitiveComponentId,
165-
0));
166-
if (!pHistory ||
167-
pHistory->LastConsideredTime < pViewState->LastRenderTime) {
164+
const PrimitiveOcclusionResult* pOcclusionResult =
165+
occlusion.Find(pSceneInfo->PrimitiveComponentId);
166+
if (!pOcclusionResult || pOcclusionResult->LastConsideredTime <
167+
pViewState->LastRenderTime) {
168168
// No valid occlusion history for this culled primitive, so create
169169
// it.
170-
FPrimitiveOcclusionHistory historyEntry{};
171-
historyEntry.PrimitiveId = pSceneInfo->PrimitiveComponentId;
172-
historyEntry.LastConsideredTime = pViewState->LastRenderTime;
173-
historyEntry.LastPixelsPercentage = 0.0f;
174-
historyEntry.WasOccludedLastFrame = true;
175-
historyEntry.OcclusionStateWasDefiniteLastFrame = true;
176-
occlusion.Add(std::move(historyEntry));
170+
occlusion.Emplace(PrimitiveOcclusionResult(
171+
pSceneInfo->PrimitiveComponentId,
172+
pViewState->LastRenderTime,
173+
0.0f,
174+
true,
175+
true));
177176
}
178177
}
179178
}

Source/CesiumRuntime/Private/CesiumViewExtension.h

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,56 @@ class ACesium3DTileset;
1616
class CesiumViewExtension : public FSceneViewExtensionBase {
1717
private:
1818
// Occlusion results for a single view.
19+
struct PrimitiveOcclusionResult {
20+
PrimitiveOcclusionResult(
21+
const FPrimitiveComponentId primitiveId,
22+
float lastConsideredTime,
23+
float lastPixelsPercentage,
24+
bool occlusionStateWasDefiniteLastFrame,
25+
bool wasOccludedLastFrame)
26+
: PrimitiveId(primitiveId),
27+
LastConsideredTime(lastConsideredTime),
28+
LastPixelsPercentage(lastPixelsPercentage),
29+
OcclusionStateWasDefiniteLastFrame(
30+
occlusionStateWasDefiniteLastFrame),
31+
WasOccludedLastFrame(wasOccludedLastFrame) {}
32+
33+
PrimitiveOcclusionResult(const FPrimitiveOcclusionHistory& renderer)
34+
: PrimitiveId(renderer.PrimitiveId),
35+
LastConsideredTime(renderer.LastConsideredTime),
36+
LastPixelsPercentage(renderer.LastPixelsPercentage),
37+
OcclusionStateWasDefiniteLastFrame(
38+
renderer.OcclusionStateWasDefiniteLastFrame),
39+
WasOccludedLastFrame(renderer.WasOccludedLastFrame) {}
40+
41+
FPrimitiveComponentId PrimitiveId;
42+
float LastConsideredTime;
43+
float LastPixelsPercentage;
44+
bool OcclusionStateWasDefiniteLastFrame;
45+
bool WasOccludedLastFrame;
46+
};
47+
48+
// Defines how PrimitiveOcclusionResult is stored in a TSet
49+
struct PrimitiveOcclusionResultKeyFuncs
50+
: BaseKeyFuncs<PrimitiveOcclusionResult, FPrimitiveComponentId> {
51+
typedef FPrimitiveComponentId KeyInitType;
52+
53+
static KeyInitType GetSetKey(const PrimitiveOcclusionResult& Element) {
54+
return Element.PrimitiveId;
55+
}
56+
57+
static bool Matches(KeyInitType A, KeyInitType B) { return A == B; }
58+
59+
static uint32 GetKeyHash(KeyInitType Key) {
60+
return GetTypeHash(Key.PrimIDValue);
61+
}
62+
};
63+
64+
// The occlusion results for a single view.
1965
struct SceneViewOcclusionResults {
2066
const FSceneView* pView = nullptr;
21-
TSet<FPrimitiveOcclusionHistory, FPrimitiveOcclusionHistoryKeyFuncs>
22-
PrimitiveOcclusionHistorySet{};
67+
TSet<PrimitiveOcclusionResult, PrimitiveOcclusionResultKeyFuncs>
68+
PrimitiveOcclusionResults{};
2369
};
2470

2571
// A collection of occlusion results by view.
@@ -35,13 +81,13 @@ class CesiumViewExtension : public FSceneViewExtensionBase {
3581
// thread.
3682
TQueue<AggregatedOcclusionUpdate, EQueueMode::Spsc> _occlusionResultsQueue;
3783

38-
// A queue to recycle the previously-allocated occlusion history sets. The
84+
// A queue to recycle the previously-allocated occlusion result sets. The
3985
// game thread recycles the sets by moving them into the queue and sending
4086
// them back to the render thread.
4187
TQueue<
42-
TSet<FPrimitiveOcclusionHistory, FPrimitiveOcclusionHistoryKeyFuncs>,
88+
TSet<PrimitiveOcclusionResult, PrimitiveOcclusionResultKeyFuncs>,
4389
EQueueMode::Spsc>
44-
_recycledOcclusionHistorySets;
90+
_recycledOcclusionResultSets;
4591

4692
// The last known frame number. This is used to determine when an occlusion
4793
// results aggregation is complete.

0 commit comments

Comments
 (0)