mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
Audit of every memoization implementation; sixteen verified defects fixed: DirectGLES backend: - Broadcast draw-buffer memo: cleared at MakeCurrent/DestroyEGLContext like its sibling shadows; its identity+version key is only monotonic within one GLContext, so a library teardown + re-init could false-hit on a recycled FBO address. - Backend texture id re-mint (RecreateBackendTexture) now bumps an attachment generation that the SyncCurrentFBO gate and every FBO twin compare, so driver FBOs re-attach instead of keeping the deleted texture name; the attachment walk re-enters until the generation is quiescent (a walk itself can re-mint). - Buffer id re-mint (persistent-map adoption, immutable-store retire) now bumps a generation the VAO twin sync compares, forcing a full re-emit of the baked glVertexAttribPointer / element-array bindings that frontend versions cannot see. - VAO element-array sync memo: bound-object identity joins the wrapping Uint16 slot version (same pairing the ResolvedDrawBuffers IBO memo already uses). DirectVulkan backend: - EBO slice memo gains the mapped-buffer guard its vertex-binding sibling has: a shadow-backed persistent map mutates with no epoch bump, so a hit must decline. - VkClearManager::MergeClearPayload keeps colorEncoding/colorInt/colorUint with the color, so deferred glClearBufferiv/uiv no longer degrade to all-zero float. - GetOrCreateComputePipeline no longer memoizes a failed creation (same contract as PipelineFactory): a transient driver failure was permanently disabling every dispatch of that program. - Explicit-LOD-0 verdict memo keys on the sampling-resolution generation; sampler filter/aniso/LOD setters bump only that counter, so the old key served a stale verdict (wrong SPIR-V variant) after glTexParameter/glSamplerParameter changes. - SetupDraw fast path declines instead of re-arming on a moved sampling-resolution generation (the snapshot bakes the LOD verdict into its pipeline), and recomputes the XfbCapture bit so the first draw after glBeginTransformFeedback cannot bind the undecorated variant and silently capture nothing. - VertexInputStateFactory eviction epoch is drawn from a process-wide source: VAO state-pointer memos outlive the factory across renderer recreation, and a fresh factory restarting at epoch 1 would dereference a dead factory's entry. - Cached render passes re-read the live renderbuffer clear payload at begin (the clear VALUE is not in the pass hash; the entry's inline snapshot replayed the creation-time color and dropped the newly queued one). - FramebufferObject gains a never-reused lifetime id, keyed into the render-pass fast-path memo and the SetupDraw snapshot beside the raw pointer + Uint16 version pair, which address reuse plus fresh version counts could equal. - SyncTextureResource's preserved-content image goes through the deferred-release ring on both failure paths instead of a synchronous destructor under the GPU. MG_State frontend: - Layer-1 compile memo is env-disciplined like layers 2/3: a node computed against a dead CompileEnv (e.g. pre-capability fallback limits) no longer answers glCompileShader forever once the environment's content changes. - Pipeline composite cache rebuilds from each stage program's last-link shader snapshot (new LinkedShaderRef list + pinned link inputs) instead of the live attach list and current compile nodes: post-link glAttachShader/glCompileShader must not leak into the composite while the (lifetimeId, linkVersion) signature still hits - GL's "as last linked" rule.
188 lines
7.4 KiB
C++
188 lines
7.4 KiB
C++
// MobileGL - MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#pragma once
|
|
#include "MG_Util/Types.h"
|
|
#include <Includes.h>
|
|
#include <MG_State/GLState/TextureState/TextureObject.h>
|
|
#include <MG_State/GLState/RenderbufferState/RenderbufferObject.h>
|
|
|
|
namespace MobileGL {
|
|
enum class FramebufferTarget {
|
|
Draw,
|
|
Read,
|
|
FramebufferTargetCount,
|
|
Unknown = -1
|
|
};
|
|
|
|
enum class FramebufferAttachmentType {
|
|
None,
|
|
|
|
FrontLeft,
|
|
FrontRight,
|
|
BackLeft,
|
|
BackRight,
|
|
|
|
Depth,
|
|
Stencil,
|
|
|
|
Color0,
|
|
Color1,
|
|
Color2,
|
|
Color3,
|
|
Color4,
|
|
Color5,
|
|
Color6,
|
|
Color7,
|
|
Color8,
|
|
Color9,
|
|
Color10,
|
|
Color11,
|
|
Color12,
|
|
Color13,
|
|
Color14,
|
|
Color15,
|
|
Color16,
|
|
Color17,
|
|
Color18,
|
|
Color19,
|
|
Color20,
|
|
Color21,
|
|
Color22,
|
|
Color23,
|
|
Color24,
|
|
Color25,
|
|
Color26,
|
|
Color27,
|
|
Color28,
|
|
Color29,
|
|
Color30,
|
|
Color31,
|
|
ColorMax = Color31,
|
|
|
|
FramebufferAttachmentTypeCount,
|
|
Unknown = -1
|
|
};
|
|
|
|
namespace MG_State::GLState {
|
|
class FramebufferAttachmentObject {
|
|
public:
|
|
explicit FramebufferAttachmentObject(const SharedPtr<MG_State::GLState::ITextureObject>& texture,
|
|
TextureUploadTarget textureUploadTarget,
|
|
Int level = 0, Int layer = 0, Bool layered = false);
|
|
explicit FramebufferAttachmentObject(const SharedPtr<RenderbufferObject>& renderbuffer);
|
|
explicit FramebufferAttachmentObject(Bool IsValid = true);
|
|
|
|
Bool IsTexture() const;
|
|
Bool IsRenderbuffer() const;
|
|
Bool IsEmpty() const;
|
|
const SharedPtr<MG_State::GLState::ITextureObject>& GetTexture() const;
|
|
const SharedPtr<RenderbufferObject>& GetRenderbuffer() const;
|
|
Int GetTextureLevel() const;
|
|
Int GetTextureLayer() const;
|
|
Bool IsLayered() const;
|
|
TextureUploadTarget GetTextureUploadTarget() const;
|
|
Bool IsComplete() const;
|
|
IntVec3 GetSize() const;
|
|
Bool IsValid() const;
|
|
|
|
private:
|
|
SharedPtr<MG_State::GLState::ITextureObject> m_texture = nullptr;
|
|
SharedPtr<RenderbufferObject> m_renderbuffer = nullptr;
|
|
TextureUploadTarget m_textureUploadTarget = TextureUploadTarget::Unknown;
|
|
Int m_textureLevel = 0;
|
|
Int m_textureLayer = 0;
|
|
Bool m_layered = false;
|
|
Bool m_isValid = true;
|
|
};
|
|
|
|
class FramebufferObject {
|
|
public:
|
|
static constexpr Uint MAX_DRAW_BUFFERS = 8;
|
|
|
|
using TargetEnum = FramebufferTarget;
|
|
using FramebufferAttachmentObjectArray =
|
|
Array<FramebufferAttachmentObject,
|
|
static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>;
|
|
using FramebufferAttachmentArray = Array<FramebufferAttachmentType, MAX_DRAW_BUFFERS>;
|
|
using FramebufferAttachmentVersionArray =
|
|
Array<Uint16, static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>;
|
|
|
|
FramebufferObject(Uint externalIndex);
|
|
|
|
void AttachTexture(FramebufferAttachmentType type, const SharedPtr<ITextureObject>& texture,
|
|
TextureUploadTarget textureUploadTarget = TextureUploadTarget::Unknown, int level = 0,
|
|
int layer = 0, Bool layered = false);
|
|
void AttachRenderbuffer(FramebufferAttachmentType type, const SharedPtr<RenderbufferObject>& renderbuffer);
|
|
void Detach(FramebufferAttachmentType type);
|
|
const FramebufferAttachmentObject& GetAttachment(FramebufferAttachmentType type) const;
|
|
const FramebufferAttachmentObjectArray& GetAllAttachmentObjects() const;
|
|
Bool CheckCompleteness() const;
|
|
// aka. `buffer` as in glDrawBuffers/glReadBuffers
|
|
void SetDrawBuffer(Uint index, FramebufferAttachmentType buffer);
|
|
const FramebufferAttachmentArray& GetDrawBuffers() const;
|
|
void SetReadBuffer(FramebufferAttachmentType buf);
|
|
FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; }
|
|
|
|
// GL_ARB_framebuffer_no_attachments state (GL 4.6 core table 23.24). The shape a
|
|
// framebuffer with no attachments would rasterize at; all zero / FALSE until set.
|
|
Int GetDefaultWidth() const { return m_defaultWidth; }
|
|
Int GetDefaultHeight() const { return m_defaultHeight; }
|
|
Int GetDefaultLayers() const { return m_defaultLayers; }
|
|
Int GetDefaultSamples() const { return m_defaultSamples; }
|
|
Bool GetDefaultFixedSampleLocations() const { return m_defaultFixedSampleLocations; }
|
|
void SetDefaultWidth(Int value);
|
|
void SetDefaultHeight(Int value);
|
|
void SetDefaultLayers(Int value);
|
|
void SetDefaultSamples(Int value);
|
|
void SetDefaultFixedSampleLocations(Bool value);
|
|
|
|
FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const {
|
|
return m_attachmentVersions;
|
|
}
|
|
|
|
Uint16 GetObjectVersion() const { return m_objectVersion; }
|
|
|
|
// Globally-unique, never-reused id for THIS object's lifetime - the same
|
|
// contract as VertexArrayObject::GetLifetimeId(), and needed for the same
|
|
// reason: neither the GL name nor the heap address can tell a
|
|
// deleted-and-recreated framebuffer from the original, and m_objectVersion
|
|
// starts at 0 for every new object, so a backend memo keyed on
|
|
// (pointer, version) alone would silently inherit the dead object's entry
|
|
// (see VkRenderPassManager's per-draw fast-path memo).
|
|
Uint64 GetLifetimeId() const { return m_lifetimeId; }
|
|
|
|
Uint GetExternalIndex() const;
|
|
Bool IsDefaultFramebuffer() const { return m_externalIndex == 0; }
|
|
|
|
private:
|
|
static Uint64 AllocateLifetimeId();
|
|
|
|
void BumpAttachmentVersion(FramebufferAttachmentType type);
|
|
|
|
const Uint m_externalIndex = 0;
|
|
const Uint64 m_lifetimeId = AllocateLifetimeId();
|
|
FramebufferAttachmentObjectArray m_attachmentObjects;
|
|
FramebufferAttachmentVersionArray m_attachmentVersions;
|
|
|
|
FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality
|
|
FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::None;
|
|
|
|
Int m_defaultWidth = 0;
|
|
Int m_defaultHeight = 0;
|
|
Int m_defaultLayers = 0;
|
|
Int m_defaultSamples = 0;
|
|
Bool m_defaultFixedSampleLocations = false;
|
|
|
|
// This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`)
|
|
Uint16 m_objectVersion = 0;
|
|
};
|
|
|
|
} // namespace MG_State::GLState
|
|
} // namespace MobileGL
|