diff --git a/MobileGL/MG_Impl/Pipe/PipeFill.cpp b/MobileGL/MG_Impl/Pipe/PipeFill.cpp index 8916a878..5c90fc24 100644 --- a/MobileGL/MG_Impl/Pipe/PipeFill.cpp +++ b/MobileGL/MG_Impl/Pipe/PipeFill.cpp @@ -133,8 +133,11 @@ namespace MobileGL::MG_Pipe { case F::GetBoundTransformFeedbackName: dst.m_boundTransformFeedbackName = ctx.GetBoundTransformFeedbackName(); break; + // The four object pins: assigned only when the object moved, so the residual fill of + // every draw does not pay a reference-count increment and decrement for a pointer it + // already holds (P14: one of these moves per Minecraft draw, the other three rarely). case F::GetBoundVertexArray: - dst.m_boundVertexArray = ctx.GetBoundVertexArray(); + if (dst.m_boundVertexArray != ctx.GetBoundVertexArray()) dst.m_boundVertexArray = ctx.GetBoundVertexArray(); break; case F::GetBufferBindingSlot: // Every global target has a slot; Index stays null - GLContext resolves it @@ -247,10 +250,11 @@ namespace MobileGL::MG_Pipe { dst.m_primitiveRestartIndex = ctx.GetPrimitiveRestartIndex(); break; case F::GetProgramForDispatch: - dst.m_programForDispatch = ctx.GetProgramForDispatch(); + if (dst.m_programForDispatch != ctx.GetProgramForDispatch()) + dst.m_programForDispatch = ctx.GetProgramForDispatch(); break; case F::GetProgramForDraw: - dst.m_programForDraw = ctx.GetProgramForDraw(); + if (dst.m_programForDraw != ctx.GetProgramForDraw()) dst.m_programForDraw = ctx.GetProgramForDraw(); break; case F::GetProvokingVertexMode: dst.m_provokingVertexMode = ctx.GetProvokingVertexMode(); @@ -291,7 +295,8 @@ namespace MobileGL::MG_Pipe { dst.m_transformFeedbackPausedPrimitiveCounter = ctx.GetTransformFeedbackPausedPrimitiveCounter(); break; case F::GetTransformFeedbackProgram: - dst.m_transformFeedbackProgram = ctx.GetTransformFeedbackProgram(); + if (dst.m_transformFeedbackProgram != ctx.GetTransformFeedbackProgram()) + dst.m_transformFeedbackProgram = ctx.GetTransformFeedbackProgram(); break; case F::GetViewport: dst.m_viewport = ctx.GetViewport(); diff --git a/MobileGL/MG_Impl/Pipe/VertexInputEmit.h b/MobileGL/MG_Impl/Pipe/VertexInputEmit.h index eff5a2dc..e01fc656 100644 --- a/MobileGL/MG_Impl/Pipe/VertexInputEmit.h +++ b/MobileGL/MG_Impl/Pipe/VertexInputEmit.h @@ -211,7 +211,9 @@ namespace MobileGL::MG_Pipe { // a Minecraft frame binds hundreds of chunk VAOs. Not for client-memory arrays, whose // entries the draw itself supplies. VaoMemo* memo = vao && ownedClientBuffers == nullptr ? &MemoFor(*vao) : nullptr; + Bool memoHit = false; if (memo != nullptr && memo->BuffersValid && memo->ConfigVersion == vao->GetConfigVersion()) { + memoHit = true; count = memo->BufferCount; for (SizeT i = 0; i < count; ++i) { m_entries[i] = memo->Buffers[i]; @@ -251,6 +253,7 @@ namespace MobileGL::MG_Pipe { if (attrib.Enabled && !attrib.Buffer) memoizable = false; } if (memo != nullptr) { + memo->HashValid = false; memo->BuffersValid = memoizable && count <= kVaoMemoBuffers; memo->ConfigVersion = vao->GetConfigVersion(); memo->BufferCount = count; @@ -258,7 +261,19 @@ namespace MobileGL::MG_Pipe { } } - const Uint64 hash = MGPipeVertexBufferSetContentHash(m_entries.data(), 0, count, baseInstance); + // The set's hash is a function of the entries and the base instance, so a memoised set + // keeps the one it was hashed with for as long as the base instance is the same. + Uint64 hash = 0; + if (memoHit && memo->HashValid && memo->HashBaseInstance == baseInstance) { + hash = memo->Hash; + } else { + hash = MGPipeVertexBufferSetContentHash(m_entries.data(), 0, count, baseInstance); + if (memo != nullptr && memo->BuffersValid) { + memo->Hash = hash; + memo->HashBaseInstance = baseInstance; + memo->HashValid = true; + } + } if (!MGPipeSetHashSuppressorInstance().ShouldEmit(MGPipeSuppressorSlot::SetVertexBuffers, hash)) { return 0; } @@ -399,6 +414,9 @@ namespace MobileGL::MG_Pipe { Bool BuffersValid = false; Uint32 ConfigVersion = 0; Uint32 BufferCount = 0; + Bool HashValid = false; + Uint32 HashBaseInstance = 0; + Uint64 Hash = 0; Array Buffers{}; }; VaoMemo& MemoFor(const VertexArrayObject& vao) {