mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-10 05:08:31 +09:00
[Refactor] (Espryt): resolve the current framebuffers from the pushed records and keep the shadowed default bind and the missing fast path exactly as they are
This commit is contained in:
@@ -192,6 +192,67 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// ---------------------------------------------------------------------------------
|
||||
// P4a: the four family arm predicates the draw path below gates on.
|
||||
// ---------------------------------------------------------------------------------
|
||||
//
|
||||
// TEMPORARY AND SAID SO. D-K3 gives the phase four Resolve<Family>SubsystemArm() latches,
|
||||
// beside ResolveResourceSubsystemArm / ResolveVertexInputSubsystemArm in Managers.cpp -
|
||||
// which is package D's file for the whole phase, so the draw-path package cannot write
|
||||
// them. It is landed BEFORE D, so it carries its own bit tests until those latches exist;
|
||||
// at the rebase every one of these becomes a call to D's, which additionally
|
||||
//
|
||||
// * LOGS each dependency refusal with one MGLOG_E naming BOTH bits, and
|
||||
// * reaches PipeSubsystemArmVerdict::NoArm and STOPS with the named
|
||||
// Fatal{PipeLegacyMemosDisabled, ...} when MOBILEGL_PIPE_LEGACY_MEMOS is 0 - which none
|
||||
// of these four families' legacy arms survives (D-K3's table: the four g_fboSynced*
|
||||
// arrays, the twin's cheap-gate trio, UnitSamplerLookupMemo's WeakPtr rows and
|
||||
// g_programTwinLookupMemo are all pre-handle arms).
|
||||
//
|
||||
// The dependency DIRECTIONS are copied here verbatim, so a mask that half-runs is refused
|
||||
// in the meantime too and the A/B is not silently wrong; the refusal log and the stop are
|
||||
// D's and are deliberately not duplicated here, because two writers of one diagnostic is
|
||||
// how the two drift.
|
||||
//
|
||||
// Latched once per process for the reason Managers.h gives for the other two: the two arms
|
||||
// keep their state in different places, so an answer that changed mid-run would strand
|
||||
// everything already built against the previous one.
|
||||
static Bool EsprytDrawTextureResourceHandlesEnabled() {
|
||||
// Bit 10 requires bit 7: a buffer texture's BufferForTexBuffer names a Buffer handle
|
||||
// and only bit 7 puts twins in that table (D-K2).
|
||||
static const Bool enabled =
|
||||
(MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemTextureResources) != 0 &&
|
||||
(MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemResources) != 0;
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static Bool EsprytDrawFramebufferHandlesEnabled() {
|
||||
// Bit 9 requires bit 10: every MGPSurface::Res names a Texture or Renderbuffer handle.
|
||||
static const Bool enabled =
|
||||
(MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemFramebuffer) != 0 &&
|
||||
EsprytDrawTextureResourceHandlesEnabled();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static Bool EsprytDrawSamplerHandlesEnabled() {
|
||||
// Bit 11 requires bit 10: every MGPBoundView::Texture and MGPImageView::Res names a
|
||||
// Texture handle, and without bit 10 every lookup would miss and the walk would
|
||||
// `continue` past a unit it should have unbound.
|
||||
static const Bool enabled =
|
||||
(MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemSamplers) != 0 &&
|
||||
EsprytDrawTextureResourceHandlesEnabled();
|
||||
return enabled;
|
||||
}
|
||||
|
||||
static Bool EsprytDrawProgramHandlesEnabled() {
|
||||
// Bit 12 depends on nothing: a ShaderCso handle names no texture and no buffer.
|
||||
static const Bool enabled =
|
||||
(MG_Config::Features.PipePush & MG_Pipe::kMGPipeSubsystemPrograms) != 0;
|
||||
return enabled;
|
||||
}
|
||||
#endif // MOBILEGL_PIPE_PUSH
|
||||
|
||||
static Bool IsDualSourceBlendFactor(BlendFactor v) {
|
||||
switch (v) {
|
||||
case BlendFactor::Src1Color:
|
||||
@@ -2226,6 +2287,147 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_fboSyncedBackendIdGenerations[SizeT(target)] = g_attachmentBackendIdGeneration;
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a e1 (D-C2, D-B3). The framebuffer family's handle arm.
|
||||
//
|
||||
// THE FOUR-PART MEMO BECOMES TWO VALUES, and the halves are not symmetric.
|
||||
// g_fboSyncedSlotVersions / ObjectVersions / Objects are three answers to one question
|
||||
// - "has the state the client would describe moved?" - asked of the frontend from the
|
||||
// backend. The applier answers it with one number: FramebufferSerial is bumped by the
|
||||
// applier before the backend is told anything, on every set_framebuffer_state it
|
||||
// accepts, and a record the client's ContentHash suppressor withheld provably describes
|
||||
// state that did not move. So one Uint64 compare replaces the three.
|
||||
//
|
||||
// The FOURTH stays, and must: g_attachmentBackendIdGeneration answers "did *I* re-mint
|
||||
// a driver texture id", which no client-side version can answer, and dropping it is the
|
||||
// shape of bug commit d7655247 fixed on the buffer side (D-B3, D-O). The ES context
|
||||
// generation joins it for the same reason the legacy arm's other memos carry it - the
|
||||
// driver FBO names die with the context.
|
||||
// Kept PER TARGET, like the arrays it replaces, and for the one reason that survives
|
||||
// the collapse: ForceBindCurrentFBO syncs a single target and has to be able to say so.
|
||||
// What collapses is the three-value "has the state moved" question into one serial.
|
||||
struct SyncedFramebufferSerialMemo {
|
||||
Uint64 serial = 0; // the FramebufferSerial this target was last synced at
|
||||
Uint64 backendIdGeneration = 0;
|
||||
Uint contextGeneration = 0;
|
||||
Bool valid = false;
|
||||
};
|
||||
static Array<SyncedFramebufferSerialMemo, SizeT(FramebufferTarget::FramebufferTargetCount)>
|
||||
g_fboSyncedSerials{};
|
||||
|
||||
static void InvalidateFramebufferHandleArmMemos() {
|
||||
for (auto& memo : g_fboSyncedSerials) memo.valid = false;
|
||||
}
|
||||
|
||||
// Record that `target` now reflects the applier's record as of `serial`. The two
|
||||
// generations ride along for the reason D-B3 keeps them: they answer questions about
|
||||
// the DRIVER's own ids that no client-side version can answer.
|
||||
static void StampSyncedFramebufferSerial(FramebufferTarget target, Uint64 serial) {
|
||||
auto& memo = g_fboSyncedSerials[SizeT(target)];
|
||||
memo.serial = serial;
|
||||
memo.backendIdGeneration = g_attachmentBackendIdGeneration;
|
||||
memo.contextGeneration = g_backendContextGeneration;
|
||||
memo.valid = true;
|
||||
}
|
||||
|
||||
static Bool SyncedFramebufferSerialIsCurrent(FramebufferTarget target, Uint64 serial) {
|
||||
const auto& memo = g_fboSyncedSerials[SizeT(target)];
|
||||
return memo.valid && memo.serial == serial &&
|
||||
memo.backendIdGeneration == g_attachmentBackendIdGeneration &&
|
||||
memo.contextGeneration == g_backendContextGeneration;
|
||||
}
|
||||
|
||||
// The handle arm of SyncCurrentFBO. Returns false when it declined - which is not a
|
||||
// failure and is the only honest answer while no client has emitted yet.
|
||||
//
|
||||
// NO LIVE RECORD IS A MISS, NEVER A HIT, and it is the same rule the vertex-input arm
|
||||
// states at SyncVaoAttributeBuffersByHandle. What is NOT the test, and this is a trap
|
||||
// worth naming: FramebufferSerial is not "has the client ever described a framebuffer".
|
||||
// MGPipeApplierReset ADVANCES every P4a working-state serial unconditionally
|
||||
// (PipeApply.cpp), because a cleared window is itself a change a twin has to hear
|
||||
// about - so the serial is non-zero after the first make-current even though no
|
||||
// set_framebuffer_state has ever been applied, and gating on it would take this arm
|
||||
// with two empty records and leave every framebuffer unsynced. The record's own Fbo
|
||||
// handle is the test, and BOTH bindings have to be described before this arm can run
|
||||
// at all, because it answers for both in one pass: an emitter walks {Draw, Read} and a
|
||||
// Target = Both record writes both, so a half-described applier is the transitional
|
||||
// state of a tree whose client half has not landed yet, and that state belongs to the
|
||||
// pre-handle arm.
|
||||
static Bool SyncCurrentFBOByRecord() {
|
||||
const auto& st = MG_Pipe::MGPipeApplier();
|
||||
if (MG_Pipe::MGPipeHandleIsNull(st.DrawFramebuffer.Fbo) ||
|
||||
MG_Pipe::MGPipeHandleIsNull(st.ReadFramebuffer.Fbo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const FramebufferTarget fboTargets[] = {FramebufferTarget::Draw, FramebufferTarget::Read};
|
||||
for (auto& target : fboTargets) {
|
||||
if (SyncedFramebufferSerialIsCurrent(target, st.FramebufferSerial)) continue;
|
||||
|
||||
const MG_Pipe::MGPFramebufferState& record = target == FramebufferTarget::Draw
|
||||
? st.DrawFramebuffer
|
||||
: st.ReadFramebuffer;
|
||||
if (record.IsDefault != 0) {
|
||||
// The default framebuffer, said by the record rather than by comparing the
|
||||
// bound object against pDefaultFramebufferInfo->defaultFBO - which is one
|
||||
// of the four identity comparisons the reserved handle {0,1} exists to
|
||||
// retire (MGPipeHandles.h). Nothing to sync except the widened-attachment
|
||||
// masks, which only SyncToBackend ever writes and would otherwise still
|
||||
// describe the user FBO that was draw-bound before. The window surface is a
|
||||
// real RGBA buffer, so nothing here is ever widened.
|
||||
if (target == FramebufferTarget::Draw) {
|
||||
g_alphaWidenedDrawBufferMask = 0;
|
||||
g_integerColorDrawBufferMask = 0;
|
||||
}
|
||||
StampSyncedFramebufferSerial(target, st.FramebufferSerial);
|
||||
continue;
|
||||
}
|
||||
|
||||
// MONOLITH GLUE, and named as such: the twin's SyncToBackend still reads the
|
||||
// frontend object (package D re-keys its BODY onto the record; this package
|
||||
// owns the call site and the decisions around it). The IDENTITY is already the
|
||||
// handle - FindByHandle(record.Fbo) - so a recycled FBO cannot be mistaken for
|
||||
// its predecessor here; the resolve-or-create fallback below is what becomes
|
||||
// GetOrCreate(record.Fbo) once package D forwards that overload.
|
||||
auto& slot = GetFramebufferBindingSlotChecked(target);
|
||||
const auto& currentFBO = slot.GetBoundObject();
|
||||
if (!currentFBO) {
|
||||
MGLOG_E_ONCE("A framebuffer record names %s but no FBO is bound to it.",
|
||||
target == FramebufferTarget::Read ? "READ" : "DRAW");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* twinSlot = g_backendFramebufferObjects.FindByHandle(record.Fbo);
|
||||
auto& backendObj =
|
||||
twinSlot ? *twinSlot : g_backendFramebufferObjects.GetOrCreate(currentFBO);
|
||||
if (!backendObj) {
|
||||
backendObj = MakeShared<BackendFramebufferObject>();
|
||||
}
|
||||
|
||||
// THE "SAME FBO AS DRAW" SKIP IS NOW A FIELD, not a pointer comparison against
|
||||
// the object the previous iteration happened to sync. Target = Both is the
|
||||
// client saying one object is bound to both bindings, so the attachment and
|
||||
// draw-buffer work the DRAW pass already did is not repeated - and the read
|
||||
// buffer, which is READ-target-specific and is what that skip used to drop, is
|
||||
// applied from the record's OWN ReadSurface. That is what makes the
|
||||
// read-buffer-shared-FBO defect class unrepresentable rather than merely fixed:
|
||||
// there is no path here that can reach the read binding without its own
|
||||
// resolved read surface.
|
||||
if (target == FramebufferTarget::Read &&
|
||||
record.Target == static_cast<Uint8>(MG_Pipe::MGPipeFramebufferTarget::Both)) {
|
||||
backendObj->SyncReadBufferToBackend(currentFBO);
|
||||
StampSyncedFramebufferSerial(target, st.FramebufferSerial);
|
||||
continue;
|
||||
}
|
||||
|
||||
backendObj->SyncToBackend(currentFBO, target);
|
||||
StampSyncedFramebufferSerial(target, st.FramebufferSerial);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif // MOBILEGL_PIPE_PUSH
|
||||
|
||||
void SyncCurrentFBO() {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
@@ -2234,6 +2436,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
TextureImpl::g_backendTextureObjects.CollectGarbageIfNeeded();
|
||||
RenderbufferImpl::g_backendRenderbufferObjects.CollectGarbageIfNeeded();
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The handle arm first, and it declines rather than half-running: with no record
|
||||
// yet it hands the walk straight back to the pre-handle arm below, unchanged.
|
||||
if (EsprytDrawFramebufferHandlesEnabled() && SyncCurrentFBOByRecord()) return;
|
||||
#endif
|
||||
|
||||
const FramebufferTarget fboTargets[] = {FramebufferTarget::Draw, FramebufferTarget::Read};
|
||||
|
||||
MG_State::GLState::FramebufferObject* lastUpdatedFBO = nullptr;
|
||||
@@ -3047,6 +3255,30 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
static Bool g_broadcastMemoValid = false;
|
||||
static Uint g_broadcastMemoCount = 1;
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a e1 (D-C4). The handle arm's key is the framebuffer record's ContentHash, and it
|
||||
// is a COMPLETE key for this answer rather than a cheaper approximation of the trio
|
||||
// above: the hash is taken over every field the record carries - Fbo included, so a
|
||||
// recycled framebuffer handle cannot be suppressed against its predecessor's record,
|
||||
// and DrawBuffers[8] included, so an unchanged hash provably means the draw-buffer
|
||||
// array did not move, which provably means the broadcast count did not move.
|
||||
//
|
||||
// The ORDERING that made this read come from the frontend is preserved and is what
|
||||
// makes reading a record legal here: the client emits every set_* BEFORE the verb and
|
||||
// the server specialises AT the verb, so by the time this runs the record already
|
||||
// describes the framebuffer this draw will use - which is exactly the property the
|
||||
// frontend read was buying (a program compiled against a stale count would otherwise
|
||||
// not be relinked until the draw after the one that needed it).
|
||||
//
|
||||
// Separate statics rather than reusing the trio's: FramebufferSerial goes from 0 to
|
||||
// non-zero exactly once in a process's life, so both arms can run in one run, and one
|
||||
// set of fields carrying two key shapes is how a stale half is compared against a live
|
||||
// one.
|
||||
static Uint64 g_broadcastMemoContentHash = 0;
|
||||
static Bool g_broadcastMemoHandleValid = false;
|
||||
static Uint g_broadcastMemoHandleCount = 1;
|
||||
#endif
|
||||
|
||||
// The identity+version key above is only monotonic WITHIN one GLContext: a
|
||||
// library teardown + re-init frees every FramebufferObject and restarts the
|
||||
// draw slot's counter at zero, so a recycled FBO address with coinciding
|
||||
@@ -3054,6 +3286,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// structurally identical SyncCurrentFBO trio (InvalidateFramebufferBindingCache).
|
||||
void InvalidateBroadcastMemo() {
|
||||
g_broadcastMemoValid = false;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
g_broadcastMemoHandleValid = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SyncCurrentProgram(const SharedPtr<MG_State::GLState::ProgramObject>& currentProgram) {
|
||||
@@ -3077,28 +3312,55 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// only runs later in PrepareForDraw: a program compiled against a stale count
|
||||
// would not be relinked until the draw after the one that needed it.
|
||||
{
|
||||
const auto& drawSlot = GetFramebufferBindingSlotChecked(FramebufferTarget::Draw);
|
||||
const auto& drawFBO = drawSlot.GetBoundObject();
|
||||
const Uint16 slotVersion = drawSlot.GetVersion();
|
||||
const Uint16 objectVersion = drawFBO ? drawFBO->GetObjectVersion() : 0;
|
||||
if (!g_broadcastMemoValid || g_broadcastMemoFbo != drawFBO.get() ||
|
||||
g_broadcastMemoSlotVersion != slotVersion || g_broadcastMemoObjectVersion != objectVersion) {
|
||||
Uint enabledDrawBuffers = 0;
|
||||
if (drawFBO) {
|
||||
const auto& drawBuffers = drawFBO->GetDrawBuffers();
|
||||
for (Uint i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
|
||||
if (drawBuffers[i] != FramebufferAttachmentType::None) {
|
||||
enabledDrawBuffers = i + 1;
|
||||
Bool broadcastCountResolved = false;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
if (EsprytDrawFramebufferHandlesEnabled()) {
|
||||
const auto& st = MG_Pipe::MGPipeApplier();
|
||||
const MG_Pipe::MGPFramebufferState& record = st.DrawFramebuffer;
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(record.Fbo)) {
|
||||
if (!g_broadcastMemoHandleValid || g_broadcastMemoContentHash != record.ContentHash) {
|
||||
Uint enabledDrawBuffers = 0;
|
||||
for (Uint i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
|
||||
// -1 is FramebufferAttachmentType::None on the wire; every
|
||||
// other value is an attachment index.
|
||||
if (record.DrawBuffers[i] >= 0) {
|
||||
enabledDrawBuffers = i + 1;
|
||||
}
|
||||
}
|
||||
g_broadcastMemoContentHash = record.ContentHash;
|
||||
g_broadcastMemoHandleCount = std::max<Uint>(enabledDrawBuffers, 1);
|
||||
g_broadcastMemoHandleValid = true;
|
||||
}
|
||||
g_fragColorBroadcastCount = g_broadcastMemoHandleCount;
|
||||
broadcastCountResolved = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!broadcastCountResolved) {
|
||||
const auto& drawSlot = GetFramebufferBindingSlotChecked(FramebufferTarget::Draw);
|
||||
const auto& drawFBO = drawSlot.GetBoundObject();
|
||||
const Uint16 slotVersion = drawSlot.GetVersion();
|
||||
const Uint16 objectVersion = drawFBO ? drawFBO->GetObjectVersion() : 0;
|
||||
if (!g_broadcastMemoValid || g_broadcastMemoFbo != drawFBO.get() ||
|
||||
g_broadcastMemoSlotVersion != slotVersion ||
|
||||
g_broadcastMemoObjectVersion != objectVersion) {
|
||||
Uint enabledDrawBuffers = 0;
|
||||
if (drawFBO) {
|
||||
const auto& drawBuffers = drawFBO->GetDrawBuffers();
|
||||
for (Uint i = 0; i < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
|
||||
if (drawBuffers[i] != FramebufferAttachmentType::None) {
|
||||
enabledDrawBuffers = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_broadcastMemoFbo = drawFBO.get();
|
||||
g_broadcastMemoSlotVersion = slotVersion;
|
||||
g_broadcastMemoObjectVersion = objectVersion;
|
||||
g_broadcastMemoCount = std::max<Uint>(enabledDrawBuffers, 1);
|
||||
g_broadcastMemoValid = true;
|
||||
}
|
||||
g_broadcastMemoFbo = drawFBO.get();
|
||||
g_broadcastMemoSlotVersion = slotVersion;
|
||||
g_broadcastMemoObjectVersion = objectVersion;
|
||||
g_broadcastMemoCount = std::max<Uint>(enabledDrawBuffers, 1);
|
||||
g_broadcastMemoValid = true;
|
||||
g_fragColorBroadcastCount = g_broadcastMemoCount;
|
||||
}
|
||||
g_fragColorBroadcastCount = g_broadcastMemoCount;
|
||||
}
|
||||
|
||||
BackendProgramObjectImpl* twin = nullptr;
|
||||
@@ -3207,6 +3469,47 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
#ifdef TRACY_ENABLE
|
||||
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
||||
#endif
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The handle arm reads the record instead of the binding slot, which is one of the
|
||||
// five GetFramebufferBindingSlotChecked call sites this phase retires. Everything the
|
||||
// pre-handle arm below is careful about is careful here for the same reasons and in
|
||||
// the same order: NO FAST PATH on any version (the skip is BindFramebufferId's job and
|
||||
// its shadow is where the cost actually is), and the DEFAULT framebuffer is bound
|
||||
// THROUGH THE SHADOW, never raw. What changes is only where the two questions - "which
|
||||
// framebuffer" and "is it the default one" - are answered: the record's Fbo and its
|
||||
// IsDefault byte, rather than the bound object's address and a comparison against
|
||||
// pDefaultFramebufferInfo->defaultFBO.
|
||||
if (EsprytDrawFramebufferHandlesEnabled()) {
|
||||
const auto& st = MG_Pipe::MGPipeApplier();
|
||||
const MG_Pipe::MGPFramebufferState& record =
|
||||
target == FramebufferTarget::Draw ? st.DrawFramebuffer : st.ReadFramebuffer;
|
||||
// The record's own handle is the "has this binding ever been described" test, not
|
||||
// FramebufferSerial - which MGPipeApplierReset advances whether or not anything
|
||||
// was ever emitted (see SyncCurrentFBOByRecord).
|
||||
if (!MG_Pipe::MGPipeHandleIsNull(record.Fbo)) {
|
||||
if (record.IsDefault == 0) {
|
||||
auto* twinEntry = FramebufferImpl::g_backendFramebufferObjects.FindByHandle(record.Fbo);
|
||||
if (twinEntry && *twinEntry) {
|
||||
(*twinEntry)->Bind(target);
|
||||
} else {
|
||||
MGLOG_E_ONCE(
|
||||
"No backend FBO found (maybe not synced) for the current %s FBO record, "
|
||||
"cannot bind FBO.",
|
||||
(target == FramebufferTarget::Read ? "READ" : "DRAW"));
|
||||
}
|
||||
} else {
|
||||
MGLOG_D("Binding default framebuffer as %s FBO",
|
||||
(target == FramebufferTarget::Read ? "READ" : "DRAW"));
|
||||
// Through the shadow: a raw bind here would leave the shadow claiming
|
||||
// the previous user FBO, false-skipping its next re-bind.
|
||||
FramebufferImpl::BindFramebufferId(
|
||||
target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
auto& slot = GetFramebufferBindingSlotChecked(target);
|
||||
// No fast path on the binding slot's version. It is a 16-bit counter that only
|
||||
// ForceBindCurrentFBO ever stamps here, so the comparison was against an arbitrarily old
|
||||
@@ -3305,6 +3608,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
FramebufferImpl::g_fboSyncedObjects[(SizeT)target] = fbo.get();
|
||||
FramebufferImpl::g_fboSyncedBackendIdGenerations[(SizeT)target] =
|
||||
FramebufferImpl::g_attachmentBackendIdGeneration;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The handle arm's equivalent, and it is stamped per target for exactly the reason the
|
||||
// memo is kept per target at all: this entry point syncs ONE binding and must not
|
||||
// claim the other one is current too.
|
||||
if (EsprytDrawFramebufferHandlesEnabled()) {
|
||||
FramebufferImpl::StampSyncedFramebufferSerial(target, MG_Pipe::MGPipeApplier().FramebufferSerial);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void BindCurrentProgramWithResources(
|
||||
@@ -5595,6 +5906,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<GLuint>(previousRead));
|
||||
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<GLuint>(previousDraw));
|
||||
FramebufferImpl::InvalidateFramebufferBindingCache();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferImpl::InvalidateFramebufferHandleArmMemos();
|
||||
#endif
|
||||
if (!resolved) {
|
||||
MGLOG_E_ONCE("BlitFramebuffer: multisample resolve fallback failed");
|
||||
}
|
||||
@@ -6110,6 +6424,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<GLuint>(previousDraw));
|
||||
g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<GLuint>(previousRead));
|
||||
FramebufferImpl::InvalidateFramebufferBindingCache();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferImpl::InvalidateFramebufferHandleArmMemos();
|
||||
#endif
|
||||
if (!ok) {
|
||||
MGLOG_E_ONCE("BlitFramebuffer: could not stage the source for the multisample replicate");
|
||||
return false;
|
||||
@@ -6209,6 +6526,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast<GLuint>(previousRead));
|
||||
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<GLuint>(previousDraw));
|
||||
FramebufferImpl::InvalidateFramebufferBindingCache();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferImpl::InvalidateFramebufferHandleArmMemos();
|
||||
#endif
|
||||
}
|
||||
if (readSamples <= 0 && drawSamples > 0) {
|
||||
// Single-sample source into a multisample destination: ES rejects the call
|
||||
@@ -10131,6 +10451,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
g_GLESFuncs.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast<GLuint>(previousDrawFramebuffer));
|
||||
}
|
||||
FramebufferImpl::InvalidateFramebufferBindingCache();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferImpl::InvalidateFramebufferHandleArmMemos();
|
||||
#endif
|
||||
|
||||
// A driver that refuses the query leaves both at 0. Fall back to what the EGL config
|
||||
// was chosen with, which is what the surface actually has.
|
||||
@@ -10575,6 +10898,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
BufferImpl::InvalidateIndexedBufferBindingCache();
|
||||
BufferImpl::InvalidatePixelBufferBindingCaches();
|
||||
FramebufferImpl::InvalidateFramebufferBindingCache();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferImpl::InvalidateFramebufferHandleArmMemos();
|
||||
#endif
|
||||
PixelStoreImpl::InvalidatePackStateCache();
|
||||
// The render-state shadow belongs in this list for the same reason as the ones above:
|
||||
// it describes the real ES context, which outlives the MobileGL context that is
|
||||
@@ -11108,6 +11434,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
ScratchFBOImpl::OnBackendContextDestroyed();
|
||||
ReleasePackedWordScratchTexture();
|
||||
FramebufferImpl::InvalidateFramebufferBindingCache();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferImpl::InvalidateFramebufferHandleArmMemos();
|
||||
#endif
|
||||
VertexArrayImpl::InvalidateVAOBindingCache();
|
||||
PixelStoreImpl::InvalidatePackStateCache();
|
||||
PrgramImpl::InvalidateBroadcastMemo();
|
||||
|
||||
Reference in New Issue
Block a user