diff --git a/neo/doom3/game/gamesys/GameTypeInfo.h b/neo/doom3/game/gamesys/GameTypeInfo.h
index 3713c514..cba585e5 100644
--- a/neo/doom3/game/gamesys/GameTypeInfo.h
+++ b/neo/doom3/game/gamesys/GameTypeInfo.h
@@ -4303,6 +4303,8 @@ static classVariableInfo_t srfTriangles_t_typeInfo[] = {
{ "vertCache_s *", "ambientCache", (intptr_t)(&((srfTriangles_t *)0)->ambientCache), sizeof( ((srfTriangles_t *)0)->ambientCache ) },
{ "vertCache_s *", "lightingCache", (intptr_t)(&((srfTriangles_t *)0)->lightingCache), sizeof( ((srfTriangles_t *)0)->lightingCache ) },
{ "vertCache_s *", "shadowCache", (intptr_t)(&((srfTriangles_t *)0)->shadowCache), sizeof( ((srfTriangles_t *)0)->shadowCache ) },
+ { "unsigned int", "ambientVbo", (intptr_t)(&((srfTriangles_t *)0)->ambientVbo), sizeof( ((srfTriangles_t *)0)->ambientVbo ) },
+ { "unsigned int", "indexVbo", (intptr_t)(&((srfTriangles_t *)0)->indexVbo), sizeof( ((srfTriangles_t *)0)->indexVbo ) },
{ "bool", "isSkeletal", (intptr_t)(&((srfTriangles_t *)0)->isSkeletal), sizeof( ((srfTriangles_t *)0)->isSkeletal ) },
{ "int", "numAllocedVerts", (intptr_t)(&((srfTriangles_t *)0)->numAllocedVerts), sizeof( ((srfTriangles_t *)0)->numAllocedVerts ) },
{ "int", "numAllocedIndices", (intptr_t)(&((srfTriangles_t *)0)->numAllocedIndices), sizeof( ((srfTriangles_t *)0)->numAllocedIndices ) },
diff --git a/neo/engine/doomdll.vcxproj b/neo/engine/doomdll.vcxproj
index 2ddbfab8..ad5d85c6 100644
--- a/neo/engine/doomdll.vcxproj
+++ b/neo/engine/doomdll.vcxproj
@@ -1606,6 +1606,7 @@
+
@@ -3102,4 +3103,4 @@
-
\ No newline at end of file
+
diff --git a/neo/engine/doomdll.vcxproj.filters b/neo/engine/doomdll.vcxproj.filters
index 385d7b76..fa236aac 100644
--- a/neo/engine/doomdll.vcxproj.filters
+++ b/neo/engine/doomdll.vcxproj.filters
@@ -782,6 +782,9 @@
Renderer
+
+ Renderer
+
Renderer
@@ -2420,4 +2423,4 @@
Sys\RC\res
-
\ No newline at end of file
+
diff --git a/neo/engine/doomdll.vcxproj.user b/neo/engine/doomdll.vcxproj.user
index 20aa55cb..08538da5 100644
--- a/neo/engine/doomdll.vcxproj.user
+++ b/neo/engine/doomdll.vcxproj.user
@@ -17,7 +17,7 @@
WindowsLocalDebugger
+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3
D:\projects\Doom3
- +set r_fullscreen 0 +set r_mode 9|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 |+set r_fullscreen 1 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3|
+ +set r_fullscreen 0 +set r_mode 9|+set r_fullscreen 1 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 |+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3|
D:\projects\Doom3\Quake4.exe
diff --git a/neo/engine/framework/Common.cpp b/neo/engine/framework/Common.cpp
index fdf806c3..c9cb1bdd 100644
--- a/neo/engine/framework/Common.cpp
+++ b/neo/engine/framework/Common.cpp
@@ -123,6 +123,9 @@ int time_gameFrame;
int time_gameDraw;
int time_frontend; // renderSystem frontend time
int time_backend; // renderSystem backend time
+float com_lastGameFrameMsec;
+float com_lastRenderFrameMsec;
+float com_pathTracingGpuMsec;
#ifdef QUAKE4
int time_waiting; // wait time, surfaced by Quake 4's timing/debug HUD path
@@ -3211,6 +3214,9 @@ void idCommonLocal::Frame(void) {
session->UpdateScreen(false);
}
+ com_lastGameFrameMsec = (float)time_gameFrame;
+ com_lastRenderFrameMsec = (float)(time_gameDraw + time_frontend + time_backend);
+
// report timing information
if (com_speeds.GetBool()) {
static int lastTime;
@@ -3221,6 +3227,12 @@ void idCommonLocal::Frame(void) {
time_gameFrame = 0;
time_gameDraw = 0;
}
+ else {
+ time_gameFrame = 0;
+ time_gameDraw = 0;
+ time_frontend = 0;
+ time_backend = 0;
+ }
com_frameNumber++;
@@ -4051,4 +4063,4 @@ void idCommonLocal::MaterialKeyForBinding(const char* binding, char* materialNam
strcpy(keyText, displayName.c_str());
*wideKey = true;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/neo/engine/framework/Common.h b/neo/engine/framework/Common.h
index b38ed912..d43bca38 100644
--- a/neo/engine/framework/Common.h
+++ b/neo/engine/framework/Common.h
@@ -177,6 +177,9 @@ extern int time_gameFrame; // game logic time
extern int time_gameDraw; // game present time
extern int time_frontend; // renderer frontend time
extern int time_backend; // renderer backend time
+extern float com_lastGameFrameMsec; // last displayed game CPU time
+extern float com_lastRenderFrameMsec;// last displayed render CPU time
+extern float com_pathTracingGpuMsec; // last completed path tracing GPU time
#ifdef QUAKE4
extern int time_waiting; // time spent waiting
#endif
diff --git a/neo/engine/framework/Console.cpp b/neo/engine/framework/Console.cpp
index 2747d457..c20805a7 100644
--- a/neo/engine/framework/Console.cpp
+++ b/neo/engine/framework/Console.cpp
@@ -212,6 +212,12 @@ float SCR_DrawFPS( float y ) {
w = strlen( s ) * BIGCHAR_WIDTH;
renderSystem->DrawBigStringExt( (SCREEN_WIDTH - 5.0f) - w, idMath::FtoiFast(y) + 2, s, colorWhite, true, localConsole.charSetShader);
+
+ y += BIGCHAR_HEIGHT + 4;
+ s = va( "game cpu:%4.1fms render cpu:%4.1fms pt gpu:%4.1fms", com_lastGameFrameMsec, com_lastRenderFrameMsec, com_pathTracingGpuMsec );
+ w = strlen( s ) * SMALLCHAR_WIDTH;
+ renderSystem->DrawSmallStringExt( (SCREEN_WIDTH - 5.0f) - w, idMath::FtoiFast(y) + 2, s, colorWhite, true, localConsole.charSetShader);
+ return y + SMALLCHAR_HEIGHT + 4;
}
return y + BIGCHAR_HEIGHT + 4;
diff --git a/neo/engine/framework/Session.cpp b/neo/engine/framework/Session.cpp
index 26f31a9e..6d98e2a7 100644
--- a/neo/engine/framework/Session.cpp
+++ b/neo/engine/framework/Session.cpp
@@ -2686,7 +2686,7 @@ void idSessionLocal::UpdateScreen( bool outOfSequence ) {
// draw everything
Draw();
- if ( com_speeds.GetBool() ) {
+ if ( com_speeds.GetBool() || com_showFPS.GetBool() ) {
renderSystem->EndFrame( &time_frontend, &time_backend );
} else {
renderSystem->EndFrame( NULL, NULL );
diff --git a/neo/engine/models/Model.cpp b/neo/engine/models/Model.cpp
index d5b5e0f6..6ec24cad 100644
--- a/neo/engine/models/Model.cpp
+++ b/neo/engine/models/Model.cpp
@@ -623,6 +623,8 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface)
desc.vertexCount = (uint32_t)numDXRVerts;
desc.indices = indices.data();
desc.indexCount = (uint32_t)numDXRIndexes;
+ desc.allowUpdate = 1;
+ desc.opaque = 1;
if (!dxrBottomAcel)
{
@@ -2223,6 +2225,14 @@ void idRenderModelStatic::FreeVertexCache( void ) {
vertexCache.Free( tri->ambientCache );
tri->ambientCache = NULL;
}
+ if ( tri->ambientVbo ) {
+ glDeleteBuffersARB( 1, &tri->ambientVbo );
+ tri->ambientVbo = 0;
+ }
+ if ( tri->indexVbo ) {
+ glDeleteBuffersARB( 1, &tri->indexVbo );
+ tri->indexVbo = 0;
+ }
// static shadows may be present
if ( tri->shadowCache ) {
vertexCache.Free( tri->shadowCache );
diff --git a/neo/engine/models/Model.h b/neo/engine/models/Model.h
index ed6e8db4..5dc94aa8 100644
--- a/neo/engine/models/Model.h
+++ b/neo/engine/models/Model.h
@@ -138,6 +138,9 @@ typedef struct srfTriangles_s {
struct vertCache_s * lightingCache; // lightingCache_t
struct vertCache_s * shadowCache; // shadowCache_t
+ unsigned int ambientVbo; // model-owned idDrawVert GL buffer
+ unsigned int indexVbo; // model-owned index GL buffer
+
bool isSkeletal;
int numAllocedVerts;
int numAllocedIndices;
diff --git a/neo/engine/opengl/gl_d3d12raylight.cpp b/neo/engine/opengl/gl_d3d12raylight.cpp
index 96f49eef..5b629e1e 100644
--- a/neo/engine/opengl/gl_d3d12raylight.cpp
+++ b/neo/engine/opengl/gl_d3d12raylight.cpp
@@ -49,6 +49,8 @@ If you have questions concerning this license or the applicable additional terms
using Microsoft::WRL::ComPtr;
+extern float com_pathTracingGpuMsec;
+
// ============================================================
// Logging / checks
// ============================================================
@@ -407,6 +409,10 @@ struct glRaytracingCmdContext_t
ComPtr fence;
HANDLE fenceEvent;
UINT64 nextFenceValue;
+ ComPtr pathTracingTimestampHeap;
+ glRaytracingBuffer_t pathTracingTimestampReadback;
+ UINT64 pathTracingTimestampFrequency;
+ UINT64 pathTracingTimestampFenceRing[GL_RAYTRACING_CMD_RING_SIZE];
bool initialized;
glRaytracingCmdContext_t()
@@ -425,9 +431,11 @@ struct glRaytracingCmdContext_t
cmdFenceValueRing[i] = 0;
blasFenceValueRing[i] = 0;
tlasFenceValueRing[i] = 0;
+ pathTracingTimestampFenceRing[i] = 0;
}
fenceEvent = nullptr;
nextFenceValue = 0;
+ pathTracingTimestampFrequency = 0;
initialized = false;
}
};
@@ -463,6 +471,66 @@ static void glRaytracingWaitIdle(void)
glRaytracingWaitFenceValue(value);
}
+static void glRaytracingPollPathTracingTimestamp(UINT slot)
+{
+ if (!g_glRaytracingCmd.pathTracingTimestampReadback.resource ||
+ !g_glRaytracingCmd.pathTracingTimestampFrequency ||
+ slot >= GL_RAYTRACING_CMD_RING_SIZE)
+ {
+ return;
+ }
+
+ const UINT64 fenceValue = g_glRaytracingCmd.pathTracingTimestampFenceRing[slot];
+ if (!fenceValue || !g_glRaytracingCmd.fence || g_glRaytracingCmd.fence->GetCompletedValue() < fenceValue)
+ return;
+
+ const UINT64 offset = sizeof(UINT64) * 2ull * slot;
+ D3D12_RANGE readRange = { offset, offset + sizeof(UINT64) * 2ull };
+ UINT64* timestamps = nullptr;
+ if (SUCCEEDED(g_glRaytracingCmd.pathTracingTimestampReadback.resource->Map(0, &readRange, reinterpret_cast(×tamps))) && timestamps != nullptr)
+ {
+ const UINT64 begin = timestamps[slot * 2 + 0];
+ const UINT64 end = timestamps[slot * 2 + 1];
+ if (end >= begin)
+ {
+ com_pathTracingGpuMsec = (float)((double)(end - begin) * 1000.0 / (double)g_glRaytracingCmd.pathTracingTimestampFrequency);
+ }
+
+ D3D12_RANGE writeRange = { 0, 0 };
+ g_glRaytracingCmd.pathTracingTimestampReadback.resource->Unmap(0, &writeRange);
+ }
+ g_glRaytracingCmd.pathTracingTimestampFenceRing[slot] = 0;
+}
+
+static int glRaytracingCreatePathTracingTimestamps(void)
+{
+ D3D12_QUERY_HEAP_DESC qh = {};
+ qh.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP;
+ qh.Count = GL_RAYTRACING_CMD_RING_SIZE * 2;
+ if (FAILED(g_glRaytracingCmd.device->CreateQueryHeap(&qh, IID_PPV_ARGS(&g_glRaytracingCmd.pathTracingTimestampHeap))))
+ return 0;
+
+ g_glRaytracingCmd.pathTracingTimestampReadback = glRaytracingCreateBuffer(
+ g_glRaytracingCmd.device.Get(),
+ sizeof(UINT64) * GL_RAYTRACING_CMD_RING_SIZE * 2ull,
+ D3D12_HEAP_TYPE_READBACK,
+ D3D12_RESOURCE_STATE_COPY_DEST,
+ D3D12_RESOURCE_FLAG_NONE);
+ if (!g_glRaytracingCmd.pathTracingTimestampReadback.resource)
+ return 0;
+
+ g_glRaytracingCmd.pathTracingTimestampFrequency = 0;
+ if (FAILED(g_glRaytracingCmd.queue->GetTimestampFrequency(&g_glRaytracingCmd.pathTracingTimestampFrequency)) ||
+ g_glRaytracingCmd.pathTracingTimestampFrequency == 0)
+ {
+ g_glRaytracingCmd.pathTracingTimestampHeap.Reset();
+ g_glRaytracingCmd.pathTracingTimestampReadback = glRaytracingBuffer_t();
+ return 0;
+ }
+
+ return 1;
+}
+
static int glRaytracingCreateDirectCommandListPair(
ID3D12Device5* device,
ComPtr& allocator,
@@ -562,6 +630,11 @@ static int glRaytracingInitCmdContext(void)
return 0;
}
+ if (!glRaytracingCreatePathTracingTimestamps())
+ {
+ glRaytracingLog("path tracing timestamp queries unavailable");
+ }
+
g_glRaytracingCmd.initialized = true;
return 1;
}
@@ -586,6 +659,7 @@ static int glRaytracingBeginCmd(void)
{
const UINT slot = (g_glRaytracingCmd.cmdRingIndex + 1u) % GL_RAYTRACING_CMD_RING_SIZE;
glRaytracingWaitFenceValue(g_glRaytracingCmd.cmdFenceValueRing[slot]);
+ glRaytracingPollPathTracingTimestamp(slot);
g_glRaytracingCmd.cmdRingIndex = slot;
g_glRaytracingCmd.cmdCurrentSlot = slot;
@@ -1965,12 +2039,52 @@ int glRaytracingUpdateMesh(glRaytracingMeshHandle_t meshHandle, const glRaytraci
if (!desc->vertices || !desc->indices || desc->vertexCount == 0 || desc->indexCount == 0)
return 0;
+ const UINT64 vbBytes = UINT64(desc->vertexCount) * sizeof(glRaytracingVertex_t);
+ const UINT64 ibBytes = UINT64(desc->indexCount) * sizeof(uint32_t);
+ const bool sameVertexData =
+ mesh->verticesCpu.size() == desc->vertexCount &&
+ memcmp(mesh->verticesCpu.data(), desc->vertices, (size_t)vbBytes) == 0;
+ const bool sameIndexData =
+ mesh->indicesCpu.size() == desc->indexCount &&
+ memcmp(mesh->indicesCpu.data(), desc->indices, (size_t)ibBytes) == 0;
+ const bool sameDesc =
+ mesh->descCpu.vertexCount == desc->vertexCount &&
+ mesh->descCpu.indexCount == desc->indexCount &&
+ mesh->descCpu.allowUpdate == desc->allowUpdate &&
+ mesh->descCpu.opaque == desc->opaque;
+
+ if (sameDesc && sameVertexData && sameIndexData)
+ return 1;
+
+ const bool canUpdateExistingBuffers =
+ desc->allowUpdate != 0 &&
+ mesh->descCpu.allowUpdate != 0 &&
+ mesh->descCpu.opaque == desc->opaque &&
+ mesh->blasBuilt != 0 &&
+ mesh->vertexBuffer.resource &&
+ mesh->indexBuffer.resource &&
+ mesh->vertexBuffer.size >= vbBytes &&
+ mesh->indexBuffer.size >= ibBytes &&
+ mesh->verticesCpu.size() == desc->vertexCount &&
+ mesh->indicesCpu.size() == desc->indexCount;
+
mesh->descCpu = *desc;
mesh->verticesCpu.assign(desc->vertices, desc->vertices + desc->vertexCount);
mesh->indicesCpu.assign(desc->indices, desc->indices + desc->indexCount);
mesh->descCpu.vertices = nullptr;
mesh->descCpu.indices = nullptr;
+ if (canUpdateExistingBuffers)
+ {
+ glRaytracingMapCopy(mesh->vertexBuffer.resource.Get(), mesh->verticesCpu.data(), (size_t)vbBytes);
+ if (!sameIndexData)
+ glRaytracingMapCopy(mesh->indexBuffer.resource.Get(), mesh->indicesCpu.data(), (size_t)ibBytes);
+
+ mesh->dirty = 1;
+ glRaytracingInvalidateInstancesForMesh(meshHandle, 0);
+ return 1;
+ }
+
// Updating a mesh destroys/replaces resources that an already submitted frame
// may still reference. Wait only for this destructive path; steady-state
// rendering remains asynchronous.
@@ -2132,6 +2246,9 @@ int glRaytracingUpdateInstanceInScene(glRaytracingSceneHandle_t worldHandle, glR
? 0u
: glRaytracingNormalizeVisibleInstanceMask(newDesc.mask);
+ if (memcmp(&inst->descCpu, &newDesc, sizeof(newDesc)) == 0)
+ return 1;
+
inst->descCpu = newDesc;
inst->dirty = 1;
@@ -6440,6 +6557,18 @@ static bool glRaytracingLightingExecuteInternal(
g_glRaytracingCmd.cmdList->SetComputeRootConstantBufferView(2, g_glRaytracingLighting.constantBuffer.gpuVA);
g_glRaytracingCmd.cmdList->SetPipelineState1(g_glRaytracingLighting.rtStateObject.Get());
+ const UINT timestampBase = g_glRaytracingCmd.cmdCurrentSlot * 2u;
+ const bool writeTimestamp =
+ g_glRaytracingCmd.pathTracingTimestampHeap.Get() != nullptr &&
+ g_glRaytracingCmd.pathTracingTimestampReadback.resource.Get() != nullptr;
+ if (writeTimestamp)
+ {
+ g_glRaytracingCmd.cmdList->EndQuery(
+ g_glRaytracingCmd.pathTracingTimestampHeap.Get(),
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampBase + 0u);
+ }
+
const UINT shaderRecordSize = (UINT)glRaytracingAlignUp(
D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES,
D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT);
@@ -6586,9 +6715,27 @@ static bool glRaytracingLightingExecuteInternal(
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
+ if (writeTimestamp)
+ {
+ g_glRaytracingCmd.cmdList->EndQuery(
+ g_glRaytracingCmd.pathTracingTimestampHeap.Get(),
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampBase + 1u);
+ g_glRaytracingCmd.cmdList->ResolveQueryData(
+ g_glRaytracingCmd.pathTracingTimestampHeap.Get(),
+ D3D12_QUERY_TYPE_TIMESTAMP,
+ timestampBase,
+ 2,
+ g_glRaytracingCmd.pathTracingTimestampReadback.resource.Get(),
+ sizeof(UINT64) * timestampBase);
+ }
+
if (!glRaytracingEndCmd())
return false;
+ if (writeTimestamp)
+ g_glRaytracingCmd.pathTracingTimestampFenceRing[g_glRaytracingCmd.cmdCurrentSlot] = g_glRaytracingCmd.cmdLastFenceValue;
+
if (useInternalDenoiser)
g_glRaytracingLighting.currentHistoryIndex = historyWriteIndex;
diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp
index 449b805c..e97d9c46 100644
--- a/neo/engine/opengl/gl_d3d12shim.cpp
+++ b/neo/engine/opengl/gl_d3d12shim.cpp
@@ -710,6 +710,8 @@ struct DrawConstants
float texEnvColor1[4];
float cameraPomPad[4];
float neuralPomPad[4];
+ float currentColor[4];
+ float vertexColorPad[4];
// Appended for translated ARBvp/ARBfp programs. Fixed-function HLSL ignores
// these fields; ARB-generated HLSL uses them as program.env/local storage.
@@ -723,12 +725,27 @@ struct GLBufferObject
GLuint id = 0;
GLenum target = 0;
GLbitfield storageFlags = 0;
+ GLenum usage = GL_STATIC_DRAW_ARB;
std::vector data;
+ uint32_t revision = 1;
bool mapped = false;
GLintptr mappedOffset = 0;
GLsizeiptr mappedLength = 0;
GLbitfield mappedAccess = 0;
+
+ ComPtr resource;
+ uint8_t* mappedGpu = nullptr;
+ D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = 0;
+ size_t gpuBytes = 0;
+
+ ComPtr packedVertexResource;
+ uint8_t* packedVertexMapped = nullptr;
+ D3D12_GPU_VIRTUAL_ADDRESS packedVertexGpuAddress = 0;
+ size_t packedVertexBytes = 0;
+ size_t packedVertexCount = 0;
+ uint32_t packedVertexRevision = 0;
+ uint64_t packedVertexLayoutHash = 0;
};
const char* vendor = "Justin Marshall";
@@ -847,6 +864,8 @@ struct BatchKey
float fogStart = 0.0f;
float fogEnd = 1.0f;
float fogColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ float currentColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ float useVertexColor = 0.0f;
GLenum blendSrc = GL_ONE;
GLenum blendDst = GL_ZERO;
@@ -932,6 +951,8 @@ static bool BatchKeyEquals(const BatchKey& a, const BatchKey& b)
a.fogStart == b.fogStart &&
a.fogEnd == b.fogEnd &&
memcmp(a.fogColor, b.fogColor, sizeof(a.fogColor)) == 0 &&
+ memcmp(a.currentColor, b.currentColor, sizeof(a.currentColor)) == 0 &&
+ a.useVertexColor == b.useVertexColor &&
a.colorWriteMask == b.colorWriteMask &&
a.cullFaceEnabled == b.cullFaceEnabled &&
a.cullMode == b.cullMode &&
@@ -995,6 +1016,13 @@ struct QueuedBatch
size_t firstVertex;
size_t vertexCount;
+
+ bool gpuIndexed = false;
+ D3D12_VERTEX_BUFFER_VIEW gpuVbv{};
+ D3D12_INDEX_BUFFER_VIEW gpuIbv{};
+ UINT gpuIndexCount = 0;
+ ComPtr gpuVertexResource;
+ ComPtr gpuIndexResource;
};
struct QD3D12Window
@@ -1195,6 +1223,22 @@ struct ImmediateVertexBuffer
count = 0;
}
+ GLVertex* ResizeForWrite(size_t newCount)
+ {
+ if (newCount > storage.size())
+ {
+ size_t newSize = storage.empty() ? 1024 : storage.size();
+ while (newSize < newCount)
+ newSize *= 2;
+
+ storage.resize(newSize);
+ ptr = storage.data();
+ }
+
+ count = newCount;
+ return ptr;
+ }
+
GLVertex& Push()
{
if (count >= storage.size())
@@ -1310,7 +1354,7 @@ struct GLState
bool motionHistoryReset = true;
QD3D12UpscalerBackend upscalerBackend = QD3D12_UPSCALER_DLSS;
- QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_PERFORMANCE;
+ QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_DLAA;
bool enableInternalTAA = true;
bool enableRayAIDenoise = false;
bool enableDLSSRayReconstruction = true;
@@ -1331,6 +1375,8 @@ struct GLState
GLenum type = GL_FLOAT;
GLsizei stride = 0;
const uint8_t* ptr = nullptr;
+ GLuint buffer = 0;
+ size_t offset = 0;
bool enabled = false;
};
@@ -1404,6 +1450,7 @@ struct GLState
GLenum blendSrc = GL_SRC_ALPHA;
GLenum blendDst = GL_ONE_MINUS_SRC_ALPHA;
GLenum alphaFunc = GL_GREATER;
+ float alphaFuncMapped = 4.0f;
float alphaRef = 0.666f;
GLenum cullMode = GL_BACK;
GLenum frontFace = GL_CCW;
@@ -1553,11 +1600,19 @@ struct QD3D12AutoCameraHistory
{
bool haveLastCamera = false;
bool haveFramePrevious = false;
+ bool haveCachedUpdate = false;
uint64_t frameSerial = UINT64_MAX;
+ uint64_t cachedFrameSerial = UINT64_MAX;
+ bool cachedMotionHistoryReset = false;
+ UINT cachedRenderWidth = 0;
+ UINT cachedRenderHeight = 0;
float lastViewToClip[16] = {};
float lastWorldToView[16] = {};
float framePreviousViewToClip[16] = {};
float framePreviousWorldToView[16] = {};
+ float cachedProjection[16] = {};
+ float cachedModelView[16] = {};
+ float cachedModelToWorld[16] = {};
};
static QD3D12AutoCameraHistory g_qd3d12AutoCamera;
@@ -1776,6 +1831,21 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices()
const float* glProjection = g_gl.projStack.back().m;
const float* modelView = g_gl.modelStack.back().m;
const float* modelToWorld = g_gl.modelMatrix.m;
+ const UINT renderWidth = g_currentWindow ? g_currentWindow->renderWidth : 0;
+ const UINT renderHeight = g_currentWindow ? g_currentWindow->renderHeight : 0;
+
+ if (g_qd3d12AutoCamera.haveCachedUpdate &&
+ g_qd3d12AutoCamera.cachedFrameSerial == g_gl.frameSerial &&
+ g_qd3d12AutoCamera.cachedMotionHistoryReset == g_gl.motionHistoryReset &&
+ g_qd3d12AutoCamera.cachedRenderWidth == renderWidth &&
+ g_qd3d12AutoCamera.cachedRenderHeight == renderHeight &&
+ g_gl.cameraState.valid &&
+ memcmp(g_qd3d12AutoCamera.cachedProjection, glProjection, sizeof(g_qd3d12AutoCamera.cachedProjection)) == 0 &&
+ memcmp(g_qd3d12AutoCamera.cachedModelView, modelView, sizeof(g_qd3d12AutoCamera.cachedModelView)) == 0 &&
+ memcmp(g_qd3d12AutoCamera.cachedModelToWorld, modelToWorld, sizeof(g_qd3d12AutoCamera.cachedModelToWorld)) == 0)
+ {
+ return true;
+ }
if (!QD3D12_IsPerspectiveProjectionCM(glProjection) ||
!QD3D12_MatrixFinite(modelView) ||
@@ -1893,6 +1963,14 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices()
QD3D12_MatrixCopy(g_qd3d12AutoCamera.lastViewToClip, viewToClip);
QD3D12_MatrixCopy(g_qd3d12AutoCamera.lastWorldToView, worldToView);
g_qd3d12AutoCamera.haveLastCamera = true;
+ g_qd3d12AutoCamera.haveCachedUpdate = true;
+ g_qd3d12AutoCamera.cachedFrameSerial = g_gl.frameSerial;
+ g_qd3d12AutoCamera.cachedMotionHistoryReset = g_gl.motionHistoryReset;
+ g_qd3d12AutoCamera.cachedRenderWidth = renderWidth;
+ g_qd3d12AutoCamera.cachedRenderHeight = renderHeight;
+ QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedProjection, glProjection);
+ QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedModelView, modelView);
+ QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedModelToWorld, modelToWorld);
return true;
}
@@ -2205,6 +2283,101 @@ static GLBufferObject* QD3D12_GetBuffer(GLuint id)
return &it->second;
}
+static void QD3D12_ResetPackedVertexBuffer(GLBufferObject& bo)
+{
+ if (bo.packedVertexResource && bo.packedVertexMapped)
+ bo.packedVertexResource->Unmap(0, nullptr);
+
+ bo.packedVertexResource.Reset();
+ bo.packedVertexMapped = nullptr;
+ bo.packedVertexGpuAddress = 0;
+ bo.packedVertexBytes = 0;
+ bo.packedVertexCount = 0;
+ bo.packedVertexRevision = 0;
+ bo.packedVertexLayoutHash = 0;
+}
+
+static void QD3D12_ResetBufferResource(GLBufferObject& bo)
+{
+ if (bo.resource && bo.mappedGpu)
+ bo.resource->Unmap(0, nullptr);
+
+ bo.resource.Reset();
+ bo.mappedGpu = nullptr;
+ bo.gpuAddress = 0;
+ bo.gpuBytes = 0;
+ QD3D12_ResetPackedVertexBuffer(bo);
+}
+
+static bool QD3D12_CreateUploadBufferResource(GLBufferObject& bo, size_t size)
+{
+ if (size == 0)
+ {
+ QD3D12_ResetBufferResource(bo);
+ return true;
+ }
+
+ if (!g_gl.device)
+ return false;
+
+ if (bo.resource && bo.gpuBytes >= size && bo.mappedGpu)
+ return true;
+
+ if (bo.resource && bo.mappedGpu)
+ bo.resource->Unmap(0, nullptr);
+
+ bo.resource.Reset();
+ bo.mappedGpu = nullptr;
+ bo.gpuAddress = 0;
+ bo.gpuBytes = 0;
+
+ D3D12_HEAP_PROPERTIES hp{};
+ hp.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ D3D12_RESOURCE_DESC rd{};
+ rd.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ rd.Width = (UINT64)size;
+ rd.Height = 1;
+ rd.DepthOrArraySize = 1;
+ rd.MipLevels = 1;
+ rd.SampleDesc.Count = 1;
+ rd.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ QD3D12_CHECK(g_gl.device->CreateCommittedResource(
+ &hp,
+ D3D12_HEAP_FLAG_NONE,
+ &rd,
+ D3D12_RESOURCE_STATE_GENERIC_READ,
+ nullptr,
+ IID_PPV_ARGS(&bo.resource)));
+
+ bo.gpuAddress = bo.resource->GetGPUVirtualAddress();
+ bo.gpuBytes = size;
+ QD3D12_CHECK(bo.resource->Map(0, nullptr, reinterpret_cast(&bo.mappedGpu)));
+ return bo.mappedGpu != nullptr;
+}
+
+static void QD3D12_UpdateBufferResource(GLBufferObject& bo, GLenum target, GLsizeiptr size, const void* data, GLenum usage)
+{
+ bo.target = target;
+ bo.usage = usage;
+ bo.storageFlags = 0;
+ bo.data.resize((size_t)size);
+ ++bo.revision;
+ QD3D12_ResetPackedVertexBuffer(bo);
+
+ if (size > 0)
+ {
+ if (data)
+ memcpy(bo.data.data(), data, (size_t)size);
+ else
+ memset(bo.data.data(), 0, (size_t)size);
+ }
+
+ if (QD3D12_CreateUploadBufferResource(bo, (size_t)size) && size > 0)
+ memcpy(bo.mappedGpu, bo.data.data(), (size_t)size);
+}
+
static TextureResource* QD3D12_FindTextureResource(GLuint id)
{
if (id == 0)
@@ -2379,6 +2552,16 @@ static const uint8_t* QD3D12_ResolveArrayPointer(const void* ptr)
return reinterpret_cast(ptr);
}
+static void QD3D12_CaptureArrayPointer(GLState::ClientArrayState& array, GLint size, GLenum type, GLsizei stride, const void* pointer)
+{
+ array.size = size;
+ array.type = type;
+ array.stride = stride;
+ array.ptr = QD3D12_ResolveArrayPointer(pointer);
+ array.buffer = g_gl.boundArrayBuffer;
+ array.offset = (g_gl.boundArrayBuffer != 0) ? (size_t)pointer : 0;
+}
+
static const void* QD3D12_ResolveElementPointer(const void* ptr, GLenum indexType, GLsizei count)
{
if (g_gl.boundElementArrayBuffer != 0)
@@ -2518,6 +2701,8 @@ cbuffer DrawCB : register(b0)
float4 gTexEnvColor1;
float4 gCameraPomPad;
float4 gNeuralPomPad;
+ float4 gCurrentColor;
+ float4 gVertexColorPad;
};
#define gUseNormalMap gMotionPad.x
@@ -2531,6 +2716,7 @@ cbuffer DrawCB : register(b0)
#define gCameraPomValid gCameraPomPad.w
#define gUseNeuralPOM gNeuralPomPad.x
#define gNeuralPOMDebug gNeuralPomPad.y
+#define gUseVertexColor gVertexColorPad.x
// Runtime Neural POM relief controls. x/y keep the existing enable/debug ABI;
// z deepens the learned UV ray offset and w deepens the learned normal slope.
// The CPU fills these from glNormalMapStrengthf() so existing material calls
@@ -3688,7 +3874,7 @@ VSOut VSMain(VSIn i)
: abs(currClip.z / max(abs(currClip.w), 0.00001));
o.uv0 = i.uv0;
o.uv1 = i.uv1;
- o.col = i.col;
+ o.col = (gUseVertexColor > 0.5) ? i.col : gCurrentColor;
o.worldPos = worldPos.xyz;
o.normal = normalize(worldNormal);
o.tangent = normalize(worldTangent);
@@ -4468,7 +4654,7 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
// Direct init is much cheaper than memset + patching fields.
out.px = 0.0f; out.py = 0.0f; out.pz = 0.0f;
out.nx = g_gl.curNormal[0]; out.ny = g_gl.curNormal[1]; out.nz = g_gl.curNormal[2];
- out.r = 1.0f; out.g = 1.0f; out.b = 1.0f; out.a = 1.0f;
+ out.r = g_gl.curColor[0]; out.g = g_gl.curColor[1]; out.b = g_gl.curColor[2]; out.a = g_gl.curColor[3];
out.u0 = 0.0f; out.v0 = 0.0f;
out.u1 = 0.0f; out.v1 = 0.0f;
out.tx = g_gl.curTangent[0]; out.ty = g_gl.curTangent[1]; out.tz = g_gl.curTangent[2];
@@ -4480,34 +4666,31 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& va = g_gl.vertexArray;
if (va.enabled && va.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(va.type);
- const size_t elemSize = (size_t)va.size * typeSize;
- const size_t stride = va.stride ? (size_t)va.stride : elemSize;
- const uint8_t* p = va.ptr + stride * (size_t)idx;
-
- switch (va.type)
+ if (va.type == GL_FLOAT)
{
- case GL_FLOAT:
- {
- const float* f = (const float*)p;
+ const size_t stride = va.stride ? (size_t)va.stride : (size_t)va.size * sizeof(float);
+ const float* f = (const float*)(va.ptr + stride * (size_t)idx);
if (va.size > 0) out.px = f[0];
if (va.size > 1) out.py = f[1];
if (va.size > 2) out.pz = f[2];
- break;
}
- case GL_DOUBLE:
+ else if (va.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = va.stride ? (size_t)va.stride : (size_t)va.size * sizeof(double);
+ const double* f = (const double*)(va.ptr + stride * (size_t)idx);
if (va.size > 0) out.px = (float)f[0];
if (va.size > 1) out.py = (float)f[1];
if (va.size > 2) out.pz = (float)f[2];
- break;
}
- default:
+ else
+ {
+ const size_t typeSize = (size_t)QD3D12_TypeSize(va.type);
+ const size_t elemSize = (size_t)va.size * typeSize;
+ const size_t stride = va.stride ? (size_t)va.stride : elemSize;
+ const uint8_t* p = va.ptr + stride * (size_t)idx;
if (va.size > 0) out.px = QD3D12_ReadScalarFast(p + 0 * typeSize, va.type);
if (va.size > 1) out.py = QD3D12_ReadScalarFast(p + 1 * typeSize, va.type);
if (va.size > 2) out.pz = QD3D12_ReadScalarFast(p + 2 * typeSize, va.type);
- break;
}
}
@@ -4517,33 +4700,30 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& na = g_gl.normalArray;
if (na.enabled && na.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(na.type);
- const size_t stride = na.stride ? (size_t)na.stride : (3 * typeSize);
- const uint8_t* p = na.ptr + stride * (size_t)idx;
-
- switch (na.type)
+ if (na.type == GL_FLOAT)
{
- case GL_FLOAT:
- {
- const float* f = (const float*)p;
+ const size_t stride = na.stride ? (size_t)na.stride : 3 * sizeof(float);
+ const float* f = (const float*)(na.ptr + stride * (size_t)idx);
out.nx = f[0];
out.ny = f[1];
out.nz = f[2];
- break;
}
- case GL_DOUBLE:
+ else if (na.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = na.stride ? (size_t)na.stride : 3 * sizeof(double);
+ const double* f = (const double*)(na.ptr + stride * (size_t)idx);
out.nx = (float)f[0];
out.ny = (float)f[1];
out.nz = (float)f[2];
- break;
}
- default:
+ else
+ {
+ const size_t typeSize = (size_t)QD3D12_TypeSize(na.type);
+ const size_t stride = na.stride ? (size_t)na.stride : (3 * typeSize);
+ const uint8_t* p = na.ptr + stride * (size_t)idx;
out.nx = QD3D12_ReadScalarFast(p + 0 * typeSize, na.type);
out.ny = QD3D12_ReadScalarFast(p + 1 * typeSize, na.type);
out.nz = QD3D12_ReadScalarFast(p + 2 * typeSize, na.type);
- break;
}
}
@@ -4553,33 +4733,30 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& ta = g_gl.tangentArray;
if (ta.enabled && ta.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(ta.type);
- const size_t stride = ta.stride ? (size_t)ta.stride : (3 * typeSize);
- const uint8_t* p = ta.ptr + stride * (size_t)idx;
-
- switch (ta.type)
+ if (ta.type == GL_FLOAT)
{
- case GL_FLOAT:
- {
- const float* f = (const float*)p;
+ const size_t stride = ta.stride ? (size_t)ta.stride : 3 * sizeof(float);
+ const float* f = (const float*)(ta.ptr + stride * (size_t)idx);
out.tx = f[0];
out.ty = f[1];
out.tz = f[2];
- break;
}
- case GL_DOUBLE:
+ else if (ta.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = ta.stride ? (size_t)ta.stride : 3 * sizeof(double);
+ const double* f = (const double*)(ta.ptr + stride * (size_t)idx);
out.tx = (float)f[0];
out.ty = (float)f[1];
out.tz = (float)f[2];
- break;
}
- default:
+ else
+ {
+ const size_t typeSize = (size_t)QD3D12_TypeSize(ta.type);
+ const size_t stride = ta.stride ? (size_t)ta.stride : (3 * typeSize);
+ const uint8_t* p = ta.ptr + stride * (size_t)idx;
out.tx = QD3D12_ReadScalarFast(p + 0 * typeSize, ta.type);
out.ty = QD3D12_ReadScalarFast(p + 1 * typeSize, ta.type);
out.tz = QD3D12_ReadScalarFast(p + 2 * typeSize, ta.type);
- break;
}
}
@@ -4589,33 +4766,30 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& ba = g_gl.bitangentArray;
if (ba.enabled && ba.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(ba.type);
- const size_t stride = ba.stride ? (size_t)ba.stride : (3 * typeSize);
- const uint8_t* p = ba.ptr + stride * (size_t)idx;
-
- switch (ba.type)
+ if (ba.type == GL_FLOAT)
{
- case GL_FLOAT:
- {
- const float* f = (const float*)p;
+ const size_t stride = ba.stride ? (size_t)ba.stride : 3 * sizeof(float);
+ const float* f = (const float*)(ba.ptr + stride * (size_t)idx);
out.bx = f[0];
out.by = f[1];
out.bz = f[2];
- break;
}
- case GL_DOUBLE:
+ else if (ba.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = ba.stride ? (size_t)ba.stride : 3 * sizeof(double);
+ const double* f = (const double*)(ba.ptr + stride * (size_t)idx);
out.bx = (float)f[0];
out.by = (float)f[1];
out.bz = (float)f[2];
- break;
}
- default:
+ else
+ {
+ const size_t typeSize = (size_t)QD3D12_TypeSize(ba.type);
+ const size_t stride = ba.stride ? (size_t)ba.stride : (3 * typeSize);
+ const uint8_t* p = ba.ptr + stride * (size_t)idx;
out.bx = QD3D12_ReadScalarFast(p + 0 * typeSize, ba.type);
out.by = QD3D12_ReadScalarFast(p + 1 * typeSize, ba.type);
out.bz = QD3D12_ReadScalarFast(p + 2 * typeSize, ba.type);
- break;
}
}
@@ -4625,14 +4799,11 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& ca = g_gl.colorArray;
if (ca.enabled && ca.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(ca.type);
- const size_t elemSize = (size_t)ca.size * typeSize;
- const size_t stride = ca.stride ? (size_t)ca.stride : elemSize;
- const uint8_t* p = ca.ptr + stride * (size_t)idx;
-
if (ca.type == GL_UNSIGNED_BYTE)
{
static const float kInv255 = 1.0f / 255.0f;
+ const size_t stride = ca.stride ? (size_t)ca.stride : (size_t)ca.size * sizeof(GLubyte);
+ const uint8_t* p = ca.ptr + stride * (size_t)idx;
if (ca.size > 0) out.r = p[0] * kInv255;
if (ca.size > 1) out.g = p[1] * kInv255;
if (ca.size > 2) out.b = p[2] * kInv255;
@@ -4640,7 +4811,8 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
}
else if (ca.type == GL_FLOAT)
{
- const float* f = (const float*)p;
+ const size_t stride = ca.stride ? (size_t)ca.stride : (size_t)ca.size * sizeof(float);
+ const float* f = (const float*)(ca.ptr + stride * (size_t)idx);
if (ca.size > 0) out.r = f[0];
if (ca.size > 1) out.g = f[1];
if (ca.size > 2) out.b = f[2];
@@ -4648,7 +4820,8 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
}
else if (ca.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = ca.stride ? (size_t)ca.stride : (size_t)ca.size * sizeof(double);
+ const double* f = (const double*)(ca.ptr + stride * (size_t)idx);
if (ca.size > 0) out.r = (float)f[0];
if (ca.size > 1) out.g = (float)f[1];
if (ca.size > 2) out.b = (float)f[2];
@@ -4656,6 +4829,10 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
}
else
{
+ const size_t typeSize = (size_t)QD3D12_TypeSize(ca.type);
+ const size_t elemSize = (size_t)ca.size * typeSize;
+ const size_t stride = ca.stride ? (size_t)ca.stride : elemSize;
+ const uint8_t* p = ca.ptr + stride * (size_t)idx;
if (ca.size > 0) out.r = QD3D12_ReadScalarFast(p + 0 * typeSize, ca.type);
if (ca.size > 1) out.g = QD3D12_ReadScalarFast(p + 1 * typeSize, ca.type);
if (ca.size > 2) out.b = QD3D12_ReadScalarFast(p + 2 * typeSize, ca.type);
@@ -4670,31 +4847,28 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& tc = g_gl.texCoordArray[0];
if (tc.enabled && tc.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type);
- const size_t elemSize = (size_t)tc.size * typeSize;
- const size_t stride = tc.stride ? (size_t)tc.stride : elemSize;
- const uint8_t* p = tc.ptr + stride * (size_t)idx;
-
- switch (tc.type)
+ if (tc.type == GL_FLOAT)
{
- case GL_FLOAT:
- {
- const float* f = (const float*)p;
+ const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(float);
+ const float* f = (const float*)(tc.ptr + stride * (size_t)idx);
if (tc.size > 0) out.u0 = f[0];
if (tc.size > 1) out.v0 = f[1];
- break;
}
- case GL_DOUBLE:
+ else if (tc.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(double);
+ const double* f = (const double*)(tc.ptr + stride * (size_t)idx);
if (tc.size > 0) out.u0 = (float)f[0];
if (tc.size > 1) out.v0 = (float)f[1];
- break;
}
- default:
+ else
+ {
+ const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type);
+ const size_t elemSize = (size_t)tc.size * typeSize;
+ const size_t stride = tc.stride ? (size_t)tc.stride : elemSize;
+ const uint8_t* p = tc.ptr + stride * (size_t)idx;
if (tc.size > 0) out.u0 = QD3D12_ReadScalarFast(p + 0 * typeSize, tc.type);
if (tc.size > 1) out.v0 = QD3D12_ReadScalarFast(p + 1 * typeSize, tc.type);
- break;
}
}
}
@@ -4706,31 +4880,28 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out)
const auto& tc = g_gl.texCoordArray[1];
if (tc.enabled && tc.ptr)
{
- const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type);
- const size_t elemSize = (size_t)tc.size * typeSize;
- const size_t stride = tc.stride ? (size_t)tc.stride : elemSize;
- const uint8_t* p = tc.ptr + stride * (size_t)idx;
-
- switch (tc.type)
+ if (tc.type == GL_FLOAT)
{
- case GL_FLOAT:
- {
- const float* f = (const float*)p;
+ const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(float);
+ const float* f = (const float*)(tc.ptr + stride * (size_t)idx);
if (tc.size > 0) out.u1 = f[0];
if (tc.size > 1) out.v1 = f[1];
- break;
}
- case GL_DOUBLE:
+ else if (tc.type == GL_DOUBLE)
{
- const double* f = (const double*)p;
+ const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(double);
+ const double* f = (const double*)(tc.ptr + stride * (size_t)idx);
if (tc.size > 0) out.u1 = (float)f[0];
if (tc.size > 1) out.v1 = (float)f[1];
- break;
}
- default:
+ else
+ {
+ const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type);
+ const size_t elemSize = (size_t)tc.size * typeSize;
+ const size_t stride = tc.stride ? (size_t)tc.stride : elemSize;
+ const uint8_t* p = tc.ptr + stride * (size_t)idx;
if (tc.size > 0) out.u1 = QD3D12_ReadScalarFast(p + 0 * typeSize, tc.type);
if (tc.size > 1) out.v1 = QD3D12_ReadScalarFast(p + 1 * typeSize, tc.type);
- break;
}
}
}
@@ -4861,10 +5032,8 @@ static bool QD3D12_PolygonOffsetEnabledForMode(GLenum originalMode)
}
}
-#ifdef _DEBUG
-#pragma optimize off
-#endif
-static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* tex0, const TextureResource* tex1, TextureResource* const* allTextures)
+static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* tex0, const TextureResource* tex1, TextureResource* const* allTextures,
+ TextureResource* selectedNormalMap = nullptr, TextureResource* selectedGlowMap = nullptr, TextureResource* selectedSpecularMap = nullptr, bool selectedMapsValid = false)
{
const bool tex0IsMaterialMapOnly = QD3D12_TextureIsMaterialMapOnlyForFixedFunctionColor(tex0);
const bool tex1IsMaterialMapOnly = QD3D12_TextureIsMaterialMapOnlyForFixedFunctionColor(tex1);
@@ -4879,7 +5048,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
for (UINT i = 0; i < QD3D12_MaxTextureUnits; ++i)
key.textureSrvIndex[i] = (allTextures && allTextures[i]) ? allTextures[i]->srvIndex : 0;
- TextureResource* normalMap = QD3D12_SelectNormalMapTexture(allTextures);
+ TextureResource* normalMap = selectedMapsValid ? selectedNormalMap : QD3D12_SelectNormalMapTexture(allTextures);
if (normalMap && normalMap->texture && normalMap->srvIndex != UINT_MAX && normalMap->gpuValid)
{
key.normalMapSrvIndex = normalMap->srvIndex;
@@ -4894,7 +5063,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
key.normalMapYSign = g_gl.currentNormalMapYSign;
key.useTessellation = false;
- TextureResource* glowMap = QD3D12_SelectGlowMapTexture(allTextures);
+ TextureResource* glowMap = selectedMapsValid ? selectedGlowMap : QD3D12_SelectGlowMapTexture(allTextures);
if (glowMap && glowMap->texture && glowMap->srvIndex != UINT_MAX)
{
key.glowMapSrvIndex = glowMap->srvIndex;
@@ -4907,7 +5076,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
}
key.glowMapStrength = g_gl.currentGlowMapStrength;
- TextureResource* specularMap = QD3D12_SelectSpecularMapTexture(allTextures);
+ TextureResource* specularMap = selectedMapsValid ? selectedSpecularMap : QD3D12_SelectSpecularMapTexture(allTextures);
if (specularMap && specularMap->texture && specularMap->srvIndex != UINT_MAX)
{
key.specularMapSrvIndex = specularMap->srvIndex;
@@ -4975,7 +5144,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
}
key.alphaRef = g_gl.alphaRef;
- key.alphaFunc = MapAlphaFunc(g_gl.alphaFunc);
+ key.alphaFunc = g_gl.alphaFuncMapped;
key.useTex0 = useTex0 ? 1.0f : 0.0f;
key.useTex1 = useTex1 ? 1.0f : 0.0f;
key.viewport = g_currentWindow->viewport;
@@ -4998,6 +5167,11 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
key.fogColor[1] = g_gl.fogColor[1];
key.fogColor[2] = g_gl.fogColor[2];
key.fogColor[3] = g_gl.fogColor[3];
+ key.currentColor[0] = g_gl.curColor[0];
+ key.currentColor[1] = g_gl.curColor[1];
+ key.currentColor[2] = g_gl.curColor[2];
+ key.currentColor[3] = g_gl.curColor[3];
+ key.useVertexColor = g_gl.colorArray.enabled ? 1.0f : 0.0f;
QD3D12_FillTexCombineKey(key, 0);
QD3D12_FillTexCombineKey(key, 1);
key.colorWriteMask = QD3D12_CurrentColorWriteMask();
@@ -5051,11 +5225,6 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource*
return key;
}
-#ifdef _DEBUG
-#pragma optimize on
-#endif
-
-
static Mat4 QD3D12_GetPreviousMVPForObject(GLuint objectId, const Mat4& currentMvp)
{
auto it = g_gl.prevObjectMVPs.find(objectId);
@@ -10855,6 +11024,9 @@ void glLoadModelMatrixf(const float* m16)
memcpy(m.m, m16, sizeof(float) * 16);
}
+ if (memcmp(g_gl.modelMatrix.m, m.m, sizeof(g_gl.modelMatrix.m)) == 0)
+ return;
+
memcpy(g_gl.modelMatrix.m, m.m, sizeof(g_gl.modelMatrix.m));
}
@@ -10887,6 +11059,128 @@ static inline void AppendVerticesFast(std::vector& dst, const std::vec
memcpy(dst.data() + oldSize, src.data(), addCount * sizeof(GLVertex));
}
+static GLVertex* QD3D12_AppendToQueuedBatch(QueuedBatch* batch, size_t outCount)
+{
+ if (!batch || outCount == 0)
+ return nullptr;
+
+ size_t first = 0;
+ GLVertex* dst = QD3D12_AllocFrameVertices(outCount, &first);
+ if (!dst)
+ return nullptr;
+
+ if (batch->vertexCount == 0)
+ {
+ batch->firstVertex = first;
+ batch->vertexCount = outCount;
+ }
+ else
+ {
+ assert(batch->firstVertex + batch->vertexCount == first);
+ batch->vertexCount += outCount;
+ }
+
+ return dst;
+}
+
+static QueuedBatch* QD3D12_PrepareImmediateBatch(GLenum mode, size_t n)
+{
+ QD3D12_EnsureFrameOpen();
+
+ const bool arbProgramsActive = QD3D12ARB_IsActive();
+
+ TextureResource* boundTextures[QD3D12_MaxTextureUnits] = {};
+ for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
+ boundTextures[unit] = &g_gl.whiteTexture;
+
+ for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
+ {
+ const GLuint boundTexture = g_gl.boundTexture[unit];
+ const bool arbUnit = arbProgramsActive && (boundTexture != 0);
+ const bool fixedColorUnit = (!arbProgramsActive) && (g_gl.texture2D[unit] && unit < 2);
+ TextureResource* tex = boundTexture != 0 ? QD3D12_FindTextureResource(boundTexture) : nullptr;
+ const bool taggedNormalUnit = (!arbProgramsActive) && tex && tex->isNormalMap;
+ const bool explicitNormalUnit = (!arbProgramsActive) && (g_gl.currentNormalMapTexture != 0) && (boundTexture == g_gl.currentNormalMapTexture);
+ const bool taggedGlowUnit = (!arbProgramsActive) && tex && tex->isGlowMap;
+ const bool explicitGlowUnit = (!arbProgramsActive) && (g_gl.currentGlowMapTexture != 0) && (boundTexture == g_gl.currentGlowMapTexture);
+ const bool taggedSpecularUnit = (!arbProgramsActive) && tex && tex->isSpecularMap;
+ const bool explicitSpecularUnit = (!arbProgramsActive) && (g_gl.currentSpecularMapTexture != 0) && (boundTexture == g_gl.currentSpecularMapTexture);
+
+ const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit || taggedSpecularUnit || explicitSpecularUnit;
+ if (wantsUnit && tex)
+ boundTextures[unit] = tex;
+ }
+
+ for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
+ {
+ TextureResource* tex = boundTextures[unit];
+ if (tex && tex != &g_gl.whiteTexture && !tex->gpuValid)
+ {
+ EnsureTextureResource(*tex);
+ UploadTexture(*tex);
+ }
+ }
+
+ TextureResource* normalMapTex = QD3D12_SelectNormalMapTexture(boundTextures);
+ if (normalMapTex && normalMapTex != &g_gl.whiteTexture && !normalMapTex->gpuValid)
+ {
+ EnsureTextureResource(*normalMapTex);
+ UploadTexture(*normalMapTex);
+ }
+ if (normalMapTex && normalMapTex != &g_gl.whiteTexture && QD3D12_TextureHasNeuralPOMData(*normalMapTex))
+ QD3D12_UploadNeuralPOM(*normalMapTex);
+
+ if (g_gl.currentNeuralPOMTexture != 0)
+ {
+ TextureResource* explicitNeural = QD3D12_FindTextureResource(g_gl.currentNeuralPOMTexture);
+ if (explicitNeural && QD3D12_TextureHasNeuralPOMData(*explicitNeural))
+ QD3D12_UploadNeuralPOM(*explicitNeural);
+ }
+
+ for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
+ {
+ TextureResource* neuralCandidate = boundTextures[unit];
+ if (neuralCandidate && neuralCandidate != &g_gl.whiteTexture && QD3D12_TextureHasNeuralPOMData(*neuralCandidate))
+ QD3D12_UploadNeuralPOM(*neuralCandidate);
+ }
+
+ TextureResource* glowMapTex = QD3D12_SelectGlowMapTexture(boundTextures);
+ if (glowMapTex && glowMapTex != &g_gl.whiteTexture && !glowMapTex->gpuValid)
+ {
+ EnsureTextureResource(*glowMapTex);
+ UploadTexture(*glowMapTex);
+ }
+
+ TextureResource* specularMapTex = QD3D12_SelectSpecularMapTexture(boundTextures);
+ if (specularMapTex && specularMapTex != &g_gl.whiteTexture && !specularMapTex->gpuValid)
+ {
+ EnsureTextureResource(*specularMapTex);
+ UploadTexture(*specularMapTex);
+ }
+
+ BatchKey key = BuildCurrentBatchKey(mode, boundTextures[0], boundTextures[1], boundTextures, normalMapTex, glowMapTex, specularMapTex, true);
+ if (key.useTessellation && !QD3D12_ImmediateVertexCountCanUseNormalMapTessellation(mode, n))
+ key.useTessellation = false;
+
+ const size_t markerCursor = g_gl.queryMarkers.size();
+ if (!g_gl.queuedBatches.empty() &&
+ !g_gl.queuedBatches.back().gpuIndexed &&
+ BatchKeyEquals(g_gl.queuedBatches.back().key, key) &&
+ g_gl.queuedBatches.back().markerEnd == markerCursor)
+ {
+ return &g_gl.queuedBatches.back();
+ }
+
+ QueuedBatch newBatch{};
+ newBatch.key = key;
+ newBatch.markerBegin = markerCursor;
+ newBatch.markerEnd = markerCursor;
+ newBatch.firstVertex = 0;
+ newBatch.vertexCount = 0;
+ g_gl.queuedBatches.push_back(newBatch);
+ return &g_gl.queuedBatches.back();
+}
+
static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
{
if (!src || n == 0)
@@ -10904,27 +11198,25 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
{
+ const GLuint boundTexture = g_gl.boundTexture[unit];
const bool arbUnit = arbProgramsActive && (g_gl.boundTexture[unit] != 0);
const bool fixedColorUnit = (!arbProgramsActive) && (g_gl.texture2D[unit] && unit < 2);
- const bool taggedNormalUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedNormalMap(g_gl.boundTexture[unit]);
+ TextureResource* tex = boundTexture != 0 ? QD3D12_FindTextureResource(boundTexture) : nullptr;
+ const bool taggedNormalUnit = (!arbProgramsActive) && tex && tex->isNormalMap;
const bool explicitNormalUnit = (!arbProgramsActive) &&
(g_gl.currentNormalMapTexture != 0) &&
- (g_gl.boundTexture[unit] == g_gl.currentNormalMapTexture);
- const bool taggedGlowUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedGlowMap(g_gl.boundTexture[unit]);
+ (boundTexture == g_gl.currentNormalMapTexture);
+ const bool taggedGlowUnit = (!arbProgramsActive) && tex && tex->isGlowMap;
const bool explicitGlowUnit = (!arbProgramsActive) &&
(g_gl.currentGlowMapTexture != 0) &&
- (g_gl.boundTexture[unit] == g_gl.currentGlowMapTexture);
- const bool taggedSpecularUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedSpecularMap(g_gl.boundTexture[unit]);
+ (boundTexture == g_gl.currentGlowMapTexture);
+ const bool taggedSpecularUnit = (!arbProgramsActive) && tex && tex->isSpecularMap;
const bool explicitSpecularUnit = (!arbProgramsActive) &&
(g_gl.currentSpecularMapTexture != 0) &&
- (g_gl.boundTexture[unit] == g_gl.currentSpecularMapTexture);
+ (boundTexture == g_gl.currentSpecularMapTexture);
const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit || taggedSpecularUnit || explicitSpecularUnit;
- if (!wantsUnit)
- continue;
-
- TextureResource* tex = QD3D12_FindTextureResource(g_gl.boundTexture[unit]);
- if (tex)
+ if (wantsUnit && tex)
boundTextures[unit] = tex;
}
@@ -10982,7 +11274,7 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
TextureResource* tex0 = boundTextures[0];
TextureResource* tex1 = boundTextures[1];
- BatchKey key = BuildCurrentBatchKey(mode, tex0, tex1, boundTextures);
+ BatchKey key = BuildCurrentBatchKey(mode, tex0, tex1, boundTextures, normalMapTex, glowMapTex, specularMapTex, true);
if (key.useTessellation && !QD3D12_ImmediateVertexCountCanUseNormalMapTessellation(mode, n))
key.useTessellation = false;
const size_t markerCursor = g_gl.queryMarkers.size();
@@ -10990,6 +11282,7 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
QueuedBatch* batch = nullptr;
if (!g_gl.queuedBatches.empty() &&
+ !g_gl.queuedBatches.back().gpuIndexed &&
BatchKeyEquals(g_gl.queuedBatches.back().key, key) &&
g_gl.queuedBatches.back().markerEnd == markerCursor)
{
@@ -11007,30 +11300,9 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n)
batch = &g_gl.queuedBatches.back();
}
- auto AppendToBatch = [&](size_t outCount) -> GLVertex*
- {
- if (outCount == 0)
- return nullptr;
-
- size_t first = 0;
- GLVertex* dst = QD3D12_AllocFrameVertices(outCount, &first);
- if (!dst)
- return nullptr;
-
- if (batch->vertexCount == 0)
- {
- batch->firstVertex = first;
- batch->vertexCount = outCount;
- }
- else
- {
- // Since arena is linear, merged batches must remain contiguous.
- assert(batch->firstVertex + batch->vertexCount == first);
- batch->vertexCount += outCount;
- }
-
- return dst;
- };
+ auto AppendToBatch = [&](size_t outCount) -> GLVertex* {
+ return QD3D12_AppendToQueuedBatch(batch, outCount);
+ };
switch (mode)
{
@@ -11302,8 +11574,7 @@ static void QD3D12_FlushQueuedBatches()
for (size_t i = 0; i < g_gl.queuedBatches.size(); ++i)
{
const QueuedBatch& batch = g_gl.queuedBatches[i];
- const GLVertex* verts = QD3D12_GetBatchVertices(batch);
- const size_t count = batch.vertexCount;
+ const size_t count = batch.gpuIndexed ? (size_t)batch.gpuIndexCount : batch.vertexCount;
if (count <= 0)
continue;
@@ -11329,7 +11600,7 @@ static void QD3D12_FlushQueuedBatches()
g_gl.sceneFogCameraValid = batch.key.cameraValid;
}
- const UINT vbBytes = (UINT)(count * sizeof(GLVertex));
+ const UINT vbBytes = batch.gpuIndexed ? 0 : (UINT)(count * sizeof(GLVertex));
const UINT cbBytes = (UINT)sizeof(DrawConstants);
if (QD3D12_EnsureUploadSpaceForDraw(vbBytes, cbBytes))
@@ -11355,8 +11626,13 @@ static void QD3D12_FlushQueuedBatches()
g_gl.cmdList->RSSetViewports(1, &batch.key.viewport);
g_gl.cmdList->RSSetScissorRects(1, &batch.key.scissor);
- UploadAlloc vbAlloc = QD3D12_AllocUpload(vbBytes, 256);
- memcpy(vbAlloc.cpu, verts, vbBytes);
+ UploadAlloc vbAlloc{};
+ if (!batch.gpuIndexed)
+ {
+ const GLVertex* verts = QD3D12_GetBatchVertices(batch);
+ vbAlloc = QD3D12_AllocUpload(vbBytes, 256);
+ memcpy(vbAlloc.cpu, verts, vbBytes);
+ }
UploadAlloc cbAlloc = QD3D12_AllocUpload(cbBytes, 256);
DrawConstants* dc = reinterpret_cast(cbAlloc.cpu);
@@ -11432,6 +11708,11 @@ static void QD3D12_FlushQueuedBatches()
const float neuralStrength = ClampValue(batch.key.normalMapStrength, 0.0f, 4.0f);
dc->neuralPomPad[2] = ClampValue(1.0f + (neuralStrength - 1.0f) * 0.65f, 1.0f, 3.0f);
dc->neuralPomPad[3] = ClampValue(1.0f + (neuralStrength - 1.0f) * 0.45f, 1.0f, 2.5f);
+ memcpy(dc->currentColor, batch.key.currentColor, sizeof(dc->currentColor));
+ dc->vertexColorPad[0] = batch.key.useVertexColor;
+ dc->vertexColorPad[1] = 0.0f;
+ dc->vertexColorPad[2] = 0.0f;
+ dc->vertexColorPad[3] = 0.0f;
if (batch.key.useARBPrograms)
{
@@ -11441,9 +11722,16 @@ static void QD3D12_FlushQueuedBatches()
}
D3D12_VERTEX_BUFFER_VIEW vbv{};
- vbv.BufferLocation = vbAlloc.gpu;
- vbv.SizeInBytes = (UINT)(count * sizeof(GLVertex));
- vbv.StrideInBytes = sizeof(GLVertex);
+ if (batch.gpuIndexed)
+ {
+ vbv = batch.gpuVbv;
+ }
+ else
+ {
+ vbv.BufferLocation = vbAlloc.gpu;
+ vbv.SizeInBytes = (UINT)(count * sizeof(GLVertex));
+ vbv.StrideInBytes = sizeof(GLVertex);
+ }
ID3D12PipelineState* pso = nullptr;
const D3D12_PRIMITIVE_TOPOLOGY_TYPE topoType =
@@ -11586,7 +11874,16 @@ static void QD3D12_FlushQueuedBatches()
}
g_gl.cmdList->IASetVertexBuffers(0, 1, &vbv);
- g_gl.cmdList->DrawInstanced((UINT)count, 1, 0, 0);
+ if (batch.gpuIndexed)
+ {
+ g_gl.cmdList->IASetIndexBuffer(&batch.gpuIbv);
+ g_gl.cmdList->DrawIndexedInstanced(batch.gpuIndexCount, 1, 0, 0, 0);
+ }
+ else
+ {
+ g_gl.cmdList->IASetIndexBuffer(nullptr);
+ g_gl.cmdList->DrawInstanced((UINT)count, 1, 0, 0);
+ }
}
g_gl.queuedBatches.clear();
@@ -11846,6 +12143,7 @@ void APIENTRY glBlendFunc(GLenum sfactor, GLenum dfactor)
void APIENTRY glAlphaFunc(GLenum func, GLclampf ref)
{
g_gl.alphaFunc = func;
+ g_gl.alphaFuncMapped = MapAlphaFunc(func);
g_gl.alphaRef = ref;
}
@@ -12098,11 +12396,11 @@ void APIENTRY glDeleteTextures(GLsizei n, const GLuint* textures)
}
}
-#ifdef _DEBUG
-#pragma optimize off
-#endif
void APIENTRY glBindTexture(GLenum, GLuint texture)
{
+ if (g_gl.boundTexture[g_gl.activeTextureUnit] == texture)
+ return;
+
g_gl.boundTexture[g_gl.activeTextureUnit] = texture;
if (texture == 0)
@@ -12130,6 +12428,9 @@ void APIENTRY glTextureNormalMap(GLuint texture, GLboolean isNormalMap)
void APIENTRY glBindNormalMapTexture(GLuint texture)
{
+ if (g_gl.currentNormalMapTexture == texture)
+ return;
+
if (texture != 0)
(void)QD3D12_EnsureTextureName(texture);
@@ -12143,7 +12444,10 @@ void APIENTRY glNormalMapTexture(GLuint texture)
void APIENTRY glNormalMapStrengthf(GLfloat strength)
{
- g_gl.currentNormalMapStrength = (strength < 0.0f) ? 0.0f : strength;
+ const float clamped = (strength < 0.0f) ? 0.0f : strength;
+ if (g_gl.currentNormalMapStrength == clamped)
+ return;
+ g_gl.currentNormalMapStrength = clamped;
}
void APIENTRY glNormalMapYSignf(GLfloat sign)
@@ -12332,6 +12636,9 @@ void APIENTRY glClearNeuralPOMMaterialQD3D12(GLuint texture)
void APIENTRY glBindNeuralPOMTextureQD3D12(GLuint texture)
{
+ if (g_gl.currentNeuralPOMTexture == texture)
+ return;
+
if (texture != 0)
(void)QD3D12_EnsureTextureName(texture);
g_gl.currentNeuralPOMTexture = texture;
@@ -12366,6 +12673,9 @@ void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap)
void APIENTRY glBindGlowMapTexture(GLuint texture)
{
+ if (g_gl.currentGlowMapTexture == texture)
+ return;
+
if (texture != 0)
{
TextureResource& tex = QD3D12_EnsureTextureName(texture);
@@ -12387,7 +12697,10 @@ void APIENTRY glGlowMapTexture(GLuint texture)
void APIENTRY glGlowMapStrengthf(GLfloat strength)
{
- g_gl.currentGlowMapStrength = (strength < 0.0f) ? 0.0f : strength;
+ const float clamped = (strength < 0.0f) ? 0.0f : strength;
+ if (g_gl.currentGlowMapStrength == clamped)
+ return;
+ g_gl.currentGlowMapStrength = clamped;
}
void APIENTRY glTagTextureSpecularMap(GLuint texture, GLboolean isSpecularMap)
@@ -12409,6 +12722,9 @@ void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap)
void APIENTRY glBindSpecularMapTexture(GLuint texture)
{
+ if (g_gl.currentSpecularMapTexture == texture)
+ return;
+
if (texture != 0)
{
TextureResource& tex = QD3D12_EnsureTextureName(texture);
@@ -12456,9 +12772,6 @@ void APIENTRY glMaterialGlassQD3D12(GLboolean enable)
{
glGlassMaterialQD3D12(enable);
}
-#ifdef _DEBUG
-#pragma optimize on
-#endif
void APIENTRY glLoadMatrixf(const GLfloat* m)
{
@@ -14015,26 +14328,50 @@ void APIENTRY glUnlockArraysEXT(void) {
void APIENTRY glNormalPointer(GLenum type, GLsizei stride, const void* pointer)
{
- g_gl.normalArray.size = 3;
- g_gl.normalArray.type = type;
- g_gl.normalArray.stride = stride;
- g_gl.normalArray.ptr = QD3D12_ResolveArrayPointer(pointer);
+ const uint8_t* resolved = QD3D12_ResolveArrayPointer(pointer);
+ const GLuint buffer = g_gl.boundArrayBuffer;
+ const size_t offset = buffer != 0 ? (size_t)pointer : 0;
+ if (g_gl.normalArray.size == 3 &&
+ g_gl.normalArray.type == type &&
+ g_gl.normalArray.stride == stride &&
+ g_gl.normalArray.ptr == resolved &&
+ g_gl.normalArray.buffer == buffer &&
+ g_gl.normalArray.offset == offset)
+ return;
+
+ QD3D12_CaptureArrayPointer(g_gl.normalArray, 3, type, stride, pointer);
}
void APIENTRY glTangentPointer(GLenum type, GLsizei stride, const void* pointer)
{
- g_gl.tangentArray.size = 3;
- g_gl.tangentArray.type = type;
- g_gl.tangentArray.stride = stride;
- g_gl.tangentArray.ptr = QD3D12_ResolveArrayPointer(pointer);
+ const uint8_t* resolved = QD3D12_ResolveArrayPointer(pointer);
+ const GLuint buffer = g_gl.boundArrayBuffer;
+ const size_t offset = buffer != 0 ? (size_t)pointer : 0;
+ if (g_gl.tangentArray.size == 3 &&
+ g_gl.tangentArray.type == type &&
+ g_gl.tangentArray.stride == stride &&
+ g_gl.tangentArray.ptr == resolved &&
+ g_gl.tangentArray.buffer == buffer &&
+ g_gl.tangentArray.offset == offset)
+ return;
+
+ QD3D12_CaptureArrayPointer(g_gl.tangentArray, 3, type, stride, pointer);
}
void APIENTRY glBinormalPointer(GLenum type, GLsizei stride, const void* pointer)
{
- g_gl.bitangentArray.size = 3;
- g_gl.bitangentArray.type = type;
- g_gl.bitangentArray.stride = stride;
- g_gl.bitangentArray.ptr = QD3D12_ResolveArrayPointer(pointer);
+ const uint8_t* resolved = QD3D12_ResolveArrayPointer(pointer);
+ const GLuint buffer = g_gl.boundArrayBuffer;
+ const size_t offset = buffer != 0 ? (size_t)pointer : 0;
+ if (g_gl.bitangentArray.size == 3 &&
+ g_gl.bitangentArray.type == type &&
+ g_gl.bitangentArray.stride == stride &&
+ g_gl.bitangentArray.ptr == resolved &&
+ g_gl.bitangentArray.buffer == buffer &&
+ g_gl.bitangentArray.offset == offset)
+ return;
+
+ QD3D12_CaptureArrayPointer(g_gl.bitangentArray, 3, type, stride, pointer);
}
void APIENTRY glNormalBuffer(GLenum type, GLsizei stride, const void* pointer)
@@ -14060,21 +14397,33 @@ void APIENTRY glEnableClientState(GLenum array)
switch (array)
{
case GL_VERTEX_ARRAY:
+ if (g_gl.vertexArray.enabled)
+ return;
g_gl.vertexArray.enabled = true;
break;
case GL_NORMAL_ARRAY:
+ if (g_gl.normalArray.enabled)
+ return;
g_gl.normalArray.enabled = true;
break;
case GL_TANGENT_ARRAY_QD3D12:
+ if (g_gl.tangentArray.enabled)
+ return;
g_gl.tangentArray.enabled = true;
break;
case GL_BINORMAL_ARRAY_QD3D12:
+ if (g_gl.bitangentArray.enabled)
+ return;
g_gl.bitangentArray.enabled = true;
break;
case GL_COLOR_ARRAY:
+ if (g_gl.colorArray.enabled)
+ return;
g_gl.colorArray.enabled = true;
break;
case GL_TEXTURE_COORD_ARRAY:
+ if (g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled)
+ return;
g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled = true;
break;
default:
@@ -14087,21 +14436,33 @@ void APIENTRY glDisableClientState(GLenum array)
switch (array)
{
case GL_VERTEX_ARRAY:
+ if (!g_gl.vertexArray.enabled)
+ return;
g_gl.vertexArray.enabled = false;
break;
case GL_NORMAL_ARRAY:
+ if (!g_gl.normalArray.enabled)
+ return;
g_gl.normalArray.enabled = false;
break;
case GL_TANGENT_ARRAY_QD3D12:
+ if (!g_gl.tangentArray.enabled)
+ return;
g_gl.tangentArray.enabled = false;
break;
case GL_BINORMAL_ARRAY_QD3D12:
+ if (!g_gl.bitangentArray.enabled)
+ return;
g_gl.bitangentArray.enabled = false;
break;
case GL_COLOR_ARRAY:
+ if (!g_gl.colorArray.enabled)
+ return;
g_gl.colorArray.enabled = false;
break;
case GL_TEXTURE_COORD_ARRAY:
+ if (!g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled)
+ return;
g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled = false;
break;
default:
@@ -14111,27 +14472,51 @@ void APIENTRY glDisableClientState(GLenum array)
void APIENTRY glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* ptr)
{
- g_gl.vertexArray.size = size;
- g_gl.vertexArray.type = type;
- g_gl.vertexArray.stride = stride;
- g_gl.vertexArray.ptr = QD3D12_ResolveArrayPointer(ptr);
+ const uint8_t* resolved = QD3D12_ResolveArrayPointer(ptr);
+ const GLuint buffer = g_gl.boundArrayBuffer;
+ const size_t offset = buffer != 0 ? (size_t)ptr : 0;
+ if (g_gl.vertexArray.size == size &&
+ g_gl.vertexArray.type == type &&
+ g_gl.vertexArray.stride == stride &&
+ g_gl.vertexArray.ptr == resolved &&
+ g_gl.vertexArray.buffer == buffer &&
+ g_gl.vertexArray.offset == offset)
+ return;
+
+ QD3D12_CaptureArrayPointer(g_gl.vertexArray, size, type, stride, ptr);
}
void APIENTRY glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* ptr)
{
- g_gl.colorArray.size = size;
- g_gl.colorArray.type = type;
- g_gl.colorArray.stride = stride;
- g_gl.colorArray.ptr = QD3D12_ResolveArrayPointer(ptr);
+ const uint8_t* resolved = QD3D12_ResolveArrayPointer(ptr);
+ const GLuint buffer = g_gl.boundArrayBuffer;
+ const size_t offset = buffer != 0 ? (size_t)ptr : 0;
+ if (g_gl.colorArray.size == size &&
+ g_gl.colorArray.type == type &&
+ g_gl.colorArray.stride == stride &&
+ g_gl.colorArray.ptr == resolved &&
+ g_gl.colorArray.buffer == buffer &&
+ g_gl.colorArray.offset == offset)
+ return;
+
+ QD3D12_CaptureArrayPointer(g_gl.colorArray, size, type, stride, ptr);
}
void APIENTRY glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* ptr)
{
auto& tc = g_gl.texCoordArray[g_gl.clientActiveTextureUnit];
- tc.size = size;
- tc.type = type;
- tc.stride = stride;
- tc.ptr = QD3D12_ResolveArrayPointer(ptr);
+ const uint8_t* resolved = QD3D12_ResolveArrayPointer(ptr);
+ const GLuint buffer = g_gl.boundArrayBuffer;
+ const size_t offset = buffer != 0 ? (size_t)ptr : 0;
+ if (tc.size == size &&
+ tc.type == type &&
+ tc.stride == stride &&
+ tc.ptr == resolved &&
+ tc.buffer == buffer &&
+ tc.offset == offset)
+ return;
+
+ QD3D12_CaptureArrayPointer(tc, size, type, stride, ptr);
}
void APIENTRY glArrayElement(GLint i)
@@ -14140,11 +14525,205 @@ void APIENTRY glArrayElement(GLint i)
QD3D12_FetchArrayVertex(i, v);
}
+static bool QD3D12_ArrayUsesOnlyBuffer(const GLState::ClientArrayState& array, GLuint buffer)
+{
+ return !array.enabled || array.buffer == buffer;
+}
+
+static bool QD3D12_CanPackCurrentVertexBuffer(GLuint vertexBuffer)
+{
+ if (vertexBuffer == 0 || !g_gl.vertexArray.enabled || g_gl.vertexArray.buffer != vertexBuffer)
+ return false;
+
+ if (g_gl.vertexArray.type != GL_FLOAT || g_gl.vertexArray.size < 3)
+ return false;
+
+ if (!QD3D12_ArrayUsesOnlyBuffer(g_gl.normalArray, vertexBuffer) ||
+ !QD3D12_ArrayUsesOnlyBuffer(g_gl.tangentArray, vertexBuffer) ||
+ !QD3D12_ArrayUsesOnlyBuffer(g_gl.bitangentArray, vertexBuffer) ||
+ !QD3D12_ArrayUsesOnlyBuffer(g_gl.colorArray, vertexBuffer))
+ return false;
+
+ for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
+ {
+ if (!QD3D12_ArrayUsesOnlyBuffer(g_gl.texCoordArray[unit], vertexBuffer))
+ return false;
+ }
+
+ return true;
+}
+
+static uint64_t QD3D12_HashClientArrayLayout(const GLState::ClientArrayState& array, uint64_t hash)
+{
+ hash ^= (uint64_t)array.enabled + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
+ hash ^= (uint64_t)array.size + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
+ hash ^= (uint64_t)array.type + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
+ hash ^= (uint64_t)array.stride + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
+ hash ^= (uint64_t)array.buffer + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
+ hash ^= (uint64_t)array.offset + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2);
+ return hash;
+}
+
+static uint64_t QD3D12_CurrentClientArrayLayoutHash()
+{
+ uint64_t hash = 1469598103934665603ull;
+ hash = QD3D12_HashClientArrayLayout(g_gl.vertexArray, hash);
+ hash = QD3D12_HashClientArrayLayout(g_gl.normalArray, hash);
+ hash = QD3D12_HashClientArrayLayout(g_gl.tangentArray, hash);
+ hash = QD3D12_HashClientArrayLayout(g_gl.bitangentArray, hash);
+ hash = QD3D12_HashClientArrayLayout(g_gl.colorArray, hash);
+ for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit)
+ hash = QD3D12_HashClientArrayLayout(g_gl.texCoordArray[unit], hash);
+ return hash;
+}
+
+static bool QD3D12_EnsurePackedVertexBuffer(GLBufferObject& bo)
+{
+ if (bo.usage == GL_STREAM_DRAW_ARB || bo.data.empty())
+ return false;
+
+ if (!QD3D12_CanPackCurrentVertexBuffer(bo.id))
+ return false;
+
+ const GLsizei strideValue = g_gl.vertexArray.stride;
+ const size_t vertexTypeSize = QD3D12_TypeSize(g_gl.vertexArray.type);
+ const size_t vertexStride = strideValue ? (size_t)strideValue : (size_t)g_gl.vertexArray.size * vertexTypeSize;
+ if (vertexStride == 0 || g_gl.vertexArray.offset >= bo.data.size())
+ return false;
+
+ const size_t vertexCount = (bo.data.size() - g_gl.vertexArray.offset) / vertexStride;
+ const size_t packedBytes = vertexCount * sizeof(GLVertex);
+ if (vertexCount == 0 || packedBytes == 0)
+ return false;
+
+ const uint64_t layoutHash = QD3D12_CurrentClientArrayLayoutHash();
+ if (bo.packedVertexResource &&
+ bo.packedVertexMapped &&
+ bo.packedVertexRevision == bo.revision &&
+ bo.packedVertexLayoutHash == layoutHash &&
+ bo.packedVertexCount == vertexCount &&
+ bo.packedVertexBytes >= packedBytes)
+ {
+ return true;
+ }
+
+ QD3D12_ResetPackedVertexBuffer(bo);
+
+ if (!g_gl.device)
+ return false;
+
+ D3D12_HEAP_PROPERTIES hp{};
+ hp.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ D3D12_RESOURCE_DESC rd{};
+ rd.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ rd.Width = (UINT64)packedBytes;
+ rd.Height = 1;
+ rd.DepthOrArraySize = 1;
+ rd.MipLevels = 1;
+ rd.SampleDesc.Count = 1;
+ rd.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+
+ QD3D12_CHECK(g_gl.device->CreateCommittedResource(
+ &hp,
+ D3D12_HEAP_FLAG_NONE,
+ &rd,
+ D3D12_RESOURCE_STATE_GENERIC_READ,
+ nullptr,
+ IID_PPV_ARGS(&bo.packedVertexResource)));
+
+ bo.packedVertexGpuAddress = bo.packedVertexResource->GetGPUVirtualAddress();
+ bo.packedVertexBytes = packedBytes;
+ bo.packedVertexCount = vertexCount;
+ QD3D12_CHECK(bo.packedVertexResource->Map(0, nullptr, reinterpret_cast(&bo.packedVertexMapped)));
+
+ GLVertex* packed = reinterpret_cast(bo.packedVertexMapped);
+ for (size_t i = 0; i < vertexCount; ++i)
+ QD3D12_FetchArrayVertex((GLint)i, packed[i]);
+
+ bo.packedVertexRevision = bo.revision;
+ bo.packedVertexLayoutHash = layoutHash;
+ return true;
+}
+
+static DXGI_FORMAT QD3D12_IndexFormatFromGL(GLenum type)
+{
+ switch (type)
+ {
+ case GL_UNSIGNED_INT: return DXGI_FORMAT_R32_UINT;
+ case GL_UNSIGNED_SHORT: return DXGI_FORMAT_R16_UINT;
+ default: return DXGI_FORMAT_UNKNOWN;
+ }
+}
+
+static size_t QD3D12_IndexSizeFromGL(GLenum type)
+{
+ switch (type)
+ {
+ case GL_UNSIGNED_INT: return sizeof(GLuint);
+ case GL_UNSIGNED_SHORT: return sizeof(GLushort);
+ case GL_UNSIGNED_BYTE: return sizeof(GLubyte);
+ default: return 0;
+ }
+}
+
+static bool QD3D12_TryQueueBufferedDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
+{
+ if (mode != GL_TRIANGLES || type == GL_UNSIGNED_BYTE)
+ return false;
+ if (QD3D12ARB_IsActive())
+ return false;
+
+ GLBufferObject* vertexBo = QD3D12_GetBuffer(g_gl.vertexArray.buffer);
+ GLBufferObject* indexBo = QD3D12_GetBuffer(g_gl.boundElementArrayBuffer);
+ if (!vertexBo || !indexBo || !indexBo->resource || !indexBo->mappedGpu)
+ return false;
+
+ if (!QD3D12_EnsurePackedVertexBuffer(*vertexBo))
+ return false;
+
+ const size_t indexSize = QD3D12_IndexSizeFromGL(type);
+ const size_t indexOffset = (g_gl.boundElementArrayBuffer != 0) ? (size_t)indices : 0;
+ const size_t indexBytes = (size_t)count * indexSize;
+ if (indexSize == 0 || indexOffset > indexBo->gpuBytes || indexBytes > (indexBo->gpuBytes - indexOffset))
+ return false;
+
+ QueuedBatch* batch = QD3D12_PrepareImmediateBatch(mode, (size_t)count);
+ if (!batch || batch->vertexCount != 0 || batch->gpuIndexed)
+ {
+ QueuedBatch newBatch{};
+ newBatch.key = batch ? batch->key : BuildCurrentBatchKey(mode, nullptr, nullptr, nullptr);
+ newBatch.markerBegin = g_gl.queryMarkers.size();
+ newBatch.markerEnd = newBatch.markerBegin;
+ g_gl.queuedBatches.push_back(newBatch);
+ batch = &g_gl.queuedBatches.back();
+ }
+
+ batch->gpuIndexed = true;
+ batch->gpuVertexResource = vertexBo->packedVertexResource;
+ batch->gpuIndexResource = indexBo->resource;
+ batch->gpuIndexCount = (UINT)count;
+ batch->vertexCount = (size_t)count;
+ batch->gpuVbv.BufferLocation = vertexBo->packedVertexGpuAddress;
+ batch->gpuVbv.SizeInBytes = (UINT)vertexBo->packedVertexBytes;
+ batch->gpuVbv.StrideInBytes = sizeof(GLVertex);
+ batch->gpuIbv.BufferLocation = indexBo->gpuAddress + indexOffset;
+ batch->gpuIbv.SizeInBytes = (UINT)indexBytes;
+ batch->gpuIbv.Format = QD3D12_IndexFormatFromGL(type);
+ return true;
+}
+
void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
{
if (count <= 0)
return;
+ if (type != GL_UNSIGNED_INT && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_BYTE)
+ {
+ g_gl.lastError = GL_INVALID_ENUM;
+ return;
+ }
+
const void* resolvedIndices = QD3D12_ResolveElementPointer(indices, type, count);
if (!resolvedIndices)
{
@@ -14154,7 +14733,47 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo
g_gl.currentPrim = mode;
- g_gl.immediateVerts.Clear();
+ if (QD3D12_TryQueueBufferedDrawElements(mode, count, type, indices))
+ return;
+
+ if (mode == GL_TRIANGLES)
+ {
+ QueuedBatch* batch = QD3D12_PrepareImmediateBatch(mode, static_cast(count));
+ GLVertex* outVerts = QD3D12_AppendToQueuedBatch(batch, static_cast(count));
+ if (!outVerts)
+ return;
+
+ switch (type)
+ {
+ case GL_UNSIGNED_INT:
+ {
+ const GLuint* idx = static_cast(resolvedIndices);
+ for (GLsizei i = 0; i < count; ++i)
+ QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]);
+ return;
+ }
+
+ case GL_UNSIGNED_SHORT:
+ {
+ const GLushort* idx = static_cast(resolvedIndices);
+ for (GLsizei i = 0; i < count; ++i)
+ QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]);
+ return;
+ }
+
+ case GL_UNSIGNED_BYTE:
+ {
+ const GLubyte* idx = static_cast(resolvedIndices);
+ for (GLsizei i = 0; i < count; ++i)
+ QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]);
+ return;
+ }
+ }
+ }
+
+ GLVertex* outVerts = g_gl.immediateVerts.ResizeForWrite(static_cast(count));
+ if (!outVerts)
+ return;
switch (type)
{
@@ -14163,8 +14782,7 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo
const GLuint* idx = static_cast(resolvedIndices);
for (GLsizei i = 0; i < count; ++i)
{
- GLVertex& v = g_gl.immediateVerts.Push();
- QD3D12_FetchArrayVertex(static_cast(idx[i]), v);
+ QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]);
}
break;
}
@@ -14174,8 +14792,7 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo
const GLushort* idx = static_cast(resolvedIndices);
for (GLsizei i = 0; i < count; ++i)
{
- GLVertex& v = g_gl.immediateVerts.Push();
- QD3D12_FetchArrayVertex(static_cast(idx[i]), v);
+ QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]);
}
break;
}
@@ -14185,29 +14802,17 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo
const GLubyte* idx = static_cast(resolvedIndices);
for (GLsizei i = 0; i < count; ++i)
{
- GLVertex& v = g_gl.immediateVerts.Push();
- QD3D12_FetchArrayVertex(static_cast(idx[i]), v);
+ QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]);
}
break;
}
default:
g_gl.lastError = GL_INVALID_ENUM;
+ g_gl.immediateVerts.Clear();
return;
}
- if (!g_gl.colorArray.enabled)
- {
- for (int i = 0; i < g_gl.immediateVerts.Size(); i++)
- {
- g_gl.immediateVerts.Data()[i].r = g_gl.curColor[0];
- g_gl.immediateVerts.Data()[i].g = g_gl.curColor[1];
- g_gl.immediateVerts.Data()[i].b = g_gl.curColor[2];
- g_gl.immediateVerts.Data()[i].a = g_gl.curColor[3];
- }
- }
-
-
FlushImmediate(mode, g_gl.immediateVerts.Data(), g_gl.immediateVerts.Size());
}
@@ -15868,8 +16473,6 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle)
if (!tlas)
return;
- glRaytracingBuildSceneForHandle(sceneHandle);
-
QD3D12_FlushQueuedBatches();
QD3D12_ResolveGBufferForCurrentFrame(*window);
@@ -16122,6 +16725,8 @@ ID3D12Resource* QD3D12_GetCurrentBackBuffer()
void APIENTRY glGeometryFlagf(GLfloat flag)
{
+ if (g_gl.currentGeometryFlag == flag)
+ return;
g_gl.currentGeometryFlag = flag;
}
@@ -16317,7 +16922,10 @@ static float QD3D12_ClampToneMapBrightness(float brightness)
void QD3D12_SetToneMapBrightness(float brightness)
{
- g_gl.toneMapBrightness = QD3D12_ClampToneMapBrightness(brightness);
+ const float clamped = QD3D12_ClampToneMapBrightness(brightness);
+ if (g_gl.toneMapBrightness == clamped)
+ return;
+ g_gl.toneMapBrightness = clamped;
}
float QD3D12_GetToneMapBrightness(void)
@@ -16504,6 +17112,10 @@ void APIENTRY glDeleteBuffers(GLsizei n, const GLuint* buffers)
if (g_gl.boundElementArrayBuffer == id)
g_gl.boundElementArrayBuffer = 0;
+ auto it = g_gl.buffers.find(id);
+ if (it != g_gl.buffers.end())
+ QD3D12_ResetBufferResource(it->second);
+
g_gl.buffers.erase(id);
}
}
@@ -16582,23 +17194,49 @@ void APIENTRY glBufferStorage(GLenum target, GLsizeiptr size, const void* data,
return;
}
- bo->target = target;
+ QD3D12_UpdateBufferResource(*bo, target, size, data, GL_STATIC_DRAW_ARB);
bo->storageFlags = flags;
- bo->data.resize((size_t)size);
-
- if (size > 0)
- {
- if (data)
- memcpy(bo->data.data(), data, (size_t)size);
- else
- memset(bo->data.data(), 0, (size_t)size);
- }
}
void APIENTRY glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage)
{
- (void)usage;
- glBufferStorage(target, size, data, 0);
+ GLuint bound = 0;
+
+ switch (target)
+ {
+ case GL_ARRAY_BUFFER:
+ bound = g_gl.boundArrayBuffer;
+ break;
+
+ case GL_ELEMENT_ARRAY_BUFFER:
+ bound = g_gl.boundElementArrayBuffer;
+ break;
+
+ default:
+ g_gl.lastError = GL_INVALID_ENUM;
+ return;
+ }
+
+ if (size < 0)
+ {
+ g_gl.lastError = GL_INVALID_VALUE;
+ return;
+ }
+
+ if (bound == 0)
+ {
+ g_gl.lastError = GL_INVALID_OPERATION;
+ return;
+ }
+
+ GLBufferObject* bo = QD3D12_GetBuffer(bound);
+ if (!bo)
+ {
+ g_gl.lastError = GL_INVALID_OPERATION;
+ return;
+ }
+
+ QD3D12_UpdateBufferResource(*bo, target, size, data, usage);
}
void APIENTRY glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data)
@@ -16640,6 +17278,11 @@ void APIENTRY glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, c
}
memcpy(bo->data.data() + (size_t)offset, data, (size_t)size);
+ ++bo->revision;
+ QD3D12_ResetPackedVertexBuffer(*bo);
+
+ if (bo->mappedGpu)
+ memcpy(bo->mappedGpu + (size_t)offset, data, (size_t)size);
}
void APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count)
@@ -16771,6 +17414,13 @@ GLboolean APIENTRY glUnmapBuffer(GLenum target)
}
bo->mapped = false;
+ if (bo->mappedAccess & GL_MAP_WRITE_BIT)
+ {
+ ++bo->revision;
+ QD3D12_ResetPackedVertexBuffer(*bo);
+ if (bo->mappedGpu && bo->mappedLength > 0)
+ memcpy(bo->mappedGpu + (size_t)bo->mappedOffset, bo->data.data() + (size_t)bo->mappedOffset, (size_t)bo->mappedLength);
+ }
bo->mappedOffset = 0;
bo->mappedLength = 0;
bo->mappedAccess = 0;
diff --git a/neo/engine/opengl/opengl.h b/neo/engine/opengl/opengl.h
index 736ded63..72b6963e 100644
--- a/neo/engine/opengl/opengl.h
+++ b/neo/engine/opengl/opengl.h
@@ -2328,6 +2328,7 @@ void QD3D12_SetUpscalerSharpness(float sharpness);
void QD3D12_SetToneMapBrightness(float brightness);
void QD3D12_SetFrameGenerationMultiplier(int multiplier);
int QD3D12_GetFrameGenerationMultiplier(void);
+void QD3D12_SetCurrentWindowDLSSAllowed(int allowed);
void QD3D12_EnableTAA(int enabled);
diff --git a/neo/engine/opengl/opengl.vcxproj b/neo/engine/opengl/opengl.vcxproj
index 638404ec..ff9f9c1e 100644
--- a/neo/engine/opengl/opengl.vcxproj
+++ b/neo/engine/opengl/opengl.vcxproj
@@ -45,7 +45,23 @@
+
+
+ USE_D3D=0;USE_AVX512=0;%(PreprocessorDefinitions)
+ MaxSpeed
+ MaxSpeed
+ MaxSpeed
+ Default
+
+
+ USE_D3D=0;USE_AVX512=0;%(PreprocessorDefinitions)
+ /arch:AVX2 %(AdditionalOptions)
+ MaxSpeed
+ MaxSpeed
+ MaxSpeed
+ Default
+
MaxSpeed
MaxSpeed
@@ -182,7 +198,11 @@
+
+
+
+
@@ -373,6 +393,8 @@
MultiThreadedDebug
streamline/include;d3d12/;
4996
+ Full
+ Default
diff --git a/neo/engine/opengl/opengl.vcxproj.filters b/neo/engine/opengl/opengl.vcxproj.filters
index 0a33b859..a4010f43 100644
--- a/neo/engine/opengl/opengl.vcxproj.filters
+++ b/neo/engine/opengl/opengl.vcxproj.filters
@@ -44,6 +44,13 @@
+
+ thirdparty\MaskedOcclusionCulling
+
+
+ thirdparty\MaskedOcclusionCulling
+
+
@@ -98,6 +105,16 @@
+
+ thirdparty\MaskedOcclusionCulling
+
+
+ thirdparty\MaskedOcclusionCulling
+
+
+ thirdparty\MaskedOcclusionCulling
+
+
@@ -106,6 +123,12 @@
{ac325e42-ffb4-4bbc-a4ec-b567658773a3}
+
+ {8e15d873-a9f6-421d-97b5-3bb07f870144}
+
+
+ {9420a408-8b6e-4ce4-bd61-97537c296ec6}
+
@@ -117,4 +140,4 @@
streamline
-
\ No newline at end of file
+
diff --git a/neo/engine/renderer/RenderSystem.cpp b/neo/engine/renderer/RenderSystem.cpp
index 823b50d6..7fa5749c 100644
--- a/neo/engine/renderer/RenderSystem.cpp
+++ b/neo/engine/renderer/RenderSystem.cpp
@@ -726,7 +726,7 @@ void idRenderSystemLocal::EndFrame( int *frontEndMsec, int *backEndMsec, bool sw
*frontEndMsec = pc.frontEndMsec;
}
if ( backEndMsec ) {
- *backEndMsec = backEnd.pc.msec;
+ *backEndMsec = backEnd.pc.cpuMsecNoSwap;
}
// print any other statistics and clear all of them
diff --git a/neo/engine/renderer/RenderSystem_init.cpp b/neo/engine/renderer/RenderSystem_init.cpp
index 8d43c31e..b03aa7d4 100644
--- a/neo/engine/renderer/RenderSystem_init.cpp
+++ b/neo/engine/renderer/RenderSystem_init.cpp
@@ -118,6 +118,8 @@ idCVar r_ignore2( "r_ignore2", "0", CVAR_RENDERER, "used for random debugging wi
idCVar r_usePreciseTriangleInteractions( "r_usePreciseTriangleInteractions", "0", CVAR_RENDERER | CVAR_BOOL, "1 = do winding clipping to determine if each ambiguous tri should be lit" );
idCVar r_useCulling( "r_useCulling", "2", CVAR_RENDERER | CVAR_INTEGER, "0 = none, 1 = sphere, 2 = sphere + box", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
idCVar r_useLightCulling( "r_useLightCulling", "3", CVAR_RENDERER | CVAR_INTEGER, "0 = none, 1 = box, 2 = exact clip of polyhedron faces, 3 = also areas", 0, 3, idCmdSystem::ArgCompletion_Integer<0,3> );
+idCVar r_useMaskedOcclusionCulling( "r_useMaskedOcclusionCulling", "1", CVAR_RENDERER | CVAR_BOOL, "threaded one-frame-late masked occlusion culling for renderer lights" );
+idCVar r_mocResolutionScale( "r_mocResolutionScale", "0.5", CVAR_RENDERER | CVAR_FLOAT, "software occlusion buffer resolution scale for masked light culling", 0.125f, 1.0f );
idCVar r_useLightScissors( "r_useLightScissors", "1", CVAR_RENDERER | CVAR_BOOL, "1 = use custom scissor rectangle for each light" );
idCVar r_useClippedLightScissors( "r_useClippedLightScissors", "1", CVAR_RENDERER | CVAR_INTEGER, "0 = full screen when near clipped, 1 = exact when near clipped, 2 = exact always", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
idCVar r_useEntityCulling( "r_useEntityCulling", "1", CVAR_RENDERER | CVAR_BOOL, "0 = none, 1 = box" );
@@ -1988,6 +1990,8 @@ idRenderSystemLocal::Shutdown
void idRenderSystemLocal::Shutdown( void ) {
common->Printf( "idRenderSystem::Shutdown()\n" );
+ R_MaskedOcclusionCulling_Shutdown();
+
R_DoneFreeType( );
if ( glConfig.isInitialized ) {
diff --git a/neo/engine/renderer/RenderWorld_load.cpp b/neo/engine/renderer/RenderWorld_load.cpp
index b911b53d..fe07bc5f 100644
--- a/neo/engine/renderer/RenderWorld_load.cpp
+++ b/neo/engine/renderer/RenderWorld_load.cpp
@@ -269,23 +269,17 @@ idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) {
tri->numIndexes = src->ParseInt();
tri->shadowCapPlaneBits = src->ParseInt();
- R_AllocStaticTriSurfShadowVerts( tri, tri->numVerts );
tri->bounds.Clear();
for ( j = 0 ; j < tri->numVerts ; j++ ) {
float vec[8];
src->Parse1DMatrix( 3, vec );
- tri->shadowVertexes[j].xyz[0] = vec[0];
- tri->shadowVertexes[j].xyz[1] = vec[1];
- tri->shadowVertexes[j].xyz[2] = vec[2];
- tri->shadowVertexes[j].xyz[3] = 1; // no homogenous value
- tri->bounds.AddPoint( tri->shadowVertexes[j].xyz.ToVec3() );
+ tri->bounds.AddPoint( idVec3( vec[0], vec[1], vec[2] ) );
}
- R_AllocStaticTriSurfIndexes( tri, tri->numIndexes );
for ( j = 0 ; j < tri->numIndexes ; j++ ) {
- tri->indexes[j] = src->ParseInt();
+ src->ParseInt();
}
// add the completed surface to the model
diff --git a/neo/engine/renderer/draw_common.cpp b/neo/engine/renderer/draw_common.cpp
index 0fff0cef..58671212 100644
--- a/neo/engine/renderer/draw_common.cpp
+++ b/neo/engine/renderer/draw_common.cpp
@@ -32,6 +32,14 @@ If you have questions concerning this license or the applicable additional terms
idCVar r_renderNeuralMaterial("r_renderNeuralMaterial", "0", CVAR_BOOL, "");
+static idDrawVert *RB_BindAmbientBuffer( const srfTriangles_t *tri ) {
+ if ( tri->ambientVbo ) {
+ glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo );
+ return (idDrawVert *)0;
+ }
+ return (idDrawVert *)vertexCache.Position( tri->ambientCache );
+}
+
/*
=====================
RB_BakeTextureMatrixIntoTexgen
@@ -389,7 +397,7 @@ void RB_T_FillDepthBuffer(const drawSurf_t* surf) {
return;
}
- if (!tri->ambientCache) {
+ if (!tri->ambientVbo && !tri->ambientCache) {
common->Printf("RB_T_FillDepthBuffer: !tri->ambientCache\n");
return;
}
@@ -435,7 +443,7 @@ void RB_T_FillDepthBuffer(const drawSurf_t* surf) {
color[3] = 1.0f;
}
- idDrawVert* ac = (idDrawVert*)vertexCache.Position(tri->ambientCache);
+ idDrawVert* ac = RB_BindAmbientBuffer(tri);
glVertexPointer(3, GL_FLOAT, sizeof(idDrawVert), ac->xyz.ToFloatPtr());
glTexCoordPointer(2, GL_FLOAT, sizeof(idDrawVert), reinterpret_cast(&ac->st));
@@ -558,7 +566,7 @@ void RB_T_FillDepthBuffer(const drawSurf_t* surf) {
// draw it
RB_DrawElementsWithCounters(tri);
- RB_FinishStageTexturing(pStage, surf, ac);
+ //RB_FinishStageTexturing(pStage, surf, ac);
if (glowMapImage) {
GL_SelectTexture(3);
@@ -642,11 +650,7 @@ void RB_STD_FillDepthBuffer( drawSurf_t **drawSurfs, int numDrawSurfs ) {
GL_State( GLS_DEPTHFUNC_LESS );
- // Enable stencil test if we are going to be using it for shadows.
- // If we didn't do this, it would be legal behavior to get z fighting
- // from the ambient pass and the light passes.
- glEnable( GL_STENCIL_TEST );
- glStencilFunc( GL_ALWAYS, 1, 255 );
+ glDisable( GL_STENCIL_TEST );
RB_RenderDrawSurfListWithFunction( drawSurfs, numDrawSurfs, RB_T_FillDepthBuffer );
@@ -827,7 +831,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
return;
}
- if ( !tri->ambientCache ) {
+ if ( !tri->ambientVbo && !tri->ambientCache ) {
common->Printf( "RB_T_RenderShaderPasses: !tri->ambientCache\n" );
return;
}
@@ -852,7 +856,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) {
RB_EnterModelDepthHack( surf->space->modelDepthHack );
}
- idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
+ idDrawVert *ac = RB_BindAmbientBuffer( tri );
glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), reinterpret_cast(&ac->st) );
@@ -1167,13 +1171,9 @@ static void RB_T_BlendLight( const drawSurf_t *surf ) {
glTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[3].ToFloatPtr() );
}
- // this gets used for both blend lights and shadow draws
- if ( tri->ambientCache ) {
- idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
+ if ( tri->ambientVbo || tri->ambientCache ) {
+ idDrawVert *ac = RB_BindAmbientBuffer( tri );
glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
- } else if ( tri->shadowCache ) {
- shadowCache_t *sc = (shadowCache_t *)vertexCache.Position( tri->shadowCache );
- glVertexPointer( 3, GL_FLOAT, sizeof( shadowCache_t ), sc->xyz.ToFloatPtr() );
}
RB_DrawElementsWithCounters( tri );
@@ -1323,7 +1323,7 @@ static void RB_FogPass( const drawSurf_t *drawSurfs, const drawSurf_t *drawSurf
frustumTris = backEnd.vLight->frustumTris;
// if we ran out of vertex cache memory, skip it
- if ( !frustumTris->ambientCache ) {
+ if ( !frustumTris->ambientVbo && !frustumTris->ambientCache ) {
return;
}
memset( &ds, 0, sizeof( ds ) );
@@ -1513,9 +1513,6 @@ void RB_STD_DrawView( void ) {
// main light renderer
RB_DXDrawInteractions();
- // disable stencil shadow test
- glStencilFunc( GL_ALWAYS, 128, 255 );
-
// now draw any non-light dependent shading passes
int processed = RB_STD_DrawShaderPasses( drawSurfs, numDrawSurfs );
diff --git a/neo/engine/renderer/draw_dx.cpp b/neo/engine/renderer/draw_dx.cpp
index 126b3f0e..fd6ccf27 100644
--- a/neo/engine/renderer/draw_dx.cpp
+++ b/neo/engine/renderer/draw_dx.cpp
@@ -174,6 +174,10 @@ void RB_DXDrawInteractions(void)
{
continue;
}
+ if (!R_MaskedOcclusionCulling_LightIsVisible(backEnd.viewDef, vLight->lightDef))
+ {
+ continue;
+ }
const renderLight_t& srcLight = vLight->lightDef->parms;
const float r = srcLight.shaderParms[SHADERPARM_RED];
diff --git a/neo/engine/renderer/tr_backend.cpp b/neo/engine/renderer/tr_backend.cpp
index 0b39e8bc..c31d58f7 100644
--- a/neo/engine/renderer/tr_backend.cpp
+++ b/neo/engine/renderer/tr_backend.cpp
@@ -626,6 +626,7 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) {
}
backEndStartTime = Sys_Milliseconds();
+ int backendCpuNoSwap = 0;
// needed for editor rendering
RB_SetDefaultGLState();
@@ -634,7 +635,9 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) {
globalImages->CompleteBackgroundImageLoads();
for ( ; cmds ; cmds = (const emptyCommand_t *)cmds->next ) {
- switch ( cmds->commandId ) {
+ const int commandStartTime = Sys_Milliseconds();
+ const renderCommand_t commandId = cmds->commandId;
+ switch ( commandId ) {
case RC_NOP:
break;
case RC_DRAW_VIEW:
@@ -662,6 +665,9 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) {
common->Error( "RB_ExecuteBackEndCommands: bad commandId" );
break;
}
+ if ( commandId != RC_SWAP_BUFFERS ) {
+ backendCpuNoSwap += Sys_Milliseconds() - commandStartTime;
+ }
}
// go back to the default texture so the editor doesn't mess up a bound image
@@ -671,6 +677,7 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) {
// stop rendering on this thread
backEndFinishTime = Sys_Milliseconds();
backEnd.pc.msec = backEndFinishTime - backEndStartTime;
+ backEnd.pc.cpuMsecNoSwap = backendCpuNoSwap;
if ( r_debugRenderToTexture.GetInteger() == 1 ) {
common->Printf( "3d: %i, 2d: %i, SetBuf: %i, SwpBuf: %i, CpyRenders: %i, CpyFrameBuf: %i\n", c_draw3d, c_draw2d, c_setBuffers, c_swapBuffers, c_copyRenders, backEnd.c_copyFrameBuffer );
diff --git a/neo/engine/renderer/tr_light.cpp b/neo/engine/renderer/tr_light.cpp
index 3ff301a0..bc363c37 100644
--- a/neo/engine/renderer/tr_light.cpp
+++ b/neo/engine/renderer/tr_light.cpp
@@ -50,8 +50,8 @@ R_CreateAmbientCache
Create it if needed
==================
*/
-bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) {
- if ( tri->ambientCache ) {
+bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting, bool staticCache ) {
+ if ( tri->ambientVbo || tri->ambientCache ) {
return true;
}
// we are going to use it for drawing, so make sure we have the tangents and normals
@@ -59,10 +59,40 @@ bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) {
R_DeriveTangents( tri );
}
- vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache );
- if ( !tri->ambientCache ) {
+ if ( !tri->verts || tri->numVerts <= 0 ) {
return false;
}
+
+ if ( staticCache ) {
+ glGenBuffersARB( 1, &tri->ambientVbo );
+ glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo );
+ glBufferDataARB( GL_ARRAY_BUFFER_ARB, (GLsizeiptrARB)( tri->numVerts * sizeof( tri->verts[0] ) ), tri->verts, GL_STATIC_DRAW_ARB );
+ return true;
+ }
+
+ vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache );
+ return tri->ambientCache != NULL;
+}
+
+/*
+==================
+R_CreateIndexCache
+
+Static surfaces own their index buffer directly instead of routing through
+idVertexCache.
+==================
+*/
+bool R_CreateIndexCache( srfTriangles_t *tri ) {
+ if ( tri->indexVbo ) {
+ return true;
+ }
+ if ( !tri->indexes || tri->numIndexes <= 0 ) {
+ return false;
+ }
+
+ glGenBuffersARB( 1, &tri->indexVbo );
+ glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, tri->indexVbo );
+ glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, (GLsizeiptrARB)( tri->numIndexes * sizeof( tri->indexes[0] ) ), tri->indexes, GL_STATIC_DRAW_ARB );
return true;
}
@@ -139,11 +169,7 @@ This is used only for a specific light
==================
*/
void R_CreatePrivateShadowCache( srfTriangles_t *tri ) {
- if ( !tri->shadowVertexes ) {
- return;
- }
-
- vertexCache.Alloc( tri->shadowVertexes, tri->numVerts * sizeof( *tri->shadowVertexes ), &tri->shadowCache );
+ (void)tri;
}
/*
@@ -155,35 +181,7 @@ takes care of projecting the verts to infinity.
==================
*/
void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ) {
- if ( tri->verts == NULL ) {
- return;
- }
-
- shadowCache_t *temp = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowCache_t ) );
-
-#if 1
-
- SIMDProcessor->CreateVertexProgramShadowCache( &temp->xyz, tri->verts, tri->numVerts );
-
-#else
-
- int numVerts = tri->numVerts;
- const idDrawVert *verts = tri->verts;
- for ( int i = 0; i < numVerts; i++ ) {
- const float *v = verts[i].xyz.ToFloatPtr();
- temp[i*2+0].xyz[0] = v[0];
- temp[i*2+1].xyz[0] = v[0];
- temp[i*2+0].xyz[1] = v[1];
- temp[i*2+1].xyz[1] = v[1];
- temp[i*2+0].xyz[2] = v[2];
- temp[i*2+1].xyz[2] = v[2];
- temp[i*2+0].xyz[3] = 1.0f; // on the model surface
- temp[i*2+1].xyz[3] = 0.0f; // will be projected to infinity
- }
-
-#endif
-
- vertexCache.Alloc( temp, tri->numVerts * 2 * sizeof( shadowCache_t ), &tri->shadowCache );
+ (void)tri;
}
/*
@@ -956,55 +954,18 @@ void R_AddLightSurfaces( void ) {
// fog lights will need to draw the light frustum triangles, so make sure they
// are in the vertex cache
if ( lightShader->IsFogLight() ) {
- if ( !light->frustumTris->ambientCache ) {
+ if ( !light->frustumTris->ambientVbo ) {
if ( !R_CreateAmbientCache( light->frustumTris, false ) ) {
// skip if we are out of vertex memory
continue;
}
}
- // touch the surface so it won't get purged
- vertexCache.Touch( light->frustumTris->ambientCache );
+ if ( r_useIndexBuffers.GetBool() ) {
+ R_CreateIndexCache( light->frustumTris );
+ }
}
- // add the prelight shadows for the static world geometry
- if ( light->parms.prelightModel && r_useOptimizedShadows.GetBool() ) {
-
- if ( !light->parms.prelightModel->NumSurfaces() ) {
- common->Error( "no surfs in prelight model '%s'", light->parms.prelightModel->Name() );
- }
-
- srfTriangles_t *tri = light->parms.prelightModel->Surface( 0 )->geometry;
- if ( !tri->shadowVertexes ) {
- common->Error( "R_AddLightSurfaces: prelight model '%s' without shadowVertexes", light->parms.prelightModel->Name() );
- }
-
- // these shadows will all have valid bounds, and can be culled normally
- if ( r_useShadowCulling.GetBool() ) {
- if ( R_CullLocalBox( tri->bounds, tr.viewDef->worldSpace.modelMatrix, 5, tr.viewDef->frustum ) ) {
- continue;
- }
- }
-
- // if we have been purged, re-upload the shadowVertexes
- if ( !tri->shadowCache ) {
- R_CreatePrivateShadowCache( tri );
- if ( !tri->shadowCache ) {
- continue;
- }
- }
-
- // touch the shadow surface so it won't get purged
- vertexCache.Touch( tri->shadowCache );
-
- if ( !tri->indexCache && r_useIndexBuffers.GetBool() ) {
- vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
- }
- if ( tri->indexCache ) {
- vertexCache.Touch( tri->indexCache );
- }
-
- R_LinkLightSurf( &vLight->globalShadows, tri, NULL, light, NULL, vLight->scissorRect, true /* FIXME? */ );
- }
+ // Stencil/prelight shadow volumes are not used by the D3D12 lighting path.
}
}
@@ -1379,18 +1340,14 @@ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) {
def->visibleCount = tr.viewCount;
// make sure we have an ambient cache
- if ( !R_CreateAmbientCache( tri, shader->ReceivesLighting() ) ) {
+ const bool staticCache = def->dynamicModel == NULL;
+ const bool needsAmbientTangents = staticCache && shader->ReceivesLighting();
+ if ( !R_CreateAmbientCache( tri, needsAmbientTangents, staticCache ) ) {
// don't add anything if the vertex cache was too full to give us an ambient cache
return;
}
- // touch it so it won't get purged
- vertexCache.Touch( tri->ambientCache );
-
- if ( r_useIndexBuffers.GetBool() && !tri->indexCache ) {
- vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true );
- }
- if ( tri->indexCache ) {
- vertexCache.Touch( tri->indexCache );
+ if ( staticCache && r_useIndexBuffers.GetBool() ) {
+ R_CreateIndexCache( tri );
}
// add the surface for drawing
@@ -1519,13 +1476,8 @@ void R_RemoveUnecessaryViewLights( void ) {
// go through each visible light
for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
- // if the light didn't have any lit surfaces visible, there is no need to
- // draw any of the shadows. We still keep the vLight for debugging
- // draws
- if ( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) {
- vLight->localShadows = NULL;
- vLight->globalShadows = NULL;
- }
+ vLight->localShadows = NULL;
+ vLight->globalShadows = NULL;
}
if ( r_useShadowSurfaceScissor.GetBool() ) {
@@ -1545,16 +1497,9 @@ void R_RemoveUnecessaryViewLights( void ) {
for ( surf = vLight->globalInteractions ; surf ; surf = surf->nextOnLight ) {
surfRect.Union( surf->scissorRect );
}
- for ( surf = vLight->localShadows ; surf ; surf = surf->nextOnLight ) {
- const_cast(surf)->scissorRect.Intersect( surfRect );
- }
-
for ( surf = vLight->localInteractions ; surf ; surf = surf->nextOnLight ) {
surfRect.Union( surf->scissorRect );
}
- for ( surf = vLight->globalShadows ; surf ; surf = surf->nextOnLight ) {
- const_cast(surf)->scissorRect.Intersect( surfRect );
- }
for ( surf = vLight->translucentInteractions ; surf ; surf = surf->nextOnLight ) {
surfRect.Union( surf->scissorRect );
diff --git a/neo/engine/renderer/tr_local.h b/neo/engine/renderer/tr_local.h
index e0f22ee3..a9b72211 100644
--- a/neo/engine/renderer/tr_local.h
+++ b/neo/engine/renderer/tr_local.h
@@ -628,6 +628,7 @@ typedef struct {
float maxLightValue; // for light scale
int msec; // total msec for backend run
+ int cpuMsecNoSwap; // backend CPU excluding swap/present wait
} backEndCounters_t;
// all state modified by the back end is separated
@@ -860,6 +861,8 @@ extern idCVar r_useNodeCommonChildren; // stop pushing reference bounds early wh
extern idCVar r_useSilRemap; // 1 = consider verts with the same XYZ, but different ST the same for shadows
extern idCVar r_useCulling; // 0 = none, 1 = sphere, 2 = sphere + box
extern idCVar r_useLightCulling; // 0 = none, 1 = box, 2 = exact clip of polyhedron faces
+extern idCVar r_useMaskedOcclusionCulling; // one-frame-late threaded MOC light culling
+extern idCVar r_mocResolutionScale; // software occlusion resolution relative to view size
extern idCVar r_useLightScissors; // 1 = use custom scissor rectangle for each light
extern idCVar r_useClippedLightScissors;// 0 = full screen when near clipped, 1 = exact when near clipped, 2 = exact always
extern idCVar r_useEntityCulling; // 0 = none, 1 = box
@@ -1142,6 +1145,9 @@ MAIN
*/
void R_RenderView( viewDef_t *parms );
+void R_MaskedOcclusionCulling_ProcessView( viewDef_t *parms );
+bool R_MaskedOcclusionCulling_LightIsVisible( const viewDef_t *viewDef, const idRenderLightLocal *light );
+void R_MaskedOcclusionCulling_Shutdown( void );
// performs radius cull first, then corner cull
bool R_CullLocalBox( const idBounds &bounds, const float modelMatrix[16], int numPlanes, const idPlane *planes );
@@ -1195,7 +1201,8 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const
void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space,
const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow );
-bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting );
+bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting, bool staticCache = true );
+bool R_CreateIndexCache( srfTriangles_t *tri );
bool R_CreateLightingCache( const idRenderEntityLocal *ent, const idRenderLightLocal *light, srfTriangles_t *tri );
void R_CreatePrivateShadowCache( srfTriangles_t *tri );
void R_CreateVertexProgramShadowCache( srfTriangles_t *tri );
diff --git a/neo/engine/renderer/tr_main.cpp b/neo/engine/renderer/tr_main.cpp
index 5f5d56fe..c018f57b 100644
--- a/neo/engine/renderer/tr_main.cpp
+++ b/neo/engine/renderer/tr_main.cpp
@@ -1123,6 +1123,10 @@ void R_RenderView( viewDef_t *parms ) {
// lightDefs that are in them and pass culling.
static_cast(parms->renderWorld)->FindViewLightsAndEntities();
+ // Queue this full light list for next frame, then prune with the last
+ // completed async masked occlusion result.
+ R_MaskedOcclusionCulling_ProcessView( tr.viewDef );
+
// constrain the view frustum to the view lights and entities
R_ConstrainViewFrustum();
diff --git a/neo/engine/renderer/tr_render.cpp b/neo/engine/renderer/tr_render.cpp
index 616f9094..3d6880f3 100644
--- a/neo/engine/renderer/tr_render.cpp
+++ b/neo/engine/renderer/tr_render.cpp
@@ -69,6 +69,14 @@ void RB_DrawElementsImmediate( const srfTriangles_t *tri ) {
glEnd();
}
+static idDrawVert *RB_BindAmbientBuffer( const srfTriangles_t *tri ) {
+ if ( tri->ambientVbo ) {
+ glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo );
+ return (idDrawVert *)0;
+ }
+ return (idDrawVert *)vertexCache.Position( tri->ambientCache );
+}
+
/*
================
@@ -90,7 +98,14 @@ void RB_DrawElementsWithCounters( const srfTriangles_t *tri ) {
}
}
- if ( tri->indexCache && r_useIndexBuffers.GetBool() ) {
+ if ( tri->indexVbo && r_useIndexBuffers.GetBool() ) {
+ glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, tri->indexVbo );
+ glDrawElements( GL_TRIANGLES,
+ r_singleTriangle.GetBool() ? 3 : tri->numIndexes,
+ GL_INDEX_TYPE,
+ (void *)0 );
+ backEnd.pc.c_vboIndexes += tri->numIndexes;
+ } else if ( tri->indexCache && r_useIndexBuffers.GetBool() ) {
glDrawElements( GL_TRIANGLES,
r_singleTriangle.GetBool() ? 3 : tri->numIndexes,
GL_INDEX_TYPE,
@@ -119,7 +134,14 @@ void RB_DrawShadowElementsWithCounters( const srfTriangles_t *tri, int numIndexe
backEnd.pc.c_shadowIndexes += numIndexes;
backEnd.pc.c_shadowVertexes += tri->numVerts;
- if ( tri->indexCache && r_useIndexBuffers.GetBool() ) {
+ if ( tri->indexVbo && r_useIndexBuffers.GetBool() ) {
+ glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, tri->indexVbo );
+ glDrawElements( GL_TRIANGLES,
+ r_singleTriangle.GetBool() ? 3 : numIndexes,
+ GL_INDEX_TYPE,
+ (void *)0 );
+ backEnd.pc.c_vboIndexes += numIndexes;
+ } else if ( tri->indexCache && r_useIndexBuffers.GetBool() ) {
glDrawElements( GL_TRIANGLES,
r_singleTriangle.GetBool() ? 3 : numIndexes,
GL_INDEX_TYPE,
@@ -145,13 +167,13 @@ Sets texcoord and vertex pointers
===============
*/
void RB_RenderTriangleSurface( const srfTriangles_t *tri ) {
- if ( !tri->ambientCache ) {
+ if ( !tri->ambientVbo && !tri->ambientCache ) {
RB_DrawElementsImmediate( tri );
return;
}
- idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache );
+ idDrawVert *ac = RB_BindAmbientBuffer( tri );
glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
@@ -413,7 +435,7 @@ void RB_BindStageTexture( const float *shaderRegisters, const textureStage_t *te
// texgens
if ( texture->texgen == TG_DIFFUSE_CUBE ) {
- glTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->normal.ToFloatPtr() );
+ glTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), RB_BindAmbientBuffer( surf->geo )->normal.ToFloatPtr() );
}
if ( texture->texgen == TG_SKYBOX_CUBE || texture->texgen == TG_WOBBLESKY_CUBE ) {
glTexCoordPointer( 3, GL_FLOAT, 0, vertexCache.Position( surf->dynamicTexCoords ) );
@@ -426,7 +448,7 @@ void RB_BindStageTexture( const float *shaderRegisters, const textureStage_t *te
glTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT );
glTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT );
glEnableClientState( GL_NORMAL_ARRAY );
- glNormalPointer( GL_FLOAT, sizeof( idDrawVert ), ((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->normal.ToFloatPtr() );
+ glNormalPointer( GL_FLOAT, sizeof( idDrawVert ), RB_BindAmbientBuffer( surf->geo )->normal.ToFloatPtr() );
glMatrixMode( GL_TEXTURE );
float mat[16];
@@ -452,7 +474,7 @@ void RB_FinishStageTexture( const textureStage_t *texture, const drawSurf_t *sur
if ( texture->texgen == TG_DIFFUSE_CUBE || texture->texgen == TG_SKYBOX_CUBE
|| texture->texgen == TG_WOBBLESKY_CUBE ) {
glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ),
- (void *)&(((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->st) );
+ (void *)&(RB_BindAmbientBuffer( surf->geo )->st) );
}
if ( texture->texgen == TG_REFLECT_CUBE ) {
@@ -610,12 +632,9 @@ void RB_BeginDrawingView (void) {
// we don't have to clear the depth / stencil buffer for 2D rendering
if ( backEnd.viewDef->viewEntitys ) {
- glStencilMask( 0xff );
- // some cards may have 7 bit stencil buffers, so don't assume this
- // should be 128
- glClearStencil( 1<<(glConfig.stencilBits-1) );
- glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+ glClear( GL_DEPTH_BUFFER_BIT );
glEnable( GL_DEPTH_TEST );
+ glDisable( GL_STENCIL_TEST );
} else {
glDisable( GL_DEPTH_TEST );
glDisable( GL_STENCIL_TEST );
@@ -728,7 +747,7 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac
const float *lightRegs = vLight->shaderRegisters;
drawInteraction_t inter;
- if ( r_skipInteractions.GetBool() || !surf->geo || !surf->geo->ambientCache ) {
+ if ( r_skipInteractions.GetBool() || !surf->geo || ( !surf->geo->ambientVbo && !surf->geo->ambientCache ) ) {
return;
}
diff --git a/neo/engine/renderer/tr_rendertools.cpp b/neo/engine/renderer/tr_rendertools.cpp
index 80371bcd..1d4b254b 100644
--- a/neo/engine/renderer/tr_rendertools.cpp
+++ b/neo/engine/renderer/tr_rendertools.cpp
@@ -474,6 +474,14 @@ void RB_ShowDepthBuffer( void ) {
R_StaticFree( depthReadback );
}
+static idDrawVert *RB_BindToolAmbientBuffer( const srfTriangles_t *tri ) {
+ if ( tri->ambientVbo ) {
+ glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo );
+ return (idDrawVert *)0;
+ }
+ return (idDrawVert *)vertexCache.Position( tri->ambientCache );
+}
+
/*
=================
RB_ShowLightCount
@@ -514,11 +522,11 @@ void RB_ShowLightCount( void ) {
for ( i = 0 ; i < 2 ; i++ ) {
for ( surf = i ? vLight->localInteractions: vLight->globalInteractions; surf; surf = (drawSurf_t *)surf->nextOnLight ) {
RB_SimpleSurfaceSetup( surf );
- if ( !surf->geo->ambientCache ) {
+ if ( !surf->geo->ambientVbo && !surf->geo->ambientCache ) {
continue;
}
- const idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
+ const idDrawVert *ac = RB_BindToolAmbientBuffer( surf->geo );
glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), &ac->xyz );
RB_DrawElementsWithCounters( surf->geo );
}
@@ -543,77 +551,7 @@ plane extends from, allowing you to see doubled edges
=================
*/
void RB_ShowSilhouette( void ) {
- int i;
- const drawSurf_t *surf;
- const viewLight_t *vLight;
-
- if ( !r_showSilhouette.GetBool() ) {
- return;
- }
-
- //
- // clear all triangle edges to black
- //
- glDisableClientState( GL_TEXTURE_COORD_ARRAY );
- globalImages->BindNull();
- glDisable( GL_TEXTURE_2D );
- glDisable( GL_STENCIL_TEST );
-
- glColor3f( 0, 0, 0 );
-
- GL_State( GLS_POLYMODE_LINE );
-
- GL_Cull( CT_TWO_SIDED );
- glDisable( GL_DEPTH_TEST );
-
- RB_RenderDrawSurfListWithFunction( backEnd.viewDef->drawSurfs, backEnd.viewDef->numDrawSurfs,
- RB_T_RenderTriangleSurface );
-
-
- //
- // now blend in edges that cast silhouettes
- //
- RB_SimpleWorldSetup();
- glColor3f( 0.5, 0, 0 );
- GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
-
- for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
- for ( i = 0 ; i < 2 ; i++ ) {
- for ( surf = i ? vLight->localShadows : vLight->globalShadows
- ; surf ; surf = (drawSurf_t *)surf->nextOnLight ) {
- RB_SimpleSurfaceSetup( surf );
-
- const srfTriangles_t *tri = surf->geo;
-
- glVertexPointer( 3, GL_FLOAT, sizeof( shadowCache_t ), vertexCache.Position( tri->shadowCache ) );
- glBegin( GL_LINES );
-
- for ( int j = 0 ; j < tri->numIndexes ; j+=3 ) {
- int i1 = tri->indexes[j+0];
- int i2 = tri->indexes[j+1];
- int i3 = tri->indexes[j+2];
-
- if ( (i1 & 1) + (i2 & 1) + (i3 & 1) == 1 ) {
- if ( (i1 & 1) + (i2 & 1) == 0 ) {
- glArrayElement( i1 );
- glArrayElement( i2 );
- } else if ( (i1 & 1 ) + (i3 & 1) == 0 ) {
- glArrayElement( i1 );
- glArrayElement( i3 );
- }
- }
- }
- glEnd();
-
- }
- }
- }
-
- glEnable( GL_DEPTH_TEST );
-
- GL_State( GLS_DEFAULT );
- glColor3f( 1,1,1 );
- GL_Cull( CT_FRONT_SIDED );
+ return;
}
@@ -627,76 +565,7 @@ and count up the total fill usage
=================
*/
static void RB_ShowShadowCount( void ) {
- int i;
- const drawSurf_t *surf;
- const viewLight_t *vLight;
-
- if ( !r_showShadowCount.GetBool() ) {
- return;
- }
-
- GL_State( GLS_DEFAULT );
-
- glClearStencil( 0 );
- glClear( GL_STENCIL_BUFFER_BIT );
-
- glEnable( GL_STENCIL_TEST );
-
- glStencilOp( GL_KEEP, GL_INCR, GL_INCR );
-
- glStencilFunc( GL_ALWAYS, 1, 255 );
-
- globalImages->defaultImage->Bind();
-
- // draw both sides
- GL_Cull( CT_TWO_SIDED );
-
- for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
- for ( i = 0 ; i < 2 ; i++ ) {
- for ( surf = i ? vLight->localShadows : vLight->globalShadows
- ; surf ; surf = (drawSurf_t *)surf->nextOnLight ) {
- RB_SimpleSurfaceSetup( surf );
- const srfTriangles_t *tri = surf->geo;
- if ( !tri->shadowCache ) {
- continue;
- }
-
- if ( r_showShadowCount.GetInteger() == 3 ) {
- // only show turboshadows
- if ( tri->numShadowIndexesNoCaps != tri->numIndexes ) {
- continue;
- }
- }
- if ( r_showShadowCount.GetInteger() == 4 ) {
- // only show static shadows
- if ( tri->numShadowIndexesNoCaps == tri->numIndexes ) {
- continue;
- }
- }
-
- shadowCache_t *cache = (shadowCache_t *)vertexCache.Position( tri->shadowCache );
- glVertexPointer( 4, GL_FLOAT, sizeof( *cache ), &cache->xyz );
- RB_DrawElementsWithCounters( tri );
- }
- }
- }
-
- // display the results
- R_ColorByStencilBuffer();
-
- if ( r_showShadowCount.GetInteger() == 2 ) {
- common->Printf( "all shadows " );
- } else if ( r_showShadowCount.GetInteger() == 3 ) {
- common->Printf( "turboShadows " );
- } else if ( r_showShadowCount.GetInteger() == 4 ) {
- common->Printf( "static shadows " );
- }
-
- if ( r_showShadowCount.GetInteger() >= 2 ) {
- RB_CountStencilBuffer();
- }
-
- GL_Cull( CT_FRONT_SIDED );
+ return;
}
diff --git a/neo/engine/renderer/tr_trisurf.cpp b/neo/engine/renderer/tr_trisurf.cpp
index d0a3b9eb..8d4116c9 100644
--- a/neo/engine/renderer/tr_trisurf.cpp
+++ b/neo/engine/renderer/tr_trisurf.cpp
@@ -340,6 +340,10 @@ void R_FreeStaticTriSurfVertexCaches( srfTriangles_t *tri ) {
// this is a real model surface
vertexCache.Free( tri->ambientCache );
tri->ambientCache = NULL;
+ if ( tri->ambientVbo ) {
+ glDeleteBuffersARB( 1, &tri->ambientVbo );
+ tri->ambientVbo = 0;
+ }
} else {
// this is a light interaction surface that references
// a different ambient model surface
@@ -350,6 +354,10 @@ void R_FreeStaticTriSurfVertexCaches( srfTriangles_t *tri ) {
vertexCache.Free( tri->indexCache );
tri->indexCache = NULL;
}
+ if ( tri->indexVbo ) {
+ glDeleteBuffersARB( 1, &tri->indexVbo );
+ tri->indexVbo = 0;
+ }
if ( tri->shadowCache && ( tri->shadowVertexes != NULL || tri->verts != NULL ) ) {
// if we don't have tri->shadowVertexes, these are a reference to a
// shadowCache on the original surface, which a vertex program
@@ -2272,4 +2280,3 @@ int R_DeformInfoMemoryUsed( deformInfo_t *deformInfo ) {
total += sizeof( *deformInfo );
return total;
}
-
diff --git a/neo/engine/tools/radiant/CamWnd.cpp b/neo/engine/tools/radiant/CamWnd.cpp
index 4daef00f..e9c80abd 100644
--- a/neo/engine/tools/radiant/CamWnd.cpp
+++ b/neo/engine/tools/radiant/CamWnd.cpp
@@ -36,6 +36,7 @@ If you have questions concerning this license or the applicable additional terms
#include "OutlinerDlg.h"
#include "splines.h"
#include
+#include
#include "../../renderer/tr_local.h"
#include "../../models/model_local.h" // for idRenderModelMD5
@@ -1564,24 +1565,38 @@ static bool CamWnd_IsTriggerLikeBrushEntity(brush_t* brush) {
CamWnd_StringStartsWithNoCase(className, "monsterclip");
}
-static bool CamWnd_TargetLinesVisible(CCamWnd* cam) {
- return (CamWnd_GetFilterMask(cam) & CAMWND_FILTER_HIDE_TARGET_LINES) == 0;
+struct camWndBrushFilterContext_t {
+ CCamWnd* cam;
+ int mask;
+ bool hasHiddenEntities;
+};
+
+static camWndBrushFilterContext_t CamWnd_MakeBrushFilterContext(CCamWnd* cam) {
+ camWndBrushFilterContext_t ctx;
+ ctx.cam = cam;
+ ctx.mask = CamWnd_GetFilterMask(cam);
+ ctx.hasHiddenEntities = Outliner_HasHiddenEntities();
+ return ctx;
}
-static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
+static bool CamWnd_TargetLinesVisible(const camWndBrushFilterContext_t& filter) {
+ return (filter.mask & CAMWND_FILTER_HIDE_TARGET_LINES) == 0;
+}
+
+static bool CamWnd_MenuFilterBrush(const camWndBrushFilterContext_t& filter, brush_t* brush) {
if (!brush) {
return false;
}
- if (brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) {
+ if (filter.hasHiddenEntities && brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) {
return true;
}
- if (!cam) {
+ if (!filter.cam) {
return false;
}
- const int mask = CamWnd_GetFilterMask(cam);
+ const int mask = filter.mask;
if (mask == 0) {
return false;
}
@@ -1618,6 +1633,11 @@ static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
return false;
}
+static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) {
+ const camWndBrushFilterContext_t filter = CamWnd_MakeBrushFilterContext(cam);
+ return CamWnd_MenuFilterBrush(filter, brush);
+}
+
/*
========================
@@ -3556,6 +3576,7 @@ void setGLMode(int mode) {
break;
case cd_texture:
+ case cd_light:
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
glShadeModel(GL_FLAT);
@@ -3577,6 +3598,215 @@ void setGLMode(int mode) {
}
}
+struct camWndWorldFaceBatch_t {
+ const idMaterial* material;
+ std::vector faces;
+};
+
+static bool CamWnd_IsFastWorldBrush(brush_t* brush) {
+ if (brush == NULL || brush->hiddenBrush) {
+ return false;
+ }
+ if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel || brush->forceWireFrame) {
+ return false;
+ }
+ if (brush->owner == NULL || brush->owner != world_entity || brush->owner->curve != NULL) {
+ return false;
+ }
+ if (brush->owner->eclass == NULL || !(brush->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN)) {
+ return false;
+ }
+ return true;
+}
+
+static bool CamWnd_CanFastDrawWorldMode(int drawMode) {
+ return drawMode == cd_solid || drawMode == cd_texture || drawMode == cd_light;
+}
+
+static bool CamWnd_ShouldDrawFastFace(face_t* face) {
+ if (face == NULL || face->face_winding == NULL || face->face_winding->GetNumPoints() < 3) {
+ return false;
+ }
+ if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) && strstr(face->texdef.name, "caulk")) {
+ return false;
+ }
+ if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_VISPORTALS) && strstr(face->texdef.name, "visportal")) {
+ return false;
+ }
+ if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_NODRAW) && strstr(face->texdef.name, "nodraw")) {
+ return false;
+ }
+ return true;
+}
+
+static camWndWorldFaceBatch_t* CamWnd_FindWorldFaceBatch(std::vector& batches, const idMaterial* material) {
+ for (size_t i = 0; i < batches.size(); i++) {
+ if (batches[i].material == material) {
+ return &batches[i];
+ }
+ }
+
+ camWndWorldFaceBatch_t batch;
+ batch.material = material;
+ batch.faces.reserve(128);
+ batches.push_back(batch);
+ return &batches.back();
+}
+
+static void CamWnd_AddFastWorldBrush(std::vector& batches, brush_t* brush, int drawMode) {
+ const bool textured = (drawMode == cd_texture || drawMode == cd_light);
+
+ for (face_t* face = brush->brush_faces; face; face = face->next) {
+ if (!CamWnd_ShouldDrawFastFace(face)) {
+ continue;
+ }
+
+ const idMaterial* material = textured ? face->d_texture : NULL;
+ CamWnd_FindWorldFaceBatch(batches, material)->faces.push_back(face);
+ }
+}
+
+static void CamWnd_EmitFastWorldFaceBatches(const std::vector& batches, int drawMode) {
+ const bool textured = (drawMode == cd_texture || drawMode == cd_light);
+
+ for (size_t batchIndex = 0; batchIndex < batches.size(); batchIndex++) {
+ const camWndWorldFaceBatch_t& batch = batches[batchIndex];
+ if (batch.faces.empty()) {
+ continue;
+ }
+
+ if (textured && batch.material != NULL) {
+ batch.material->GetEditorImage()->Bind();
+ }
+ else {
+ globalImages->BindNull();
+ }
+
+ glBegin(GL_TRIANGLES);
+ for (size_t faceIndex = 0; faceIndex < batch.faces.size(); faceIndex++) {
+ face_t* face = batch.faces[faceIndex];
+ idWinding* w = face->face_winding;
+ const int numPoints = w->GetNumPoints();
+ const float alpha = face->d_texture ? face->d_texture->GetEditorAlpha() : 1.0f;
+
+ glColor4f(face->d_color.x, face->d_color.y, face->d_color.z, alpha);
+
+ for (int i = 2; i < numPoints; i++) {
+ if (textured) {
+ glTexCoord2fv(&(*w)[0][3]);
+ }
+ glVertex3fv((*w)[0].ToFloatPtr());
+
+ if (textured) {
+ glTexCoord2fv(&(*w)[i - 1][3]);
+ }
+ glVertex3fv((*w)[i - 1].ToFloatPtr());
+
+ if (textured) {
+ glTexCoord2fv(&(*w)[i][3]);
+ }
+ glVertex3fv((*w)[i].ToFloatPtr());
+ }
+ }
+ glEnd();
+ }
+
+ globalImages->BindNull();
+}
+
+static void CamWnd_DrawActiveBrushes(CCamWnd* cam, bool renderMode, bool entityMode, const camWndBrushFilterContext_t& filter) {
+ std::vector batches;
+ std::vector slowBrushes;
+ batches.reserve(64);
+ slowBrushes.reserve(64);
+
+ for (brush_t* brush = active_brushes.next; brush != &active_brushes; brush = brush->next) {
+ if (cam->CullBrush(brush, false)) {
+ continue;
+ }
+
+ if (FilterBrush(brush)) {
+ continue;
+ }
+
+ if (CamWnd_MenuFilterBrush(filter, brush)) {
+ continue;
+ }
+
+ if (renderMode) {
+ if (!(entityMode && brush->owner->eclass->fixedsize)) {
+ continue;
+ }
+ slowBrushes.push_back(brush);
+ continue;
+ }
+
+ if (CamWnd_CanFastDrawWorldMode(cam->Camera().draw_mode) && CamWnd_IsFastWorldBrush(brush)) {
+ CamWnd_AddFastWorldBrush(batches, brush, cam->Camera().draw_mode);
+ }
+ else {
+ slowBrushes.push_back(brush);
+ }
+ }
+
+ if (!batches.empty()) {
+ setGLMode(cam->Camera().draw_mode);
+ CamWnd_EmitFastWorldFaceBatches(batches, cam->Camera().draw_mode);
+ }
+
+ for (size_t i = 0; i < slowBrushes.size(); i++) {
+ setGLMode(cam->Camera().draw_mode);
+ Brush_Draw(slowBrushes[i]);
+ }
+}
+
+static void CamWnd_DrawRadiantLightFrustum(const renderLight_t& lightParms, const idVec3& color) {
+ idPlane planes[6];
+ R_RenderLightFrustum(lightParms, planes);
+ srfTriangles_t* tri = R_PolytopeSurface(6, planes, NULL);
+ if (tri == NULL) {
+ return;
+ }
+
+ glColor3fv(color.ToFloatPtr());
+ for (int i = 0; i < tri->numIndexes; i += 3) {
+ glBegin(GL_LINE_LOOP);
+ glVertex3fv(tri->verts[tri->indexes[i]].xyz.ToFloatPtr());
+ glVertex3fv(tri->verts[tri->indexes[i + 1]].xyz.ToFloatPtr());
+ glVertex3fv(tri->verts[tri->indexes[i + 2]].xyz.ToFloatPtr());
+ glEnd();
+ }
+
+ R_FreeStaticTriSurf(tri);
+}
+
+static void CamWnd_DrawRadiantLightFrustums(CCamWnd* cam) {
+ if (cam == NULL || !cam->GetRenderMode() || !(cam->GetEntityMode() || g_bShowLightVolumes)) {
+ return;
+ }
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glLineWidth(1.0f);
+ globalImages->BindNull();
+
+ for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) {
+ if (ent->eclass == NULL || !(ent->eclass->nShowFlags & ECLASS_LIGHT) || ent->epairs.GetBool("start_off")) {
+ continue;
+ }
+
+ idDict spawnArgs = ent->epairs;
+ renderLight_t lightParms;
+ gameEdit->ParseSpawnArgsToRenderLight(&spawnArgs, &lightParms);
+
+ CamWnd_DrawRadiantLightFrustum(lightParms, idVec3(1.0f, 0.0f, 1.0f));
+ }
+
+ glEnable(GL_DEPTH_TEST);
+}
+
static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) {
if (face == NULL || face->face_winding == NULL) {
return false;
@@ -3590,13 +3820,13 @@ static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) {
return true;
}
-static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list) {
+static void CamWnd_DrawMoveSelectedBrushSidesForList(const camWndBrushFilterContext_t& filter, brush_t* list) {
if (list == NULL || list->next == NULL) {
return;
}
for (brush_t* brush = list->next; brush != list; brush = brush->next) {
- if (CamWnd_MenuFilterBrush(cam, brush)) {
+ if (CamWnd_MenuFilterBrush(filter, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel) {
@@ -3611,7 +3841,7 @@ static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list
}
}
-static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) {
+static void CamWnd_DrawMoveSelectedBrushSides(const camWndBrushFilterContext_t& filter) {
glPushAttrib(GL_CURRENT_BIT);
globalImages->BindNull();
glDisable(GL_LIGHTING);
@@ -3621,8 +3851,8 @@ static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) {
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
- CamWnd_DrawMoveSelectedBrushSidesForList(cam, &active_brushes);
- CamWnd_DrawMoveSelectedBrushSidesForList(cam, &selected_brushes);
+ CamWnd_DrawMoveSelectedBrushSidesForList(filter, &active_brushes);
+ CamWnd_DrawMoveSelectedBrushSidesForList(filter, &selected_brushes);
glPopAttrib();
}
@@ -3756,29 +3986,10 @@ void CCamWnd::Cam_Draw() {
Cam_BuildMatrix();
- for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) {
+ const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this);
- if (CullBrush(brush, false)) {
- continue;
- }
-
- if (FilterBrush(brush)) {
- continue;
- }
-
- if (CamWnd_MenuFilterBrush(this, brush)) {
- continue;
- }
-
- if (renderMode) {
- if (!(entityMode && brush->owner->eclass->fixedsize)) {
- continue;
- }
- }
-
- setGLMode(m_Camera.draw_mode);
- Brush_Draw(brush);
- }
+ CamWnd_DrawActiveBrushes(this, renderMode, entityMode, brushFilter);
+ CamWnd_DrawRadiantLightFrustums(this);
//glDepthMask ( 1 ); // Ok, write now
@@ -3791,7 +4002,7 @@ void CCamWnd::Cam_Draw() {
if (!renderMode) {
// draw normally
for (brush = pList->next; brush != pList; brush = brush->next) {
- if (CamWnd_MenuFilterBrush(this, brush)) {
+ if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
if (brush->pPatch) {
@@ -3812,7 +4023,7 @@ void CCamWnd::Cam_Draw() {
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
globalImages->BindNull();
for (brush = pList->next; brush != pList; brush = brush->next) {
- if (CamWnd_MenuFilterBrush(this, brush)) {
+ if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0) {
@@ -3860,7 +4071,7 @@ void CCamWnd::Cam_Draw() {
glColor4f(1.0f, 0.0f, 0.0f, 0.25f);
for (brush = pList->next; brush != pList; brush = brush->next) {
- if (CamWnd_MenuFilterBrush(this, brush)) {
+ if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
if (brush->pPatch || brush->modelHandle > 0) {
@@ -3872,7 +4083,7 @@ void CCamWnd::Cam_Draw() {
}
}
- CamWnd_DrawMoveSelectedBrushSides(this);
+ CamWnd_DrawMoveSelectedBrushSides(brushFilter);
CamWnd_FaceExtrudeDrawPreview(this);
CamWnd_GizmoDraw(this);
@@ -3913,7 +4124,7 @@ void CCamWnd::Cam_Draw() {
// draw pointfile
glEnable(GL_DEPTH_TEST);
- if (CamWnd_TargetLinesVisible(this)) {
+ if (CamWnd_TargetLinesVisible(brushFilter)) {
CamWnd_DrawVisiblePathLines();
}
@@ -4196,12 +4407,12 @@ static unsigned int EditorHashBrushGeometry(brush_t* brush, const idVec3& origin
return hash;
}
-static unsigned int EditorHashBModelGeometry(CCamWnd* cam, entity_t* ent) {
+static unsigned int EditorHashBModelGeometry(entity_t* ent, const camWndBrushFilterContext_t& filter) {
unsigned int hash = EDITOR_RENDER_HASH_INIT;
hash = EditorHashString(hash, ValueForKey(ent, "name"));
for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) {
- if (FilterBrush(brush) || CamWnd_MenuFilterBrush(cam, brush) || Map_IsBrushFiltered(brush)) {
+ if (FilterBrush(brush) || CamWnd_MenuFilterBrush(filter, brush) || Map_IsBrushFiltered(brush)) {
continue;
}
const unsigned int brushHash = EditorHashBrushGeometry(brush, ent->origin);
@@ -4255,12 +4466,12 @@ static idRenderModel* EditorBuildSingleBrushModel(brush_t* brush, const idVec3&
return model;
}
-static idRenderModel* EditorBuildBModel(CCamWnd* cam, entity_t* ent, const char* name) {
+static idRenderModel* EditorBuildBModel(entity_t* ent, const char* name, const camWndBrushFilterContext_t& filter) {
idTriList tris(1024);
idMatList mats(1024);
for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) {
- if (FilterBrush(brush) || CamWnd_MenuFilterBrush(cam, brush) || Map_IsBrushFiltered(brush)) {
+ if (FilterBrush(brush) || CamWnd_MenuFilterBrush(filter, brush) || Map_IsBrushFiltered(brush)) {
continue;
}
Brush_ToTris(brush, &tris, &mats, false, true);
@@ -4600,6 +4811,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
idDict spawnArgs;
const char* name = NULL;
editorRenderEntityState_t* entityState = EditorTouchEntityState(ent);
+ const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this);
// The old code used update=false as "tear down and recreate". The
// incremental path keeps the handles and uses UpdateEntityDef /
@@ -4611,7 +4823,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
// If the entity is no longer renderable, remove only this entity's defs.
if (ent->brushes.onext == &ent->brushes ||
FilterBrush(ent->brushes.onext) ||
- CamWnd_MenuFilterBrush(this, ent->brushes.onext) ||
+ CamWnd_MenuFilterBrush(brushFilter, ent->brushes.onext) ||
CullBrush(ent->brushes.onext, true) ||
Map_IsBrushFiltered(ent->brushes.onext)) {
EditorFreeEntityModelDef(entityState, ent);
@@ -4636,7 +4848,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
// Brush model entity. Rebuild the renderModel only when the
// entity-local brush geometry/materials changed. Plain movement
// is handled by UpdateEntityDef below.
- const unsigned int geometryHash = EditorHashBModelGeometry(this, ent);
+ const unsigned int geometryHash = EditorHashBModelGeometry(ent, brushFilter);
editorRenderBModelState_t* bmodelState = EditorTouchBModelState(ent);
idRenderModel* oldModel = NULL;
@@ -4646,7 +4858,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) {
idStr::Icmp(bmodelState->modelName.c_str(), name) != 0);
if (geometryChanged) {
- idRenderModel* newModel = EditorBuildBModel(this, ent, name);
+ idRenderModel* newModel = EditorBuildBModel(ent, name, brushFilter);
if (newModel) {
oldModel = bmodelState->model;
bmodelState->model = newModel;
@@ -5348,6 +5560,7 @@ void CCamWnd::DrawEntityData() {
idVec3 color(0, 1, 0);
glColor3fv(color.ToFloatPtr());
+ const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this);
brush_t* brushList = &active_brushes;
int pass = 0;
while (brushList) {
@@ -5361,7 +5574,7 @@ void CCamWnd::DrawEntityData() {
continue;
}
- if (CamWnd_MenuFilterBrush(this, brush)) {
+ if (CamWnd_MenuFilterBrush(brushFilter, brush)) {
continue;
}
diff --git a/neo/engine/tools/radiant/XYWnd.cpp b/neo/engine/tools/radiant/XYWnd.cpp
index 3d6fe511..5bfd957a 100644
--- a/neo/engine/tools/radiant/XYWnd.cpp
+++ b/neo/engine/tools/radiant/XYWnd.cpp
@@ -5474,97 +5474,140 @@ bool FilterBrush(brush_t *pb) {
the lines can be visible when neither end is. Called for both camera view and xy view.
=======================================================================================================================
*/
-void DrawPathLines(void) {
- int i, k;
- idVec3 mid, mid1;
- entity_t *se, *te;
- brush_t *sb, *tb;
- const char *psz;
- idVec3 dir, s1, s2;
- float len, f;
- int arrows;
- int num_entities;
- const char *ent_target[MAX_MAP_ENTITIES];
- entity_t *ent_entity[MAX_MAP_ENTITIES];
+struct pathLineNamedEntity_t {
+ const char* name;
+ entity_t* entity;
+ brush_t* brush;
+};
+static bool DrawPathLines_IsTargetKey(const char* key) {
+ if (key == NULL) {
+ return false;
+ }
+
+ if (idStr::Cmp(key, "target") == 0) {
+ return true;
+ }
+
+ if (idStr::Cmpn(key, "target", 6) != 0 || key[6] == '\0') {
+ return false;
+ }
+
+ for (const char* c = key + 6; *c != '\0'; c++) {
+ if (*c < '0' || *c > '9') {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static brush_t* DrawPathLines_FirstBrush(entity_t* ent) {
+ if (ent == NULL) {
+ return NULL;
+ }
+
+ brush_t* brush = ent->brushes.onext;
+ return (brush != &ent->brushes) ? brush : NULL;
+}
+
+static void DrawPathLines_AppendNamedEntity(idList& namedEntities, idHashIndex& nameHash, entity_t* ent) {
+ const char* name = ValueForKey(ent, "name");
+ if (name == NULL || name[0] == '\0') {
+ return;
+ }
+
+ brush_t* brush = DrawPathLines_FirstBrush(ent);
+ if (brush == NULL) {
+ return;
+ }
+
+ pathLineNamedEntity_t named;
+ named.name = name;
+ named.entity = ent;
+ named.brush = brush;
+
+ const int index = namedEntities.Append(named);
+ nameHash.Add(nameHash.GenerateKey(name, true), index);
+}
+
+static void DrawPathLines_DrawConnection(const pathLineNamedEntity_t& namedTarget, entity_t* source, brush_t* sourceBrush) {
+ if (namedTarget.entity == NULL || namedTarget.entity->eclass == NULL || source == NULL || sourceBrush == NULL) {
+ return;
+ }
+
+ idVec3 mid = namedTarget.brush->owner->origin;
+ idVec3 mid1 = sourceBrush->owner->origin;
+ idVec3 dir, s1, s2;
+
+ VectorSubtract(mid1, mid, dir);
+ const float len = dir.Normalize();
+ s1[0] = -dir[1] * 8 + dir[0] * 8;
+ s2[0] = dir[1] * 8 + dir[0] * 8;
+ s1[1] = dir[0] * 8 + dir[1] * 8;
+ s2[1] = -dir[0] * 8 + dir[1] * 8;
+
+ glColor3f(namedTarget.entity->eclass->color[0], namedTarget.entity->eclass->color[1], namedTarget.entity->eclass->color[2]);
+
+ glBegin(GL_LINES);
+ glVertex3fv(mid.ToFloatPtr());
+ glVertex3fv(mid1.ToFloatPtr());
+
+ const int arrows = (int)(len / 256) + 1;
+ for (int i = 0; i < arrows; i++) {
+ const float f = len * (i + 0.5f) / arrows;
+
+ mid1 = mid + (f * dir);
+
+ glVertex3fv(mid1.ToFloatPtr());
+ glVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]);
+ glVertex3fv(mid1.ToFloatPtr());
+ glVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]);
+ }
+
+ glEnd();
+}
+
+void DrawPathLines(void) {
if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) {
return;
}
- num_entities = 0;
- for (te = entities.next; te != &entities && num_entities != MAX_MAP_ENTITIES; te = te->next) {
- for (int i = 0; i < 2048; i++) {
- if (i == 0) {
- ent_target[num_entities] = ValueForKey(te, "target");
- } else {
- ent_target[num_entities] = ValueForKey(te, va("target%i", i));
- }
- if (ent_target[num_entities][0]) {
- ent_entity[num_entities] = te;
- num_entities++;
- } else if (i > 16) {
- break;
- }
- }
+ idList namedEntities;
+ idHashIndex nameHash(1024, 1024);
+ namedEntities.SetGranularity(256);
+
+ for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) {
+ DrawPathLines_AppendNamedEntity(namedEntities, nameHash, ent);
}
- for (se = entities.next; se != &entities; se = se->next) {
- psz = ValueForKey(se, "name");
-
- if (psz == NULL || psz[0] == '\0') {
+ for (entity_t* source = entities.next; source != &entities; source = source->next) {
+ brush_t* sourceBrush = DrawPathLines_FirstBrush(source);
+ if (sourceBrush == NULL) {
continue;
}
- sb = se->brushes.onext;
- if (sb == &se->brushes) {
- continue;
- }
-
- for (k = 0; k < num_entities; k++) {
- if (strcmp(ent_target[k], psz)) {
+ const int numKeys = source->epairs.GetNumKeyVals();
+ for (int keyIndex = 0; keyIndex < numKeys; keyIndex++) {
+ const idKeyValue* kv = source->epairs.GetKeyVal(keyIndex);
+ if (kv == NULL || !DrawPathLines_IsTargetKey(kv->GetKey().c_str())) {
continue;
}
- te = ent_entity[k];
- tb = te->brushes.onext;
- if (tb == &te->brushes) {
+ const char* targetName = kv->GetValue().c_str();
+ if (targetName == NULL || targetName[0] == '\0') {
continue;
}
- mid = sb->owner->origin;
- mid1 = tb->owner->origin;
-
- VectorSubtract(mid1, mid, dir);
- len = dir.Normalize();
- s1[0] = -dir[1] * 8 + dir[0] * 8;
- s2[0] = dir[1] * 8 + dir[0] * 8;
- s1[1] = dir[0] * 8 + dir[1] * 8;
- s2[1] = -dir[0] * 8 + dir[1] * 8;
-
- glColor3f(se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]);
-
- glBegin(GL_LINES);
- glVertex3fv(mid.ToFloatPtr());
- glVertex3fv(mid1.ToFloatPtr());
-
- arrows = (int)(len / 256) + 1;
-
- for (i = 0; i < arrows; i++) {
- f = len * (i + 0.5) / arrows;
-
- mid1 = mid + (f * dir);
-
- glVertex3fv(mid1.ToFloatPtr());
- glVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]);
- glVertex3fv(mid1.ToFloatPtr());
- glVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]);
+ const int hashKey = nameHash.GenerateKey(targetName, true);
+ for (int index = nameHash.First(hashKey); index != -1; index = nameHash.Next(index)) {
+ const pathLineNamedEntity_t& namedTarget = namedEntities[index];
+ if (strcmp(namedTarget.name, targetName) == 0) {
+ DrawPathLines_DrawConnection(namedTarget, source, sourceBrush);
+ }
}
-
- glEnd();
}
}
-
- return;
}
//