[Refactor] (Espryt): key the narrowed fp64 vertex stream on the buffer's handle instead of its lifetime id

This commit is contained in:
2026-09-08 04:52:15 -04:00
parent cd05de504e
commit 68db6db7f8
2 changed files with 76 additions and 22 deletions
+37 -6
View File
@@ -218,8 +218,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
VertexArrayImpl::g_backendVertexArrayObjects.DestroyByLifetimeId(lifetimeId); VertexArrayImpl::g_backendVertexArrayObjects.DestroyByLifetimeId(lifetimeId);
break; break;
default: default:
// Buffer already has its own death signal (BufferBackendOps::OnDestroy) and // Buffer death crosses as ResourceDestroy (P3a): the catalogue has a call for
// every other kind has no backend twin table here. // it, so no seventh NotifyStateObjectDestroyed raiser is added. The notice
// exists for kinds that have NO such call - it carries {kind, lifetimeId} and
// not the object, and one entry point serves all of them because the answer is
// the same. The order at the buffer's death is fixed and not negotiable:
// resource_destroy first (the applier clears Live, the backend retires the
// twin), then MGPipeSlots().Free - the allocator erases its lifetimeId -> slot
// mapping on Free, so a notice resolved twice finds nothing the second time.
// On the legacy arm the signal is still BufferBackendOps::OnDestroy. Every
// other kind has no backend twin table here.
break; break;
} }
} }
@@ -2243,6 +2251,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
SizeT ResourceWidthForHandle(MG_Pipe::MGPipeHandle res) { return ResourceWidthOf(res); } SizeT ResourceWidthForHandle(MG_Pipe::MGPipeHandle res) { return ResourceWidthOf(res); }
Uint64 ResourceSerialForHandle(MG_Pipe::MGPipeHandle res) { return ResourceSerialOf(res); }
void MarkBufferGpuWritten(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject) { void MarkBufferGpuWritten(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject) {
if (!bufferObject) return; if (!bufferObject) return;
@@ -4028,6 +4037,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
#if MOBILEGL_PIPE_LEGACY_MEMOS
Bool BackendVertexArrayObject::SyncFloat64AttributeAsFloat32( Bool BackendVertexArrayObject::SyncFloat64AttributeAsFloat32(
Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib, Uint32 fetchBaseInstance) { Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib, Uint32 fetchBaseInstance) {
#ifdef TRACY_ENABLE #ifdef TRACY_ENABLE
@@ -4147,6 +4157,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
(const void*)(firstElement * convertedElementSize)); (const void*)(firstElement * convertedElementSize));
return true; return true;
} }
#endif // MOBILEGL_PIPE_LEGACY_MEMOS
#if MOBILEGL_PIPE_PUSH #if MOBILEGL_PIPE_PUSH
Bool BackendVertexArrayObject::SyncFloat64AttributeAsFloat32ByHandle( Bool BackendVertexArrayObject::SyncFloat64AttributeAsFloat32ByHandle(
@@ -4199,7 +4210,21 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false; return false;
} }
auto& stream = m_convertedAttributeStreams[attribIndex];
Uint& convertedBufferId = m_convertedAttributeBufferIds[attribIndex]; Uint& convertedBufferId = m_convertedAttributeBufferIds[attribIndex];
const Uint64 sourceSerial = BufferImpl::ResourceSerialForHandle(binding.Res);
// The memo, re-keyed: the pin is the source buffer's {slot, gen} instead of a
// frontend lifetime id, and the freshness input is the applier's server-owned
// Serial instead of the frontend change serial. A PERSISTENTLY MAPPED source is
// still never trusted - it is written through the pointer with no call at all, so
// no serial on either side can prove the converted copy is current.
const Bool memoHit = stream.valid && convertedBufferId != 0 && !resource->persistentMapped &&
stream.sourceHandle == binding.Res &&
stream.sourceChangeSerial == sourceSerial &&
stream.sourceOffset == static_cast<SizeT>(attrib.Offset) &&
stream.sourceStride == sourceStride &&
stream.componentCount == componentCount && stream.elementCount == elementCount;
if (!memoHit) {
if (convertedBufferId == 0) { if (convertedBufferId == 0) {
g_GLESFuncs.glGenBuffers(1, &convertedBufferId); g_GLESFuncs.glGenBuffers(1, &convertedBufferId);
if (convertedBufferId == 0) { if (convertedBufferId == 0) {
@@ -4209,10 +4234,6 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false; return false;
} }
} }
// NO MEMO YET on this arm: its key is the source buffer's identity plus its change
// serial, and re-keying ConvertedFloat64Stream onto the buffer's {slot, gen} is the
// next commit's whole subject. Until then every walk reconverts, which is correct
// and slower on a path that only 64-bit vertex arrays reach.
Vector<Float> converted; Vector<Float> converted;
NarrowDoubleStreamToFloat32(sourceBase + attrib.Offset, sourceStride, componentCount, elementCount, NarrowDoubleStreamToFloat32(sourceBase + attrib.Offset, sourceStride, componentCount, elementCount,
converted); converted);
@@ -4223,6 +4244,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageVertexClient, MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageVertexClient,
static_cast<Uint64>(converted.size() * sizeof(Float))); static_cast<Uint64>(converted.size() * sizeof(Float)));
} }
stream.valid = true;
stream.sourceHandle = binding.Res;
stream.sourceChangeSerial = sourceSerial;
stream.sourceOffset = static_cast<SizeT>(attrib.Offset);
stream.sourceStride = sourceStride;
stream.componentCount = componentCount;
stream.elementCount = elementCount;
MGLOG_D("DirectGLES: narrowed the 64-bit vertex array at attribute %u to %zu float32 element(s).",
attribIndex, elementCount);
}
const SizeT convertedElementSize = componentCount * sizeof(Float); const SizeT convertedElementSize = componentCount * sizeof(Float);
if (neverAdvances) { if (neverAdvances) {
+23
View File
@@ -741,6 +741,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
// thing outside BufferImpl that needs it is the fp64 narrowing, whose source extent // thing outside BufferImpl that needs it is the fp64 narrowing, whose source extent
// used to be BufferObject::GetSize(). // used to be BufferObject::GetSize().
SizeT ResourceWidthForHandle(MG_Pipe::MGPipeHandle res); SizeT ResourceWidthForHandle(MG_Pipe::MGPipeHandle res);
// The applier's server-owned mutation serial for this resource, 0 when it has no
// record. It is what the narrowed-fp64 memo keys its freshness on now that the
// frontend change serial is gone from the backend's view.
Uint64 ResourceSerialForHandle(MG_Pipe::MGPipeHandle res);
#endif #endif
// Registered as the frontend's BufferBackendOps at backend init and on // Registered as the frontend's BufferBackendOps at backend init and on
@@ -1008,8 +1012,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
// dropping it. Returns false when the stream cannot be built, in which case the // dropping it. Returns false when the stream cannot be built, in which case the
// caller must DISABLE the array - leaving a 64-bit array enabled with no pointer is // caller must DISABLE the array - leaving a 64-bit array enabled with no pointer is
// what the Adreno driver turns into a SIGSEGV at the next draw. // what the Adreno driver turns into a SIGSEGV at the next draw.
#if MOBILEGL_PIPE_LEGACY_MEMOS
Bool SyncFloat64AttributeAsFloat32(Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib, Bool SyncFloat64AttributeAsFloat32(Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib,
Uint32 fetchBaseInstance); Uint32 fetchBaseInstance);
#endif
#if MOBILEGL_PIPE_PUSH #if MOBILEGL_PIPE_PUSH
// The handle arm of the whole vertex-elements half. Everything it needs arrives in // The handle arm of the whole vertex-elements half. Everything it needs arrives in
@@ -1030,7 +1036,24 @@ namespace MobileGL::MG_Backend::DirectGLES {
// is part of the key, so a glBufferSubData into the source invalidates it. // is part of the key, so a glBufferSubData into the source invalidates it.
struct ConvertedFloat64Stream { struct ConvertedFloat64Stream {
Bool valid = false; Bool valid = false;
#if MOBILEGL_PIPE_LEGACY_MEMOS
// The pre-handle pin: a FRONTEND lifetime id, i.e. the key
// ARCHITECTURE.md 9.5 lists for deletion as "ConvertedVertexStreamKey's
// sourcePin". Kept compiled for the legacy arm (and therefore present in
// every pull build, which is what keeps sizeof(this) still).
Uint64 sourceLifetimeId = 0; Uint64 sourceLifetimeId = 0;
#endif
#if MOBILEGL_PIPE_PUSH
// What replaces it: the source buffer's {slot, gen}. It is the SAME identity
// the rest of the backend now keys on, it cannot be reproduced by a recycled
// frontend address, and it costs the walk no allocator probe - the handle is
// already in the vertex-buffer entry that named the source.
MG_Pipe::MGPipeHandle sourceHandle = MG_Pipe::kMGPipeNullHandle;
#endif
// On the handle arm this is the applier's server-owned Serial rather than the
// frontend change serial; both answer the same question - "have the source
// bytes moved since the conversion" - and neither is trusted for a
// persistently mapped buffer, which is written with no call at all.
Uint64 sourceChangeSerial = 0; Uint64 sourceChangeSerial = 0;
SizeT sourceOffset = 0; SizeT sourceOffset = 0;
SizeT sourceStride = 0; SizeT sourceStride = 0;