mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Backend, MG_Impl] complete GLES XFB lifetimes, lazy readback and sampler windows
This commit is contained in:
@@ -2394,7 +2394,15 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
}
|
||||
|
||||
void Ops_H_Readback(MG_Pipe::MGPipeHandle res, const MG_Pipe::MGPReadback& record) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// An empty capture may conservatively request a writeback before
|
||||
// any draw materialized this buffer. Resolve its staged resource
|
||||
// exactly as a GPU consumer would, then return the real bytes.
|
||||
auto* resource = MG_Config::Transport != MG_Config::TransportMode::Monolith
|
||||
? EnsureBufferResourceForHandle(nullptr, res) : FindBufferResourceForHandle(res);
|
||||
#else
|
||||
auto* resource = FindBufferResourceForHandle(res);
|
||||
#endif
|
||||
if (!resource || resource->id == 0 || !resource->storageInitialized) return;
|
||||
if (!CanTouchGLNow() || resource->contextGeneration != g_bufferContextGeneration) return;
|
||||
if (resource->persistentMapped) {
|
||||
@@ -2715,9 +2723,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
if (bytes == nullptr || record.Blob.Size == 0) return;
|
||||
const auto* stored = PipeTextureRecordForHandle(res);
|
||||
if (stored == nullptr) return;
|
||||
const IntVec3 extent = MG_Remote::Server::StagedTextureMipExtent(
|
||||
stored->Desc.Target, stored->Desc.Width, stored->Desc.Height, stored->Desc.Depth,
|
||||
static_cast<Uint32>(record.Level));
|
||||
const IntVec3 extent = record.LevelWidth && record.LevelHeight && record.LevelDepth
|
||||
? IntVec3{static_cast<Int>(record.LevelWidth), static_cast<Int>(record.LevelHeight),
|
||||
static_cast<Int>(record.LevelDepth)}
|
||||
: MG_Remote::Server::StagedTextureMipExtent(
|
||||
stored->Desc.Target, stored->Desc.Width, stored->Desc.Height, stored->Desc.Depth,
|
||||
static_cast<Uint32>(record.Level));
|
||||
store.Adopt(MG_Remote::Server::StagedTextureStore::KeyForHandle(res),
|
||||
MG_Pipe::MGPipeSubDataUploadTargetOf(record.Target), record.Level, extent,
|
||||
bytes, static_cast<SizeT>(record.Blob.Size));
|
||||
|
||||
@@ -737,6 +737,15 @@ namespace MobileGL::MG_Pipe {
|
||||
}
|
||||
m_lastVaoLifetime = vaoLifetime;
|
||||
m_lastVaoConfig = vaoConfig;
|
||||
// A redundant bind of the default texture can grow the unit window
|
||||
// without changing either binding or sampling generations. Both sets
|
||||
// must still publish that new prefix, including its null samplers.
|
||||
const Int maxTextureUnit = ctx.GetMaxTouchedTextureUnit();
|
||||
if (!m_primed || maxTextureUnit != m_lastMaxTextureUnit) {
|
||||
dirty |= MGPipeDirtyBit(MGPipeDirty::NewSamplerViews) |
|
||||
MGPipeDirtyBit(MGPipeDirty::NewSamplers);
|
||||
}
|
||||
m_lastMaxTextureUnit = maxTextureUnit;
|
||||
|
||||
// ---- bit 2: the PACK half of the pixel store, BitwiseEqual ----
|
||||
const PixelStoreParameters pack = ctx.GetPixelStoreParameters(false);
|
||||
@@ -791,6 +800,7 @@ namespace MobileGL::MG_Pipe {
|
||||
std::memset(m_lastPushed, 0, sizeof(m_lastPushed));
|
||||
m_lastVaoLifetime = 0;
|
||||
m_lastVaoConfig = 0;
|
||||
m_lastMaxTextureUnit = -1;
|
||||
m_renderStateVersion.Reset();
|
||||
m_pipelineStateVersion.Reset();
|
||||
m_framebufferBind.Reset();
|
||||
@@ -881,6 +891,7 @@ namespace MobileGL::MG_Pipe {
|
||||
Uint64 m_lastPushed[kMGPipeDirtyCount]{};
|
||||
Uint64 m_lastVaoLifetime = 0;
|
||||
Uint32 m_lastVaoConfig = 0;
|
||||
Int m_lastMaxTextureUnit = -1;
|
||||
MGPipeWidenedCounter m_renderStateVersion;
|
||||
MGPipeWidenedCounter m_pipelineStateVersion;
|
||||
// The draw framebuffer BINDING slot version, widened for the same reason: a Uint16
|
||||
|
||||
@@ -17,6 +17,12 @@
|
||||
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
||||
#include <MG_Backend/DirectGLES/Managers.h>
|
||||
#include <MG_Backend/DirectGLES/DirectGLES.h>
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
#include <Config.h>
|
||||
#include <MG_Impl/Pipe/SlotAllocator.h>
|
||||
#include <MG_Remote/Client/ClientSession.h>
|
||||
#include <MG_Remote/Server/ServerLoop.h>
|
||||
#endif
|
||||
#define MGITEST_PIPE_APPLY_PEEK_LIVE 1
|
||||
#endif
|
||||
#endif
|
||||
@@ -74,8 +80,70 @@ namespace MGITest {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static bool ReadAppliedTextureParams(MGB::TextureImpl::BackendTextureObject* twin,
|
||||
unsigned glTarget, EsprytAppliedTextureParamsPeek* out);
|
||||
|
||||
static void CopyTextureParamsRecord(const MGP::MGPipeResourceRecord& record, unsigned slot,
|
||||
PipeTextureParamsRecordPeek& out) {
|
||||
out.Slot = slot;
|
||||
out.Gen = static_cast<unsigned>(record.Gen);
|
||||
out.ParamsSerial = static_cast<unsigned long long>(record.ParamsSerial);
|
||||
for (int channel = 0; channel < 4; ++channel) out.Swizzle[channel] = SwizzleToGLEnum(record.Params.Swizzle[channel]);
|
||||
out.DepthStencilMode = DepthStencilModeToGLEnum(record.Params.DepthStencilMode);
|
||||
}
|
||||
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
enum class ServerPeekKind { Record, Applied, View };
|
||||
static bool PeekOnApplyThread(unsigned glTextureName, unsigned target, ServerPeekKind kind, void* output) {
|
||||
auto* object = FrontendTexture(glTextureName);
|
||||
auto* session = MobileGL::MG_Remote::Client::ClientSession::Active();
|
||||
if (!object || !session || !output) return false;
|
||||
struct Request {
|
||||
MGP::MGPipeHandle Texture, View;
|
||||
unsigned Target;
|
||||
ServerPeekKind Kind;
|
||||
void* Output;
|
||||
bool Result = false;
|
||||
} request{MGP::MGPipeSlots().FindByLifetimeId(MGP::MGPipeKind::Texture, object->GetLifetimeId()),
|
||||
MGP::MGPipeSlots().FindByLifetimeId(MGP::MGPipeKind::SamplerViewCso, object->GetLifetimeId()),
|
||||
target, kind, output};
|
||||
if (MGP::MGPipeHandleIsNull(request.Texture)) return false;
|
||||
if (session->WaitForApplied(session->LastPublishedSeq(), 30000) !=
|
||||
MobileGL::MG_Remote::Transport::SessionWait::Reached) return false;
|
||||
const auto result = MobileGL::MG_Remote::Server::ServerLoopInstance().RunProbeOnApplyThreadForTesting(
|
||||
+[](void* opaque) -> MobileGLResult {
|
||||
auto& r = *static_cast<Request*>(opaque);
|
||||
const auto& records = MGP::MGPipeApplier().TextureResources;
|
||||
if (r.Texture.Slot >= records.size()) return MOBILEGL_OK;
|
||||
const auto& record = records[r.Texture.Slot];
|
||||
if (!record.Live || record.Gen != r.Texture.Gen) return MOBILEGL_OK;
|
||||
if (r.Kind == ServerPeekKind::Record) {
|
||||
CopyTextureParamsRecord(record, r.Texture.Slot, *static_cast<PipeTextureParamsRecordPeek*>(r.Output));
|
||||
r.Result = true;
|
||||
return MOBILEGL_OK;
|
||||
}
|
||||
auto* found = MGB::TextureImpl::g_backendTextureObjects.FindByHandle(r.Texture);
|
||||
if (!found || !*found) return MOBILEGL_OK;
|
||||
if (r.Kind == ServerPeekKind::Applied)
|
||||
r.Result = ReadAppliedTextureParams(found->get(), r.Target,
|
||||
static_cast<EsprytAppliedTextureParamsPeek*>(r.Output));
|
||||
else {
|
||||
*static_cast<bool*>(r.Output) = !MGP::MGPipeHandleIsNull(r.View) &&
|
||||
MGB::SamplerViewImpl::FindSamplerViewForHandle(r.View) != nullptr;
|
||||
r.Result = true;
|
||||
}
|
||||
return MOBILEGL_OK;
|
||||
}, &request);
|
||||
return result == MOBILEGL_OK && request.Result;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool PeekPipeTextureParamsRecord(unsigned glTextureName, PipeTextureParamsRecordPeek* out) {
|
||||
if (out == nullptr) return false;
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (MobileGL::MG_Config::Transport != MobileGL::MG_Config::TransportMode::Monolith)
|
||||
return PeekOnApplyThread(glTextureName, 0, ServerPeekKind::Record, out);
|
||||
#endif
|
||||
const MGP::MGPipeApplierState& applier = MGP::MGPipeApplier();
|
||||
// Slot 0 is the reserved null handle and is never live (MGPipeHandles.h), so the scan
|
||||
// starts at 1 and a match at 0 is impossible rather than merely unlikely.
|
||||
@@ -83,13 +151,7 @@ namespace MGITest {
|
||||
const MGP::MGPipeResourceRecord& record = applier.TextureResources[slot];
|
||||
if (!record.Live) continue;
|
||||
if (record.Desc.GlNameForDiag != static_cast<MobileGL::Uint32>(glTextureName)) continue;
|
||||
out->Slot = static_cast<unsigned>(slot);
|
||||
out->Gen = static_cast<unsigned>(record.Gen);
|
||||
out->ParamsSerial = static_cast<unsigned long long>(record.ParamsSerial);
|
||||
for (int channel = 0; channel < 4; ++channel) {
|
||||
out->Swizzle[channel] = SwizzleToGLEnum(record.Params.Swizzle[channel]);
|
||||
}
|
||||
out->DepthStencilMode = DepthStencilModeToGLEnum(record.Params.DepthStencilMode);
|
||||
CopyTextureParamsRecord(record, static_cast<unsigned>(slot), *out);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -97,10 +159,18 @@ namespace MGITest {
|
||||
|
||||
bool PeekEsprytAppliedTextureParams(unsigned glTextureName, unsigned glTarget,
|
||||
EsprytAppliedTextureParamsPeek* out) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (MobileGL::MG_Config::Transport != MobileGL::MG_Config::TransportMode::Monolith)
|
||||
return PeekOnApplyThread(glTextureName, glTarget, ServerPeekKind::Applied, out);
|
||||
#endif
|
||||
return ReadAppliedTextureParams(EsprytTwin(glTextureName), glTarget, out);
|
||||
}
|
||||
|
||||
static bool ReadAppliedTextureParams(MGB::TextureImpl::BackendTextureObject* twin,
|
||||
unsigned glTarget, EsprytAppliedTextureParamsPeek* out) {
|
||||
if (out == nullptr) return false;
|
||||
const int bindingQuery = BindingQueryFor(glTarget);
|
||||
if (bindingQuery == 0) return false;
|
||||
MGB::TextureImpl::BackendTextureObject* const twin = EsprytTwin(glTextureName);
|
||||
if (twin == nullptr) return false;
|
||||
const MobileGL::Uint backendId = twin->GetBackendTextureId();
|
||||
if (backendId == 0) return false;
|
||||
@@ -151,6 +221,10 @@ namespace MGITest {
|
||||
}
|
||||
|
||||
bool PeekEsprytHasSamplerViewForTexture(unsigned glTextureName, bool* outExists) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
if (MobileGL::MG_Config::Transport != MobileGL::MG_Config::TransportMode::Monolith)
|
||||
return PeekOnApplyThread(glTextureName, 0, ServerPeekKind::View, outExists);
|
||||
#endif
|
||||
if (outExists == nullptr) return false;
|
||||
MobileGL::MG_State::GLState::ITextureObject* const object = FrontendTexture(glTextureName);
|
||||
if (object == nullptr) return false;
|
||||
|
||||
@@ -848,6 +848,7 @@ namespace MobileGL::MG_Pipe {
|
||||
MGPipeHandle VerbCopyTexDst = kMGPipeNullHandle;
|
||||
MGPipeHandle VerbStorageBlockProgram = kMGPipeNullHandle;
|
||||
Uint64 BoundStreamOutputLifetimeId = 0;
|
||||
Uint64 VerbDeleteStreamOutputLifetimeId = 0;
|
||||
UnorderedMap<Uint64, MGPStreamOutputBegin> StreamOutputSpans;
|
||||
// generate_mipmap's texture (MGPMipPlan::Res).
|
||||
MGPipeHandle VerbMipRes = kMGPipeNullHandle;
|
||||
@@ -876,6 +877,7 @@ namespace MobileGL::MG_Pipe {
|
||||
VerbBlitNamedConsumed = false;
|
||||
VerbCopyTexDst = kMGPipeNullHandle;
|
||||
VerbStorageBlockProgram = kMGPipeNullHandle;
|
||||
VerbDeleteStreamOutputLifetimeId = 0;
|
||||
VerbMipRes = kMGPipeNullHandle;
|
||||
VerbMipBaseLevel = VerbMipLevelCount = 0;
|
||||
VerbIndirectBuffer = kMGPipeNullHandle;
|
||||
|
||||
@@ -1069,7 +1069,9 @@ namespace MobileGL::MG_Remote::Server {
|
||||
state.StreamOutputSpans.erase(object.LifetimeId);
|
||||
if (state.BoundStreamOutputLifetimeId == object.LifetimeId) state.BoundStreamOutputLifetimeId = 0;
|
||||
}
|
||||
state.VerbDeleteStreamOutputLifetimeId = object.LifetimeId;
|
||||
if (table->GL.DeleteTransformFeedback) table->GL.DeleteTransformFeedback(object.GlName);
|
||||
state.VerbDeleteStreamOutputLifetimeId = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ namespace {
|
||||
X(TrackerWalk, TheIndexBufferBitFiresWhenTheSlotVersionWrapsOntoADifferentBuffer) \
|
||||
X(TrackerWalk, ABaseInstanceSurvivesTheFirstWalkOnAFreshContext) \
|
||||
X(TrackerWalk, ASamplerBindAloneFiresTheSamplerStateBit) \
|
||||
X(TrackerWalk, ARedundantDefaultBindStillPublishesAGrowingSamplerWindow) \
|
||||
X(TrackerWalk, ARestagedProgramPipelineFiresTheProgramBits) \
|
||||
X(TrackerWalk, ARelinkOfAStageProgramFiresTheProgramBits) \
|
||||
X(TrackerWalk, UseProgramZeroLeavesTheBoundPipelineDrivingTheProgramBits) \
|
||||
@@ -784,6 +785,18 @@ namespace {
|
||||
EXPECT_EQ(Walk(), 0u) << "the widened shutter fires forever";
|
||||
}
|
||||
|
||||
TEST_F(TrackerWalk, ARedundantDefaultBindStillPublishesAGrowingSamplerWindow) {
|
||||
Walk();
|
||||
ASSERT_EQ(Walk(), 0u);
|
||||
const Uint64 generation = Ctx().GetTextureBindGeneration();
|
||||
Ctx().NoteTextureUnitTouched(7, false);
|
||||
ASSERT_EQ(Ctx().GetTextureBindGeneration(), generation);
|
||||
const Uint32 dirty = Walk();
|
||||
EXPECT_NE(dirty & MGPipeDirtyBit(MGPipeDirty::NewSamplerViews), 0u);
|
||||
EXPECT_NE(dirty & MGPipeDirtyBit(MGPipeDirty::NewSamplers), 0u);
|
||||
EXPECT_EQ(Walk(), 0u);
|
||||
}
|
||||
|
||||
// BITS 6/7/8 UNDER A SEPARABLE PROGRAM PIPELINE. GetCurrentProgram() is null for the whole
|
||||
// life of a bound pipeline, so all three shutters used to latch 0 and never move again
|
||||
// after the first walk: glUseProgramStages would rebuild the composite and EmitShaderState
|
||||
|
||||
Reference in New Issue
Block a user