diff --git a/MobileGL/BackendCaps.h b/MobileGL/BackendCaps.h new file mode 100644 index 00000000..b56f7bff --- /dev/null +++ b/MobileGL/BackendCaps.h @@ -0,0 +1,422 @@ +// MobileGL - MobileGL/BackendCaps.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 +#include + +// The vocabulary every layer uses to talk about a backend's capabilities: which backend it is, +// what each texture format supports, and the probed limits and features. It holds values only; +// the backend itself is MG_Backend/BackendObject.h. +namespace MobileGL { + enum class BackendType { + DirectGLES, + DirectVulkan, + BackendTypeCount, + Unknown = -1 + }; + + namespace MG_Backend { + enum class FormatCapability : Uint64 { + Creatable = 1ull << 0, + + Sampled = 1ull << 1, + LinearFilter = 1ull << 2, + GenerateMipmap = 1ull << 3, + TextureGather = 1ull << 4, + TextureShadow = 1ull << 5, + + FramebufferRenderable = 1ull << 6, + FramebufferLayered = 1ull << 7, + MultisampleTexture = 1ull << 8, + MultisampleRenderbuffer = 1ull << 9, + + ColorAttachment = 1ull << 10, + DepthAttachment = 1ull << 11, + StencilAttachment = 1ull << 12, + + TextureBuffer = 1ull << 13 + }; + + using FormatCapabilityFlags = Flags; + + inline constexpr Array kReportedFormatCapabilities = { + FormatCapability::Creatable, + FormatCapability::Sampled, + FormatCapability::LinearFilter, + FormatCapability::GenerateMipmap, + FormatCapability::TextureGather, + FormatCapability::TextureShadow, + FormatCapability::FramebufferRenderable, + FormatCapability::FramebufferLayered, + FormatCapability::MultisampleTexture, + FormatCapability::MultisampleRenderbuffer, + FormatCapability::ColorAttachment, + FormatCapability::DepthAttachment, + FormatCapability::StencilAttachment, + FormatCapability::TextureBuffer, + }; + + inline constexpr SizeT kFormatCapabilityTextureTargetCount = + static_cast(TextureTarget::TextureTargetCount); + inline constexpr SizeT kFormatCapabilityRenderbufferTargetIndex = kFormatCapabilityTextureTargetCount; + inline constexpr SizeT kFormatCapabilityTargetCount = kFormatCapabilityTextureTargetCount + 1; + inline constexpr SizeT kFormatCapabilityFormatCount = + static_cast(TextureInternalFormat::TextureInternalFormatCount); + + using FormatCapabilityTable = + Array, kFormatCapabilityTargetCount>; + using FormatSampleCountTable = + Array, kFormatCapabilityFormatCount>, kFormatCapabilityTargetCount>; + + struct FormatCapabilityCache { + FormatCapabilityTable FullCaps{}; + FormatCapabilityTable CaveatCaps{}; + FormatSampleCountTable SampleCounts{}; + + void Clear(); + }; + + Bool HasFormatCapability(FormatCapabilityFlags caps, FormatCapability capability); + SizeT GetFormatCapabilityTargetIndex(TextureTarget target); + SizeT GetRenderbufferFormatCapabilityTargetIndex(); + const char* GetFormatCapabilityName(FormatCapability capability); + String GetFormatCapabilityTargetName(SizeT targetIndex); + void PrintFormatCapabilities(const FormatCapabilityCache& cache); + + // Device features that decide whether an extension string may be advertised, beyond the + // per-backend positional flags of BuildAdvertisedExtensions. Each is the PROBED driver + // capability (an ES version / extension on DirectGLES, a VkPhysicalDeviceFeatures bit on + // DirectVulkan), never a vendor guess. Defaults describe the no-device case. + struct AdvertisedFeatureSupport { + // ES 3.2 or GL_EXT_tessellation_shader; VkPhysicalDeviceFeatures::tessellationShader. + Bool TessellationShader = false; + // At least one cull distance: GL_EXT_clip_cull_distance with a non-zero + // GL_MAX_CULL_DISTANCES; VkPhysicalDeviceFeatures::shaderCullDistance with a non-zero + // maxCullDistances. + Bool CullDistance = false; + // GL_EXT_clip_control on DirectGLES. DirectVulkan applies clip control in its own + // position fixup and does not read this. + Bool ClipControl = false; + }; + + // Coarse GPU vendor identity for gating device-specific quirks. Detected from the + // Vulkan physical-device vendorID or the GLES GL_VENDOR/GL_RENDERER strings; stays + // Unknown when detection is inconclusive, in which case auto-gated quirks stay off. + enum class GpuVendorKind : Uint8 { + Unknown = 0, + Qualcomm, + Arm, + Nvidia, + Amd, + Intel, + ImgTec, + // Software rasterizers (llvmpipe/lavapipe, SwiftShader). + Software, + }; + + struct DynamicBackendParameters { + Uint64 UniformBufferOffsetAlignment = 256; + // GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, which is a SEPARATE limit from the + // uniform one and is routinely larger: Adreno 830 reports 32 for uniform buffers and + // 64 for storage buffers. Answering the storage query with the uniform value let an + // application bind a storage range at an offset the driver cannot address, which it + // accepted without error and then wrote somewhere else entirely. + Uint64 ShaderStorageBufferOffsetAlignment = 256; + // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT. 1.0 means the backend cannot filter anisotropically, + // which is also why the extension is not advertised in that case. + Float MaxTextureMaxAnisotropy = 1.0f; + Float AliasedLineWidthRangeMin = 1.0f; + Float AliasedLineWidthRangeMax = 1.0f; + Float SmoothLineWidthRangeMin = 1.0f; + Float SmoothLineWidthRangeMax = 1.0f; + Float SmoothLineWidthGranularity = 1.0f; + Float PointSizeRangeMin = 1.0f; + Float PointSizeRangeMax = 1.0f; + Float PointSizeGranularity = 1.0f; + Int Max3DTextureSize = 16384; + Int MaxArrayTextureLayers = 2048; + Int MaxCubeMapTextureSize = 16384; + Int MaxFramebufferWidth = 16384; + Int MaxFramebufferHeight = 16384; + Int MaxFramebufferLayers = 2048; + Int MaxRenderbufferSize = 16384; + Int MaxTextureSize = 16384; + Int MaxColorTextureSamples = 1; + Int MaxDepthTextureSamples = 1; + Int MaxFramebufferSamples = 1; + Int MaxIntegerSamples = 1; + Int MaxSamples = 1; + Int MaxSampleMaskWords = 1; + // Tessellation limits; defaults are the GL 4.0 core minimums. + Int MaxPatchVertices = 32; + Int MaxTessGenLevel = 64; + // GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET. Defaults are the GL 4.0 core + // minimums, which every ES 3.1 driver also guarantees. + Int MinProgramTextureGatherOffset = -8; + Int MaxProgramTextureGatherOffset = 7; + Int MaxTextureImageUnits = 32; + Int MaxVertexTextureImageUnits = 32; + Int MaxComputeTextureImageUnits = 32; + Int MaxCombinedTextureImageUnits = 192; + Int MaxVertexAttribs = 16; + Int MaxComputeShaderStorageBlocks = 8; + Int MaxCombinedShaderStorageBlocks = 32; + // Per-stage GL_MAX_*_SHADER_STORAGE_BLOCKS. Zero is a legal answer for the four + // non-compute, non-fragment stages and these defaults are the spec minimums, not + // placeholders: GL 4.6 table 23.64 and ES 3.2 table 21.44 both set the minimum for + // vertex, tessellation control, tessellation evaluation and geometry at 0, and only + // fragment (8 in GL, 4 in ES) and compute are guaranteed to have any. Every real ARM + // GLES driver takes that allowance - a Mali-G925 reports 0 for all four - so a + // backend that cannot honour a graphics-stage storage block MUST report 0 here + // rather than a hopeful number. Advertising a non-zero count the driver will refuse + // does not make the block work; it only moves the failure from an honest + // "unsupported" at query time to a backend link error the frontend never surfaces, + // after which every draw with that program silently renders nothing. + Int MaxVertexShaderStorageBlocks = 0; + Int MaxTessControlShaderStorageBlocks = 0; + Int MaxTessEvaluationShaderStorageBlocks = 0; + Int MaxGeometryShaderStorageBlocks = 0; + Int MaxFragmentShaderStorageBlocks = 8; + Int MaxComputeUniformBlocks = 12; + Int MaxComputeWorkGroupInvocations = 128; + // GL_MAX_COMPUTE_WORK_GROUP_COUNT / GL_MAX_COMPUTE_WORK_GROUP_SIZE, one value per + // axis. These six, with the invocations limit above, are the only indexed limits a + // backend genuinely OWNS - the device answers them (glGetIntegeri_v on DirectGLES, + // VkPhysicalDeviceLimits::maxComputeWorkGroupCount/Size on DirectVulkan) - and so + // the only ones that survive the retirement of the GetIntegeri_v table entry: they + // cross the MGPipe boundary inside MGPCaps, by inclusion of this struct (plan B + // section 4.4.1). Every other indexed pname names frontend state. RAW driver + // answers, like the invocations limit: GL_Getter and the compile environment floor + // them at the shared MIN_COMPUTE_WORK_GROUP_* minimums themselves. The defaults are + // the GL 4.3 core minimums (table 23.60) and describe the no-backend case, as + // MaxClipDistances' does. + Int MaxComputeWorkGroupCount[3] = {65535, 65535, 65535}; + Int MaxComputeWorkGroupSize[3] = {1024, 1024, 64}; + Int MaxShaderStorageBufferBindings = 8; + Int MaxTextureBufferSize = 65536; + // GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT; 1 means the offset is unconstrained. + Int TextureBufferOffsetAlignment = 1; + Int MaxUniformBufferBindings = 24; + Int MaxUniformBlockSize = 16384; + Int MaxImageUnits = 8; + Int MaxCombinedImageUniforms = 8; + Int MaxVertexImageUniforms = 0; + Int MaxTessControlImageUniforms = 0; + Int MaxTessEvaluationImageUniforms = 0; + Int MaxGeometryImageUniforms = 0; + Int MaxFragmentImageUniforms = 8; + Int MaxComputeImageUniforms = 8; + Int MaxDrawBuffers = 8; + Int MaxColorAttachments = 8; + // GL_MAX_CLIP_DISTANCES. Zero is a legal answer here, not a placeholder, and a + // backend that cannot host a clip distance MUST report it: advertising eight the + // backend will refuse does not make gl_ClipDistance work, it only moves the failure + // from an honest "unsupported" at query time to a backend shader-compile error the + // frontend never surfaces, after which every draw with that program silently renders + // nothing. DirectGLES fills it from GL_EXT_clip_cull_distance, DirectVulkan from the + // shaderClipDistance device feature. The DEFAULT stays at the GL 4.3 core minimum + // because it describes the no-backend case (standalone shader compiles, unit tests), + // where there is no device to be honest about and BuildTBuiltInResource still has to + // hand glslang a workable gl_MaxClipDistances. + Int MaxClipDistances = 8; + // GL_MAX_CULL_DISTANCES and GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, under exactly + // the contract stated for MaxClipDistances above: ZERO IS A LEGAL ANSWER and a + // backend that cannot host a cull distance MUST report it. The failure this prevents + // is worse than the clip one, because cull distance discards the whole primitive: + // glslang bounds gl_CullDistance[i] against maxCullDistances and expands + // gl_MaxCullDistances from it, SPIRV-Cross then emits + // `#extension GL_EXT_clip_cull_distance : require` into the ESSL, and a host driver + // without that extension rejects the program in an info log nobody surfaces. These + // used to be bare 8s inside BuildTBuiltInResource with no backend consulted at all. + // The DEFAULTS are the GL 4.5 core minimums for the same reason MaxClipDistances' + // is: they describe the no-backend case (standalone compiles, unit tests). + Int MaxCullDistances = 8; + Int MaxCombinedClipAndCullDistances = 8; + Int MaxViewports = 16; + // GL_LAYER_PROVOKING_VERTEX / GL_VIEWPORT_INDEX_PROVOKING_VERTEX: which vertex of a + // primitive supplies gl_Layer and gl_ViewportIndex. GL 4.6 table 23.65 makes + // GL_UNDEFINED_VERTEX a legal answer for both, and it is the honest default - naming + // a convention is a statement about behaviour, so a backend that does not pin one + // must not claim it does. DirectGLES fills the layer one from the ES 3.2 query and + // the viewport one from GL_OES_viewport_array, and leaves UNDEFINED where the + // capability is absent: without the viewport array extension only viewport 0 is ever + // rasterized, so no convention selects anything. DirectVulkan keeps UNDEFINED for + // both - which vertex provokes is decided per pipeline by + // VulkanRenderer::SelectProvokingVertexMode out of VK_EXT_provoking_vertex, + // provokingVertexModePerPipeline and the topology, so no single convention is true + // of the backend. + Uint32 LayerProvokingVertex = GL_UNDEFINED_VERTEX; + Uint32 ViewportIndexProvokingVertex = GL_UNDEFINED_VERTEX; + Int MaxViewportWidth = 16384; + Int MaxViewportHeight = 16384; + Float ViewportBoundsRangeMin = 0.0f; + Float ViewportBoundsRangeMax = 0.0f; + Int ViewportSubpixelBits = 0; + // GL 4.x fragment-interpolation offset limits. These defaults are the + // core minimums and are replaced by live GLES/Vulkan device limits. + Float MinFragmentInterpolationOffset = -0.5f; + // For four fractional bits the greatest required legal offset is + // 0.5 - 2^-4 = 0.4375 (GL 4.6 table 23.70). + Float MaxFragmentInterpolationOffset = 0.4375f; + Int FragmentInterpolationOffsetBits = 4; + Bool SupportsWideLines = false; + // Whether a framebuffer whose depth and stencil attachments are distinct + // images can be rendered to. GL only requires support when both refer to the + // same image and lets an implementation answer GL_FRAMEBUFFER_UNSUPPORTED + // otherwise, which is what DirectVulkan (one combined attachment) and the + // real ES drivers behind DirectGLES both do. Defaults to true so a backend + // that never sets it keeps the permissive behaviour. + Bool SupportsDistinctDepthStencilAttachments = true; + // Whether attaching a single layer of a 3D or array texture to a framebuffer actually + // renders to that layer. DirectGLES hands the layer straight to + // glFramebufferTextureLayer, so it does; DirectVulkan maps a GL layer onto a Vulkan + // array layer with no notion of a 3D depth slice, so it does not yet. Defaults to false + // so a backend that never sets it gets the conservative answer. + // Which layered texture targets this backend can attach ONE layer of to a framebuffer + // and then really clear, render and read back that layer. Bit (1u << TextureTarget) is + // set for each supported target. Deliberately per target rather than one flag: the three + // ways a GL layer maps onto Vulkan are independent capabilities. A 2D or 2D multisample + // array layer IS a VkImage array layer and needs nothing extra; a 3D texture's layer is + // a z slice, which needs a 2D-array-compatible image and a per-slice clear that + // vkCmdClearColorImage cannot express; a cube map array needs an image shape and the + // imageCubeArray feature before it can be attached at any layer at all. Defaults to 0 so + // a backend that never sets it gets the conservative answer. + Uint32 PerLayerFramebufferAttachmentTargets = 0; + + static constexpr Uint32 PerLayerFramebufferAttachmentBit(TextureTarget target) { + return (static_cast(target) >= 0 && + static_cast(target) < static_cast(TextureTarget::TextureTargetCount)) + ? (1u << static_cast(target)) + : 0u; + } + + Bool SupportsPerLayerFramebufferAttachment(TextureTarget target) const { + const Uint32 bit = PerLayerFramebufferAttachmentBit(target); + return bit != 0 && (PerLayerFramebufferAttachmentTargets & bit) != 0; + } + // Whether this backend can CONSUME a shader module that still declares 64-bit floats, + // i.e. whether `double` survives the transpile instead of being narrowed to `float` + // (ShaderTranspiler::DemoteFloat64Pass). Detected, never assumed: + // * DirectVulkan sets it from VkPhysicalDeviceFeatures::shaderFloat64, the feature + // VUID-VkShaderModuleCreateInfo-pCode-08740 requires before a module declaring + // OpCapability Float64 may be created at all. lavapipe has it; Adreno and Mali + // both report VK_FALSE, so no real mobile device does. + // * DirectGLES can NEVER have it. GLSL ES has no 64-bit float type in any version + // or extension, so SPIRV-Cross cannot emit one ("FP64 not supported in ES + // profile") and the demotion there is mathematically mandatory, always. + // Defaults to false so a backend that never sets it - and the no-backend case, which + // is what standalone shader compiles and the unit tests run under - keeps the + // demotion, which is the behaviour that works everywhere. + Bool SupportsShaderFloat64 = false; + // Whether glVertexAttribLFormat / glVertexArrayAttribLFormat can be honoured, i.e. + // whether a 64-bit vertex attribute can actually reach a shader unconverted. Detected, + // never assumed: DirectVulkan needs VkPhysicalDeviceFeatures::shaderFloat64 (the + // attribute travels as its 32-bit word pair, so no VK_FORMAT_R64* is required, but the + // bitcast result is Float64); DirectGLES can never have it, ESSL having no fp64 type at + // all. Defaults to false so a backend that never sets it gets the conservative answer. + // + // INDEPENDENT of SupportsShaderFloat64, and it has to be: this flag decides a VkFormat + // from the VAO ATTRIBUTE alone, which does not know what type the shader declared, and + // glVertexAttribFormat(GL_DOUBLE) feeding a plain `in vec4` is both legal and common + // (KHR-GL43.vertex_attrib_binding.basic-input-case4/5, advanced-bindingUpdate). A + // backend with native fp64 that still cannot FETCH 64 bits keeps this false and relies + // on the per-MODULE rule in ShaderCompiler::SanitizeAndOptimizeBinary instead: a vertex + // module that declares a 64-bit float INPUT is demoted whole, so the two shader-side + // halves (PackDoubleVertexInputsPass and VertexInputStateFactory::ToVkVertexFormat) + // still see one consistent world. + Bool SupportsFloat64VertexAttributes = false; + // Whether a TESSELLATION stage of this backend may access gl_PointSize - i.e. + // whether a module declaring OpCapability TessellationPointSize can reach the + // driver at all. DirectVulkan sets both this and the geometry twin from the one + // shaderTessellationAndGeometryPointSize feature; DirectGLES sets them + // independently from the EXT/OES_tessellation_point_size / + // geometry_point_size extension pairs (PointSizeTier), which really do come + // separately. When absent, ProgramSpirvTask demotes the built-in to an ordinary + // varying program-wide (ShaderCompiler:: + // DemoteTessellationGeometryPointSizeForProgram); MOBILEGL_POINT_SIZE_DEMOTION + // overrides the detection in either direction at backend init. + // + // Defaults TRUE, deliberately against the house "assume absent" rule: false + // ARMS a rewrite, so the conservative no-backend answer (standalone compiles, + // unit tests) is the one that leaves modules untouched. A backend that never + // sets it gets standard modules and, at worst, the old honest declines. + Bool SupportsTessellationPointSize = true; + // The geometry-stage twin (OpCapability GeometryPointSize). + Bool SupportsGeometryPointSize = true; + Uint64 MaxShaderStorageBlockSize = 128 * 1024 * 1024; + Uint32 SubgroupSize = 0; + Uint32 SubgroupSupportedStages = 0; + Uint32 SubgroupSupportedFeatures = 0; + // GL_SAMPLE_POSITION (GL 4.6 core 14.3.1.1) as the device actually places the samples + // of a 2-, 4-, 8- and 16-sample pixel: (x, y) pairs in [0, 1], the N-sample row starting + // at pair N - 2. Bit log2(N) of SamplePositionCountMask says row N was filled in; a row + // the backend could not determine stays unset rather than being guessed. Read and + // written through Get/SetSamplePositions only. + static constexpr Int kMaxSamplePositionCount = 16; + Float SamplePositions[60] = {}; // 2 * (2 + 4 + 8 + 16) + Uint32 SamplePositionCountMask = 0; + Bool SubgroupQuadOperationsInAllStages = false; + GpuVendorKind GpuVendor = GpuVendorKind::Unknown; + // GL_EXT_fragment_shading_rate and its two companions. Each is a statement about the + // device, so all of them default to "absent" and the extension strings are advertised + // only from these: DirectGLES fills them from the driver's own + // EXT_fragment_shading_rate(_primitive, _attachment), DirectVulkan from + // VK_KHR_fragment_shading_rate's pipeline / primitive / attachment features. + // SupportsFragmentShadingRate glShadingRateEXT and gl_ShadingRateEXT + // SupportsPrimitiveFragmentShadingRate gl_PrimitiveShadingRateEXT, combiner op 0 + // SupportsAttachmentFragmentShadingRate the R8UI rate attachment, combiner op 1 + Bool SupportsFragmentShadingRate = false; + Bool SupportsPrimitiveFragmentShadingRate = false; + Bool SupportsAttachmentFragmentShadingRate = false; + // The four GL_FRAGMENT_SHADING_RATE_*_SUPPORTED_EXT booleans, as the device answers them. + Bool FragmentShadingRateNonTrivialCombiners = false; + Bool FragmentShadingRateWithShaderDepthStencilWrites = false; + Bool FragmentShadingRateWithSampleMask = false; + Bool FragmentShadingRatePrimitiveWithMultiViewport = false; + Bool FragmentShadingRateAttachmentWithDefaultFramebuffer = false; + // What glGetFragmentShadingRatesEXT answers, one mask per sample count 1, 2, 4 .. 64 + // (index = log2(samples)): bit (log2(w) << 2 | log2(h)) is set when that rate is + // honoured exactly at that sample count, i.e. gl_ShadingRateEXT reads it back + // unclamped. Zero masks mean no rate at all, which is what a sample count the device + // cannot shade coarsely answers. + Uint16 FragmentShadingRateMasks[7] = {}; + // The GL_{MIN,MAX}_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_{WIDTH,HEIGHT}_EXT, + // _ASPECT_RATIO_EXT and _LAYERS_EXT limits. Meaningful only with the attachment. + Int MinFragmentShadingRateAttachmentTexelWidth = 0; + Int MinFragmentShadingRateAttachmentTexelHeight = 0; + Int MaxFragmentShadingRateAttachmentTexelWidth = 0; + Int MaxFragmentShadingRateAttachmentTexelHeight = 0; + Int MaxFragmentShadingRateAttachmentTexelAspectRatio = 0; + Int MaxFragmentShadingRateAttachmentLayers = 0; + + + static constexpr Bool IsSamplePositionCount(Int samples) { + return samples >= 2 && samples <= kMaxSamplePositionCount && (samples & (samples - 1)) == 0; + } + static constexpr Uint32 SamplePositionCountBit(Int samples) { + return 1u << static_cast(std::countr_zero(static_cast(samples))); + } + // `xy` holds `samples` (x, y) pairs. + void SetSamplePositions(Int samples, const Float* xy) { + if (!IsSamplePositionCount(samples)) return; + for (Int i = 0; i < 2 * samples; ++i) SamplePositions[2 * (samples - 2) + i] = xy[i]; + SamplePositionCountMask |= SamplePositionCountBit(samples); + } + Bool GetSamplePosition(Int samples, Int index, Float* outXY) const { + if (!IsSamplePositionCount(samples) || index < 0 || index >= samples || + (SamplePositionCountMask & SamplePositionCountBit(samples)) == 0) { + return false; + } + outXY[0] = SamplePositions[2 * (samples - 2 + index)]; + outXY[1] = SamplePositions[2 * (samples - 2 + index) + 1]; + return true; + } + }; + } // namespace MG_Backend +} // namespace MobileGL diff --git a/MobileGL/Config.h b/MobileGL/Config.h index f7ac25b0..37726b00 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -8,7 +8,7 @@ #pragma once #include -#include +#include namespace MobileGL::MG_Config { inline const String ProjectName = "MobileGL"; diff --git a/MobileGL/GlobalObjects.cpp b/MobileGL/GlobalObjects.cpp index 2b3cc0e1..c536999f 100644 --- a/MobileGL/GlobalObjects.cpp +++ b/MobileGL/GlobalObjects.cpp @@ -7,6 +7,7 @@ // End of Source File Header #include "Config.h" +#include namespace MobileGL { namespace MG_Config { diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 1cf8682f..8fabf46d 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include "MG_State/GLState/TextureState/TextureEnum.h" #include #include @@ -19,13 +20,6 @@ namespace MobileGL { class RenderbufferObject; } - enum class BackendType { - DirectGLES, - DirectVulkan, - BackendTypeCount, - Unknown = -1 - }; - namespace MG_Backend { // One endpoint of a glCopyImageSubData. GL 4.6 core 18.3.2 accepts GL_RENDERBUFFER // alongside the ten whole-image texture targets, and a renderbuffer name lives in a @@ -49,73 +43,6 @@ namespace MobileGL { } }; - enum class FormatCapability : Uint64 { - Creatable = 1ull << 0, - - Sampled = 1ull << 1, - LinearFilter = 1ull << 2, - GenerateMipmap = 1ull << 3, - TextureGather = 1ull << 4, - TextureShadow = 1ull << 5, - - FramebufferRenderable = 1ull << 6, - FramebufferLayered = 1ull << 7, - MultisampleTexture = 1ull << 8, - MultisampleRenderbuffer = 1ull << 9, - - ColorAttachment = 1ull << 10, - DepthAttachment = 1ull << 11, - StencilAttachment = 1ull << 12, - - TextureBuffer = 1ull << 13 - }; - - using FormatCapabilityFlags = Flags; - - inline constexpr Array kReportedFormatCapabilities = { - FormatCapability::Creatable, - FormatCapability::Sampled, - FormatCapability::LinearFilter, - FormatCapability::GenerateMipmap, - FormatCapability::TextureGather, - FormatCapability::TextureShadow, - FormatCapability::FramebufferRenderable, - FormatCapability::FramebufferLayered, - FormatCapability::MultisampleTexture, - FormatCapability::MultisampleRenderbuffer, - FormatCapability::ColorAttachment, - FormatCapability::DepthAttachment, - FormatCapability::StencilAttachment, - FormatCapability::TextureBuffer, - }; - - inline constexpr SizeT kFormatCapabilityTextureTargetCount = - static_cast(TextureTarget::TextureTargetCount); - inline constexpr SizeT kFormatCapabilityRenderbufferTargetIndex = kFormatCapabilityTextureTargetCount; - inline constexpr SizeT kFormatCapabilityTargetCount = kFormatCapabilityTextureTargetCount + 1; - inline constexpr SizeT kFormatCapabilityFormatCount = - static_cast(TextureInternalFormat::TextureInternalFormatCount); - - using FormatCapabilityTable = - Array, kFormatCapabilityTargetCount>; - using FormatSampleCountTable = - Array, kFormatCapabilityFormatCount>, kFormatCapabilityTargetCount>; - - struct FormatCapabilityCache { - FormatCapabilityTable FullCaps{}; - FormatCapabilityTable CaveatCaps{}; - FormatSampleCountTable SampleCounts{}; - - void Clear(); - }; - - Bool HasFormatCapability(FormatCapabilityFlags caps, FormatCapability capability); - SizeT GetFormatCapabilityTargetIndex(TextureTarget target); - SizeT GetRenderbufferFormatCapabilityTargetIndex(); - const char* GetFormatCapabilityName(FormatCapability capability); - String GetFormatCapabilityTargetName(SizeT targetIndex); - void PrintFormatCapabilities(const FormatCapabilityCache& cache); - // Opaque backend fence-sync handle, created by GLFunctionsTable::FenceSync // and released by GLFunctionsTable::DeleteSync. using BackendSyncHandle = void*; @@ -309,336 +236,6 @@ namespace MobileGL { void (*SetSwapInterval)(Int interval); }; - // Device features that decide whether an extension string may be advertised, beyond the - // per-backend positional flags of BuildAdvertisedExtensions. Each is the PROBED driver - // capability (an ES version / extension on DirectGLES, a VkPhysicalDeviceFeatures bit on - // DirectVulkan), never a vendor guess. Defaults describe the no-device case. - struct AdvertisedFeatureSupport { - // ES 3.2 or GL_EXT_tessellation_shader; VkPhysicalDeviceFeatures::tessellationShader. - Bool TessellationShader = false; - // At least one cull distance: GL_EXT_clip_cull_distance with a non-zero - // GL_MAX_CULL_DISTANCES; VkPhysicalDeviceFeatures::shaderCullDistance with a non-zero - // maxCullDistances. - Bool CullDistance = false; - // GL_EXT_clip_control on DirectGLES. DirectVulkan applies clip control in its own - // position fixup and does not read this. - Bool ClipControl = false; - }; - - // Coarse GPU vendor identity for gating device-specific quirks. Detected from the - // Vulkan physical-device vendorID or the GLES GL_VENDOR/GL_RENDERER strings; stays - // Unknown when detection is inconclusive, in which case auto-gated quirks stay off. - enum class GpuVendorKind : Uint8 { - Unknown = 0, - Qualcomm, - Arm, - Nvidia, - Amd, - Intel, - ImgTec, - // Software rasterizers (llvmpipe/lavapipe, SwiftShader). - Software, - }; - - struct DynamicBackendParameters { - Uint64 UniformBufferOffsetAlignment = 256; - // GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, which is a SEPARATE limit from the - // uniform one and is routinely larger: Adreno 830 reports 32 for uniform buffers and - // 64 for storage buffers. Answering the storage query with the uniform value let an - // application bind a storage range at an offset the driver cannot address, which it - // accepted without error and then wrote somewhere else entirely. - Uint64 ShaderStorageBufferOffsetAlignment = 256; - // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT. 1.0 means the backend cannot filter anisotropically, - // which is also why the extension is not advertised in that case. - Float MaxTextureMaxAnisotropy = 1.0f; - Float AliasedLineWidthRangeMin = 1.0f; - Float AliasedLineWidthRangeMax = 1.0f; - Float SmoothLineWidthRangeMin = 1.0f; - Float SmoothLineWidthRangeMax = 1.0f; - Float SmoothLineWidthGranularity = 1.0f; - Float PointSizeRangeMin = 1.0f; - Float PointSizeRangeMax = 1.0f; - Float PointSizeGranularity = 1.0f; - Int Max3DTextureSize = 16384; - Int MaxArrayTextureLayers = 2048; - Int MaxCubeMapTextureSize = 16384; - Int MaxFramebufferWidth = 16384; - Int MaxFramebufferHeight = 16384; - Int MaxFramebufferLayers = 2048; - Int MaxRenderbufferSize = 16384; - Int MaxTextureSize = 16384; - Int MaxColorTextureSamples = 1; - Int MaxDepthTextureSamples = 1; - Int MaxFramebufferSamples = 1; - Int MaxIntegerSamples = 1; - Int MaxSamples = 1; - Int MaxSampleMaskWords = 1; - // Tessellation limits; defaults are the GL 4.0 core minimums. - Int MaxPatchVertices = 32; - Int MaxTessGenLevel = 64; - // GL_MIN/MAX_PROGRAM_TEXTURE_GATHER_OFFSET. Defaults are the GL 4.0 core - // minimums, which every ES 3.1 driver also guarantees. - Int MinProgramTextureGatherOffset = -8; - Int MaxProgramTextureGatherOffset = 7; - Int MaxTextureImageUnits = 32; - Int MaxVertexTextureImageUnits = 32; - Int MaxComputeTextureImageUnits = 32; - Int MaxCombinedTextureImageUnits = 192; - Int MaxVertexAttribs = 16; - Int MaxComputeShaderStorageBlocks = 8; - Int MaxCombinedShaderStorageBlocks = 32; - // Per-stage GL_MAX_*_SHADER_STORAGE_BLOCKS. Zero is a legal answer for the four - // non-compute, non-fragment stages and these defaults are the spec minimums, not - // placeholders: GL 4.6 table 23.64 and ES 3.2 table 21.44 both set the minimum for - // vertex, tessellation control, tessellation evaluation and geometry at 0, and only - // fragment (8 in GL, 4 in ES) and compute are guaranteed to have any. Every real ARM - // GLES driver takes that allowance - a Mali-G925 reports 0 for all four - so a - // backend that cannot honour a graphics-stage storage block MUST report 0 here - // rather than a hopeful number. Advertising a non-zero count the driver will refuse - // does not make the block work; it only moves the failure from an honest - // "unsupported" at query time to a backend link error the frontend never surfaces, - // after which every draw with that program silently renders nothing. - Int MaxVertexShaderStorageBlocks = 0; - Int MaxTessControlShaderStorageBlocks = 0; - Int MaxTessEvaluationShaderStorageBlocks = 0; - Int MaxGeometryShaderStorageBlocks = 0; - Int MaxFragmentShaderStorageBlocks = 8; - Int MaxComputeUniformBlocks = 12; - Int MaxComputeWorkGroupInvocations = 128; - // GL_MAX_COMPUTE_WORK_GROUP_COUNT / GL_MAX_COMPUTE_WORK_GROUP_SIZE, one value per - // axis. These six, with the invocations limit above, are the only indexed limits a - // backend genuinely OWNS - the device answers them (glGetIntegeri_v on DirectGLES, - // VkPhysicalDeviceLimits::maxComputeWorkGroupCount/Size on DirectVulkan) - and so - // the only ones that survive the retirement of the GetIntegeri_v table entry: they - // cross the MGPipe boundary inside MGPCaps, by inclusion of this struct (plan B - // section 4.4.1). Every other indexed pname names frontend state. RAW driver - // answers, like the invocations limit: GL_Getter and the compile environment floor - // them at the shared MIN_COMPUTE_WORK_GROUP_* minimums themselves. The defaults are - // the GL 4.3 core minimums (table 23.60) and describe the no-backend case, as - // MaxClipDistances' does. - Int MaxComputeWorkGroupCount[3] = {65535, 65535, 65535}; - Int MaxComputeWorkGroupSize[3] = {1024, 1024, 64}; - Int MaxShaderStorageBufferBindings = 8; - Int MaxTextureBufferSize = 65536; - // GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT; 1 means the offset is unconstrained. - Int TextureBufferOffsetAlignment = 1; - Int MaxUniformBufferBindings = 24; - Int MaxUniformBlockSize = 16384; - Int MaxImageUnits = 8; - Int MaxCombinedImageUniforms = 8; - Int MaxVertexImageUniforms = 0; - Int MaxTessControlImageUniforms = 0; - Int MaxTessEvaluationImageUniforms = 0; - Int MaxGeometryImageUniforms = 0; - Int MaxFragmentImageUniforms = 8; - Int MaxComputeImageUniforms = 8; - Int MaxDrawBuffers = 8; - Int MaxColorAttachments = 8; - // GL_MAX_CLIP_DISTANCES. Zero is a legal answer here, not a placeholder, and a - // backend that cannot host a clip distance MUST report it: advertising eight the - // backend will refuse does not make gl_ClipDistance work, it only moves the failure - // from an honest "unsupported" at query time to a backend shader-compile error the - // frontend never surfaces, after which every draw with that program silently renders - // nothing. DirectGLES fills it from GL_EXT_clip_cull_distance, DirectVulkan from the - // shaderClipDistance device feature. The DEFAULT stays at the GL 4.3 core minimum - // because it describes the no-backend case (standalone shader compiles, unit tests), - // where there is no device to be honest about and BuildTBuiltInResource still has to - // hand glslang a workable gl_MaxClipDistances. - Int MaxClipDistances = 8; - // GL_MAX_CULL_DISTANCES and GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, under exactly - // the contract stated for MaxClipDistances above: ZERO IS A LEGAL ANSWER and a - // backend that cannot host a cull distance MUST report it. The failure this prevents - // is worse than the clip one, because cull distance discards the whole primitive: - // glslang bounds gl_CullDistance[i] against maxCullDistances and expands - // gl_MaxCullDistances from it, SPIRV-Cross then emits - // `#extension GL_EXT_clip_cull_distance : require` into the ESSL, and a host driver - // without that extension rejects the program in an info log nobody surfaces. These - // used to be bare 8s inside BuildTBuiltInResource with no backend consulted at all. - // The DEFAULTS are the GL 4.5 core minimums for the same reason MaxClipDistances' - // is: they describe the no-backend case (standalone compiles, unit tests). - Int MaxCullDistances = 8; - Int MaxCombinedClipAndCullDistances = 8; - Int MaxViewports = 16; - // GL_LAYER_PROVOKING_VERTEX / GL_VIEWPORT_INDEX_PROVOKING_VERTEX: which vertex of a - // primitive supplies gl_Layer and gl_ViewportIndex. GL 4.6 table 23.65 makes - // GL_UNDEFINED_VERTEX a legal answer for both, and it is the honest default - naming - // a convention is a statement about behaviour, so a backend that does not pin one - // must not claim it does. DirectGLES fills the layer one from the ES 3.2 query and - // the viewport one from GL_OES_viewport_array, and leaves UNDEFINED where the - // capability is absent: without the viewport array extension only viewport 0 is ever - // rasterized, so no convention selects anything. DirectVulkan keeps UNDEFINED for - // both - which vertex provokes is decided per pipeline by - // VulkanRenderer::SelectProvokingVertexMode out of VK_EXT_provoking_vertex, - // provokingVertexModePerPipeline and the topology, so no single convention is true - // of the backend. - Uint32 LayerProvokingVertex = GL_UNDEFINED_VERTEX; - Uint32 ViewportIndexProvokingVertex = GL_UNDEFINED_VERTEX; - Int MaxViewportWidth = 16384; - Int MaxViewportHeight = 16384; - Float ViewportBoundsRangeMin = 0.0f; - Float ViewportBoundsRangeMax = 0.0f; - Int ViewportSubpixelBits = 0; - // GL 4.x fragment-interpolation offset limits. These defaults are the - // core minimums and are replaced by live GLES/Vulkan device limits. - Float MinFragmentInterpolationOffset = -0.5f; - // For four fractional bits the greatest required legal offset is - // 0.5 - 2^-4 = 0.4375 (GL 4.6 table 23.70). - Float MaxFragmentInterpolationOffset = 0.4375f; - Int FragmentInterpolationOffsetBits = 4; - Bool SupportsWideLines = false; - // Whether a framebuffer whose depth and stencil attachments are distinct - // images can be rendered to. GL only requires support when both refer to the - // same image and lets an implementation answer GL_FRAMEBUFFER_UNSUPPORTED - // otherwise, which is what DirectVulkan (one combined attachment) and the - // real ES drivers behind DirectGLES both do. Defaults to true so a backend - // that never sets it keeps the permissive behaviour. - Bool SupportsDistinctDepthStencilAttachments = true; - // Whether attaching a single layer of a 3D or array texture to a framebuffer actually - // renders to that layer. DirectGLES hands the layer straight to - // glFramebufferTextureLayer, so it does; DirectVulkan maps a GL layer onto a Vulkan - // array layer with no notion of a 3D depth slice, so it does not yet. Defaults to false - // so a backend that never sets it gets the conservative answer. - // Which layered texture targets this backend can attach ONE layer of to a framebuffer - // and then really clear, render and read back that layer. Bit (1u << TextureTarget) is - // set for each supported target. Deliberately per target rather than one flag: the three - // ways a GL layer maps onto Vulkan are independent capabilities. A 2D or 2D multisample - // array layer IS a VkImage array layer and needs nothing extra; a 3D texture's layer is - // a z slice, which needs a 2D-array-compatible image and a per-slice clear that - // vkCmdClearColorImage cannot express; a cube map array needs an image shape and the - // imageCubeArray feature before it can be attached at any layer at all. Defaults to 0 so - // a backend that never sets it gets the conservative answer. - Uint32 PerLayerFramebufferAttachmentTargets = 0; - - static constexpr Uint32 PerLayerFramebufferAttachmentBit(TextureTarget target) { - return (static_cast(target) >= 0 && - static_cast(target) < static_cast(TextureTarget::TextureTargetCount)) - ? (1u << static_cast(target)) - : 0u; - } - - Bool SupportsPerLayerFramebufferAttachment(TextureTarget target) const { - const Uint32 bit = PerLayerFramebufferAttachmentBit(target); - return bit != 0 && (PerLayerFramebufferAttachmentTargets & bit) != 0; - } - // Whether this backend can CONSUME a shader module that still declares 64-bit floats, - // i.e. whether `double` survives the transpile instead of being narrowed to `float` - // (ShaderTranspiler::DemoteFloat64Pass). Detected, never assumed: - // * DirectVulkan sets it from VkPhysicalDeviceFeatures::shaderFloat64, the feature - // VUID-VkShaderModuleCreateInfo-pCode-08740 requires before a module declaring - // OpCapability Float64 may be created at all. lavapipe has it; Adreno and Mali - // both report VK_FALSE, so no real mobile device does. - // * DirectGLES can NEVER have it. GLSL ES has no 64-bit float type in any version - // or extension, so SPIRV-Cross cannot emit one ("FP64 not supported in ES - // profile") and the demotion there is mathematically mandatory, always. - // Defaults to false so a backend that never sets it - and the no-backend case, which - // is what standalone shader compiles and the unit tests run under - keeps the - // demotion, which is the behaviour that works everywhere. - Bool SupportsShaderFloat64 = false; - // Whether glVertexAttribLFormat / glVertexArrayAttribLFormat can be honoured, i.e. - // whether a 64-bit vertex attribute can actually reach a shader unconverted. Detected, - // never assumed: DirectVulkan needs VkPhysicalDeviceFeatures::shaderFloat64 (the - // attribute travels as its 32-bit word pair, so no VK_FORMAT_R64* is required, but the - // bitcast result is Float64); DirectGLES can never have it, ESSL having no fp64 type at - // all. Defaults to false so a backend that never sets it gets the conservative answer. - // - // INDEPENDENT of SupportsShaderFloat64, and it has to be: this flag decides a VkFormat - // from the VAO ATTRIBUTE alone, which does not know what type the shader declared, and - // glVertexAttribFormat(GL_DOUBLE) feeding a plain `in vec4` is both legal and common - // (KHR-GL43.vertex_attrib_binding.basic-input-case4/5, advanced-bindingUpdate). A - // backend with native fp64 that still cannot FETCH 64 bits keeps this false and relies - // on the per-MODULE rule in ShaderCompiler::SanitizeAndOptimizeBinary instead: a vertex - // module that declares a 64-bit float INPUT is demoted whole, so the two shader-side - // halves (PackDoubleVertexInputsPass and VertexInputStateFactory::ToVkVertexFormat) - // still see one consistent world. - Bool SupportsFloat64VertexAttributes = false; - // Whether a TESSELLATION stage of this backend may access gl_PointSize - i.e. - // whether a module declaring OpCapability TessellationPointSize can reach the - // driver at all. DirectVulkan sets both this and the geometry twin from the one - // shaderTessellationAndGeometryPointSize feature; DirectGLES sets them - // independently from the EXT/OES_tessellation_point_size / - // geometry_point_size extension pairs (PointSizeTier), which really do come - // separately. When absent, ProgramSpirvTask demotes the built-in to an ordinary - // varying program-wide (ShaderCompiler:: - // DemoteTessellationGeometryPointSizeForProgram); MOBILEGL_POINT_SIZE_DEMOTION - // overrides the detection in either direction at backend init. - // - // Defaults TRUE, deliberately against the house "assume absent" rule: false - // ARMS a rewrite, so the conservative no-backend answer (standalone compiles, - // unit tests) is the one that leaves modules untouched. A backend that never - // sets it gets standard modules and, at worst, the old honest declines. - Bool SupportsTessellationPointSize = true; - // The geometry-stage twin (OpCapability GeometryPointSize). - Bool SupportsGeometryPointSize = true; - Uint64 MaxShaderStorageBlockSize = 128 * 1024 * 1024; - Uint32 SubgroupSize = 0; - Uint32 SubgroupSupportedStages = 0; - Uint32 SubgroupSupportedFeatures = 0; - // GL_SAMPLE_POSITION (GL 4.6 core 14.3.1.1) as the device actually places the samples - // of a 2-, 4-, 8- and 16-sample pixel: (x, y) pairs in [0, 1], the N-sample row starting - // at pair N - 2. Bit log2(N) of SamplePositionCountMask says row N was filled in; a row - // the backend could not determine stays unset rather than being guessed. Read and - // written through Get/SetSamplePositions only. - static constexpr Int kMaxSamplePositionCount = 16; - Float SamplePositions[60] = {}; // 2 * (2 + 4 + 8 + 16) - Uint32 SamplePositionCountMask = 0; - Bool SubgroupQuadOperationsInAllStages = false; - GpuVendorKind GpuVendor = GpuVendorKind::Unknown; - // GL_EXT_fragment_shading_rate and its two companions. Each is a statement about the - // device, so all of them default to "absent" and the extension strings are advertised - // only from these: DirectGLES fills them from the driver's own - // EXT_fragment_shading_rate(_primitive, _attachment), DirectVulkan from - // VK_KHR_fragment_shading_rate's pipeline / primitive / attachment features. - // SupportsFragmentShadingRate glShadingRateEXT and gl_ShadingRateEXT - // SupportsPrimitiveFragmentShadingRate gl_PrimitiveShadingRateEXT, combiner op 0 - // SupportsAttachmentFragmentShadingRate the R8UI rate attachment, combiner op 1 - Bool SupportsFragmentShadingRate = false; - Bool SupportsPrimitiveFragmentShadingRate = false; - Bool SupportsAttachmentFragmentShadingRate = false; - // The four GL_FRAGMENT_SHADING_RATE_*_SUPPORTED_EXT booleans, as the device answers them. - Bool FragmentShadingRateNonTrivialCombiners = false; - Bool FragmentShadingRateWithShaderDepthStencilWrites = false; - Bool FragmentShadingRateWithSampleMask = false; - Bool FragmentShadingRatePrimitiveWithMultiViewport = false; - Bool FragmentShadingRateAttachmentWithDefaultFramebuffer = false; - // What glGetFragmentShadingRatesEXT answers, one mask per sample count 1, 2, 4 .. 64 - // (index = log2(samples)): bit (log2(w) << 2 | log2(h)) is set when that rate is - // honoured exactly at that sample count, i.e. gl_ShadingRateEXT reads it back - // unclamped. Zero masks mean no rate at all, which is what a sample count the device - // cannot shade coarsely answers. - Uint16 FragmentShadingRateMasks[7] = {}; - // The GL_{MIN,MAX}_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_{WIDTH,HEIGHT}_EXT, - // _ASPECT_RATIO_EXT and _LAYERS_EXT limits. Meaningful only with the attachment. - Int MinFragmentShadingRateAttachmentTexelWidth = 0; - Int MinFragmentShadingRateAttachmentTexelHeight = 0; - Int MaxFragmentShadingRateAttachmentTexelWidth = 0; - Int MaxFragmentShadingRateAttachmentTexelHeight = 0; - Int MaxFragmentShadingRateAttachmentTexelAspectRatio = 0; - Int MaxFragmentShadingRateAttachmentLayers = 0; - - - static constexpr Bool IsSamplePositionCount(Int samples) { - return samples >= 2 && samples <= kMaxSamplePositionCount && (samples & (samples - 1)) == 0; - } - static constexpr Uint32 SamplePositionCountBit(Int samples) { - return 1u << static_cast(std::countr_zero(static_cast(samples))); - } - // `xy` holds `samples` (x, y) pairs. - void SetSamplePositions(Int samples, const Float* xy) { - if (!IsSamplePositionCount(samples)) return; - for (Int i = 0; i < 2 * samples; ++i) SamplePositions[2 * (samples - 2) + i] = xy[i]; - SamplePositionCountMask |= SamplePositionCountBit(samples); - } - Bool GetSamplePosition(Int samples, Int index, Float* outXY) const { - if (!IsSamplePositionCount(samples) || index < 0 || index >= samples || - (SamplePositionCountMask & SamplePositionCountBit(samples)) == 0) { - return false; - } - outXY[0] = SamplePositions[2 * (samples - 2 + index)]; - outXY[1] = SamplePositions[2 * (samples - 2 + index) + 1]; - return true; - } - }; - enum class WindowBackend { Android, X11, @@ -977,5 +574,8 @@ namespace MobileGL { void DestroyPendingEGLSurfaceIfUnused(EGLSurface surface); void ReleaseEGLCurrentThread(const std::thread::id& threadKey); }; + + extern UniquePtr& pActiveBackendObject; + extern GlobalBackendFunctionsTable gBackendFunctionsTable; } // namespace MG_Backend } // namespace MobileGL diff --git a/MobileGL/MG_Backend/BackendObjects.h b/MobileGL/MG_Backend/BackendObjects.h index 76e48617..54be815b 100644 --- a/MobileGL/MG_Backend/BackendObjects.h +++ b/MobileGL/MG_Backend/BackendObjects.h @@ -13,9 +13,6 @@ #include "DirectVulkan/BackendObject_DirectVulkan.h" namespace MobileGL::MG_Backend { - extern UniquePtr& pActiveBackendObject; - extern GlobalBackendFunctionsTable gBackendFunctionsTable; - #if MOBILEGL_BUILD_DISAGGREGATED // P5 v1. The counterpart of Init()'s single split hook, and a NO-OP in every run that is // not split. It exists ONLY in a split build: G1 admits no new symbol in the pull build, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererInternal.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererInternal.h index 8a51450f..b79d61cb 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererInternal.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererInternal.h @@ -43,6 +43,7 @@ #include "MG_Util/SelfTest/PrimitivesGeneratedNoXfbProbe.h" #include "MG_Util/Texture/PixelStoreProcessor.h" #include +#include // P7 wave 0: the seam WireFramebuffer.cpp's MagmaWireFatal and WireDraw.cpp's // WireBufferLegacyFatal die through. Declared by MG_Pipe on purpose - see PipeSessionFail.h. #include diff --git a/MobileGL/MG_Pipe/MGPipeTypes.h b/MobileGL/MG_Pipe/MGPipeTypes.h index ba12cc35..6d39e590 100644 --- a/MobileGL/MG_Pipe/MGPipeTypes.h +++ b/MobileGL/MG_Pipe/MGPipeTypes.h @@ -17,10 +17,9 @@ // MGHostSpan, the one shape that changes with the transport, rides only in a variable tail. // Sizes are asserted because the generated wire records (generated/PipeWire.inc) are memcpy'd, // so a field changing width is a protocol break no other test would see. -// BackendObject.h supplies DynamicBackendParameters (embedded in MGPCaps), BackendType and, -// through TextureEnum.h, the texture enums; that reach into MG_Backend is why the include-purity -// gate (scripts/check_include_closure.py) checks MGPipeValueTypes.h rather than this header. -#include +// BackendCaps.h supplies DynamicBackendParameters (embedded in MGPCaps), BackendType and, +// through TextureEnum.h, the texture enums. +#include #include "MGPipeValueTypes.h" namespace MobileGL::MG_Pipe { diff --git a/MobileGL/MG_Remote/Client/SlotCaps.h b/MobileGL/MG_Remote/Client/SlotCaps.h index 5142c8d6..971bb3d5 100644 --- a/MobileGL/MG_Remote/Client/SlotCaps.h +++ b/MobileGL/MG_Remote/Client/SlotCaps.h @@ -19,6 +19,7 @@ #include #include +#include #if MOBILEGL_BUILD_DISAGGREGATED #include diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 6f4d11fb..5ee3714e 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -9,7 +9,7 @@ #include "Core.h" #include "MG_State/GLState/RenderbufferState/RenderbufferObject.h" #include "MG_State/EGLState/Core.h" -#include +#include #include #include #include diff --git a/MobileGL/MG_Test/Pipe/MagmaPipeIdentityTest.cpp b/MobileGL/MG_Test/Pipe/MagmaPipeIdentityTest.cpp index b317e2eb..6f24a3ee 100644 --- a/MobileGL/MG_Test/Pipe/MagmaPipeIdentityTest.cpp +++ b/MobileGL/MG_Test/Pipe/MagmaPipeIdentityTest.cpp @@ -24,7 +24,9 @@ using namespace MobileGL; namespace { +#if MOBILEGL_BUILD_DISAGGREGATED using namespace MobileGL::MG_Backend::DirectVulkan; +#endif using MG_Pipe::MGPipeHandle; // Historical test name retained. Each backend's readiness is now a real gate: diff --git a/MobileGL/MG_Util/SelfTest/DriverPost.h b/MobileGL/MG_Util/SelfTest/DriverPost.h index 543e6d77..1d3731cb 100644 --- a/MobileGL/MG_Util/SelfTest/DriverPost.h +++ b/MobileGL/MG_Util/SelfTest/DriverPost.h @@ -10,7 +10,7 @@ #include "DriverBugProbes.h" #include -#include +#include namespace MobileGL::MG_Util::SelfTest { // One row of a backend power-on self-test (POST) report. diff --git a/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp b/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp index 9afff0eb..bcd97b15 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.cpp @@ -7,7 +7,7 @@ // End of Source File Header #include "CompileEnv.h" -#include +#include #include namespace MobileGL::MG_Util::ShaderTranspiler { diff --git a/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.h b/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.h index 01235d4f..6bb70d5d 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.h +++ b/MobileGL/MG_Util/ShaderTranspiler/CompileEnv.h @@ -9,7 +9,7 @@ #pragma once #include #include -#include +#include namespace MobileGL::MG_Util::ShaderTranspiler { // GL_MAX_COMPUTE_WORK_GROUP_COUNT / _SIZE core minimums (GL 4.6 core table 23.45), in ONE diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 0192a378..8513bfff 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -64,7 +64,7 @@ #include "source/opt/ir_context.h" #include "ShaderSourceProcessor.h" -#include +#include #include #include #include diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp index 187162a4..89358ca5 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderSourceProcessor.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/scripts/gen_pipe.py b/scripts/gen_pipe.py index 69b1d7f0..6ab4ea61 100644 --- a/scripts/gen_pipe.py +++ b/scripts/gen_pipe.py @@ -226,12 +226,13 @@ def parse_calls(): MEMCMP_FALLBACK_TYPES = set() # Where the structs named in PipeFields.def are declared: the payload header, the value -# header, the host-span header and - for DynamicBackendParameters, the caps block - the -# backend object header. +# header, the host-span header, the backend capability header (DynamicBackendParameters, the +# caps block) and the backend object header. FIELD_LIST_STRUCT_HEADERS = [ os.path.join(PIPE_DIR, "MGPipeTypes.h"), os.path.join(PIPE_DIR, "MGPipeValueTypes.h"), os.path.join(PIPE_DIR, "MGPipeHostSpan.h"), + os.path.join(REPO_ROOT, "MobileGL", "BackendCaps.h"), FUNCTION_TABLE_HEADER, ] BACKEND_DIR = os.path.join(REPO_ROOT, "MobileGL", "MG_Backend")