Added missing files.

This commit is contained in:
Justin Marshall
2026-05-21 15:37:31 -07:00
parent a89d43628c
commit fec456fdd9
3 changed files with 761 additions and 0 deletions
+220
View File
@@ -0,0 +1,220 @@
/*
===========================================================================
Radiant masked occlusion culling bridge
===========================================================================
*/
#include "gl_radiant_moc.h"
#define USE_D3D 0
#define USE_AVX512 0
#include "thirdparty/MaskedOcclusionCulling/MaskedOcclusionCulling.h"
#include <algorithm>
namespace {
struct radiantMOCState_t {
MaskedOcclusionCulling* moc;
unsigned int width;
unsigned int height;
bool active;
qglRadiantMOCStats_t stats;
};
radiantMOCState_t s_radiantMOC = {};
static unsigned int RadiantMOC_RoundUp(unsigned int value, unsigned int alignment) {
return (value + alignment - 1) & ~(alignment - 1);
}
static radiantMOCState_t* RadiantMOC_FromContext(qglMOCContext_t context) {
return reinterpret_cast<radiantMOCState_t*>(context);
}
} // namespace
qglMOCContext_t QGL_MOC_Create() {
radiantMOCState_t* context = new radiantMOCState_t;
context->moc = NULL;
context->width = 0;
context->height = 0;
context->active = false;
context->stats.occluderTriangles = 0;
context->stats.testedRects = 0;
context->stats.occludedRects = 0;
return context;
}
void QGL_MOC_Destroy(qglMOCContext_t context) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (state == NULL) {
return;
}
if (state->moc != NULL) {
MaskedOcclusionCulling::Destroy(state->moc);
state->moc = NULL;
}
delete state;
}
bool QGL_MOC_BeginFrame(qglMOCContext_t context, int width, int height, float nearClip) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (state == NULL) {
return false;
}
const unsigned int mocWidth = RadiantMOC_RoundUp(std::max(width, 64), 8);
const unsigned int mocHeight = RadiantMOC_RoundUp(std::max(height, 64), 4);
if (state->moc == NULL) {
state->moc = MaskedOcclusionCulling::Create(MaskedOcclusionCulling::AVX2);
}
if (state->moc == NULL) {
state->active = false;
return false;
}
if (state->width != mocWidth || state->height != mocHeight) {
state->moc->SetResolution(mocWidth, mocHeight);
state->width = mocWidth;
state->height = mocHeight;
}
state->moc->SetNearClipPlane(nearClip);
state->moc->ClearBuffer();
state->active = true;
state->stats.occluderTriangles = 0;
state->stats.testedRects = 0;
state->stats.occludedRects = 0;
return true;
}
void QGL_MOC_EndFrame(qglMOCContext_t context) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (state != NULL) {
state->active = false;
}
}
bool QGL_MOC_IsActive(qglMOCContext_t context) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
return state != NULL && state->active && state->moc != NULL;
}
void QGL_MOC_RenderTriangles(qglMOCContext_t context, const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (!QGL_MOC_IsActive(context) || verts == NULL || indices == NULL || numTriangles <= 0) {
return;
}
MaskedOcclusionCulling::VertexLayout layout(sizeof(qglRadiantMOCClipVertex_t), 4, 8);
state->moc->RenderTriangles(&verts[0].x, indices, numTriangles, NULL, MaskedOcclusionCulling::BACKFACE_NONE, MaskedOcclusionCulling::CLIP_PLANE_ALL, layout);
state->stats.occluderTriangles += numTriangles;
}
bool QGL_MOC_TestTriangles(qglMOCContext_t context, const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (!QGL_MOC_IsActive(context) || verts == NULL || indices == NULL || numTriangles <= 0) {
return true;
}
MaskedOcclusionCulling::VertexLayout layout(sizeof(qglRadiantMOCClipVertex_t), 4, 8);
state->stats.testedRects++;
const MaskedOcclusionCulling::CullingResult result = state->moc->TestTriangles(&verts[0].x, indices, numTriangles, NULL, MaskedOcclusionCulling::BACKFACE_NONE, MaskedOcclusionCulling::CLIP_PLANE_ALL, layout);
if (result == MaskedOcclusionCulling::OCCLUDED || result == MaskedOcclusionCulling::VIEW_CULLED) {
state->stats.occludedRects++;
return false;
}
return true;
}
void QGL_MOC_RenderIndexedTriangles(qglMOCContext_t context, const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (!QGL_MOC_IsActive(context) || verts == NULL || indices == NULL || numTriangles <= 0 || modelToClipMatrix == NULL) {
return;
}
MaskedOcclusionCulling::VertexLayout layout(vertexStride, yOffset, zOffset);
state->moc->RenderTriangles(verts, indices, numTriangles, modelToClipMatrix, MaskedOcclusionCulling::BACKFACE_NONE, MaskedOcclusionCulling::CLIP_PLANE_ALL, layout);
state->stats.occluderTriangles += numTriangles;
}
bool QGL_MOC_TestIndexedTriangles(qglMOCContext_t context, const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (!QGL_MOC_IsActive(context) || verts == NULL || indices == NULL || numTriangles <= 0 || modelToClipMatrix == NULL) {
return true;
}
MaskedOcclusionCulling::VertexLayout layout(vertexStride, yOffset, zOffset);
state->stats.testedRects++;
const MaskedOcclusionCulling::CullingResult result = state->moc->TestTriangles(verts, indices, numTriangles, modelToClipMatrix, MaskedOcclusionCulling::BACKFACE_NONE, MaskedOcclusionCulling::CLIP_PLANE_ALL, layout);
if (result == MaskedOcclusionCulling::OCCLUDED || result == MaskedOcclusionCulling::VIEW_CULLED) {
state->stats.occludedRects++;
return false;
}
return true;
}
bool QGL_MOC_TestRect(qglMOCContext_t context, float xmin, float ymin, float xmax, float ymax, float wmin) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (!QGL_MOC_IsActive(context)) {
return true;
}
state->stats.testedRects++;
const MaskedOcclusionCulling::CullingResult result = state->moc->TestRect(xmin, ymin, xmax, ymax, wmin);
if (result == MaskedOcclusionCulling::OCCLUDED || result == MaskedOcclusionCulling::VIEW_CULLED) {
state->stats.occludedRects++;
return false;
}
return true;
}
qglRadiantMOCStats_t QGL_MOC_GetStats(qglMOCContext_t context) {
radiantMOCState_t* state = RadiantMOC_FromContext(context);
if (state == NULL) {
qglRadiantMOCStats_t emptyStats = {};
return emptyStats;
}
return state->stats;
}
bool QGL_RadiantMOC_BeginFrame(int width, int height, float nearClip) {
return QGL_MOC_BeginFrame(&s_radiantMOC, width, height, nearClip);
}
void QGL_RadiantMOC_EndFrame() {
QGL_MOC_EndFrame(&s_radiantMOC);
}
bool QGL_RadiantMOC_IsActive() {
return QGL_MOC_IsActive(&s_radiantMOC);
}
void QGL_RadiantMOC_RenderTriangles(const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles) {
QGL_MOC_RenderTriangles(&s_radiantMOC, verts, indices, numTriangles);
}
bool QGL_RadiantMOC_TestTriangles(const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles) {
return QGL_MOC_TestTriangles(&s_radiantMOC, verts, indices, numTriangles);
}
void QGL_RadiantMOC_RenderIndexedTriangles(const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset) {
QGL_MOC_RenderIndexedTriangles(&s_radiantMOC, verts, indices, numTriangles, modelToClipMatrix, vertexStride, yOffset, zOffset);
}
bool QGL_RadiantMOC_TestIndexedTriangles(const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset) {
return QGL_MOC_TestIndexedTriangles(&s_radiantMOC, verts, indices, numTriangles, modelToClipMatrix, vertexStride, yOffset, zOffset);
}
bool QGL_RadiantMOC_TestRect(float xmin, float ymin, float xmax, float ymax, float wmin) {
return QGL_MOC_TestRect(&s_radiantMOC, xmin, ymin, xmax, ymax, wmin);
}
qglRadiantMOCStats_t QGL_RadiantMOC_GetStats() {
return s_radiantMOC.stats;
}
+45
View File
@@ -0,0 +1,45 @@
/*
===========================================================================
Radiant masked occlusion culling bridge
===========================================================================
*/
#pragma once
struct qglRadiantMOCClipVertex_t {
float x;
float y;
float w;
};
struct qglRadiantMOCStats_t {
int occluderTriangles;
int testedRects;
int occludedRects;
};
typedef void* qglMOCContext_t;
qglMOCContext_t QGL_MOC_Create();
void QGL_MOC_Destroy(qglMOCContext_t context);
bool QGL_MOC_BeginFrame(qglMOCContext_t context, int width, int height, float nearClip);
void QGL_MOC_EndFrame(qglMOCContext_t context);
bool QGL_MOC_IsActive(qglMOCContext_t context);
void QGL_MOC_RenderTriangles(qglMOCContext_t context, const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles);
bool QGL_MOC_TestTriangles(qglMOCContext_t context, const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles);
void QGL_MOC_RenderIndexedTriangles(qglMOCContext_t context, const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset);
bool QGL_MOC_TestIndexedTriangles(qglMOCContext_t context, const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset);
bool QGL_MOC_TestRect(qglMOCContext_t context, float xmin, float ymin, float xmax, float ymax, float wmin);
qglRadiantMOCStats_t QGL_MOC_GetStats(qglMOCContext_t context);
bool QGL_RadiantMOC_BeginFrame(int width, int height, float nearClip);
void QGL_RadiantMOC_EndFrame();
bool QGL_RadiantMOC_IsActive();
void QGL_RadiantMOC_RenderTriangles(const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles);
bool QGL_RadiantMOC_TestTriangles(const qglRadiantMOCClipVertex_t* verts, const unsigned int* indices, int numTriangles);
void QGL_RadiantMOC_RenderIndexedTriangles(const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset);
bool QGL_RadiantMOC_TestIndexedTriangles(const float* verts, const unsigned int* indices, int numTriangles, const float* modelToClipMatrix, int vertexStride, int yOffset, int zOffset);
bool QGL_RadiantMOC_TestRect(float xmin, float ymin, float xmax, float ymax, float wmin);
qglRadiantMOCStats_t QGL_RadiantMOC_GetStats();
+496
View File
@@ -0,0 +1,496 @@
/*
===========================================================================
Renderer masked occlusion culling for one-frame-late light rejection.
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "tr_local.h"
#include "../opengl/gl_radiant_moc.h"
#include <float.h>
#include <windows.h>
#include <vector>
struct rendererMOCLightQuery_t {
int lightIndex;
int lastModifiedFrameNum;
const srfTriangles_t* tri;
int numTriangles;
float modelToClip[16];
float xmin;
float ymin;
float xmax;
float ymax;
float wmin;
};
struct rendererMOCIndexedMesh_t {
const srfTriangles_t* tri;
int numTriangles;
float modelToClip[16];
};
struct rendererMOCJob_t {
int sequence;
idRenderWorldLocal* world;
int lightCount;
int width;
int height;
float nearClip;
std::vector<rendererMOCIndexedMesh_t> occluders;
std::vector<rendererMOCLightQuery_t> lightQueries;
};
struct rendererMOCResult_t {
bool valid;
bool consumed;
int sequence;
idRenderWorldLocal* world;
std::vector<unsigned char> lightVisible;
std::vector<int> lightModifiedFrames;
int occluderTriangles;
int testedLights;
int occludedLights;
};
static CRITICAL_SECTION s_rendererMOCCriticalSection;
static HANDLE s_rendererMOCWorkEvent = NULL;
static HANDLE s_rendererMOCThread = NULL;
static bool s_rendererMOCInitialized = false;
static bool s_rendererMOCShutdown = false;
static bool s_rendererMOCHasPendingJob = false;
static bool s_rendererMOCWorkerBusy = false;
static bool s_rendererMOCHasCompletedResult = false;
static int s_rendererMOCSequence = 0;
static rendererMOCJob_t s_rendererMOCPendingJob;
static rendererMOCResult_t s_rendererMOCCompletedResult;
static rendererMOCResult_t s_rendererMOCLastResult;
static float R_MOC_ClampFloat(float value, float minValue, float maxValue) {
if (value < minValue) {
return minValue;
}
if (value > maxValue) {
return maxValue;
}
return value;
}
static bool R_MOC_BuildModelToClipMatrix(const viewDef_t* viewDef, const float* modelMatrix, float matrix[16]) {
const float tanX = idMath::Tan16(DEG2RAD(viewDef->renderView.fov_x * 0.5f));
const float tanY = idMath::Tan16(DEG2RAD(viewDef->renderView.fov_y * 0.5f));
if (tanX <= 0.0f || tanY <= 0.0f) {
return false;
}
for (int column = 0; column < 3; column++) {
idVec3 worldAxis;
if (modelMatrix != NULL) {
worldAxis.Set(modelMatrix[column * 4 + 0], modelMatrix[column * 4 + 1], modelMatrix[column * 4 + 2]);
} else {
worldAxis.Zero();
worldAxis[column] = 1.0f;
}
matrix[column * 4 + 0] = (worldAxis * viewDef->renderView.viewaxis[1]) / tanX;
matrix[column * 4 + 1] = (worldAxis * viewDef->renderView.viewaxis[2]) / tanY;
matrix[column * 4 + 2] = 0.0f;
matrix[column * 4 + 3] = worldAxis * viewDef->renderView.viewaxis[0];
}
idVec3 worldOrigin;
if (modelMatrix != NULL) {
worldOrigin.Set(modelMatrix[12], modelMatrix[13], modelMatrix[14]);
} else {
worldOrigin.Zero();
}
const idVec3 local = worldOrigin - viewDef->renderView.vieworg;
matrix[12] = (local * viewDef->renderView.viewaxis[1]) / tanX;
matrix[13] = (local * viewDef->renderView.viewaxis[2]) / tanY;
matrix[14] = 0.0f;
matrix[15] = local * viewDef->renderView.viewaxis[0];
return true;
}
static bool R_MOC_MaterialCanOcclude(const idMaterial* shader) {
if (shader == NULL || !shader->IsDrawn()) {
return false;
}
if (shader->Coverage() != MC_OPAQUE) {
return false;
}
if (shader->HasSubview() || shader->IsSky()) {
return false;
}
if (shader->GetContentFlags() & CONTENTS_AREAPORTAL) {
return false;
}
return true;
}
static void R_MOC_AddOccluderSurface(const viewDef_t* viewDef, const viewEntity_t* vEntity, const modelSurface_t* surface, rendererMOCJob_t& job) {
if (surface == NULL || surface->geometry == NULL || !R_MOC_MaterialCanOcclude(surface->shader)) {
return;
}
const srfTriangles_t* tri = surface->geometry;
if (tri->numIndexes < 3 || tri->numVerts <= 0 || tri->indexes == NULL || tri->verts == NULL) {
return;
}
if (sizeof(tri->indexes[0]) != sizeof(unsigned int)) {
return;
}
rendererMOCIndexedMesh_t mesh;
mesh.tri = tri;
mesh.numTriangles = tri->numIndexes / 3;
if (!R_MOC_BuildModelToClipMatrix(viewDef, vEntity->modelMatrix, mesh.modelToClip)) {
return;
}
job.occluders.push_back(mesh);
}
static void R_MOC_AddOccludersFromView(const viewDef_t* viewDef, rendererMOCJob_t& job) {
for (const viewEntity_t* vEntity = viewDef->viewEntitys; vEntity; vEntity = vEntity->next) {
const idRenderEntityLocal* entity = vEntity->entityDef;
if (entity == NULL || entity->parms.hModel == NULL) {
continue;
}
if (vEntity->scissorRect.IsEmpty()) {
continue;
}
if (entity->parms.hModel->IsDynamicModel() != DM_STATIC) {
continue;
}
const int numSurfaces = entity->parms.hModel->NumSurfaces();
for (int surfaceNum = 0; surfaceNum < numSurfaces; surfaceNum++) {
R_MOC_AddOccluderSurface(viewDef, vEntity, entity->parms.hModel->Surface(surfaceNum), job);
}
}
}
static bool R_MOC_BuildLightQuery(const viewDef_t* viewDef, const idRenderLightLocal* light, rendererMOCLightQuery_t& query) {
if (light == NULL || light->frustumTris == NULL || light->frustumTris->numVerts <= 0) {
return false;
}
const srfTriangles_t* tri = light->frustumTris;
if (tri->numIndexes < 3 || tri->indexes == NULL) {
return false;
}
if (sizeof(tri->indexes[0]) != sizeof(unsigned int)) {
return false;
}
const int numTriangles = tri->numIndexes / 3;
if (numTriangles <= 0) {
return false;
}
query.lightIndex = light->index;
query.lastModifiedFrameNum = light->lastModifiedFrameNum;
query.tri = tri;
query.numTriangles = numTriangles;
query.xmin = -1.0f;
query.ymin = -1.0f;
query.xmax = 1.0f;
query.ymax = 1.0f;
query.wmin = r_znear.GetFloat();
if (!R_MOC_BuildModelToClipMatrix(viewDef, NULL, query.modelToClip)) {
return false;
}
return true;
}
static void R_MOC_AddLightQueriesFromView(const viewDef_t* viewDef, rendererMOCJob_t& job) {
for (const viewLight_t* vLight = viewDef->viewLights; vLight; vLight = vLight->next) {
if (vLight->viewInsideLight) {
continue;
}
rendererMOCLightQuery_t query;
if (R_MOC_BuildLightQuery(viewDef, vLight->lightDef, query)) {
job.lightQueries.push_back(query);
}
}
}
static bool R_MOC_BuildJob(const viewDef_t* viewDef, rendererMOCJob_t& job) {
if (viewDef == NULL || viewDef->renderWorld == NULL) {
return false;
}
if (viewDef->isSubview || viewDef->isMirror || viewDef->isXraySubview) {
return false;
}
if (viewDef->renderView.width <= 0 || viewDef->renderView.height <= 0) {
return false;
}
idRenderWorldLocal* world = static_cast<idRenderWorldLocal*>(viewDef->renderWorld);
const float scale = R_MOC_ClampFloat(r_mocResolutionScale.GetFloat(), 0.125f, 1.0f);
job.sequence = 0;
job.world = world;
job.lightCount = world->lightDefs.Num();
job.width = Max(64, idMath::Ftoi(viewDef->renderView.width * scale));
job.height = Max(64, idMath::Ftoi(viewDef->renderView.height * scale));
job.nearClip = r_znear.GetFloat() > 0.0f ? r_znear.GetFloat() : 1.0f;
job.occluders.clear();
job.lightQueries.clear();
job.occluders.reserve(4096);
job.lightQueries.reserve(256);
R_MOC_AddOccludersFromView(viewDef, job);
R_MOC_AddLightQueriesFromView(viewDef, job);
return job.lightCount > 0 && !job.lightQueries.empty();
}
static void R_MOC_RunJob(const rendererMOCJob_t& job, rendererMOCResult_t& result, qglMOCContext_t context) {
result.valid = true;
result.consumed = false;
result.sequence = job.sequence;
result.world = job.world;
result.lightVisible.assign(job.lightCount, 1);
result.lightModifiedFrames.assign(job.lightCount, -1);
result.occluderTriangles = 0;
result.testedLights = static_cast<int>(job.lightQueries.size());
result.occludedLights = 0;
if (!QGL_MOC_BeginFrame(context, job.width, job.height, job.nearClip)) {
return;
}
for (size_t i = 0; i < job.occluders.size(); i++) {
const rendererMOCIndexedMesh_t& mesh = job.occluders[i];
if (mesh.tri == NULL || mesh.tri->verts == NULL || mesh.tri->indexes == NULL || mesh.numTriangles <= 0) {
continue;
}
QGL_MOC_RenderIndexedTriangles(context,
mesh.tri->verts[0].xyz.ToFloatPtr(),
reinterpret_cast<const unsigned int*>(mesh.tri->indexes),
mesh.numTriangles,
mesh.modelToClip,
sizeof(mesh.tri->verts[0]),
4,
8);
result.occluderTriangles += mesh.numTriangles;
}
for (size_t i = 0; i < job.lightQueries.size(); i++) {
const rendererMOCLightQuery_t& query = job.lightQueries[i];
if (query.lightIndex < 0 || query.lightIndex >= static_cast<int>(result.lightVisible.size())) {
continue;
}
bool visible = true;
if (query.tri != NULL && query.tri->verts != NULL && query.tri->indexes != NULL && query.numTriangles > 0) {
visible = QGL_MOC_TestIndexedTriangles(context,
query.tri->verts[0].xyz.ToFloatPtr(),
reinterpret_cast<const unsigned int*>(query.tri->indexes),
query.numTriangles,
query.modelToClip,
sizeof(query.tri->verts[0]),
4,
8);
} else if (query.xmin < query.xmax && query.ymin < query.ymax) {
visible = QGL_MOC_TestRect(context, query.xmin, query.ymin, query.xmax, query.ymax, query.wmin);
}
result.lightVisible[query.lightIndex] = visible ? 1 : 0;
result.lightModifiedFrames[query.lightIndex] = query.lastModifiedFrameNum;
if (!visible) {
result.occludedLights++;
}
}
QGL_MOC_EndFrame(context);
}
static DWORD WINAPI R_MOC_ThreadProc(LPVOID) {
qglMOCContext_t context = QGL_MOC_Create();
for (;;) {
WaitForSingleObject(s_rendererMOCWorkEvent, INFINITE);
rendererMOCJob_t job;
EnterCriticalSection(&s_rendererMOCCriticalSection);
if (s_rendererMOCShutdown) {
LeaveCriticalSection(&s_rendererMOCCriticalSection);
break;
}
if (!s_rendererMOCHasPendingJob) {
LeaveCriticalSection(&s_rendererMOCCriticalSection);
continue;
}
job = s_rendererMOCPendingJob;
s_rendererMOCPendingJob = rendererMOCJob_t();
s_rendererMOCHasPendingJob = false;
s_rendererMOCWorkerBusy = true;
LeaveCriticalSection(&s_rendererMOCCriticalSection);
rendererMOCResult_t result;
R_MOC_RunJob(job, result, context);
EnterCriticalSection(&s_rendererMOCCriticalSection);
s_rendererMOCCompletedResult = result;
s_rendererMOCHasCompletedResult = true;
s_rendererMOCWorkerBusy = false;
LeaveCriticalSection(&s_rendererMOCCriticalSection);
}
QGL_MOC_Destroy(context);
return 0;
}
static void R_MOC_Init() {
if (s_rendererMOCInitialized) {
return;
}
InitializeCriticalSection(&s_rendererMOCCriticalSection);
s_rendererMOCWorkEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (s_rendererMOCWorkEvent == NULL) {
DeleteCriticalSection(&s_rendererMOCCriticalSection);
return;
}
s_rendererMOCShutdown = false;
s_rendererMOCThread = CreateThread(NULL, 0, R_MOC_ThreadProc, NULL, 0, NULL);
if (s_rendererMOCThread == NULL) {
CloseHandle(s_rendererMOCWorkEvent);
s_rendererMOCWorkEvent = NULL;
DeleteCriticalSection(&s_rendererMOCCriticalSection);
return;
}
s_rendererMOCInitialized = true;
}
static void R_MOC_HarvestCompletedResult() {
if (!s_rendererMOCInitialized) {
return;
}
EnterCriticalSection(&s_rendererMOCCriticalSection);
if (s_rendererMOCHasCompletedResult) {
s_rendererMOCLastResult = s_rendererMOCCompletedResult;
s_rendererMOCCompletedResult = rendererMOCResult_t();
s_rendererMOCHasCompletedResult = false;
}
LeaveCriticalSection(&s_rendererMOCCriticalSection);
}
static void R_MOC_TrySubmitJob(rendererMOCJob_t job) {
if (!s_rendererMOCInitialized || s_rendererMOCThread == NULL || s_rendererMOCWorkEvent == NULL) {
return;
}
EnterCriticalSection(&s_rendererMOCCriticalSection);
if (!s_rendererMOCHasPendingJob && !s_rendererMOCWorkerBusy) {
job.sequence = ++s_rendererMOCSequence;
s_rendererMOCPendingJob = job;
s_rendererMOCHasPendingJob = true;
SetEvent(s_rendererMOCWorkEvent);
}
LeaveCriticalSection(&s_rendererMOCCriticalSection);
}
static void R_MOC_PruneViewLights(viewDef_t* viewDef) {
if (!s_rendererMOCLastResult.valid || s_rendererMOCLastResult.consumed || s_rendererMOCLastResult.world != viewDef->renderWorld) {
return;
}
viewLight_t** link = &viewDef->viewLights;
while (*link != NULL) {
viewLight_t* vLight = *link;
idRenderLightLocal* light = vLight->lightDef;
const int lightIndex = light ? light->index : -1;
if (lightIndex >= 0 &&
lightIndex < static_cast<int>(s_rendererMOCLastResult.lightVisible.size()) &&
lightIndex < static_cast<int>(s_rendererMOCLastResult.lightModifiedFrames.size()) &&
s_rendererMOCLastResult.lightModifiedFrames[lightIndex] == light->lastModifiedFrameNum &&
s_rendererMOCLastResult.lightVisible[lightIndex] == 0) {
*link = vLight->next;
light->viewCount = -1;
continue;
}
link = &(*link)->next;
}
s_rendererMOCLastResult.consumed = true;
}
bool R_MaskedOcclusionCulling_LightIsVisible(const viewDef_t* viewDef, const idRenderLightLocal* light) {
if (!r_useMaskedOcclusionCulling.GetBool() || viewDef == NULL || light == NULL) {
return true;
}
if (!s_rendererMOCLastResult.valid || s_rendererMOCLastResult.world != viewDef->renderWorld) {
return true;
}
const int lightIndex = light->index;
if (lightIndex < 0 ||
lightIndex >= static_cast<int>(s_rendererMOCLastResult.lightVisible.size()) ||
lightIndex >= static_cast<int>(s_rendererMOCLastResult.lightModifiedFrames.size())) {
return true;
}
if (s_rendererMOCLastResult.lightModifiedFrames[lightIndex] != light->lastModifiedFrameNum) {
return true;
}
return s_rendererMOCLastResult.lightVisible[lightIndex] != 0;
}
void R_MaskedOcclusionCulling_ProcessView(viewDef_t* viewDef) {
if (!r_useMaskedOcclusionCulling.GetBool()) {
return;
}
R_MOC_Init();
R_MOC_HarvestCompletedResult();
rendererMOCJob_t job;
if (R_MOC_BuildJob(viewDef, job)) {
R_MOC_TrySubmitJob(job);
}
R_MOC_PruneViewLights(viewDef);
}
void R_MaskedOcclusionCulling_Shutdown() {
if (!s_rendererMOCInitialized) {
return;
}
EnterCriticalSection(&s_rendererMOCCriticalSection);
s_rendererMOCShutdown = true;
SetEvent(s_rendererMOCWorkEvent);
LeaveCriticalSection(&s_rendererMOCCriticalSection);
if (s_rendererMOCThread != NULL) {
WaitForSingleObject(s_rendererMOCThread, INFINITE);
CloseHandle(s_rendererMOCThread);
s_rendererMOCThread = NULL;
}
if (s_rendererMOCWorkEvent != NULL) {
CloseHandle(s_rendererMOCWorkEvent);
s_rendererMOCWorkEvent = NULL;
}
DeleteCriticalSection(&s_rendererMOCCriticalSection);
s_rendererMOCInitialized = false;
s_rendererMOCPendingJob = rendererMOCJob_t();
s_rendererMOCCompletedResult = rendererMOCResult_t();
s_rendererMOCLastResult = rendererMOCResult_t();
s_rendererMOCHasPendingJob = false;
s_rendererMOCHasCompletedResult = false;
s_rendererMOCWorkerBusy = false;
}