[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
+53 -22
View File
@@ -218,8 +218,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
VertexArrayImpl::g_backendVertexArrayObjects.DestroyByLifetimeId(lifetimeId);
break;
default:
// Buffer already has its own death signal (BufferBackendOps::OnDestroy) and
// every other kind has no backend twin table here.
// Buffer death crosses as ResourceDestroy (P3a): the catalogue has a call for
// 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;
}
}
@@ -2243,6 +2251,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
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) {
if (!bufferObject) return;
@@ -4028,6 +4037,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
#if MOBILEGL_PIPE_LEGACY_MEMOS
Bool BackendVertexArrayObject::SyncFloat64AttributeAsFloat32(
Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib, Uint32 fetchBaseInstance) {
#ifdef TRACY_ENABLE
@@ -4147,6 +4157,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
(const void*)(firstElement * convertedElementSize));
return true;
}
#endif // MOBILEGL_PIPE_LEGACY_MEMOS
#if MOBILEGL_PIPE_PUSH
Bool BackendVertexArrayObject::SyncFloat64AttributeAsFloat32ByHandle(
@@ -4199,29 +4210,49 @@ namespace MobileGL::MG_Backend::DirectGLES {
return false;
}
auto& stream = m_convertedAttributeStreams[attribIndex];
Uint& convertedBufferId = m_convertedAttributeBufferIds[attribIndex];
if (convertedBufferId == 0) {
g_GLESFuncs.glGenBuffers(1, &convertedBufferId);
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) {
MGLOG_E_ONCE("Failed to create the float32 scratch buffer for the 64-bit vertex array at "
"attribute %u.",
attribIndex);
return false;
g_GLESFuncs.glGenBuffers(1, &convertedBufferId);
if (convertedBufferId == 0) {
MGLOG_E_ONCE("Failed to create the float32 scratch buffer for the 64-bit vertex array at "
"attribute %u.",
attribIndex);
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;
NarrowDoubleStreamToFloat32(sourceBase + attrib.Offset, sourceStride, componentCount, elementCount,
converted);
BufferImpl::BindBufferId(GL_ARRAY_BUFFER, convertedBufferId);
g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(converted.size() * sizeof(Float)),
converted.data(), GL_STREAM_DRAW);
if (MG_Util::PipeStats::Enabled()) {
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageVertexClient,
static_cast<Uint64>(converted.size() * sizeof(Float)));
Vector<Float> converted;
NarrowDoubleStreamToFloat32(sourceBase + attrib.Offset, sourceStride, componentCount, elementCount,
converted);
BufferImpl::BindBufferId(GL_ARRAY_BUFFER, convertedBufferId);
g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER, static_cast<GLsizeiptr>(converted.size() * sizeof(Float)),
converted.data(), GL_STREAM_DRAW);
if (MG_Util::PipeStats::Enabled()) {
MG_Util::PipeStats::AddBytes(MG_Util::PipeStats::ByteClass::StageVertexClient,
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);
+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
// used to be BufferObject::GetSize().
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
// 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
// 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.
#if MOBILEGL_PIPE_LEGACY_MEMOS
Bool SyncFloat64AttributeAsFloat32(Uint attribIndex, const MG_State::GLState::VertexAttribute& attrib,
Uint32 fetchBaseInstance);
#endif
#if MOBILEGL_PIPE_PUSH
// 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.
struct ConvertedFloat64Stream {
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;
#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;
SizeT sourceOffset = 0;
SizeT sourceStride = 0;