mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-12 20:43:23 +09:00
- FramebufferSrgb, DepthClamp and TextureCubeMapSeamless get real storage. All three fell
to SetCapability's "not supported currently" arm and IsCapabilityEnabled's default:
glEnable was swallowed and glIsEnabled lied, so DirectGLES' sRGB block and the
DirectVulkan read points consumed a constant. The three Bools land in the three
alignment bytes at [581, 584) between ColorMasks and ClearColor, so
sizeof(RenderStateParameters) stays 1168 and NO existing offset moves - Espryt's
kBlendSpanBegin/kBlendSpanEnd (312/536) and the whole chunk table depend on that.
- MGPipeRenderStateSpans.{h,cpp}: the pipeline/dynamic split, written in exactly one place.
The rule is the only rule - a byte is pipeline state iff a public RenderState setter that
calls BumpVersions() writes it - which makes G7's "the subset hash moves iff
m_pipelineStateVersion moves" true by construction. 16 boundaries, all offsetof or
sizeof, alternating dynamic/pipeline: 8 dynamic chunks / 772 bytes and 7 pipeline chunks
/ 396 bytes, partitioning [0, 1168) exactly, asserted at compile time.
MGPipeComputePipelineSubsetHash is XXH64 over the seven pipeline chunks, seeded with a
table version so a chunk-table change invalidates every persisted key.
- The pipeline subset is now a strict SUPERSET of the 24 members ComputePipelineStateHash
hashed: 44 members, adding sample coverage, the front face, the provoking vertex, the
scissor-test mask, the back polygon mode, eleven capability bools the hash never read and
the three above. Demoting those setters to ++m_version instead would have changed
MG_State semantics in the PULL build for the push path's sake. The hash runs only when
m_pipelineStateVersion moves, which is exactly when Magma re-hashed before.
- PipeApply.{h,cpp}: the in-process applier, the server half of the P2 calls. The server's
working RenderStateParameters IS PipeInputs::m_renderState, which is why DirectGLES'
SyncRenderState is not one line changed and why the verify comparator stops being a
tautology. Per-context CSO store indexed by slot, gen-validated; the residual block's
capability bits are compared against the assembled block, so a capability a later call
takes over and forgets to carry is Fatal{PipeResidualDiverged}.
MGPipeDeriveRenderStateFields is a declared STUB - its 29 derivations are commit c1.
- SlotAllocator.{h,cpp}: the client's per-kind {slot, gen} allocator, free list plus
high-water, first allocatable slot 1, gen bumping only on slot REUSE, a debug assert on
gen wrap, the composite ShaderCso band held back, and a lifetimeId -> slot map per kind so
a GL name never enters a key. In the contract because both Track H slices need it.
- ResidualValueBlock 1248 -> 8 bytes, one Uint64 of capability bits.
RenderStateParameters retired to create/bind_render_state and set_dynamic_state, Pack to
set_pixel_pack_state, the patch quintet to set_patch_state. gen_pipe.py now emits the
member-by-member offsetof assertions the ratchet comment always promised.
- gen_pipe.py: PIPELINE_STATE_MEMBERS grows to the 44-member set in declaration order and
PipeSpanTable.inc's "deliberately absent" block records the answers instead of the
questions; Coverage.def gains MGP_COVERAGE_EMITTED_LIST (34 rows) and PipeFilled.inc
gains kMGPipeFieldEmittedBy[], which is what lets the residual fill loop skip a field a
P2 call now supplies. One more --self-test negative control covers the new list.
- MOBILEGL_PIPE_PUSH becomes a per-subsystem bitmask with named bits (0..6 migrated at P2,
bit 63 the CSO-content-addressing negative control), defaulting to 0x7f in a push build
and staying 0 in a pull build. New CMake option MOBILEGL_PIPE_LEGACY_MEMOS, ON, forced ON
when MOBILEGL_PIPE_PUSH=OFF where it is the only arm. New Features.PipeHandleAbaControl
under MOBILEGL_PIPE_PUSH, negative control C for HandleRecycleScenario.
- PipeStats gains CallClass::{RenderStateCsoMints, RenderStateCsoBinds} (csom / csob on the
summary line), and they are PUSH-ONLY: growing the enum in the pull build would resize
the counter arrays, the name table and FormatWindowLine for two counters that could never
leave zero, and G1 admits no such resize.
- Four MG_Test/Pipe stubs plus their CMake registration, so the packages that own their
contents never touch MG_Test/Pipe/CMakeLists.txt.
G1, pull build, symbol_report --threshold 0: 0 added, 0 removed, 0 renamed, 4 resized, and
every resize is attributed:
RenderState::RenderState() 1700 -> 1848 (+148) the three {}
RenderState::SetCapability(CapabilityInput,bool) 850 -> 927 (+77) three switch arms
RenderState::IsCapabilityEnabled(CapabilityInput) 239 -> 268 (+29) three switch arms
_GLOBAL__sub_I_DirectGLES.cpp 1340 -> 1331 (-9) the static
initialiser of DirectGLES.cpp's `static RenderStateParameters
g_syncedRenderStateParameters` re-scheduling around the three new default-initialised
members. A shrink, and the only unforeseen entry; it is a direct consequence of the
struct gaining members and touches no interface.
186 lines
10 KiB
C++
186 lines
10 KiB
C++
// MobileGL - MobileGL/MG_State/GLState/RenderState/RenderState.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 <Includes.h>
|
|
#include <MG_Pipe/MGPipeValueTypes.h>
|
|
|
|
namespace MobileGL {
|
|
namespace MG_State {
|
|
namespace GLState {
|
|
class RenderState {
|
|
public:
|
|
RenderState();
|
|
|
|
Uint GetVersion() const;
|
|
// Version of the pipeline-relevant subset only - see m_pipelineStateVersion.
|
|
Uint GetPipelineStateVersion() const;
|
|
const RenderStateParameters& GetAllParameters() const;
|
|
|
|
// Rasterization
|
|
// ARB_viewport_array defines glViewport as ViewportIndexedf on EVERY index, so the
|
|
// classic setter broadcasts; GetViewport answers for index 0 (rounded to the
|
|
// integers glGetIntegerv(GL_VIEWPORT) and both backends want) and is BY VALUE for
|
|
// that reason. The indexed pair is the verbatim float state.
|
|
void SetViewport(IntVec4 viewport); // x, y, width, height
|
|
IntVec4 GetViewport() const; // x, y, width, height, viewport 0, rounded
|
|
void SetViewportIndexed(Uint index, FloatVec4 viewport);
|
|
const FloatVec4& GetViewportIndexed(Uint index) const;
|
|
void SetLineWidth(Float width);
|
|
Float GetLineWidth() const;
|
|
void SetPointSize(Float size);
|
|
Float GetPointSize() const;
|
|
void SetPatchVertices(Uint vertices);
|
|
Uint GetPatchVertices() const;
|
|
void SetPatchDefaultOuterLevel(const FloatVec4& levels);
|
|
const FloatVec4& GetPatchDefaultOuterLevel() const;
|
|
void SetPatchDefaultInnerLevel(const FloatVec2& levels);
|
|
const FloatVec2& GetPatchDefaultInnerLevel() const;
|
|
void SetPolygonOffset(Float factor, Float units);
|
|
// glPolygonOffsetClamp. Writes the same factor/units as glPolygonOffset plus the
|
|
// clamp, because that is what the entry point does - glPolygonOffset is the
|
|
// clamp = 0 case of it (GL 4.6 core 14.6.5).
|
|
void SetPolygonOffsetClamped(Float factor, Float units, Float clamp);
|
|
Float GetPolygonOffsetFactor() const;
|
|
Float GetPolygonOffsetUnits() const;
|
|
Float GetPolygonOffsetClamp() const;
|
|
void SetClipControl(GLenum origin, GLenum depth);
|
|
GLenum GetClipOrigin() const;
|
|
GLenum GetClipDepthMode() const;
|
|
// Hints. target must be one of the 4 GL 3.3 core hint targets (validated by the caller).
|
|
void SetHint(GLenum target, GLenum mode);
|
|
GLenum GetHint(GLenum target) const;
|
|
void SetPointFadeThresholdSize(Float size);
|
|
Float GetPointFadeThresholdSize() const;
|
|
void SetPointSpriteCoordOrigin(GLenum origin);
|
|
GLenum GetPointSpriteCoordOrigin() const;
|
|
// Color clamping (glClampColor). Core profile has only GL_CLAMP_READ_COLOR.
|
|
void SetClampReadColor(GLenum clamp);
|
|
GLenum GetClampReadColor() const;
|
|
// Polygon mode (glPolygonMode). Core sets both faces together; the query reports both.
|
|
void SetPolygonMode(GLenum front, GLenum back);
|
|
GLenum GetPolygonModeFront() const;
|
|
GLenum GetPolygonModeBack() const;
|
|
void SetPrimitiveRestartIndex(Uint32 index);
|
|
Uint32 GetPrimitiveRestartIndex() const;
|
|
|
|
// Capabilities
|
|
void SetCapability(CapabilityInput cap, Bool enabled);
|
|
Bool IsCapabilityEnabled(CapabilityInput cap) const;
|
|
void SetCapabilityIndexed(CapabilityInput cap, Uint index, Bool enabled);
|
|
Bool IsCapabilityEnabledIndexed(CapabilityInput cap, Uint index) const;
|
|
|
|
// Blending
|
|
void SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha);
|
|
void GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
|
|
BlendFactor& dstAlpha) const;
|
|
void SetBlendFuncIndexed(Uint index, BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha,
|
|
BlendFactor dstAlpha);
|
|
void GetBlendFuncIndexed(Uint index, BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
|
|
BlendFactor& dstAlpha) const;
|
|
void SetBlendEquation(BlendEquation color, BlendEquation alpha);
|
|
void GetBlendEquation(BlendEquation& color, BlendEquation& alpha) const;
|
|
void SetBlendEquationIndexed(Uint index, BlendEquation color, BlendEquation alpha);
|
|
void GetBlendEquationIndexed(Uint index, BlendEquation& color, BlendEquation& alpha) const;
|
|
void SetLogicOp(LogicOperation logicOp);
|
|
LogicOperation GetLogicOp() const;
|
|
|
|
// Depth
|
|
void SetDepthFunc(DepthTestFunc func);
|
|
DepthTestFunc GetDepthFunc() const;
|
|
void SetDepthMask(Bool flag);
|
|
Bool GetDepthMask() const;
|
|
void SetStencilFunc(StencilFace face, DepthTestFunc func, Int ref, Uint32 mask);
|
|
void SetStencilMask(StencilFace face, Uint32 mask);
|
|
void SetStencilOp(StencilFace face, StencilOperation fail, StencilOperation depthFail,
|
|
StencilOperation depthPass);
|
|
const StencilFaceState& GetStencilState(StencilFace face) const;
|
|
|
|
// Color Mask. SetColorMask broadcasts to every draw buffer and GetColorMask returns
|
|
// draw buffer 0; the indexed forms address a single draw buffer (glColorMaski). The
|
|
// caller is responsible for validating index against MAX_DRAW_BUFFERS.
|
|
void SetColorMask(BoolVec4 mask);
|
|
BoolVec4 GetColorMask() const;
|
|
void SetColorMaskIndexed(Uint index, BoolVec4 mask);
|
|
BoolVec4 GetColorMaskIndexed(Uint index) const;
|
|
|
|
// Clear State
|
|
void SetClearColor(FloatVec4 color);
|
|
const FloatVec4& GetClearColor() const;
|
|
void SetClearDepth(Float depth);
|
|
Float GetClearDepth() const;
|
|
void SetClearStencil(Int stencil);
|
|
Uint32 GetClearStencil() const;
|
|
void SetBlendColor(FloatVec4 color);
|
|
const FloatVec4& GetBlendColor() const;
|
|
// glDepthRange(f) writes every viewport's range (ARB_viewport_array); the indexed
|
|
// pair is glDepthRangeIndexed / glDepthRangeArrayv. GetDepthRange answers index 0.
|
|
void SetDepthRange(FloatVec2 range);
|
|
const FloatVec2& GetDepthRange() const;
|
|
void SetDepthRangeIndexed(Uint index, FloatVec2 range);
|
|
const FloatVec2& GetDepthRangeIndexed(Uint index) const;
|
|
void SetSampleCoverage(Float value, Bool invert);
|
|
Float GetSampleCoverageValue() const;
|
|
Bool GetSampleCoverageInvert() const;
|
|
void SetSampleMaskValue(Uint32 mask);
|
|
Uint32 GetSampleMaskValue() const;
|
|
// glMinSampleShading. `value` is stored as given; the entry point clamps.
|
|
void SetMinSampleShadingValue(Float value);
|
|
Float GetMinSampleShadingValue() const;
|
|
|
|
// Pixel Store
|
|
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
|
Int GetPixelStoreParam(PixelStoreParam param) const;
|
|
PixelStoreParameters GetPixelStoreParameters(Bool isUnpack) const;
|
|
|
|
// Cull Face
|
|
void SetCullFaceMode(CullFaceMode mode);
|
|
CullFaceMode GetCullFaceMode() const;
|
|
void SetFrontFaceMode(FrontFaceMode mode);
|
|
FrontFaceMode GetFrontFaceMode() const;
|
|
void SetProvokingVertexMode(ProvokingVertexMode mode);
|
|
ProvokingVertexMode GetProvokingVertexMode() const;
|
|
|
|
// Scissor. glScissor writes every rectangle (ARB_viewport_array); GetScissorBox
|
|
// answers for index 0.
|
|
void SetScissorBox(IntVec4 box); // x, y, width, height
|
|
const IntVec4& GetScissorBox() const; // x, y, width, height
|
|
void SetScissorBoxIndexed(Uint index, IntVec4 box);
|
|
const IntVec4& GetScissorBoxIndexed(Uint index) const;
|
|
|
|
private:
|
|
// Bump both: any state change invalidates the draw snapshot, and this one also
|
|
// changes the VkPipeline (or its DirectGLES equivalent).
|
|
void BumpVersions() {
|
|
++m_version;
|
|
++m_pipelineStateVersion;
|
|
}
|
|
|
|
Uint16 m_version = 0;
|
|
// Only the subset of render state that a backend bakes INTO a pipeline object.
|
|
// Viewport, scissor, depth range, blend colour, line width, polygon offset, stencil
|
|
// write mask, the clear values, hints and the point-size family are all either
|
|
// dynamic pipeline state or not pipeline state at all, so changing one of them must
|
|
// not evict a cached pipeline. Keeping one counter for both made a glViewport call
|
|
// knock the next draw off the pipeline memo AND the draw fast path.
|
|
Uint16 m_pipelineStateVersion = 0;
|
|
// Value-initialised, PADDING INCLUDED. The MGPipe CSO key is a byte-range
|
|
// hash over RenderStateParameters and the residual block's trip wire is a
|
|
// byte-level compare, so indeterminate padding would make a CSO handle
|
|
// reproducible only within one context and would make the trip wire
|
|
// meaningless. Costs one .text resize of this constructor.
|
|
RenderStateParameters m_parameters{};
|
|
|
|
// Pixel Store
|
|
PixelStoreParameters m_pixelStorePackParameters{};
|
|
PixelStoreParameters m_pixelStoreUnpackParameters{};
|
|
};
|
|
} // namespace GLState
|
|
} // namespace MG_State
|
|
} // namespace MobileGL
|