mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08: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.
This commit is contained in:
@@ -91,9 +91,15 @@ namespace MobileGL {
|
||||
BlockRelayout(IRContext* irContext, Bool std140)
|
||||
: m_irContext(irContext), m_std140(std140) {}
|
||||
|
||||
// Size and alignment of `typeId`, applying every stride decoration it implies
|
||||
// on the way down. Zero size means "not a type this layout knows how to
|
||||
// describe"; the caller then leaves the block alone rather than guessing.
|
||||
// Size and alignment of `typeId`, QUEUING every offset/stride decoration it
|
||||
// implies on the way down. Zero size means "not a type this layout knows how
|
||||
// to describe"; the caller then leaves the block alone rather than guessing.
|
||||
// The queue is what makes that fallback honest: measurement must be
|
||||
// side-effect-free until it is known to succeed, or a mid-struct failure
|
||||
// would leave the block half-relaid-out - members before the failing one at
|
||||
// compacted 32-bit offsets, members after it at the original 64-bit ones, a
|
||||
// layout matching neither convention. Commit() flushes the queue and is
|
||||
// called only on a successful Measure of the whole block.
|
||||
struct Extent {
|
||||
Uint32 size = 0;
|
||||
Uint32 alignment = 0;
|
||||
@@ -108,7 +114,29 @@ namespace MobileGL {
|
||||
return extent;
|
||||
}
|
||||
|
||||
// Flushes the decoration writes a successful Measure queued. Call exactly
|
||||
// once, only when Measure returned a non-zero size; a failed measurement's
|
||||
// queue dies with this per-block instance, leaving the module untouched.
|
||||
void Commit() {
|
||||
for (const PendingDecoration& pending : m_pendingWrites) {
|
||||
if (pending.member) {
|
||||
ApplyMemberDecoration(pending.targetId, pending.memberIndex, pending.decoration,
|
||||
pending.value);
|
||||
} else {
|
||||
ApplyTypeDecoration(pending.targetId, pending.decoration, pending.value);
|
||||
}
|
||||
}
|
||||
m_pendingWrites.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
struct PendingDecoration {
|
||||
Bool member = false;
|
||||
Uint32 targetId = 0;
|
||||
Uint32 memberIndex = 0;
|
||||
spv::Decoration decoration = spv::Decoration::Offset;
|
||||
Uint32 value = 0;
|
||||
};
|
||||
Extent MeasureUncached(Uint32 typeId) {
|
||||
const Instruction* type = m_irContext->get_def_use_mgr()->GetDef(typeId);
|
||||
if (type == nullptr) return {};
|
||||
@@ -204,7 +232,17 @@ namespace MobileGL {
|
||||
return length->GetSingleWordInOperand(0);
|
||||
}
|
||||
|
||||
// Queue-only during measurement; the module is mutated in Commit().
|
||||
void SetTypeDecoration(Uint32 targetId, spv::Decoration decoration, Uint32 value) {
|
||||
m_pendingWrites.push_back({false, targetId, 0, decoration, value});
|
||||
}
|
||||
|
||||
void SetMemberDecoration(Uint32 structId, Uint32 member, spv::Decoration decoration,
|
||||
Uint32 value) {
|
||||
m_pendingWrites.push_back({true, structId, member, decoration, value});
|
||||
}
|
||||
|
||||
void ApplyTypeDecoration(Uint32 targetId, spv::Decoration decoration, Uint32 value) {
|
||||
for (Instruction& annotation : m_irContext->annotations()) {
|
||||
if (annotation.opcode() != spv::Op::OpDecorate) continue;
|
||||
if (annotation.GetSingleWordInOperand(0) != targetId) continue;
|
||||
@@ -216,8 +254,8 @@ namespace MobileGL {
|
||||
}
|
||||
}
|
||||
|
||||
void SetMemberDecoration(Uint32 structId, Uint32 member, spv::Decoration decoration,
|
||||
Uint32 value) {
|
||||
void ApplyMemberDecoration(Uint32 structId, Uint32 member, spv::Decoration decoration,
|
||||
Uint32 value) {
|
||||
for (Instruction& annotation : m_irContext->annotations()) {
|
||||
if (annotation.opcode() != spv::Op::OpMemberDecorate) continue;
|
||||
if (annotation.GetSingleWordInOperand(0) != structId) continue;
|
||||
@@ -233,6 +271,7 @@ namespace MobileGL {
|
||||
IRContext* m_irContext = nullptr;
|
||||
Bool m_std140 = true;
|
||||
std::unordered_map<Uint32, Extent> m_extents;
|
||||
std::vector<PendingDecoration> m_pendingWrites;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -529,9 +568,12 @@ namespace MobileGL {
|
||||
// A member shape the layout rules here do not describe. Leaving the block
|
||||
// at its 64-bit offsets keeps the module valid for Vulkan; SPIRV-Cross will
|
||||
// decline it for ESSL, which is the same outcome as before the demotion.
|
||||
// Nothing was written: Measure only queues, and the queue dies here.
|
||||
MGLOG_D("DemoteFloat64Pass: block %%%u contains a member this pass cannot lay "
|
||||
"out; its 64-bit offsets are left in place",
|
||||
blockType->result_id());
|
||||
} else {
|
||||
relayout.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user