mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 12:18:30 +09:00
[Fix] (DirectVulkan, GLImpl, MG_State, ShaderTranspiler): second audit round over the remaining memo sites
Six more verified defects from the residual memo/cache mechanisms: - Program resource cache (DirectVulkan reflection): glShaderStorageBlockBinding deliberately does not bump the backend state version, and the SSO pipeline composite is unnamed so the by-name in-place patch can never reach its slot - the composite kept serving pre-rebind SSBO bindings. The cache now keys on the program's block-binding version; a binding-only change re-applies the overrides by name instead of re-running spirv-reflect. SetShaderStorageBlockBinding also gains the equality bail-out its uniform-block sibling has, so the composite mirror's replay stops churning the version every draw. - LinkProgram's allowVSOnlyPrograms function-static latch never set its own initialized flag (dead memo, re-read every call) - and completing it would have frozen a per-backend capability across re-initialization. Replaced with a fresh per-link read from the null-checked active backend. - Query object registry: drained at full library teardown (DestroyAllQueryObjects, mirroring DestroyAllSyncObjects) - undeleted queries and their backend wrappers leaked across Destroy/Initialize cycles, stale ids stayed IsQuery == GL_TRUE in the re-initialized library, and a later delete could hand the old backend's wrapper to a different backend's DeleteBackendQuery. - Converted vertex streams and the host-side EBO max-index scan now SyncGpuWrites before reading the coherent mapping: XFB/SSBO/image writes are merely recorded at that point, so the conversion read pre-write bytes (the restart-index rewrite already synced; these two host reads did not). - Zero-stride converted bindings: both converters rejected stride 0, making the factory's documented single-element conversion unreachable and silently dropping every draw using such a binding; the stride is substituted with the element size for the one-element case. - DemoteFloat64Pass block relayout: measurement queued into the module eagerly, so a mid-struct failure left a half-relaid-out block (compacted offsets before the failing member, 64-bit offsets after) while claiming the block was left alone. Decoration writes are now collected and committed only when the whole block measures successfully. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MsqQQF7ugn7MqZXcmnmz1z
This commit is contained in:
@@ -69,6 +69,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
// slot's ownership unambiguous.
|
||||
Uint64 programLifetimeId = 0;
|
||||
Uint32 backendStateVersion = 0;
|
||||
// glShaderStorageBlockBinding deliberately does NOT bump the backend state
|
||||
// version, and the pipeline composite is unnamed so the in-place patch in
|
||||
// DirectVulkan::ShaderStorageBlockBinding can never reach its slot - the
|
||||
// mirror replay bumps only the program's block-binding version. Without this
|
||||
// key the composite's slot kept serving the pre-rebind block.binding.
|
||||
Uint32 blockBindingVersion = 0;
|
||||
Vector<StorageBlockResource> storageBlocks;
|
||||
Vector<BufferVariableResource> bufferVariables;
|
||||
GLint computeWorkGroupSize[3] = {1, 1, 1};
|
||||
@@ -156,18 +162,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
auto& cache = g_programResourceCaches[program.GetExternalIndex()];
|
||||
const Uint64 programLifetimeId = program.GetLifetimeId();
|
||||
const Uint32 backendStateVersion = program.GetBackendStateVersion();
|
||||
const Uint32 blockBindingVersion = program.GetBlockBindingVersion();
|
||||
// The lifetime id must match too: a new program that reuses a deleted
|
||||
// program's name and happens to land on the same backendStateVersion (both
|
||||
// count from zero) would otherwise be served the dead program's reflection.
|
||||
if (cache.programLifetimeId == programLifetimeId &&
|
||||
cache.backendStateVersion == backendStateVersion &&
|
||||
(!cache.storageBlocks.empty() || !cache.bufferVariables.empty())) {
|
||||
if (cache.blockBindingVersion != blockBindingVersion) {
|
||||
// Only the block bindings moved (glShaderStorageBlockBinding, or the
|
||||
// pipeline composite's mirror replay - neither touches the backend
|
||||
// state version): the reflection itself is unchanged, so re-apply the
|
||||
// overrides by name instead of re-running spirv-reflect. Overrides
|
||||
// only ever accumulate, so a block without one still holds its
|
||||
// declared binding.
|
||||
for (auto& block : cache.storageBlocks) {
|
||||
const Int rebound = program.GetShaderStorageBlockBindingOverride(block.name);
|
||||
if (rebound >= 0) block.binding = static_cast<Uint32>(rebound);
|
||||
}
|
||||
cache.blockBindingVersion = blockBindingVersion;
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
cache = {};
|
||||
cache.programLifetimeId = programLifetimeId;
|
||||
cache.backendStateVersion = backendStateVersion;
|
||||
cache.blockBindingVersion = blockBindingVersion;
|
||||
|
||||
Vector<SpvReflectShaderModule> modules;
|
||||
Vector<Bool> validModules;
|
||||
|
||||
Reference in New Issue
Block a user