Skip to content

Commit

Permalink
Make occlusion culling work in UE 4.26.
Browse files Browse the repository at this point in the history
By using an explicit struct to shuttle the necessary data, rather than
trying to copy an engine data structure.
  • Loading branch information
kring committed Aug 4, 2022
1 parent 40acc2b commit a65eb64
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 37 deletions.
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Change Log

### ? - ?

##### Fixes :wrench:

- Fixed a bug that caused a crash in Unreal Engine 4.26 when enabling the experimental tileset occlusion culling feature.

### v1.16.1 - 2022-08-01

##### Fixes :wrench:
Expand Down
63 changes: 31 additions & 32 deletions Source/CesiumRuntime/Private/CesiumViewExtension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ TileOcclusionState CesiumViewExtension::getPrimitiveOcclusionState(

for (const SceneViewOcclusionResults& viewOcclusionResults :
_currentOcclusionResults.occlusionResultsByView) {
const FPrimitiveOcclusionHistory* pHistory =
viewOcclusionResults.PrimitiveOcclusionHistorySet.Find(
FPrimitiveOcclusionHistoryKey(id, 0));
const PrimitiveOcclusionResult* pOcclusionResult =
viewOcclusionResults.PrimitiveOcclusionResults.Find(id);

if (pHistory && pHistory->LastConsideredTime >= frameTimeCutoff) {
if (!pHistory->OcclusionStateWasDefiniteLastFrame) {
if (pOcclusionResult &&
pOcclusionResult->LastConsideredTime >= frameTimeCutoff) {
if (!pOcclusionResult->OcclusionStateWasDefiniteLastFrame) {
return TileOcclusionState::OcclusionUnavailable;
}

if (previouslyOccluded) {
if (pHistory->LastPixelsPercentage > 0.01f) {
if (pOcclusionResult->LastPixelsPercentage > 0.01f) {
return TileOcclusionState::NotOccluded;
}
} else if (!pHistory->WasOccludedLastFrame) {
} else if (!pOcclusionResult->WasOccludedLastFrame) {
return TileOcclusionState::NotOccluded;
}

Expand Down Expand Up @@ -71,9 +71,9 @@ void CesiumViewExtension::BeginRenderViewFamily(
// Recycle the current occlusion results.
for (SceneViewOcclusionResults& occlusionResults :
_currentOcclusionResults.occlusionResultsByView) {
occlusionResults.PrimitiveOcclusionHistorySet.Reset();
_recycledOcclusionHistorySets.Enqueue(
std::move(occlusionResults.PrimitiveOcclusionHistorySet));
occlusionResults.PrimitiveOcclusionResults.Reset();
_recycledOcclusionResultSets.Enqueue(
std::move(occlusionResults.PrimitiveOcclusionResults));
}
_currentOcclusionResults = {};

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

if (!_recycledOcclusionHistorySets.IsEmpty()) {
if (!_recycledOcclusionResultSets.IsEmpty()) {
// Recycle a previously allocated occlusion history set, if one is
// available.
occlusionResults.PrimitiveOcclusionHistorySet =
std::move(*_recycledOcclusionHistorySets.Peek());
_recycledOcclusionHistorySets.Pop();
occlusionResults.PrimitiveOcclusionHistorySet.Append(
pViewState->PrimitiveOcclusionHistorySet);
occlusionResults.PrimitiveOcclusionResults =
std::move(*_recycledOcclusionResultSets.Peek());
_recycledOcclusionResultSets.Pop();
} else {
// If no previously-allocated set exists, just allocate a new one. It
// will be recycled later.
occlusionResults.PrimitiveOcclusionHistorySet =
pViewState->PrimitiveOcclusionHistorySet;
}

occlusionResults.PrimitiveOcclusionResults.Reserve(
pViewState->PrimitiveOcclusionHistorySet.Num());
for (const auto& element : pViewState->PrimitiveOcclusionHistorySet) {
occlusionResults.PrimitiveOcclusionResults.Emplace(element);
}

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

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

const FPrimitiveOcclusionHistory* pHistory =
occlusion.Find(FPrimitiveOcclusionHistoryKey(
pSceneInfo->PrimitiveComponentId,
0));
if (!pHistory ||
pHistory->LastConsideredTime < pViewState->LastRenderTime) {
const PrimitiveOcclusionResult* pOcclusionResult =
occlusion.Find(pSceneInfo->PrimitiveComponentId);
if (!pOcclusionResult || pOcclusionResult->LastConsideredTime <
pViewState->LastRenderTime) {
// No valid occlusion history for this culled primitive, so create
// it.
FPrimitiveOcclusionHistory historyEntry{};
historyEntry.PrimitiveId = pSceneInfo->PrimitiveComponentId;
historyEntry.LastConsideredTime = pViewState->LastRenderTime;
historyEntry.LastPixelsPercentage = 0.0f;
historyEntry.WasOccludedLastFrame = true;
historyEntry.OcclusionStateWasDefiniteLastFrame = true;
occlusion.Add(std::move(historyEntry));
occlusion.Emplace(PrimitiveOcclusionResult(
pSceneInfo->PrimitiveComponentId,
pViewState->LastRenderTime,
0.0f,
true,
true));
}
}
}
Expand Down
56 changes: 51 additions & 5 deletions Source/CesiumRuntime/Private/CesiumViewExtension.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,56 @@ class ACesium3DTileset;
class CesiumViewExtension : public FSceneViewExtensionBase {
private:
// Occlusion results for a single view.
struct PrimitiveOcclusionResult {
PrimitiveOcclusionResult(
const FPrimitiveComponentId primitiveId,
float lastConsideredTime,
float lastPixelsPercentage,
bool occlusionStateWasDefiniteLastFrame,
bool wasOccludedLastFrame)
: PrimitiveId(primitiveId),
LastConsideredTime(lastConsideredTime),
LastPixelsPercentage(lastPixelsPercentage),
OcclusionStateWasDefiniteLastFrame(
occlusionStateWasDefiniteLastFrame),
WasOccludedLastFrame(wasOccludedLastFrame) {}

PrimitiveOcclusionResult(const FPrimitiveOcclusionHistory& renderer)
: PrimitiveId(renderer.PrimitiveId),
LastConsideredTime(renderer.LastConsideredTime),
LastPixelsPercentage(renderer.LastPixelsPercentage),
OcclusionStateWasDefiniteLastFrame(
renderer.OcclusionStateWasDefiniteLastFrame),
WasOccludedLastFrame(renderer.WasOccludedLastFrame) {}

FPrimitiveComponentId PrimitiveId;
float LastConsideredTime;
float LastPixelsPercentage;
bool OcclusionStateWasDefiniteLastFrame;
bool WasOccludedLastFrame;
};

// Defines how PrimitiveOcclusionResult is stored in a TSet
struct PrimitiveOcclusionResultKeyFuncs
: BaseKeyFuncs<PrimitiveOcclusionResult, FPrimitiveComponentId> {
typedef FPrimitiveComponentId KeyInitType;

static KeyInitType GetSetKey(const PrimitiveOcclusionResult& Element) {
return Element.PrimitiveId;
}

static bool Matches(KeyInitType A, KeyInitType B) { return A == B; }

static uint32 GetKeyHash(KeyInitType Key) {
return GetTypeHash(Key.PrimIDValue);
}
};

// The occlusion results for a single view.
struct SceneViewOcclusionResults {
const FSceneView* pView = nullptr;
TSet<FPrimitiveOcclusionHistory, FPrimitiveOcclusionHistoryKeyFuncs>
PrimitiveOcclusionHistorySet{};
TSet<PrimitiveOcclusionResult, PrimitiveOcclusionResultKeyFuncs>
PrimitiveOcclusionResults{};
};

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

// A queue to recycle the previously-allocated occlusion history sets. The
// A queue to recycle the previously-allocated occlusion result sets. The
// game thread recycles the sets by moving them into the queue and sending
// them back to the render thread.
TQueue<
TSet<FPrimitiveOcclusionHistory, FPrimitiveOcclusionHistoryKeyFuncs>,
TSet<PrimitiveOcclusionResult, PrimitiveOcclusionResultKeyFuncs>,
EQueueMode::Spsc>
_recycledOcclusionHistorySets;
_recycledOcclusionResultSets;

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

0 comments on commit a65eb64

Please sign in to comment.