diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index d4a88b23..380b78f7 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -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& 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(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 converted; - NarrowDoubleStreamToFloat32(sourceBase + attrib.Offset, sourceStride, componentCount, elementCount, - converted); - BufferImpl::BindBufferId(GL_ARRAY_BUFFER, convertedBufferId); - g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER, static_cast(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(converted.size() * sizeof(Float))); + Vector converted; + NarrowDoubleStreamToFloat32(sourceBase + attrib.Offset, sourceStride, componentCount, elementCount, + converted); + BufferImpl::BindBufferId(GL_ARRAY_BUFFER, convertedBufferId); + g_GLESFuncs.glBufferData(GL_ARRAY_BUFFER, static_cast(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(converted.size() * sizeof(Float))); + } + stream.valid = true; + stream.sourceHandle = binding.Res; + stream.sourceChangeSerial = sourceSerial; + stream.sourceOffset = static_cast(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); diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.h b/MobileGL/MG_Backend/DirectGLES/Managers.h index d118ea7c..05146a3b 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.h +++ b/MobileGL/MG_Backend/DirectGLES/Managers.h @@ -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;