From 28a1d31c28b9f86d29ef795083d5972bd46c91a5 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Thu, 21 May 2026 16:13:45 -0700 Subject: [PATCH] Texture batching is now more efficient --- neo/engine/opengl/gl_d3d12shim.cpp | 303 +++++++++++++---------------- 1 file changed, 140 insertions(+), 163 deletions(-) diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp index 784197ac..b3669ece 100644 --- a/neo/engine/opengl/gl_d3d12shim.cpp +++ b/neo/engine/opengl/gl_d3d12shim.cpp @@ -657,6 +657,8 @@ static void QD3D12_RequestAsyncMipBuild(TextureResource& tex); static void QD3D12_InvalidateTextureMipChain(TextureResource& tex, bool invalidateBase); static bool QD3D12_TextureHasNeuralPOMData(const TextureResource& tex); static bool QD3D12_UploadNeuralPOM(TextureResource& tex); +static void EnsureTextureResource(TextureResource& tex); +static void UploadTexture(TextureResource& tex); static UINT QD3D12_NeuralPOMFallbackSrvIndex(); static void QD3D12_CreateNeuralPOMZeroBuffer(); @@ -812,7 +814,7 @@ struct BatchKey uint32_t arbFragmentRevision = 0; ID3DBlob* arbVertexBlob = nullptr; ID3DBlob* arbFragmentBlob = nullptr; - QD3D12ARBDrawConstantArrays arbConstants{}; + std::shared_ptr arbConstants; float texComb0RGB[4] = {}; float texComb0Alpha[4] = {}; @@ -965,8 +967,8 @@ static uint64_t QD3D12_HashBatchKey(const BatchKey& key) QD3D12_MixHash(h, key.arbFragmentRevision); QD3D12_MixHash(h, reinterpret_cast(key.arbVertexBlob)); QD3D12_MixHash(h, reinterpret_cast(key.arbFragmentBlob)); - if (key.useARBPrograms) - QD3D12_MixHashBytes(h, &key.arbConstants, sizeof(key.arbConstants)); + if (key.useARBPrograms && key.arbConstants) + QD3D12_MixHashBytes(h, key.arbConstants.get(), sizeof(*key.arbConstants)); QD3D12_MixHashBytes(h, key.texComb0RGB, sizeof(key.texComb0RGB)); QD3D12_MixHashBytes(h, key.texComb0Alpha, sizeof(key.texComb0Alpha)); QD3D12_MixHashBytes(h, key.texComb0Operand, sizeof(key.texComb0Operand)); @@ -1121,7 +1123,9 @@ static bool BatchKeyDeepEquals(const BatchKey& a, const BatchKey& b) a.arbFragmentRevision == b.arbFragmentRevision && a.arbVertexBlob == b.arbVertexBlob && a.arbFragmentBlob == b.arbFragmentBlob && - (!a.useARBPrograms || memcmp(&a.arbConstants, &b.arbConstants, sizeof(a.arbConstants)) == 0) && + (!a.useARBPrograms || + (a.arbConstants && b.arbConstants && + memcmp(a.arbConstants.get(), b.arbConstants.get(), sizeof(*a.arbConstants)) == 0)) && memcmp(a.mvp.m, b.mvp.m, sizeof(a.mvp.m)) == 0 && memcmp(a.prevMvp.m, b.prevMvp.m, sizeof(a.prevMvp.m)) == 0 && memcmp(a.modelMatrix.m, b.modelMatrix.m, sizeof(a.modelMatrix.m)) == 0; @@ -1410,6 +1414,15 @@ struct ImmediateVertexBuffer } }; +struct QD3D12PreparedImmediateTextures +{ + TextureResource* boundTextures[QD3D12_MaxTextureUnits] = {}; + TextureResource* normalMapTex = nullptr; + TextureResource* glowMapTex = nullptr; + TextureResource* specularMapTex = nullptr; + uint64_t stamp = 0; +}; + struct GLState { VertexArena frameVerts; @@ -1634,6 +1647,9 @@ struct GLState BatchKey cachedImmediateBatchKey{}; uint64_t cachedImmediateBatchStamp = 0; bool cachedImmediateBatchKeyValid = false; + QD3D12PreparedImmediateTextures cachedImmediateTextures{}; + uint64_t cachedImmediateTextureStamp = 0; + bool cachedImmediateTexturesValid = false; GLenum matrixMode = GL_MODELVIEW; std::vector modelStack{ Mat4::Identity() }; @@ -5287,9 +5303,13 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* key.arbFragmentRevision = QD3D12ARB_GetBoundFragmentRevision(); key.arbVertexBlob = QD3D12ARB_GetVertexShaderBlob(); key.arbFragmentBlob = QD3D12ARB_GetFragmentShaderBlob(); - QD3D12ARB_FillDrawConstantArrays(&key.arbConstants); + key.arbConstants = std::make_shared(); + QD3D12ARB_FillDrawConstantArrays(key.arbConstants.get()); if (!key.arbVertexBlob || !key.arbFragmentBlob) + { key.useARBPrograms = false; + key.arbConstants.reset(); + } } key.alphaRef = g_gl.alphaRef; @@ -5564,6 +5584,113 @@ static BatchKey QD3D12_GetOrBuildImmediateBatchKey( return key; } +static uint64_t QD3D12_GatherImmediateTextureCandidates(bool arbProgramsActive, TextureResource** boundTextures) +{ + uint64_t h = 1469598103934665603ull; + QD3D12_MixHash(h, arbProgramsActive ? 1ull : 0ull); + QD3D12_MixHash(h, g_gl.currentNormalMapTexture); + QD3D12_MixHash(h, g_gl.currentGlowMapTexture); + QD3D12_MixHash(h, g_gl.currentSpecularMapTexture); + QD3D12_MixHash(h, g_gl.currentNeuralPOMTexture); + QD3D12_MixHash(h, g_gl.neuralPOMEnabled ? 1ull : 0ull); + + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + { + boundTextures[unit] = &g_gl.whiteTexture; + + const GLuint boundTexture = g_gl.boundTexture[unit]; + QD3D12_MixHash(h, boundTexture); + QD3D12_MixHash(h, g_gl.texture2D[unit] ? 1ull : 0ull); + + TextureResource* tex = boundTexture != 0 ? QD3D12_FindTextureResource(boundTexture) : nullptr; + QD3D12_MixTextureStamp(h, tex); + + const bool fixedColorUnit = (!arbProgramsActive) && (g_gl.texture2D[unit] && unit < 2); + 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 arbUnit = arbProgramsActive && (boundTexture != 0); + + if ((arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit || taggedSpecularUnit || explicitSpecularUnit) && tex) + boundTextures[unit] = tex; + } + + QD3D12_MixTextureStamp(h, g_gl.currentNormalMapTexture != 0 ? QD3D12_FindTextureResource(g_gl.currentNormalMapTexture) : nullptr); + QD3D12_MixTextureStamp(h, g_gl.currentGlowMapTexture != 0 ? QD3D12_FindTextureResource(g_gl.currentGlowMapTexture) : nullptr); + QD3D12_MixTextureStamp(h, g_gl.currentSpecularMapTexture != 0 ? QD3D12_FindTextureResource(g_gl.currentSpecularMapTexture) : nullptr); + QD3D12_MixTextureStamp(h, g_gl.currentNeuralPOMTexture != 0 ? QD3D12_FindTextureResource(g_gl.currentNeuralPOMTexture) : nullptr); + + return h ? h : 1ull; +} + +static QD3D12PreparedImmediateTextures QD3D12_PrepareImmediateTextures(bool arbProgramsActive) +{ + QD3D12PreparedImmediateTextures prepared{}; + prepared.stamp = QD3D12_GatherImmediateTextureCandidates(arbProgramsActive, prepared.boundTextures); + + if (g_gl.cachedImmediateTexturesValid && + g_gl.cachedImmediateTextureStamp == prepared.stamp) + { + return g_gl.cachedImmediateTextures; + } + + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + { + TextureResource* tex = prepared.boundTextures[unit]; + if (tex && tex != &g_gl.whiteTexture && !tex->gpuValid) + { + EnsureTextureResource(*tex); + UploadTexture(*tex); + } + } + + prepared.normalMapTex = QD3D12_SelectNormalMapTexture(prepared.boundTextures); + if (prepared.normalMapTex && prepared.normalMapTex != &g_gl.whiteTexture && !prepared.normalMapTex->gpuValid) + { + EnsureTextureResource(*prepared.normalMapTex); + UploadTexture(*prepared.normalMapTex); + } + if (prepared.normalMapTex && prepared.normalMapTex != &g_gl.whiteTexture && QD3D12_TextureHasNeuralPOMData(*prepared.normalMapTex)) + QD3D12_UploadNeuralPOM(*prepared.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 = prepared.boundTextures[unit]; + if (neuralCandidate && neuralCandidate != &g_gl.whiteTexture && QD3D12_TextureHasNeuralPOMData(*neuralCandidate)) + QD3D12_UploadNeuralPOM(*neuralCandidate); + } + + prepared.glowMapTex = QD3D12_SelectGlowMapTexture(prepared.boundTextures); + if (prepared.glowMapTex && prepared.glowMapTex != &g_gl.whiteTexture && !prepared.glowMapTex->gpuValid) + { + EnsureTextureResource(*prepared.glowMapTex); + UploadTexture(*prepared.glowMapTex); + } + + prepared.specularMapTex = QD3D12_SelectSpecularMapTexture(prepared.boundTextures); + if (prepared.specularMapTex && prepared.specularMapTex != &g_gl.whiteTexture && !prepared.specularMapTex->gpuValid) + { + EnsureTextureResource(*prepared.specularMapTex); + UploadTexture(*prepared.specularMapTex); + } + + prepared.stamp = QD3D12_GatherImmediateTextureCandidates(arbProgramsActive, prepared.boundTextures); + g_gl.cachedImmediateTextures = prepared; + g_gl.cachedImmediateTextureStamp = prepared.stamp; + g_gl.cachedImmediateTexturesValid = true; + return prepared; +} + static Mat4 QD3D12_GetPreviousMVPForObject(GLuint objectId, const Mat4& currentMvp) { auto it = g_gl.prevObjectMVPs.find(objectId); @@ -11427,77 +11554,8 @@ 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 = QD3D12_GetOrBuildImmediateBatchKey(mode, n, boundTextures, normalMapTex, glowMapTex, specularMapTex); + const QD3D12PreparedImmediateTextures preparedTextures = QD3D12_PrepareImmediateTextures(arbProgramsActive); + BatchKey key = QD3D12_GetOrBuildImmediateBatchKey(mode, n, preparedTextures.boundTextures, preparedTextures.normalMapTex, preparedTextures.glowMapTex, preparedTextures.specularMapTex); const size_t markerCursor = g_gl.queryMarkers.size(); if (!g_gl.queuedBatches.empty() && @@ -11526,89 +11584,8 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) QD3D12_EnsureFrameOpen(); const bool arbProgramsActive = QD3D12ARB_IsActive(); - const bool useTex0 = arbProgramsActive ? (g_gl.boundTexture[0] != 0) : g_gl.texture2D[0]; - const bool useTex1 = arbProgramsActive ? (g_gl.boundTexture[1] != 0) : g_gl.texture2D[1]; - - 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 && (g_gl.boundTexture[unit] != 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); - } - - // Neural payloads can live on the diffuse texture, the normal-map texture, or - // any explicitly bound material texture. Upload any bound payload before the - // batch key is built so BuildCurrentBatchKey can select it immediately. - 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 = QD3D12_GetOrBuildImmediateBatchKey(mode, n, boundTextures, normalMapTex, glowMapTex, specularMapTex); + const QD3D12PreparedImmediateTextures preparedTextures = QD3D12_PrepareImmediateTextures(arbProgramsActive); + BatchKey key = QD3D12_GetOrBuildImmediateBatchKey(mode, n, preparedTextures.boundTextures, preparedTextures.normalMapTex, preparedTextures.glowMapTex, preparedTextures.specularMapTex); const size_t markerCursor = g_gl.queryMarkers.size(); QueuedBatch* batch = nullptr; @@ -12046,11 +12023,11 @@ static void QD3D12_FlushQueuedBatches() dc->vertexColorPad[2] = 0.0f; dc->vertexColorPad[3] = 0.0f; - if (batch.key.useARBPrograms) + if (batch.key.useARBPrograms && batch.key.arbConstants) { - memcpy(dc->arbEnv, batch.key.arbConstants.env, sizeof(dc->arbEnv)); - memcpy(dc->arbLocalVP, batch.key.arbConstants.vertexLocal, sizeof(dc->arbLocalVP)); - memcpy(dc->arbLocalFP, batch.key.arbConstants.fragmentLocal, sizeof(dc->arbLocalFP)); + memcpy(dc->arbEnv, batch.key.arbConstants->env, sizeof(dc->arbEnv)); + memcpy(dc->arbLocalVP, batch.key.arbConstants->vertexLocal, sizeof(dc->arbLocalVP)); + memcpy(dc->arbLocalFP, batch.key.arbConstants->fragmentLocal, sizeof(dc->arbLocalFP)); } D3D12_VERTEX_BUFFER_VIEW vbv{};