Texture batching is now more efficient

This commit is contained in:
Justin Marshall
2026-05-21 16:13:45 -07:00
parent b53458b3b5
commit 28a1d31c28
+140 -163
View File
@@ -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<QD3D12ARBDrawConstantArrays> 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<uintptr_t>(key.arbVertexBlob));
QD3D12_MixHash(h, reinterpret_cast<uintptr_t>(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<Mat4> 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<QD3D12ARBDrawConstantArrays>();
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{};