diff --git a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp index 90159adf..91bfca50 100644 --- a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp +++ b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.cpp @@ -9,9 +9,21 @@ #include "RenderbufferObject.h" #include +#include + namespace MobileGL { namespace MG_State { namespace GLState { + namespace { + // Starts at 1 so a zero-initialized cache slot can never carry a live + // renderbuffer's id. + std::atomic g_nextRenderbufferLifetimeId{1}; + } + + Uint64 RenderbufferObject::AllocateLifetimeId() { + return g_nextRenderbufferLifetimeId.fetch_add(1, std::memory_order_relaxed); + } + RenderbufferObject::RenderbufferObject(Uint externalIndex) : m_externalIndex(externalIndex) {} Uint RenderbufferObject::GetExternalIndex() const { diff --git a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h index 11153772..1e1f304b 100644 --- a/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h +++ b/MobileGL/MG_State/GLState/RenderbufferState/RenderbufferObject.h @@ -42,9 +42,20 @@ namespace MobileGL { Int GetDepthSize() const; Int GetStencilSize() const; Int GetSamples() const; + // Globally-unique, never-reused id for THIS object's lifetime - same contract + // and same motivation as BufferObject::GetLifetimeId(), + // ProgramObject::GetLifetimeId() and VertexArrayObject::GetLifetimeId(). A + // backend that folds a renderbuffer's IDENTITY into a cache key must use this, + // never the GL name (LIFO-recycled by glGenRenderbuffers) and never the heap + // address (recycled by the allocator): both let a deleted-and-recreated + // renderbuffer answer to a dead one's cache entry. + Uint64 GetLifetimeId() const { return m_lifetimeId; } private: + static Uint64 AllocateLifetimeId(); + Uint m_externalIndex = 0; + const Uint64 m_lifetimeId = AllocateLifetimeId(); TextureInternalFormat m_internalFormat = TextureInternalFormat::RGBA; Int m_width = 0; Int m_height = 0; diff --git a/MobileGL/MG_Test/State/ObjectLifetimeIdTest.cpp b/MobileGL/MG_Test/State/ObjectLifetimeIdTest.cpp index 3290be28..0f4a3f02 100644 --- a/MobileGL/MG_Test/State/ObjectLifetimeIdTest.cpp +++ b/MobileGL/MG_Test/State/ObjectLifetimeIdTest.cpp @@ -32,6 +32,7 @@ #include "Includes.h" #include +#include #include using namespace MobileGL; @@ -138,3 +139,20 @@ TEST(ObjectLifetimeIdTest, LiveVertexArrayObjectsHaveDistinctLifetimeIds) { TEST(ObjectLifetimeIdTest, LiveBufferObjectsHaveDistinctLifetimeIds) { ExpectDistinctIdsWhileBothAlive("BufferObject"); } + +// The renderbuffer had no lifetime id at all until plan B ยง11 P0 gave it one: it is +// the one FBO attachment source whose identity a backend twin registry can only have +// keyed on the heap address or the GL name, both of which recycle. +TEST(ObjectLifetimeIdTest, RenderbufferObjectAtARecycledAddressCarriesAFreshLifetimeId) { + using MG_State::GLState::RenderbufferObject; + const int reuseCount = ProbeLifetimeIdAcrossAddressReuse("RenderbufferObject"); + if (reuseCount == 0) { + GTEST_SKIP() << "inconclusive, not proven: this allocator never handed the same address back across 64 " + "construct/destroy rounds, so the recycled-address case was never exercised"; + } + RecordProperty("address_reuses_observed", reuseCount); +} + +TEST(ObjectLifetimeIdTest, LiveRenderbufferObjectsHaveDistinctLifetimeIds) { + ExpectDistinctIdsWhileBothAlive("RenderbufferObject"); +}