mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-15 22:13:24 +09:00
[Fix] (Pipe): an evicted sampler CSO is deleted from every share group of its session.
This commit is contained in:
@@ -576,12 +576,12 @@ namespace MobileGL::MG_Pipe {
|
||||
// here - a content-addressed CSO has no frontend object whose death is being
|
||||
// announced, which is precisely why this eviction is the only death path it has.
|
||||
//
|
||||
// THE DELETE REACHES THE BOUND SHARE GROUP ONLY, so it is sent only when that group
|
||||
// holds the record; a group that was never sent the create would count
|
||||
// it as a refused object call. Other groups keep an unreferenced record at this slot
|
||||
// (nothing may name it: the count is 0) until a create for the slot's next
|
||||
// generation is published there and starts it over, or the group is torn down.
|
||||
if (IsPublishedIn(m_entries[index], MGPipeCurrentShareGroupToken())) {
|
||||
// THE DELETE ENDS THE HANDLE IN EVERY SHARE GROUP IT WAS PUBLISHED TO, not only the
|
||||
// bound one: the applier drops a sampler CSO from every group of its session
|
||||
// (MGPipeApplyDeleteSamplerState), because the handle this frees is one identity for
|
||||
// the whole session. So it is sent whenever ANY group was sent the create - and not
|
||||
// at all for an entry no group holds, which the applier would count as a refusal.
|
||||
if (m_entries[index].PublishedGroupCount != 0) {
|
||||
MGPipeRouteDeleteSamplerState(handle);
|
||||
}
|
||||
// AND THE LATCH GOES WITH THE DELETE. This is the "an emitter that drops a record
|
||||
|
||||
@@ -663,6 +663,25 @@ namespace MobileGL::MG_Pipe {
|
||||
return raw;
|
||||
}
|
||||
|
||||
// Every share group's object records in the session the calling thread's records are
|
||||
// applied for, or nothing on a thread that serves no session (the process-wide applier is
|
||||
// then the only one). The pointers stay valid after the lock is dropped: a group's records
|
||||
// are held by UniquePtr, and only this session's own apply thread adds or drops its groups.
|
||||
Vector<MGPipeObjectRecords*> BoundSessionGroups() {
|
||||
Vector<MGPipeObjectRecords*> groups;
|
||||
const MGPipeApplierKeyResolver resolve = g_applierKeyResolver;
|
||||
MGPipeApplierKey key;
|
||||
if (resolve == nullptr || !resolve(&key)) return groups;
|
||||
std::lock_guard<std::mutex> guard(g_applierSessionsMutex);
|
||||
const auto session = g_applierSessions.find(key.SessionKey);
|
||||
if (session == g_applierSessions.end()) return groups;
|
||||
for (auto& [groupKey, records] : session->second.Groups) {
|
||||
(void)groupKey;
|
||||
if (records) groups.push_back(records.get());
|
||||
}
|
||||
return groups;
|
||||
}
|
||||
|
||||
thread_local MGPipeApplierState* t_applierCached = nullptr;
|
||||
thread_local Uint64 t_applierCachedSession = 0;
|
||||
thread_local Uint64 t_applierCachedContext = 0;
|
||||
@@ -3286,6 +3305,27 @@ namespace MobileGL::MG_Pipe {
|
||||
void MGPipeApplyDeleteSamplerState(const MGPHandleOnly& handle) {
|
||||
MOBILEGL_ASSERT(handle.Kind == static_cast<Uint32>(MGPipeKind::SamplerCso),
|
||||
"delete_sampler_state on kind %u", handle.Kind);
|
||||
// A SAMPLER CSO IS ONE VALUE FOR THE WHOLE SESSION, and its delete drops it from every
|
||||
// share group that holds it. The client's cache is content-addressed and process-wide:
|
||||
// one handle names one canonical value, published into each group that uses it (a
|
||||
// record per group, all of them copies), and the cache's eviction frees the handle -
|
||||
// which ends it in every group at once, whichever context happens to be bound. Dropping
|
||||
// only the bound group's copy left every other group a record at a slot the client hands
|
||||
// out again. The handle space is the client's, so no other object can hold this slot at
|
||||
// this generation in any of the session's groups.
|
||||
if (const Vector<MGPipeObjectRecords*> groups = BoundSessionGroups(); !groups.empty()) {
|
||||
Bool dropped = false;
|
||||
for (MGPipeObjectRecords* group : groups) {
|
||||
MGPipeSamplerCsoRecord* held = FindIn(group->SamplerCsos, handle.Handle);
|
||||
if (held == nullptr) continue;
|
||||
const Uint32 gen = held->Gen;
|
||||
*held = MGPipeSamplerCsoRecord{};
|
||||
held->Gen = gen;
|
||||
dropped = true;
|
||||
}
|
||||
if (!dropped) (void)ResolveObject(MGPipeApplier().SamplerCsos, "delete_sampler_state", handle.Handle);
|
||||
return;
|
||||
}
|
||||
MGPipeSamplerCsoRecord* record =
|
||||
ResolveObject(MGPipeApplier().SamplerCsos, "delete_sampler_state", handle.Handle);
|
||||
if (record == nullptr) return;
|
||||
|
||||
@@ -908,6 +908,40 @@ namespace {
|
||||
Cache().Release(second);
|
||||
}
|
||||
|
||||
// AN EVICTION ENDS THE VALUE IN EVERY SHARE GROUP IT WAS PUBLISHED TO. The cache frees the
|
||||
// handle, which is one identity for the whole session, whichever context is bound when the LRU
|
||||
// runs. Red while the delete reached the bound group only: the other group kept a record at a
|
||||
// slot the cache hands out again.
|
||||
TEST(SamplerEmit, AnEvictionDropsTheValueFromEveryShareGroupItWasPublishedTo) {
|
||||
EmitterScope scope;
|
||||
ShareGroupPair groups(/*shared=*/false);
|
||||
Uint64 payload = 0;
|
||||
const SamplerParameters params = DistinctParameters();
|
||||
|
||||
groups.MakeCurrent(0);
|
||||
const MGPipeHandle victim = Cache().Acquire(params, payload);
|
||||
groups.MakeCurrent(1);
|
||||
ASSERT_EQ(Cache().Acquire(params, payload), victim);
|
||||
ASSERT_NE(BoundGroupSamplerRecord(victim), nullptr) << "the second group was not sent the create";
|
||||
Cache().Release(victim);
|
||||
Cache().Release(victim);
|
||||
|
||||
// The first group fills the cache with values nothing keeps naming: the victim is the
|
||||
// least recently used entry, and the LRU takes it while the FIRST group is bound.
|
||||
groups.MakeCurrent(0);
|
||||
SamplerParameters filler = DistinctParameters();
|
||||
for (SizeT i = 0; i < kMGPipeSamplerCsoCacheCapacity; ++i) {
|
||||
filler.minLod = static_cast<float>(i) + 0.5f;
|
||||
Cache().Release(Cache().Acquire(filler, payload));
|
||||
}
|
||||
ASSERT_FALSE(Cache().RecordIsPublished(victim)) << "the LRU did not take the victim";
|
||||
EXPECT_EQ(BoundGroupSamplerRecord(victim), nullptr) << "the bound group still holds the evicted value";
|
||||
groups.MakeCurrent(1);
|
||||
EXPECT_EQ(BoundGroupSamplerRecord(victim), nullptr)
|
||||
<< "the other share group still holds a sampler CSO the cache has freed";
|
||||
EXPECT_EQ(MGPipeApplier().RefusedObjectCalls, 0u);
|
||||
}
|
||||
|
||||
// ============================ D-F2: the sampler view ============================
|
||||
|
||||
TEST(SamplerEmit, AnUnchangedTextureReIssuesNothing) {
|
||||
|
||||
Reference in New Issue
Block a user