mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Feat] (Pipe, State): mint a {slot, gen} handle for every texture and renderbuffer and publish its create, respecify, parameters and accumulated sub-data as pipe calls
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -25,15 +25,22 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
FramebufferObject::~FramebufferObject() {
|
||||
// P2 step e2: ANNOUNCE the death instead of leaving the backend to discover it in a
|
||||
// garbage sweep. This is the last SharedPtr to this object dropping - not the
|
||||
// glDelete* that only marks the name and leaves a still-bound object very much
|
||||
// alive - so it is the exact moment the backend's twin, and the driver storage
|
||||
// that twin owns, stop being reachable. The notice carries the lifetime id
|
||||
// because the object no longer exists to be passed, and because the lifetime id
|
||||
// is what the client slot allocator resolves the handle from. No-op unless a
|
||||
// backend registered the ops (a pull build declares none at all).
|
||||
NotifyStateObjectDestroyed(MG_Pipe::MGPipeKind::Framebuffer, m_lifetimeId);
|
||||
// P4a D-I2: A FRAMEBUFFER HAS A HANDLE AND NO WIRE LIFETIME. PipeCalls.def carries
|
||||
// resource_destroy and five delete_* rows and NO framebuffer delete, because a
|
||||
// framebuffer is not a resource and is not a CSO - it is STATE, and
|
||||
// set_framebuffer_state is the only call that names one - and the catalogue is closed,
|
||||
// so P4a invents no row. The helper is therefore steps 2 and 3 only: the death notice,
|
||||
// raised while the handle still resolves (this is the P2 step-e2 announcement that used
|
||||
// to stand here alone - the last SharedPtr to this object dropping, not the glDelete*
|
||||
// that only marks the name and leaves a still-bound object very much alive), and then
|
||||
// the slot.
|
||||
//
|
||||
// What makes a dangling Fbo unreachable is the frontend's own
|
||||
// MarkFramebufferObjectForDeletion path, which already rebinds any slot holding the
|
||||
// victim to framebuffer 0; and a RECYCLED framebuffer handle can never be suppressed
|
||||
// against its predecessor's record, because Fbo carries Gen and Gen is inside the
|
||||
// record's ContentHash.
|
||||
MG_Pipe::MGPipeEmitFramebufferDestroyAndFree(m_lifetimeId);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
#include "RenderbufferObject.h"
|
||||
#include <MG_Util/Metrics/TextureMetrics.h>
|
||||
#include <MG_State/GLState/StateObjectDeathNotice.h>
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The second and last MG_State translation unit that sees the client's texture emitter. A
|
||||
// renderbuffer has no base class to hang protected helpers on and exactly one .cpp, so the
|
||||
// include is the whole coupling; see MG_Impl/Pipe/TextureEmit.h's header comment for why the
|
||||
// declaration cannot live in MG_Pipe/PipeMutation.h this phase.
|
||||
#include <MG_Impl/Pipe/TextureEmit.h>
|
||||
#endif
|
||||
|
||||
#include <atomic>
|
||||
|
||||
@@ -25,19 +32,27 @@ namespace MobileGL {
|
||||
return g_nextRenderbufferLifetimeId.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
RenderbufferObject::RenderbufferObject(Uint externalIndex) : m_externalIndex(externalIndex) {}
|
||||
RenderbufferObject::RenderbufferObject(Uint externalIndex) : m_externalIndex(externalIndex) {
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a D-D1: resource_create from the constructor, carrying no storage. A
|
||||
// renderbuffer is an INDEPENDENT class on the wire - it shares MGPResourceDesc's
|
||||
// shape with textures and buffers and nothing else - and its handle is minted
|
||||
// whatever the subsystem bitmask says, because MGPSurface::Res names it out of the
|
||||
// framebuffer subsystem.
|
||||
MG_Pipe::MGPipeMintAndCreateRenderbuffer(*this);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
RenderbufferObject::~RenderbufferObject() {
|
||||
// P2 step e2: ANNOUNCE the death instead of leaving the backend to discover it in a
|
||||
// garbage sweep. This is the last SharedPtr to this object dropping - not
|
||||
// glDeleteRenderbuffers, which only marks the name and leaves a still-bound object very much
|
||||
// alive - so it is the exact moment the backend's twin, and the driver storage that
|
||||
// twin owns, stop being reachable. The notice carries the lifetime id because the
|
||||
// object no longer exists to be passed, and because the lifetime id is what the client
|
||||
// slot allocator resolves the handle from. No-op unless a backend registered the ops
|
||||
// (a pull build declares none at all).
|
||||
NotifyStateObjectDestroyed(MG_Pipe::MGPipeKind::Renderbuffer, m_lifetimeId);
|
||||
// P4a D-I1, the fixed three-step order: the wire delete first (published-gated,
|
||||
// because a slot is not evidence of a record), then the death notice - the P2
|
||||
// step-e2 announcement that used to stand here alone, raised while the handle
|
||||
// still resolves - and the slot last. Steps 2 and 3 are the contract's helper;
|
||||
// step 1 is this package's, one statement earlier, because the helper's file
|
||||
// belongs to the contract package for the whole phase.
|
||||
MG_Pipe::MGPipeEmitRenderbufferResourceDestroy(m_lifetimeId);
|
||||
MG_Pipe::MGPipeEmitRenderbufferDestroyAndFree(m_lifetimeId);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -96,17 +111,43 @@ namespace MobileGL {
|
||||
void RenderbufferObject::SetInternalFormat(TextureInternalFormat format) {
|
||||
m_internalFormat = format;
|
||||
m_componentSizes = MG_Util::GetComponentSizesForInternalFormat(format);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenderbufferObject::AllocateStorage(IntVec2 size) {
|
||||
m_width = size.x();
|
||||
m_height = size.y();
|
||||
m_allocated = true;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenderbufferObject::SetSamples(Int samples) {
|
||||
m_samples = samples;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// D-D2: THE RENDERBUFFER PUBLICATION HOLE, CLOSED BY EMISSION AND NOT BY A NEW
|
||||
// VERSION. These three setters bump no version and raise no notice, and the
|
||||
// framebuffer dirty bit's shutter does not move when an ALREADY-ATTACHED renderbuffer
|
||||
// is re-storaged - so `glBindRenderbuffer; glRenderbufferStorage(newSize)` on an
|
||||
// attached renderbuffer was invisible to everything downstream. Emitting from the
|
||||
// storage entry point closes it; a version counter here would resize the pull build's
|
||||
// object and break G1, and widening the shutter would fire the framebuffer emission on
|
||||
// an unrelated renderbuffer write.
|
||||
//
|
||||
// The emitter dedupes on the built descriptor, so glRenderbufferStorage's three-setter
|
||||
// sequence publishes once rather than three times.
|
||||
void RenderbufferObject::PipePublishDescriptor() {
|
||||
MG_Pipe::MGPipeEmitRenderbufferRespecify(*this);
|
||||
}
|
||||
#endif
|
||||
} // namespace GLState
|
||||
} // namespace MG_State
|
||||
} // namespace MobileGL
|
||||
|
||||
@@ -59,6 +59,13 @@ namespace MobileGL {
|
||||
|
||||
private:
|
||||
static Uint64 AllocateLifetimeId();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a D-D2: resource_respecify, from every storage-defining setter. Non-virtual
|
||||
// and push-only, so the pull build's object layout is untouched (P4a's
|
||||
// admitted-resize set is EMPTY); defined in RenderbufferObject.cpp, which is the
|
||||
// one translation unit that includes the client emitter.
|
||||
void PipePublishDescriptor();
|
||||
#endif
|
||||
|
||||
Uint m_externalIndex = 0;
|
||||
const Uint64 m_lifetimeId = AllocateLifetimeId();
|
||||
|
||||
@@ -12,6 +12,15 @@
|
||||
#include "MG_Util/Types.h"
|
||||
#include <MG_Util/Metrics/TextureMetrics.h>
|
||||
#include <MG_Pipe/PipeMutation.h>
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// THE ONE MG_State TRANSLATION UNIT THAT SEES THE CLIENT'S TEXTURE EMITTER, on purpose: the
|
||||
// three PipePublish* helpers below are declared on TextureObjectBase and defined here, so the
|
||||
// cube's, the view's and the buffer texture's translation units call an inherited member and
|
||||
// still see only a declaration. That is the layering MG_Pipe/PipeMutation.h gives the buffer
|
||||
// family; P4a cannot use that header because it belongs to the contract package for the whole
|
||||
// phase and carries no texture row (TextureEmit.h's header comment has the full argument).
|
||||
#include <MG_Impl/Pipe/TextureEmit.h>
|
||||
#endif
|
||||
|
||||
namespace MobileGL {
|
||||
namespace MG_State {
|
||||
@@ -29,15 +38,44 @@ namespace MobileGL {
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
TextureObjectBase::~TextureObjectBase() {
|
||||
// P2 step e2: ANNOUNCE the death instead of leaving the backend to discover it in a
|
||||
// garbage sweep. This is the last SharedPtr to this object dropping - not the
|
||||
// glDelete* that only marks the name and leaves a still-bound object very much
|
||||
// alive - so it is the exact moment the backend's twin, and the driver storage
|
||||
// that twin owns, stop being reachable. The notice carries the lifetime id
|
||||
// because the object no longer exists to be passed, and because the lifetime id
|
||||
// is what the client slot allocator resolves the handle from. No-op unless a
|
||||
// backend registered the ops (a pull build declares none at all).
|
||||
NotifyStateObjectDestroyed(MG_Pipe::MGPipeKind::Texture, m_lifetimeId);
|
||||
// P4a D-I1: BACKEND-NEUTRAL FROM DAY ONE, and the three-step order is fixed.
|
||||
//
|
||||
// 1. the wire delete FIRST - it drops the applier's record while the record
|
||||
// still exists, so a recycled slot cannot inherit a field. Published-gated
|
||||
// rather than slot-gated: a backend twin table mints a slot through
|
||||
// MGPipeSlots().Acquire whether or not the subsystem ever asked this client
|
||||
// to emit a create, and a resource_destroy on such a handle is a refused
|
||||
// call the applier counts and asserts on;
|
||||
// 2. the death notice SECOND - it resolves the handle through the allocator,
|
||||
// and a backend told after the free could no longer find its twin. This is
|
||||
// the P2 step-e2 announcement that used to stand here alone: the last
|
||||
// SharedPtr to this object dropping, not the glDelete* that only marks the
|
||||
// name and leaves a still-bound object very much alive;
|
||||
// 3. the slot LAST, and a double free on a stale generation is a proven no-op.
|
||||
//
|
||||
// Steps 2 and 3 - plus the SamplerViewCso minted off this same lifetime id - are
|
||||
// the contract's helper. The BUILT-IN SAMPLER is deliberately not released from
|
||||
// here: it is a real SamplerObject with its own lifetime id and its own
|
||||
// destructor, which runs immediately after this body and takes the same helper
|
||||
// shape. Step 1 is the client package's, one statement earlier, because the
|
||||
// helper's file belongs to the contract package for the whole phase.
|
||||
MG_Pipe::MGPipeEmitTextureResourceDestroy(m_lifetimeId);
|
||||
MG_Pipe::MGPipeEmitTextureDestroyAndFree(m_lifetimeId);
|
||||
}
|
||||
|
||||
// ---- P4a's three client emission points (see TextureObject.h) ----
|
||||
|
||||
void TextureObjectBase::PipePublishDescriptor() {
|
||||
MG_Pipe::MGPipeEmitTextureRespecify(*this);
|
||||
}
|
||||
|
||||
void TextureObjectBase::PipePublishParams() {
|
||||
MG_Pipe::MGPipeEmitTextureParams(*this);
|
||||
}
|
||||
|
||||
void TextureObjectBase::PipeNoteLevelDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
||||
Bool dirty) {
|
||||
MG_Pipe::MGPipeNoteTextureLevelDirty(*this, uploadTarget, mipmapLevel, dirty);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -69,6 +107,24 @@ namespace MobileGL {
|
||||
m_sampler->SetWrapT(SamplerWrapMode::ClampToEdge);
|
||||
m_sampler->SetWrapR(SamplerWrapMode::ClampToEdge);
|
||||
}
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// P4a D-D1: A RESOURCE EXISTS BEFORE ANYTHING CAN NAME IT, so resource_create is
|
||||
// emitted from the constructor and carries no storage - the store is defined
|
||||
// lazily by the first respecify and every backend already tolerates a resource
|
||||
// with none. The handle itself is minted whatever the subsystem bitmask says,
|
||||
// because set_framebuffer_state and set_sampler_views name this texture by handle
|
||||
// out of two different subsystems.
|
||||
//
|
||||
// NOTHING VIRTUAL IS TOUCHED HERE and that is a correctness requirement rather
|
||||
// than a style: the derived object does not exist yet, and
|
||||
// ITextureObject::GetStorageType is PURE - calling it from a base constructor is
|
||||
// undefined behaviour. The target and the GL name are the two facts a create
|
||||
// carries and both are plain members by this point; the storage kind is derived
|
||||
// from the target, which is exact (TextureObjectBuffer is the only class that
|
||||
// reports Buffer and TextureBuffer is the only target it is constructed with).
|
||||
MG_Pipe::MGPipeMintAndCreateTexture(static_cast<ITextureObject*>(this), m_lifetimeId, target,
|
||||
externalIndex);
|
||||
#endif
|
||||
}
|
||||
|
||||
TextureInternalFormat TextureObjectBase::GetFormat() const {
|
||||
@@ -116,6 +172,14 @@ namespace MobileGL {
|
||||
BumpShapeVersion();
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The format is BOTH halves: a descriptor field the backend allocates from and a
|
||||
// parameter the swizzle / depth-stencil push reads, so both records move. It is
|
||||
// also the last statement of both glTexBuffer entry points, which is what
|
||||
// publishes a buffer texture's window without a call site in MG_Impl/GLImpl.
|
||||
PipePublishDescriptor();
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint TextureObjectBase::GetExternalIndex() const {
|
||||
@@ -142,6 +206,9 @@ namespace MobileGL {
|
||||
m_sampler->SetBorderColor(color);
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
const IntVec4& TextureObjectBase::GetBorderColorI() const {
|
||||
@@ -157,6 +224,9 @@ namespace MobileGL {
|
||||
m_sampler->SetBorderColorI(color);
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
const UintVec4& TextureObjectBase::GetBorderColorUI() const {
|
||||
@@ -172,6 +242,9 @@ namespace MobileGL {
|
||||
m_sampler->SetBorderColorUI(color);
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
BorderColorForm TextureObjectBase::GetBorderColorForm() const {
|
||||
@@ -222,6 +295,9 @@ namespace MobileGL {
|
||||
}
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureObjectBase::SetSwizzleParamRGBA(const Vec4<TextureSwizzleParam>& values) {
|
||||
@@ -230,6 +306,9 @@ namespace MobileGL {
|
||||
m_swizzleParams = values;
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
const UintVec2& TextureObjectBase::GetLevelRange() const {
|
||||
@@ -249,6 +328,12 @@ namespace MobileGL {
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
BumpShapeVersion();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// The level range is PARAMS, not a descriptor field: the record carries
|
||||
// BaseLevel / MaxLevel and the storage is untouched. The descriptor is deduped on
|
||||
// its own bytes, so the shape bump above costs nothing here.
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureObjectBase::SetMaxLevel(Uint maxLevel) {
|
||||
@@ -261,6 +346,9 @@ namespace MobileGL {
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
BumpShapeVersion();
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
Bool TextureObjectBase::IsImmutable() const {
|
||||
@@ -281,6 +369,14 @@ namespace MobileGL {
|
||||
}
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// Immutable is a descriptor fact the backend reads - and NOT a request for an
|
||||
// acknowledgement: MGPipeResourceRespecifyNeedsAck names the buffer target
|
||||
// explicitly, because texture allocation is lazy in monolith and stays lazy in
|
||||
// split. The level clamp above is params.
|
||||
PipePublishDescriptor();
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint16 TextureObjectBase::GetTextureParamsVersion() const {
|
||||
@@ -319,6 +415,9 @@ namespace MobileGL {
|
||||
m_samples = samples;
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
Bool TextureObjectBase::HasFixedSampleLocations() const {
|
||||
@@ -329,6 +428,9 @@ namespace MobileGL {
|
||||
m_fixedSampleLocations = fixedSampleLocations;
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint64 TextureObjectBase::GetLifetimeId() const {
|
||||
@@ -360,11 +462,23 @@ namespace MobileGL {
|
||||
MipmapInput input) {
|
||||
BumpShapeVersion();
|
||||
m_textureStorage.AllocateLevel(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// AFTER the allocation, never before: BumpShapeVersion runs first and a descriptor
|
||||
// built there would describe the level set this call is about to change. Every
|
||||
// storage-defining GL entry point - glTexImage*, glCompressedTexImage*,
|
||||
// glTexStorage*, glTextureView and the generated-mip storage grow - reaches
|
||||
// storage through here, which is what makes the emission complete without one call
|
||||
// site per entry point in MG_Impl/GLImpl.
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureObjectWithOneMipmap::TruncateMipmapLevels(TextureUploadTarget uploadTarget, Uint levelCount) {
|
||||
BumpShapeVersion();
|
||||
m_textureStorage.TruncateToLevelCount(GetIndexOfTextureUploadTarget(uploadTarget), levelCount);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureObjectWithOneMipmap::UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
||||
@@ -383,6 +497,9 @@ namespace MobileGL {
|
||||
MGP_NOTE_AGGREGATE(TextureContent);
|
||||
}
|
||||
m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipeNoteLevelDirty(uploadTarget, mipmapLevel, dirty);
|
||||
#endif
|
||||
}
|
||||
|
||||
Bool TextureObjectWithOneMipmap::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const {
|
||||
@@ -395,6 +512,13 @@ namespace MobileGL {
|
||||
MGP_NOTE_AGGREGATE(TextureContent);
|
||||
m_textureStorage.MarkDirtyRegion(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, offset,
|
||||
size);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// THE DRAIN LIST IS KEYED ON THE STORAGE OWNER FOR FREE: TextureObjectView
|
||||
// forwards this call to the OWNER's method after remapping the level and the
|
||||
// region, so an upload through a view and an upload through the owner arrive here
|
||||
// on the same object with the same owner-side coordinates.
|
||||
PipeNoteLevelDirty(uploadTarget, mipmapLevel, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
MipmapDirtyRegion TextureObjectWithOneMipmap::GetStorageDirtyRegion(TextureUploadTarget uploadTarget,
|
||||
|
||||
@@ -190,6 +190,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_depthStencilTextureMode = mode;
|
||||
++m_textureParamsVersion;
|
||||
MGP_NOTE_AGGREGATE(TextureParams);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// D-E3's whole point, at the one site that proves it: the depth-stencil mode of a
|
||||
// texture that is ONLY the READ framebuffer's attachment reaches the driver, because
|
||||
// set_texture_params is addressed by resource and is independent of every binding.
|
||||
PipePublishParams();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -201,6 +207,34 @@ namespace MobileGL::MG_State::GLState {
|
||||
// otherwise invisible to such a memo (no bind moved).
|
||||
void BumpShapeVersion();
|
||||
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// ---- P4a's client emission points (brief D-D1, D-D3, D-E1, D-I1) ----
|
||||
//
|
||||
// NON-VIRTUAL AND PUSH-ONLY, both deliberately: a virtual would grow the vtable and a
|
||||
// member would grow the object, and P4a's admitted-resize set is EMPTY - every edit
|
||||
// that reaches the pull build is inside this guard, so the pull build's symbol set is
|
||||
// byte-for-byte the one it had before the phase.
|
||||
//
|
||||
// DECLARED HERE AND DEFINED IN TextureObject.cpp, which is the ONE MG_State
|
||||
// translation unit that includes the client's MG_Impl/Pipe/TextureEmit.h. Every other
|
||||
// texture .cpp - the cube's, the view's, the buffer texture's - calls the inherited
|
||||
// helper and still sees only a declaration, which is the same layering
|
||||
// MG_Pipe/PipeMutation.h gives the buffer family (that header is the contract
|
||||
// package's for the whole phase and carries no texture row, which is why the
|
||||
// declaration lives here instead; see TextureEmit.h's header comment).
|
||||
//
|
||||
// resource_respecify. Called from BumpShapeVersion and from the three parameter
|
||||
// setters that move a DESCRIPTOR field without moving the shape (immutable levels,
|
||||
// sample count, fixed sample locations). The emitter dedupes on the built descriptor,
|
||||
// so an over-call costs one 88-byte compare and never an extra record.
|
||||
void PipePublishDescriptor();
|
||||
// set_texture_params, from every mutator that bumps m_textureParamsVersion.
|
||||
void PipePublishParams();
|
||||
// The sub-data DRAIN LIST. `dirty` false is a level going clean - a respecify, a
|
||||
// truncation, or the emitter's own clear after an accepted record.
|
||||
void PipeNoteLevelDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel, Bool dirty);
|
||||
#endif
|
||||
|
||||
const Uint m_externalIndex;
|
||||
const Uint64 m_lifetimeId;
|
||||
const TextureTarget m_target = TextureTarget::Unknown;
|
||||
|
||||
@@ -31,11 +31,20 @@ namespace MobileGL {
|
||||
MipmapInput input) {
|
||||
BumpShapeVersion();
|
||||
m_textureStorage.AllocateLevel(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, input);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// AFTER the allocation, for TextureObjectWithOneMipmap's reason: BumpShapeVersion
|
||||
// runs first and a descriptor built there would describe the level set this call
|
||||
// is about to change.
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureObject2DCube::TruncateMipmapLevels(TextureUploadTarget uploadTarget, Uint levelCount) {
|
||||
BumpShapeVersion();
|
||||
m_textureStorage.TruncateToLevelCount(GetIndexOfTextureUploadTarget(uploadTarget), levelCount);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
void TextureObject2DCube::UpdateMipmapSubData(TextureUploadTarget uploadTarget, Uint mipmapLevel,
|
||||
@@ -53,6 +62,11 @@ namespace MobileGL {
|
||||
MGP_NOTE_AGGREGATE(TextureContent);
|
||||
}
|
||||
m_textureStorage.MarkDirty(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, dirty);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// SIX FACES, SIX BLOBS, SIX DRAIN KEYS: the upload target is the face, and it is
|
||||
// what the sub-data record's Target byte carries beside the resource target.
|
||||
PipeNoteLevelDirty(uploadTarget, mipmapLevel, dirty);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool TextureObject2DCube::IsStorageDirty(TextureUploadTarget uploadTarget, Uint mipmapLevel) const {
|
||||
@@ -65,6 +79,9 @@ namespace MobileGL {
|
||||
MGP_NOTE_AGGREGATE(TextureContent);
|
||||
m_textureStorage.MarkDirtyRegion(GetIndexOfTextureUploadTarget(uploadTarget), mipmapLevel, offset,
|
||||
size);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
PipeNoteLevelDirty(uploadTarget, mipmapLevel, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
MipmapDirtyRegion TextureObject2DCube::GetStorageDirtyRegion(TextureUploadTarget uploadTarget,
|
||||
|
||||
@@ -29,6 +29,15 @@ namespace MobileGL {
|
||||
void SetBufferRange(SizeT offset, SizeT size) {
|
||||
m_bufferRangeOffset = offset;
|
||||
m_bufferRangeSize = size;
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// Both glTexBuffer entry points bind the backing buffer and then set the
|
||||
// window, so this is the first statement at which the descriptor's
|
||||
// BufferForTexBuffer / BufOffset / BufSize trio is complete. SetInternalFormat
|
||||
// publishes again one statement later and is deduped away when the format did
|
||||
// not move - which is exactly the case a re-attach of a DIFFERENT buffer at
|
||||
// the same format would otherwise fall through.
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
SizeT GetBufferRangeOffset() const { return m_bufferRangeOffset; }
|
||||
// Resolved against the buffer's current size, so kWholeBuffer tracks it.
|
||||
|
||||
@@ -79,6 +79,18 @@ namespace MobileGL::MG_State::GLState {
|
||||
// to the view. GetImmutableLevels() forwards to the owner for the actual GL query, which
|
||||
// GL 4.6 core 8.18 defines as the ORIGINAL texture's value.
|
||||
SetImmutableLevels(numLevels);
|
||||
#if MOBILEGL_PIPE_PUSH
|
||||
// ViewOf, and it is published from HERE rather than left to SetImmutableLevels above,
|
||||
// which early-returns when the composed level count happens to be the base class's
|
||||
// current value - a degenerate view (the spec's min() composition narrowed to zero
|
||||
// levels) would otherwise never publish the one field that makes it a view. The
|
||||
// descriptor is deduped on its own bytes, so the ordinary case pays one compare.
|
||||
//
|
||||
// ONE HOP ALWAYS REACHES STORAGE: glTextureView composes a view-of-a-view onto the ROOT
|
||||
// at creation, which is what the spec's additive min-level rule describes and what the
|
||||
// assertion above pins, so the owner named here is never itself a view.
|
||||
PipePublishDescriptor();
|
||||
#endif
|
||||
}
|
||||
|
||||
Uint TextureObjectView::GetImmutableLevels() const {
|
||||
|
||||
Reference in New Issue
Block a user