diff --git a/MobileGL/MG_Pipe/MGPipeTypes.h b/MobileGL/MG_Pipe/MGPipeTypes.h index 7d8f73d4..710a90c8 100644 --- a/MobileGL/MG_Pipe/MGPipeTypes.h +++ b/MobileGL/MG_Pipe/MGPipeTypes.h @@ -22,19 +22,19 @@ // these structs (generated/PipeWire.inc) are memcpy'd; a field silently changing width is a // protocol break that no test would otherwise see. // -// P0.5 DEBT, recorded here so it is impossible to miss: two payloads reach into headers -// this directory is eventually forbidden to see - MGPCaps embeds MG_Backend's -// DynamicBackendParameters, and ResidualValueBlock embeds MG_State's RenderStateParameters -// and PixelStoreParameters. Both are deliberate: the caps block IS that struct (section -// 4.4.1) and the residual block is the migration carrier for Track V (section 6.3). P0.5 -// extracts MGPipeValueTypes.h and both includes below go away; until then purity gate A -// (section 10.3) cannot be armed for this header. +// P0.5 DEBT, half repaid. The MG_State half is gone: ResidualValueBlock's +// RenderStateParameters and PixelStoreParameters now come from MGPipeValueTypes.h, so +// this header no longer reaches into MG_State. What remains is MGPCaps embedding +// MG_Backend's DynamicBackendParameters - deliberate, the caps block IS that struct +// (section 4.4.1) - and that one include is what still keeps purity gate A (section +// 10.3) off this header; the gate asserts MGPipeValueTypes.h instead. The caps block +// needs fixed-width members before it can move (a type change, not a move): P1/P7. #include -#include +#include "MGPipeValueTypes.h" namespace MobileGL::MG_Pipe { using MG_Backend::DynamicBackendParameters; - // Both live directly in namespace MobileGL today; P0.5 moves them into + // Both live directly in namespace MobileGL and, since P0.5, are declared in // MG_Pipe/MGPipeValueTypes.h. using MobileGL::PixelStoreParameters; using MobileGL::RenderStateParameters; diff --git a/MobileGL/MG_Pipe/MGPipeValueTypes.h b/MobileGL/MG_Pipe/MGPipeValueTypes.h new file mode 100644 index 00000000..2b44d1e7 --- /dev/null +++ b/MobileGL/MG_Pipe/MGPipeValueTypes.h @@ -0,0 +1,549 @@ +// MobileGL - MobileGL/MG_Pipe/MGPipeValueTypes.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 +#ifndef MOBILEGL_MG_PIPE_VALUE_TYPES_H // belt and braces: this file is reachable both as +#define MOBILEGL_MG_PIPE_VALUE_TYPES_H // and <...> (CMakeLists.txt:531,535) +#include +#include // includes only + +#include // offsetof +#include + +// The value types MG_Pipe payloads embed (plan B section 6.3; ARCHITECTURE.md section on +// the value header): the render-state, pixel-store, sampler and vertex-attribute value +// structs and the enums they are made of. They lived in MG_State::GLState until P0.5; +// the MG_State headers that used to define them now include this file, so every existing +// spelling (namespace and name) compiles unchanged. +// +// PURITY: nothing from MG_State, MG_Impl, MG_Backend or MG_Remote - +// scripts/check_include_closure.py probe "value-header" (ROADMAP P0.5; ARCHITECTURE.md +// section 10.3 gate A). Adding one turns CI red. MG_Pipe never includes MG_State back. + +namespace MobileGL { + // GL_MAX_DRAW_BUFFERS as MobileGL advertises it. FramebufferObject::MAX_DRAW_BUFFERS is + // defined from this constant, so the two cannot drift. + inline constexpr Uint kMGMaxDrawBuffers = 8; + + enum class BlendFactor { + Zero, + One, + SrcColor, + OneMinusSrcColor, + DstColor, + OneMinusDstColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha, + ConstantColor, + OneMinusConstantColor, + ConstantAlpha, + OneMinusConstantAlpha, + // Dual-source blend factors (GL_SRC1_*, glBindFragDataLocationIndexed); require the + // dualSrcBlend device feature. + Src1Color, + OneMinusSrc1Color, + Src1Alpha, + OneMinusSrc1Alpha, + BlendFactorCount, + Unknown = -1 + }; + + enum class BlendEquation { + Add, + Subtract, + ReverseSubtract, + Min, + Max, + BlendEquationCount, + Unknown = -1 + }; + + enum class LogicOperation { + Clear, + And, + AndReverse, + Copy, + AndInverted, + Noop, + Xor, + Or, + Nor, + Equiv, + Invert, + OrReverse, + CopyInverted, + OrInverted, + Nand, + Set, + LogicOperationCount, + Unknown = -1 + }; + + enum class DepthTestFunc { + Never, + Less, + Equal, + LessEqual, + Greater, + NotEqual, + GreaterEqual, + Always, + DepthTestFuncCount, + Unknown = -1 + }; + + enum class StencilOperation { + Keep, + Zero, + Replace, + IncrementClamp, + DecrementClamp, + Invert, + IncrementWrap, + DecrementWrap, + StencilOperationCount, + Unknown = -1 + }; + + enum class StencilFace { + Front, + Back, + StencilFaceCount, + Unknown = -1 + }; + + enum class PixelStoreParam { + // Pack Parameters + PackAlignment, + PackRowLength, + PackImageHeight, + PackSkipRows, + PackSkipPixels, + PackSkipImages, + PackSwapBytes, + PackLSBFirst, + + // Unpack Parameters + UnpackAlignment, + UnpackRowLength, + UnpackImageHeight, + UnpackSkipRows, + UnpackSkipPixels, + UnpackSkipImages, + UnpackSwapBytes, + UnpackLSBFirst, + + PixelStoreParamCount, + Unknown = -1 + }; + + enum class CullFaceMode { + Front, + Back, + FrontAndBack, + CullFaceModeCount, + Unknown = -1 + }; + + enum class FrontFaceMode { + CounterClockwise, + Clockwise, + FrontFaceModeCount, + Unknown = -1 + }; + + enum class ProvokingVertexMode { + FirstVertex, + LastVertex, + ProvokingVertexModeCount, + Unknown = -1 + }; + + enum class CapabilityInput { + Blend, + ClipDistance0, + ClipDistance1, + ClipDistance2, + ClipDistance3, + ClipDistance4, + ClipDistance5, + ClipDistance6, + ClipDistance7, + ColorLogicOp, + CullFace, + DebugOutput, + DebugOutputSynchronous, + DepthClamp, + DepthTest, + Dither, + FramebufferSrgb, + LineSmooth, + Multisample, + PolygonOffsetFill, + PolygonOffsetLine, + PolygonOffsetPoint, + PolygonSmooth, + PrimitiveRestart, + PrimitiveRestartFixedIndex, + RasterizerDiscard, + SampleAlphaToCoverage, + SampleAlphaToOne, + SampleCoverage, + SampleShading, + SampleMask, + ScissorTest, + StencilTest, + TextureCubeMapSeamless, + ProgramPointSize, + CapabilityInputCount, + Unknown = -1 + }; + + struct PixelStoreParameters { + Bool SwapBytes = false; + Bool LSBFirst = false; + Int RowLength = 0; + Int ImageHeight = 0; + Int SkipPixels = 0; + Int SkipRows = 0; + Int SkipImages = 0; + Int Alignment = 4; + }; + + struct PerBufferBlendState { + Bool Enabled = false; + BlendFactor SrcFactorRGB = BlendFactor::One; + BlendFactor DstFactorRGB = BlendFactor::Zero; + BlendFactor SrcFactorAlpha = BlendFactor::One; + BlendFactor DstFactorAlpha = BlendFactor::Zero; + BlendEquation ColorEquation = BlendEquation::Add; + BlendEquation AlphaEquation = BlendEquation::Add; + }; + + struct StencilFaceState { + DepthTestFunc Func = DepthTestFunc::Always; + Int Ref = 0; + Uint32 ValueMask = 0xffffffffu; + Uint32 WriteMask = 0xffffffffu; + StencilOperation FailOp = StencilOperation::Keep; + StencilOperation PassDepthFailOp = StencilOperation::Keep; + StencilOperation PassDepthPassOp = StencilOperation::Keep; + }; + + struct RenderStateParameters { + // ARB_viewport_array / GL 4.6 core 13.6.1: the viewport, the scissor rectangle, the depth + // range and the scissor-test enable are all arrays indexed by gl_ViewportIndex, and the + // spec floor for MAX_VIEWPORTS is 16. MobileGL advertises exactly 16 on both backends, so + // this is also what GL_MAX_VIEWPORTS reports (see the backend loaders' caps.MaxViewports). + static constexpr Uint MAX_VIEWPORTS = 16; + + // Rasterization + // The viewport rectangle is FLOAT state as of GL 4.1 - ViewportIndexedf writes fractional + // values and GetFloati_v(GL_VIEWPORT) must hand them back bit-exact + // (KHR-GL43.viewport_array.viewport_api compares with ==, no tolerance). glViewport's + // integers are simply one way to write it. Index 0 is what a program that never assigns + // gl_ViewportIndex rasterizes against, and what the classic glViewport / + // glGetIntegerv(GL_VIEWPORT) pair addresses. Both backends rasterize the rectangle + // rounded back to integers; the STATE stays exact, which is the half the conformance + // suite checks (see the KNOWN INFIDELITY note in AdvertisedLimitsScenario.cpp). + Array Viewports{}; // x, y, width, height + Float LineWidth = 1.0f; + Float PointSize = 1.0f; + // GL_PATCH_VERTICES: how many vertices one tessellation patch consumes. + Uint PatchVertices = 3; + // GL_PATCH_DEFAULT_OUTER_LEVEL / GL_PATCH_DEFAULT_INNER_LEVEL (glPatchParameterfv). The + // tessellation levels used when a program has an evaluation stage and NO control stage - + // GL's fixed-function pass-through (4.6 core 11.2.2). Both backends have to synthesize + // that stage, and they bake these numbers into it, so a change here makes an already-built + // one stale exactly as PATCH_VERTICES does. Default 1.0, per table 23.44. + FloatVec4 PatchDefaultOuterLevel = FloatVec4(1.0f, 1.0f, 1.0f, 1.0f); + FloatVec2 PatchDefaultInnerLevel = FloatVec2(1.0f, 1.0f); + Float PolygonOffsetFactor = 0.0f; + Float PolygonOffsetUnits = 0.0f; + // GL_POLYGON_OFFSET_CLAMP (GL 4.6 core 14.6.5 / GL_EXT_polygon_offset_clamp): the maximum + // magnitude of the offset glPolygonOffsetClamp's third argument allows. Zero - the default + // - means "no clamp", which is exactly the behaviour glPolygonOffset leaves behind. + Float PolygonOffsetClamp = 0.0f; + + // glClipControl (GL 4.5 core 13.5). Defaults per table 23.7 are the pre-4.5 fixed + // behaviour: origin at the lower left, depth mapped from -1..1. + GLenum ClipOrigin = GL_LOWER_LEFT; + GLenum ClipDepthMode = GL_NEGATIVE_ONE_TO_ONE; + + // Blending + Array BlendStates; + LogicOperation LogicOp = LogicOperation::Copy; + + // Depth + Bool DepthTestEnabled = false; + DepthTestFunc DepthFunc = DepthTestFunc::Less; + Bool DepthMask = true; + + // Color Mask. Per-draw-buffer state (glColorMaski); glColorMask broadcasts to all buffers. + // Every entry is initialized to all-true in RenderState's constructor. + Array ColorMasks; + + // Clear State + FloatVec4 ClearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f); + Float ClearDepth = 1.0f; + Uint32 ClearStencil = 0; + FloatVec4 BlendColor = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f); + // Per-viewport depth range (glDepthRangeIndexed / glDepthRangeArrayv). Every entry is + // initialized to (0, 1) in RenderState's constructor - a default member initializer would + // not survive the Array<> aggregate. Kept float rather than double: DepthRangeArrayv takes + // GLdouble, but the value reaches the hardware as VkViewport::minDepth/maxDepth (float) on + // Magma and glDepthRangef on Espryt, so a double store would only widen the readback and + // then lose it again at the same place. + Array DepthRanges{}; + Float SampleCoverageValue = 1.0f; + Bool SampleCoverageInvert = false; + Uint32 SampleMaskValue = 0xffffffffu; + // glMinSampleShading (ARB_sample_shading / GL 4.0 core 14.3.1). The fraction of samples + // that get their own independent shading when GL_SAMPLE_SHADING is enabled; the initial + // value is 0, and the value is clamped to [0, 1] on the way in. + Float MinSampleShadingValue = 0.0f; + Array StencilStates{}; + + // Cull Face + Bool CullFaceEnabled = false; + CullFaceMode CullFaceModeSetting = CullFaceMode::Back; + FrontFaceMode FrontFaceModeSetting = FrontFaceMode::CounterClockwise; + ProvokingVertexMode ProvokingVertexModeSetting = ProvokingVertexMode::LastVertex; + + // Hints (glHint). All GL 3.3 core hint targets default to GL_DONT_CARE. + GLenum LineSmoothHint = GL_DONT_CARE; + GLenum PolygonSmoothHint = GL_DONT_CARE; + GLenum TextureCompressionHint = GL_DONT_CARE; + GLenum FragmentShaderDerivativeHint = GL_DONT_CARE; + + // Point parameters (glPointParameter). Only the two GL 3.3 core pnames. + Float PointFadeThresholdSize = 1.0f; + GLenum PointSpriteCoordOrigin = GL_UPPER_LEFT; + + // Color clamping (glClampColor). Core profile exposes only GL_CLAMP_READ_COLOR. + GLenum ClampReadColor = GL_FIXED_ONLY; + + // Polygon rasterization mode (glPolygonMode). Core profile sets front and back together, + // but GL_POLYGON_MODE still reports both slots, so keep them separate for a faithful query. + GLenum PolygonModeFront = GL_FILL; + GLenum PolygonModeBack = GL_FILL; + + // Primitive restart index (glPrimitiveRestartIndex); consumed when GL_PRIMITIVE_RESTART is + // enabled during an indexed draw. Default 0. + Uint32 PrimitiveRestartIndex = 0; + + // Scissor + Bool ColorLogicOpEnabled = false; + Bool DebugOutputEnabled = false; + Bool DebugOutputSynchronousEnabled = false; + Bool DitherEnabled = true; + Bool LineSmoothEnabled = false; + Bool MultisampleEnabled = true; + Bool PolygonOffsetFillEnabled = false; + Bool PolygonOffsetLineEnabled = false; + Bool PolygonOffsetPointEnabled = false; + Bool PolygonSmoothEnabled = false; + Bool PrimitiveRestartEnabled = false; + Bool PrimitiveRestartFixedIndexEnabled = false; + Bool RasterizerDiscardEnabled = false; + Bool SampleAlphaToCoverageEnabled = false; + Bool SampleAlphaToOneEnabled = false; + Bool SampleCoverageEnabled = false; + Bool SampleMaskEnabled = false; + Bool SampleShadingEnabled = false; + Bool StencilTestEnabled = false; + Bool ProgramPointSizeEnabled = false; + // glEnable(GL_SCISSOR_TEST) enables the test for EVERY viewport, glEnablei for one + // (GL 4.6 core 17.3.2), so this is 16 bits and not a bool. Bit 0 is what the classic + // glIsEnabled(GL_SCISSOR_TEST) reports and what both backends currently consume. Unlike + // ClipDistanceEnabledMask below it DOES bump the pipeline version, because DirectGLES + // turns it into a real glEnable/glDisable. + Uint32 ScissorTestEnabledMask = 0; + Array ScissorBoxes{}; // x, y, width, height + // One bit per viewport, set the first time the application writes that index's scissor + // rectangle - glScissor broadcasts and sets all 16, glScissorIndexed/glScissorArrayv set + // the indices they name. It exists because the RECTANGLE cannot answer "has the + // application spoken?": ScissorBoxes starts all-zero (its spec initial value is the size + // of a window the frontend does not know yet, see the RenderState constructor), and + // glScissor(0, 0, 0, 0) is a legal GL state meaning "the scissor test rejects every + // fragment". A backend that reads an empty rectangle as the never-written sentinel + // therefore INVERTS that request into "accept every fragment"; DirectGLES did exactly + // that and KHR-GL43.viewport_array.scissor_zero_dimension caught it. Deliberately beside + // ScissorBoxes so it shares their tail span (after LogicOp) and DirectGLES' span memcmp + // picks a transition up like any other state. + Uint32 ScissorBoxWrittenMask = 0; + // glEnable(GL_CLIP_DISTANCE0 + i) for i in [0, 8), one bit each. A bitmask rather than + // eight bools because every consumer wants the set, not an individual flag, and because + // the SYNC_CAPABILITY/SET_CAPABILITY macros key off a "Enabled" field name that + // eight numbered capabilities cannot share. Lives in the tail span (after LogicOp), so + // DirectGLES' span memcmp picks a change up like any other capability. + Uint32 ClipDistanceEnabledMask = 0; + }; + + enum class SamplerFilterMode { + Nearest, + Linear, + SamplerFilterCount, + Unknown = -1 + }; + + enum class SamplerMipmapMode { + None, + Nearest, + Linear, + SamplerMipmapModeCount, + Unknown = -1 + }; + + enum class SamplerWrapMode { + ClampToEdge, + MirroredRepeat, + Repeat, + ClampToBorder, + MirrorClampToEdge, + SamplerWrapModeCount, + Unknown = -1 + }; + + enum class SamplerCompareMode { + None, + CompareToTexture, + SamplerCompareModeCount, + Unknown = -1 + }; + + enum class SamplerCompareFunc { + Never, + Less, + Equal, + LessEqual, + Greater, + NotEqual, + GreaterEqual, + Always, + SamplerCompareFuncCount, + Unknown = -1 + }; + + // Which of the three GL_TEXTURE_BORDER_COLOR entry-point families last wrote the border colour, + // and therefore which of the three stored representations is AUTHORITATIVE. GL 4.6 core 8.10: + // TexParameterIiv/Iuiv store an integer border colour "unmodified, with an internal data type of + // integer", TexParameterfv stores a floating-point one, and the derived forms are only a + // convenience for a getter of the other spelling. A backend cannot pick the right driver entry + // point (glSamplerParameterIiv vs fv) or the right VkBorderColor family without this: numerically + // the three representations are always populated, so the value alone says nothing about the form. + enum class BorderColorForm : Uint8 { + Float, + Int, + Uint + }; + + struct SamplerParameters { + SamplerWrapMode wrapS = SamplerWrapMode::Repeat; + SamplerWrapMode wrapT = SamplerWrapMode::Repeat; + SamplerWrapMode wrapR = SamplerWrapMode::Repeat; + SamplerFilterMode minFilter = SamplerFilterMode::Nearest; + SamplerFilterMode magFilter = SamplerFilterMode::Linear; + SamplerMipmapMode mipmapMode = SamplerMipmapMode::Linear; + Float minLod = -1000.0f; + Float maxLod = 1000.0f; + Float lodBias = 0.0f; + Float maxAnisotropy = 1.0f; + // GL 4.6 core table 23.18 / GLES 3.2 table 21.16: TEXTURE_COMPARE_FUNC starts at LEQUAL, + // for both sampler objects and the sampler state a texture object carries. + SamplerCompareFunc compareFunc = SamplerCompareFunc::LessEqual; + SamplerCompareMode compareMode = SamplerCompareMode::None; + // TEXTURE_BORDER_COLOR is sampler state (GL 4.6 core table 23.18), so it belongs here and + // not on the texture - a texture object reaches it through the sampler object it owns. The + // three representations are the float, integer and unsigned-integer forms glSamplerParameterfv, + // glSamplerParameterIiv and glSamplerParameterIuiv set; whichever is written last defines + // the colour and the other two follow it, so a getter always has an answer. + FloatVec4 borderColor = {0.0f, 0.0f, 0.0f, 0.0f}; + IntVec4 borderColorI = {0, 0, 0, 0}; + UintVec4 borderColorUI = {0, 0, 0, 0}; + BorderColorForm borderColorForm = BorderColorForm::Float; + }; + + namespace MG_State::GLState { + class BufferObject; + + struct VertexAttribute { + Bool Enabled = false; + int Size = 4; + DataType Type = DataType::Float32; + Bool Normalized = false; + // The RESOLVED byte distance between consecutive elements, never the raw + // glVertexAttrib*Pointer argument: a pointer call's stride 0 means "tightly + // packed" and is resolved to the element size here, so a zero that survives + // into this field can only have come from the binding model, where a zero + // VERTEX_BINDING_STRIDE means the opposite - every vertex reads the SAME + // element and the fetch address never advances (GL 4.6 core 10.3.1). Backends + // consume this verbatim; collapsing 0 back into the element size is what made + // KHR-GL43.vertex_attrib_binding.basic-input-case7/8 read past the buffer. + int Stride = 0; + SizeT Offset = 0; + Bool IsInteger = false; + // GL_BGRA vertex size: four components in reversed (B,G,R,A) memory order. Size stays 4. + // Set only by the long (L) format entry points. It is NOT implied by + // Type == Float64: VertexAttribFormat(GL_DOUBLE) also reads doubles from memory but + // asks for them *converted to float*, while VertexAttribLFormat keeps all 64 bits + // (GL 4.6 core 10.3.2). Backends have to tell the two apart, and it is what + // GL_VERTEX_ATTRIB_ARRAY_LONG reports. + Bool IsLong = false; + Bool IsBgra = false; + Uint Divisor = 0; + SharedPtr Buffer; + + // GL 4.6 core table 23.3: VERTEX_ATTRIB_ARRAY_STRIDE and _POINTER are the + // arguments of the last glVertexAttrib*Pointer call on this attribute, + // reported verbatim, and NOTHING else writes them - not glVertexAttribFormat, + // not glBindVertexBuffer. Stride/Offset above are the *resolved* draw inputs + // and the binding model does overwrite those, so the two views have to be + // stored apart or the binding-model sequence reports a legacy state it never + // set (KHR-GL4x.vertex_attrib_binding.basic-state3). + int LegacyStride = 0; + SizeT LegacyPointer = 0; + }; + + // ARB_vertex_attrib_binding separate binding point. Attributes configured through the + // binding-point API are resolved eagerly into the flat VertexAttribute view above, so + // backends keep consuming resolved attributes and never see binding points. + struct VertexBufferBindingPoint { + SharedPtr Buffer; + SizeT Offset = 0; + // GL 4.6 core table 23.4: the initial VERTEX_BINDING_STRIDE is 16, not 0. + int Stride = 16; + Uint Divisor = 0; + }; + + struct VertexAttributeVersion { + Uint16 FormatVersion = 0; + Uint16 BufferVersion = 0; + Uint16 SwitchVersion = 0; + }; + } // namespace MG_State::GLState + + // ---- trip wires (P0.5). Sizes are what every ABI MobileGL ships on produces: every + // member is a fixed-width scalar, an enum of one, or an array of those - no pointer, no + // SizeT - except the vertex types, which carry SharedPtr by design and are + // therefore not trivially copyable (MGPipeTypes.h carries them as a blob). + static_assert(std::is_trivially_copyable_v && sizeof(PixelStoreParameters) == 28); + static_assert(std::is_trivially_copyable_v && sizeof(PerBufferBlendState) == 28); + static_assert(std::is_trivially_copyable_v && sizeof(StencilFaceState) == 28); + static_assert(std::is_trivially_copyable_v); + static_assert(std::is_standard_layout_v); // offsetof legality + static_assert(sizeof(RenderStateParameters) == 1168, + "RenderStateParameters changed size; MGL_RESIDUAL_BLOCK_SIZE and the Espryt spans depend on it"); + static_assert(offsetof(RenderStateParameters, BlendStates) < offsetof(RenderStateParameters, LogicOp)); + static_assert(std::tuple_size_v == kMGMaxDrawBuffers); + static_assert(std::is_trivially_copyable_v && sizeof(SamplerParameters) == 100); + static_assert(std::is_trivially_copyable_v && + sizeof(MG_State::GLState::VertexAttributeVersion) == 6); +} // namespace MobileGL +#endif // MOBILEGL_MG_PIPE_VALUE_TYPES_H diff --git a/MobileGL/MG_Pipe/generated/PipeVerify.inc b/MobileGL/MG_Pipe/generated/PipeVerify.inc index db652e6e..e6f676ec 100644 --- a/MobileGL/MG_Pipe/generated/PipeVerify.inc +++ b/MobileGL/MG_Pipe/generated/PipeVerify.inc @@ -230,11 +230,11 @@ inline Bool MGPipeFieldEqual(const T& a, const T& b) { } else if constexpr (requires(const T& x, const T& y) { x == y; }) { return a == b; } else { - // MEMCMP FALLBACK. Only reached by the payload members that are still MG_State / - // MG_Backend value structs (RenderStateParameters, PixelStoreParameters, - // DynamicBackendParameters) and by MGHostSpan. Those are exactly the types P0.5 - // moves into MGPipeValueTypes.h, at which point they get field lists of their own - // and this branch stops being reachable from any payload. + // MEMCMP FALLBACK. Only reached by the payload members that are still value structs + // without a field list (RenderStateParameters, PixelStoreParameters, + // DynamicBackendParameters) and by MGHostSpan. P0.5 moved the first two into + // MGPipeValueTypes.h; P1 gives them field lists of their own, at which point this + // branch stops being reachable from any payload. return std::memcmp(&a, &b, sizeof(T)) == 0; } } diff --git a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h index aebe2162..8e3bf156 100644 --- a/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h +++ b/MobileGL/MG_State/GLState/FramebufferState/FramebufferObject.h @@ -11,6 +11,7 @@ #include #include #include +#include namespace MobileGL { enum class FramebufferTarget { @@ -103,7 +104,7 @@ namespace MobileGL { class FramebufferObject { public: - static constexpr Uint MAX_DRAW_BUFFERS = 8; + static constexpr Uint MAX_DRAW_BUFFERS = MobileGL::kMGMaxDrawBuffers; using TargetEnum = FramebufferTarget; using FramebufferAttachmentObjectArray = diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp index 8945b5b6..475b31c3 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.cpp +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "RenderState.h" +#include #include "MG_Util/Debug/Log.h" #include "MG_Util/Types.h" diff --git a/MobileGL/MG_State/GLState/RenderState/RenderState.h b/MobileGL/MG_State/GLState/RenderState/RenderState.h index 6e301db3..654126ec 100644 --- a/MobileGL/MG_State/GLState/RenderState/RenderState.h +++ b/MobileGL/MG_State/GLState/RenderState/RenderState.h @@ -8,367 +8,9 @@ #pragma once #include -#include -#include +#include namespace MobileGL { - enum class BlendFactor { - Zero, - One, - SrcColor, - OneMinusSrcColor, - DstColor, - OneMinusDstColor, - SrcAlpha, - OneMinusSrcAlpha, - DstAlpha, - OneMinusDstAlpha, - ConstantColor, - OneMinusConstantColor, - ConstantAlpha, - OneMinusConstantAlpha, - // Dual-source blend factors (GL_SRC1_*, glBindFragDataLocationIndexed); require the - // dualSrcBlend device feature. - Src1Color, - OneMinusSrc1Color, - Src1Alpha, - OneMinusSrc1Alpha, - BlendFactorCount, - Unknown = -1 - }; - - enum class BlendEquation { - Add, - Subtract, - ReverseSubtract, - Min, - Max, - BlendEquationCount, - Unknown = -1 - }; - - enum class LogicOperation { - Clear, - And, - AndReverse, - Copy, - AndInverted, - Noop, - Xor, - Or, - Nor, - Equiv, - Invert, - OrReverse, - CopyInverted, - OrInverted, - Nand, - Set, - LogicOperationCount, - Unknown = -1 - }; - - enum class DepthTestFunc { - Never, - Less, - Equal, - LessEqual, - Greater, - NotEqual, - GreaterEqual, - Always, - DepthTestFuncCount, - Unknown = -1 - }; - - enum class StencilOperation { - Keep, - Zero, - Replace, - IncrementClamp, - DecrementClamp, - Invert, - IncrementWrap, - DecrementWrap, - StencilOperationCount, - Unknown = -1 - }; - - enum class StencilFace { - Front, - Back, - StencilFaceCount, - Unknown = -1 - }; - - enum class PixelStoreParam { - // Pack Parameters - PackAlignment, - PackRowLength, - PackImageHeight, - PackSkipRows, - PackSkipPixels, - PackSkipImages, - PackSwapBytes, - PackLSBFirst, - - // Unpack Parameters - UnpackAlignment, - UnpackRowLength, - UnpackImageHeight, - UnpackSkipRows, - UnpackSkipPixels, - UnpackSkipImages, - UnpackSwapBytes, - UnpackLSBFirst, - - PixelStoreParamCount, - Unknown = -1 - }; - - enum class CullFaceMode { - Front, - Back, - FrontAndBack, - CullFaceModeCount, - Unknown = -1 - }; - - enum class FrontFaceMode { - CounterClockwise, - Clockwise, - FrontFaceModeCount, - Unknown = -1 - }; - - enum class ProvokingVertexMode { - FirstVertex, - LastVertex, - ProvokingVertexModeCount, - Unknown = -1 - }; - - enum class CapabilityInput { - Blend, - ClipDistance0, - ClipDistance1, - ClipDistance2, - ClipDistance3, - ClipDistance4, - ClipDistance5, - ClipDistance6, - ClipDistance7, - ColorLogicOp, - CullFace, - DebugOutput, - DebugOutputSynchronous, - DepthClamp, - DepthTest, - Dither, - FramebufferSrgb, - LineSmooth, - Multisample, - PolygonOffsetFill, - PolygonOffsetLine, - PolygonOffsetPoint, - PolygonSmooth, - PrimitiveRestart, - PrimitiveRestartFixedIndex, - RasterizerDiscard, - SampleAlphaToCoverage, - SampleAlphaToOne, - SampleCoverage, - SampleShading, - SampleMask, - ScissorTest, - StencilTest, - TextureCubeMapSeamless, - ProgramPointSize, - CapabilityInputCount, - Unknown = -1 - }; - - struct PixelStoreParameters { - Bool SwapBytes = false; - Bool LSBFirst = false; - Int RowLength = 0; - Int ImageHeight = 0; - Int SkipPixels = 0; - Int SkipRows = 0; - Int SkipImages = 0; - Int Alignment = 4; - }; - - struct PerBufferBlendState { - Bool Enabled = false; - BlendFactor SrcFactorRGB = BlendFactor::One; - BlendFactor DstFactorRGB = BlendFactor::Zero; - BlendFactor SrcFactorAlpha = BlendFactor::One; - BlendFactor DstFactorAlpha = BlendFactor::Zero; - BlendEquation ColorEquation = BlendEquation::Add; - BlendEquation AlphaEquation = BlendEquation::Add; - }; - - struct StencilFaceState { - DepthTestFunc Func = DepthTestFunc::Always; - Int Ref = 0; - Uint32 ValueMask = 0xffffffffu; - Uint32 WriteMask = 0xffffffffu; - StencilOperation FailOp = StencilOperation::Keep; - StencilOperation PassDepthFailOp = StencilOperation::Keep; - StencilOperation PassDepthPassOp = StencilOperation::Keep; - }; - - struct RenderStateParameters { - // ARB_viewport_array / GL 4.6 core 13.6.1: the viewport, the scissor rectangle, the depth - // range and the scissor-test enable are all arrays indexed by gl_ViewportIndex, and the - // spec floor for MAX_VIEWPORTS is 16. MobileGL advertises exactly 16 on both backends, so - // this is also what GL_MAX_VIEWPORTS reports (see the backend loaders' caps.MaxViewports). - static constexpr Uint MAX_VIEWPORTS = 16; - - // Rasterization - // The viewport rectangle is FLOAT state as of GL 4.1 - ViewportIndexedf writes fractional - // values and GetFloati_v(GL_VIEWPORT) must hand them back bit-exact - // (KHR-GL43.viewport_array.viewport_api compares with ==, no tolerance). glViewport's - // integers are simply one way to write it. Index 0 is what a program that never assigns - // gl_ViewportIndex rasterizes against, and what the classic glViewport / - // glGetIntegerv(GL_VIEWPORT) pair addresses. Both backends rasterize the rectangle - // rounded back to integers; the STATE stays exact, which is the half the conformance - // suite checks (see the KNOWN INFIDELITY note in AdvertisedLimitsScenario.cpp). - Array Viewports{}; // x, y, width, height - Float LineWidth = 1.0f; - Float PointSize = 1.0f; - // GL_PATCH_VERTICES: how many vertices one tessellation patch consumes. - Uint PatchVertices = 3; - // GL_PATCH_DEFAULT_OUTER_LEVEL / GL_PATCH_DEFAULT_INNER_LEVEL (glPatchParameterfv). The - // tessellation levels used when a program has an evaluation stage and NO control stage - - // GL's fixed-function pass-through (4.6 core 11.2.2). Both backends have to synthesize - // that stage, and they bake these numbers into it, so a change here makes an already-built - // one stale exactly as PATCH_VERTICES does. Default 1.0, per table 23.44. - FloatVec4 PatchDefaultOuterLevel = FloatVec4(1.0f, 1.0f, 1.0f, 1.0f); - FloatVec2 PatchDefaultInnerLevel = FloatVec2(1.0f, 1.0f); - Float PolygonOffsetFactor = 0.0f; - Float PolygonOffsetUnits = 0.0f; - // GL_POLYGON_OFFSET_CLAMP (GL 4.6 core 14.6.5 / GL_EXT_polygon_offset_clamp): the maximum - // magnitude of the offset glPolygonOffsetClamp's third argument allows. Zero - the default - // - means "no clamp", which is exactly the behaviour glPolygonOffset leaves behind. - Float PolygonOffsetClamp = 0.0f; - - // glClipControl (GL 4.5 core 13.5). Defaults per table 23.7 are the pre-4.5 fixed - // behaviour: origin at the lower left, depth mapped from -1..1. - GLenum ClipOrigin = GL_LOWER_LEFT; - GLenum ClipDepthMode = GL_NEGATIVE_ONE_TO_ONE; - - // Blending - Array BlendStates; - LogicOperation LogicOp = LogicOperation::Copy; - - // Depth - Bool DepthTestEnabled = false; - DepthTestFunc DepthFunc = DepthTestFunc::Less; - Bool DepthMask = true; - - // Color Mask. Per-draw-buffer state (glColorMaski); glColorMask broadcasts to all buffers. - // Every entry is initialized to all-true in RenderState's constructor. - Array ColorMasks; - - // Clear State - FloatVec4 ClearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f); - Float ClearDepth = 1.0f; - Uint32 ClearStencil = 0; - FloatVec4 BlendColor = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f); - // Per-viewport depth range (glDepthRangeIndexed / glDepthRangeArrayv). Every entry is - // initialized to (0, 1) in RenderState's constructor - a default member initializer would - // not survive the Array<> aggregate. Kept float rather than double: DepthRangeArrayv takes - // GLdouble, but the value reaches the hardware as VkViewport::minDepth/maxDepth (float) on - // Magma and glDepthRangef on Espryt, so a double store would only widen the readback and - // then lose it again at the same place. - Array DepthRanges{}; - Float SampleCoverageValue = 1.0f; - Bool SampleCoverageInvert = false; - Uint32 SampleMaskValue = 0xffffffffu; - // glMinSampleShading (ARB_sample_shading / GL 4.0 core 14.3.1). The fraction of samples - // that get their own independent shading when GL_SAMPLE_SHADING is enabled; the initial - // value is 0, and the value is clamped to [0, 1] on the way in. - Float MinSampleShadingValue = 0.0f; - Array StencilStates{}; - - // Cull Face - Bool CullFaceEnabled = false; - CullFaceMode CullFaceModeSetting = CullFaceMode::Back; - FrontFaceMode FrontFaceModeSetting = FrontFaceMode::CounterClockwise; - ProvokingVertexMode ProvokingVertexModeSetting = ProvokingVertexMode::LastVertex; - - // Hints (glHint). All GL 3.3 core hint targets default to GL_DONT_CARE. - GLenum LineSmoothHint = GL_DONT_CARE; - GLenum PolygonSmoothHint = GL_DONT_CARE; - GLenum TextureCompressionHint = GL_DONT_CARE; - GLenum FragmentShaderDerivativeHint = GL_DONT_CARE; - - // Point parameters (glPointParameter). Only the two GL 3.3 core pnames. - Float PointFadeThresholdSize = 1.0f; - GLenum PointSpriteCoordOrigin = GL_UPPER_LEFT; - - // Color clamping (glClampColor). Core profile exposes only GL_CLAMP_READ_COLOR. - GLenum ClampReadColor = GL_FIXED_ONLY; - - // Polygon rasterization mode (glPolygonMode). Core profile sets front and back together, - // but GL_POLYGON_MODE still reports both slots, so keep them separate for a faithful query. - GLenum PolygonModeFront = GL_FILL; - GLenum PolygonModeBack = GL_FILL; - - // Primitive restart index (glPrimitiveRestartIndex); consumed when GL_PRIMITIVE_RESTART is - // enabled during an indexed draw. Default 0. - Uint32 PrimitiveRestartIndex = 0; - - // Scissor - Bool ColorLogicOpEnabled = false; - Bool DebugOutputEnabled = false; - Bool DebugOutputSynchronousEnabled = false; - Bool DitherEnabled = true; - Bool LineSmoothEnabled = false; - Bool MultisampleEnabled = true; - Bool PolygonOffsetFillEnabled = false; - Bool PolygonOffsetLineEnabled = false; - Bool PolygonOffsetPointEnabled = false; - Bool PolygonSmoothEnabled = false; - Bool PrimitiveRestartEnabled = false; - Bool PrimitiveRestartFixedIndexEnabled = false; - Bool RasterizerDiscardEnabled = false; - Bool SampleAlphaToCoverageEnabled = false; - Bool SampleAlphaToOneEnabled = false; - Bool SampleCoverageEnabled = false; - Bool SampleMaskEnabled = false; - Bool SampleShadingEnabled = false; - Bool StencilTestEnabled = false; - Bool ProgramPointSizeEnabled = false; - // glEnable(GL_SCISSOR_TEST) enables the test for EVERY viewport, glEnablei for one - // (GL 4.6 core 17.3.2), so this is 16 bits and not a bool. Bit 0 is what the classic - // glIsEnabled(GL_SCISSOR_TEST) reports and what both backends currently consume. Unlike - // ClipDistanceEnabledMask below it DOES bump the pipeline version, because DirectGLES - // turns it into a real glEnable/glDisable. - Uint32 ScissorTestEnabledMask = 0; - Array ScissorBoxes{}; // x, y, width, height - // One bit per viewport, set the first time the application writes that index's scissor - // rectangle - glScissor broadcasts and sets all 16, glScissorIndexed/glScissorArrayv set - // the indices they name. It exists because the RECTANGLE cannot answer "has the - // application spoken?": ScissorBoxes starts all-zero (its spec initial value is the size - // of a window the frontend does not know yet, see the RenderState constructor), and - // glScissor(0, 0, 0, 0) is a legal GL state meaning "the scissor test rejects every - // fragment". A backend that reads an empty rectangle as the never-written sentinel - // therefore INVERTS that request into "accept every fragment"; DirectGLES did exactly - // that and KHR-GL43.viewport_array.scissor_zero_dimension caught it. Deliberately beside - // ScissorBoxes so it shares their tail span (after LogicOp) and DirectGLES' span memcmp - // picks a transition up like any other state. - Uint32 ScissorBoxWrittenMask = 0; - // glEnable(GL_CLIP_DISTANCE0 + i) for i in [0, 8), one bit each. A bitmask rather than - // eight bools because every consumer wants the set, not an individual flag, and because - // the SYNC_CAPABILITY/SET_CAPABILITY macros key off a "Enabled" field name that - // eight numbered capabilities cannot share. Lives in the tail span (after LogicOp), so - // DirectGLES' span memcmp picks a change up like any other capability. - Uint32 ClipDistanceEnabledMask = 0; - }; - namespace MG_State { namespace GLState { class RenderState { diff --git a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h index 9f97b2d6..90b4619c 100644 --- a/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h +++ b/MobileGL/MG_State/GLState/SamplerState/SamplerObject.h @@ -8,93 +8,9 @@ #pragma once #include -#include +#include namespace MobileGL { - enum class SamplerFilterMode { - Nearest, - Linear, - SamplerFilterCount, - Unknown = -1 - }; - - enum class SamplerMipmapMode { - None, - Nearest, - Linear, - SamplerMipmapModeCount, - Unknown = -1 - }; - - enum class SamplerWrapMode { - ClampToEdge, - MirroredRepeat, - Repeat, - ClampToBorder, - MirrorClampToEdge, - SamplerWrapModeCount, - Unknown = -1 - }; - - enum class SamplerCompareMode { - None, - CompareToTexture, - SamplerCompareModeCount, - Unknown = -1 - }; - - enum class SamplerCompareFunc { - Never, - Less, - Equal, - LessEqual, - Greater, - NotEqual, - GreaterEqual, - Always, - SamplerCompareFuncCount, - Unknown = -1 - }; - - // Which of the three GL_TEXTURE_BORDER_COLOR entry-point families last wrote the border colour, - // and therefore which of the three stored representations is AUTHORITATIVE. GL 4.6 core 8.10: - // TexParameterIiv/Iuiv store an integer border colour "unmodified, with an internal data type of - // integer", TexParameterfv stores a floating-point one, and the derived forms are only a - // convenience for a getter of the other spelling. A backend cannot pick the right driver entry - // point (glSamplerParameterIiv vs fv) or the right VkBorderColor family without this: numerically - // the three representations are always populated, so the value alone says nothing about the form. - enum class BorderColorForm : Uint8 { - Float, - Int, - Uint - }; - - struct SamplerParameters { - SamplerWrapMode wrapS = SamplerWrapMode::Repeat; - SamplerWrapMode wrapT = SamplerWrapMode::Repeat; - SamplerWrapMode wrapR = SamplerWrapMode::Repeat; - SamplerFilterMode minFilter = SamplerFilterMode::Nearest; - SamplerFilterMode magFilter = SamplerFilterMode::Linear; - SamplerMipmapMode mipmapMode = SamplerMipmapMode::Linear; - Float minLod = -1000.0f; - Float maxLod = 1000.0f; - Float lodBias = 0.0f; - Float maxAnisotropy = 1.0f; - // GL 4.6 core table 23.18 / GLES 3.2 table 21.16: TEXTURE_COMPARE_FUNC starts at LEQUAL, - // for both sampler objects and the sampler state a texture object carries. - SamplerCompareFunc compareFunc = SamplerCompareFunc::LessEqual; - SamplerCompareMode compareMode = SamplerCompareMode::None; - // TEXTURE_BORDER_COLOR is sampler state (GL 4.6 core table 23.18), so it belongs here and - // not on the texture - a texture object reaches it through the sampler object it owns. The - // three representations are the float, integer and unsigned-integer forms glSamplerParameterfv, - // glSamplerParameterIiv and glSamplerParameterIuiv set; whichever is written last defines - // the colour and the other two follow it, so a getter always has an answer. - FloatVec4 borderColor = {0.0f, 0.0f, 0.0f, 0.0f}; - IntVec4 borderColorI = {0, 0, 0, 0}; - UintVec4 borderColorUI = {0, 0, 0, 0}; - BorderColorForm borderColorForm = BorderColorForm::Float; - }; - namespace MG_State { namespace GLState { class SamplerObject { diff --git a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h index 2435c375..8a6d22f6 100644 --- a/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h +++ b/MobileGL/MG_State/GLState/VertexArrayState/VertexArrayObject.h @@ -10,65 +10,11 @@ #include #include "../BufferState/BufferObject.h" #include "MG_Util/Types.h" +#include namespace MobileGL { namespace MG_State { namespace GLState { - struct VertexAttribute { - Bool Enabled = false; - int Size = 4; - DataType Type = DataType::Float32; - Bool Normalized = false; - // The RESOLVED byte distance between consecutive elements, never the raw - // glVertexAttrib*Pointer argument: a pointer call's stride 0 means "tightly - // packed" and is resolved to the element size here, so a zero that survives - // into this field can only have come from the binding model, where a zero - // VERTEX_BINDING_STRIDE means the opposite - every vertex reads the SAME - // element and the fetch address never advances (GL 4.6 core 10.3.1). Backends - // consume this verbatim; collapsing 0 back into the element size is what made - // KHR-GL43.vertex_attrib_binding.basic-input-case7/8 read past the buffer. - int Stride = 0; - SizeT Offset = 0; - Bool IsInteger = false; - // GL_BGRA vertex size: four components in reversed (B,G,R,A) memory order. Size stays 4. - // Set only by the long (L) format entry points. It is NOT implied by - // Type == Float64: VertexAttribFormat(GL_DOUBLE) also reads doubles from memory but - // asks for them *converted to float*, while VertexAttribLFormat keeps all 64 bits - // (GL 4.6 core 10.3.2). Backends have to tell the two apart, and it is what - // GL_VERTEX_ATTRIB_ARRAY_LONG reports. - Bool IsLong = false; - Bool IsBgra = false; - Uint Divisor = 0; - SharedPtr Buffer; - - // GL 4.6 core table 23.3: VERTEX_ATTRIB_ARRAY_STRIDE and _POINTER are the - // arguments of the last glVertexAttrib*Pointer call on this attribute, - // reported verbatim, and NOTHING else writes them - not glVertexAttribFormat, - // not glBindVertexBuffer. Stride/Offset above are the *resolved* draw inputs - // and the binding model does overwrite those, so the two views have to be - // stored apart or the binding-model sequence reports a legacy state it never - // set (KHR-GL4x.vertex_attrib_binding.basic-state3). - int LegacyStride = 0; - SizeT LegacyPointer = 0; - }; - - // ARB_vertex_attrib_binding separate binding point. Attributes configured through the - // binding-point API are resolved eagerly into the flat VertexAttribute view above, so - // backends keep consuming resolved attributes and never see binding points. - struct VertexBufferBindingPoint { - SharedPtr Buffer; - SizeT Offset = 0; - // GL 4.6 core table 23.4: the initial VERTEX_BINDING_STRIDE is 16, not 0. - int Stride = 16; - Uint Divisor = 0; - }; - - struct VertexAttributeVersion { - Uint16 FormatVersion = 0; - Uint16 BufferVersion = 0; - Uint16 SwitchVersion = 0; - }; - class VertexArrayObject { public: // Storage capacity, not the GL-visible limit. GL_MAX_VERTEX_ATTRIBS is reported as diff --git a/MobileGL/MG_Test/Pipe/PipeCatalogueTest.cpp b/MobileGL/MG_Test/Pipe/PipeCatalogueTest.cpp index ab9907dd..5ce193a4 100644 --- a/MobileGL/MG_Test/Pipe/PipeCatalogueTest.cpp +++ b/MobileGL/MG_Test/Pipe/PipeCatalogueTest.cpp @@ -113,6 +113,39 @@ TEST(PipeCatalogue, ResidualBlockSizeIsPinned) { EXPECT_GE(sizeof(ResidualValueBlock), sizeof(RenderStateParameters) + sizeof(PixelStoreParameters)); } +// P0.5 moved the value structs into MG_Pipe/MGPipeValueTypes.h. These are the runtime twins +// of that header's static assertions, so the numbers show up in ctest output on every +// platform - including one where a static assertion is skipped. Every number here is also +// what MGL_RESIDUAL_BLOCK_SIZE (MGPipeTypes.h) and the Espryt offsetof spans depend on. +TEST(PipeCatalogue, ValueTypeLayoutsArePinned) { + EXPECT_EQ(sizeof(PixelStoreParameters), 28u); + EXPECT_EQ(sizeof(PerBufferBlendState), 28u); + EXPECT_EQ(sizeof(StencilFaceState), 28u); + EXPECT_EQ(sizeof(RenderStateParameters), 1168u); + EXPECT_EQ(sizeof(SamplerParameters), 100u); + EXPECT_EQ(sizeof(MG_State::GLState::VertexAttributeVersion), 6u); + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_TRUE(std::is_standard_layout_v); + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_TRUE(std::is_trivially_copyable_v); + EXPECT_LT(offsetof(RenderStateParameters, BlendStates), offsetof(RenderStateParameters, LogicOp)); + EXPECT_EQ(std::tuple_size_v, static_cast(kMGMaxDrawBuffers)); + EXPECT_EQ(std::tuple_size_v, static_cast(kMGMaxDrawBuffers)); + EXPECT_EQ(kMGMaxDrawBuffers, 8u); +} + +// The move did not alter the carrier: the residual block is still the render-state struct, +// then the pack struct, then the 8-aligned capability word, at the offsets it had before. +TEST(PipeCatalogue, ResidualBlockIsExactlyItsTwoValueStructsPlusPatchTail) { + EXPECT_EQ(offsetof(ResidualValueBlock, RenderState), 0u); + EXPECT_EQ(offsetof(ResidualValueBlock, Pack), sizeof(RenderStateParameters)); + EXPECT_EQ(offsetof(ResidualValueBlock, CapabilityBits), 1200u); + EXPECT_EQ(offsetof(ResidualValueBlock, PatchVertices), 1208u); +} + // G3's opcode numbering is the wire protocol. Position in PipeCalls.def, 1-based, no holes. TEST(PipeCatalogue, WireOpcodesAreThePositionsInTheCatalogue) { EXPECT_EQ(static_cast(MGPWireOp::GetCaps), 1); diff --git a/scripts/gen_pipe.py b/scripts/gen_pipe.py index 8b128798..79588593 100644 --- a/scripts/gen_pipe.py +++ b/scripts/gen_pipe.py @@ -161,8 +161,9 @@ def parse_calls(): # The member types the G4 comparator falls back to memcmp for (see gen_verify): the -# MG_State / MG_Backend value structs and MGHostSpan. They are not call payloads and get -# field lists of their own in P0.5. Nothing else may be missing from PipeFields.def. +# MG_Pipe / MG_Backend value structs and MGHostSpan. They are not call payloads and get +# field lists of their own in P1 (P0.5 moved the types). Nothing else may be missing from +# PipeFields.def. MEMCMP_FALLBACK_TYPES = { "RenderStateParameters", "PixelStoreParameters", @@ -407,11 +408,11 @@ inline Bool MGPipeFieldEqual(const T& a, const T& b) { } else if constexpr (requires(const T& x, const T& y) { x == y; }) { return a == b; } else { - // MEMCMP FALLBACK. Only reached by the payload members that are still MG_State / - // MG_Backend value structs (RenderStateParameters, PixelStoreParameters, - // DynamicBackendParameters) and by MGHostSpan. Those are exactly the types P0.5 - // moves into MGPipeValueTypes.h, at which point they get field lists of their own - // and this branch stops being reachable from any payload. + // MEMCMP FALLBACK. Only reached by the payload members that are still value structs + // without a field list (RenderStateParameters, PixelStoreParameters, + // DynamicBackendParameters) and by MGHostSpan. P0.5 moved the first two into + // MGPipeValueTypes.h; P1 gives them field lists of their own, at which point this + // branch stops being reachable from any payload. return std::memcmp(&a, &b, sizeof(T)) == 0; } }