From 546e932f0bec8de29b614980b54c70948b5acda3 Mon Sep 17 00:00:00 2001 From: Michal Krol Date: Wed, 6 May 2026 09:21:16 +0200 Subject: [PATCH] Add VK_EXT_fragment_coverage_mask Publish VK_EXT_fragment_coverage_mask, exposing the SPV_EXT_fragment_coverage_mask SPIR-V extension to Vulkan. The new FragmentCoverageMaskEXT fragment shader input built-in contains the complete set of samples covered by the fragment regardless of the fragment shader's execution mode, addressing the limitation that SampleMask is masked to the sample(s) processed by the current invocation under per-sample shading. Adds VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT to query and enable the feature, registers the SPV_EXT_fragment_coverage_mask SPIR-V extension and FragmentCoverageEXT capability, and adds the proposal, appendix, and built-in variable description. --- appendices/VK_EXT_fragment_coverage_mask.adoc | 46 +++++++ chapters/features.adoc | 24 ++++ chapters/interfaces.adoc | 36 ++++++ proposals/VK_EXT_fragment_coverage_mask.adoc | 113 ++++++++++++++++++ xml/vk.xml | 20 +++- 5 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 appendices/VK_EXT_fragment_coverage_mask.adoc create mode 100644 proposals/VK_EXT_fragment_coverage_mask.adoc diff --git a/appendices/VK_EXT_fragment_coverage_mask.adoc b/appendices/VK_EXT_fragment_coverage_mask.adoc new file mode 100644 index 000000000..03a06ea60 --- /dev/null +++ b/appendices/VK_EXT_fragment_coverage_mask.adoc @@ -0,0 +1,46 @@ +// Copyright 2026 The Khronos Group Inc. +// +// SPDX-License-Identifier: CC-BY-4.0 + +include::{generated}/meta/{refprefix}VK_EXT_fragment_coverage_mask.adoc[] + +=== Other Extension Metadata + +*Last Modified Date*:: + 2026-06-15 +*Contributors*:: + - Michal Krol, Broadcom + - Brian Paul, Broadcom + - Roland Scheidegger, Broadcom + +=== Description + +This extension adds support for the code:FragmentCoverageEXT capability +from the `SPV_EXT_fragment_coverage_mask` SPIR-V extension to Vulkan, +introducing a new fragment shader input built-in, +code:FragmentCoverageMaskEXT, that contains the complete set of samples +covered by a fragment regardless of the fragment shader's execution mode. + +The existing code:SampleMask input is masked to only include the bits +corresponding to the sample(s) being processed by the current invocation +when the fragment shader executes in a per-sample shading mode. +This makes it impossible for the shader to determine the entire set of +samples covered by the fragment, which in turn makes it difficult to port +shaders that rely on the equivalent input coverage mask available in other +APIs such as D3D10. + +The code:FragmentCoverageMaskEXT built-in always contains the full +coverage mask, equivalent to the input coverage mask available in those +APIs, where each set bit represents a sample covered by the fragment. + +include::{generated}/interfaces/VK_EXT_fragment_coverage_mask.adoc[] + +=== New SPIR-V Capabilities + + * <> + +=== Version History + + * Revision 1, 2026-06-15 (Michal Krol) + ** Initial revision (promoted from `VK_MESA_fragment_coverage_mask`) diff --git a/chapters/features.adoc b/chapters/features.adoc index 424e6a899..e617be9b9 100644 --- a/chapters/features.adoc +++ b/chapters/features.adoc @@ -3098,6 +3098,30 @@ include::{generated}/validity/structs/VkPhysicalDeviceFragmentShaderInterlockFea -- endif::VK_EXT_fragment_shader_interlock[] +ifdef::VK_EXT_fragment_coverage_mask[] +[open,refpage='VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT',desc='Structure describing the fragment coverage mask feature that can be supported by an implementation',type='structs'] +-- +The sname:VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT structure is +defined as: + +include::{generated}/api/structs/VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT.adoc[] + +This structure describes the following feature: + + * pname:sType is a elink:VkStructureType value identifying this structure. + * pname:pNext is `NULL` or a pointer to a structure extending this + structure. + * [[features-fragmentCoverageMask]] + pname:fragmentCoverageMask indicates that the implementation supports + the code:FragmentCoverageEXT SPIR-V capability. + +:refpage: VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT +include::{chapters}/features.adoc[tag=features] + +include::{generated}/validity/structs/VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT.adoc[] +-- +endif::VK_EXT_fragment_coverage_mask[] + ifdef::VK_NV_cooperative_matrix[] [open,refpage='VkPhysicalDeviceCooperativeMatrixFeaturesNV',desc='Structure describing cooperative matrix features that can be supported by an implementation',type='structs'] -- diff --git a/chapters/interfaces.adoc b/chapters/interfaces.adoc index 80ac31248..96df09a35 100644 --- a/chapters/interfaces.adoc +++ b/chapters/interfaces.adoc @@ -4835,6 +4835,42 @@ the processing of a fragment. **** -- +ifdef::VK_EXT_fragment_coverage_mask[] +[[interfaces-builtin-variables-fragmentcoveragemaskext]] +[open,refpage='FragmentCoverageMaskEXT',desc='Full coverage mask for a fragment',type='builtins'] +-- +:refpage: FragmentCoverageMaskEXT + +code:FragmentCoverageMaskEXT:: + +Decorating a variable with the code:FragmentCoverageMaskEXT built-in +decoration will make any variable contain the complete set of samples +covered by the fragment, regardless of the fragment shader's execution +mode. ++ +Unlike code:SampleMask, the value of code:FragmentCoverageMaskEXT is not +masked to the sample(s) being processed by the current invocation when +the fragment shader is executing in a +<> mode. ++ +A variable decorated with code:FragmentCoverageMaskEXT is an array of +integers. +Bits are mapped to samples in a manner where bit B of mask M +(`FragmentCoverageMaskEXT[M]`) corresponds to sample +[eq]#32 {times} M {plus} B#. + +.Valid Usage +**** + * The code:FragmentCoverageMaskEXT decoration must: be used only within + the code:Fragment {ExecutionModel} + * The variable decorated with code:FragmentCoverageMaskEXT must: be + declared using the code:Input {StorageClass} + * The variable decorated with code:FragmentCoverageMaskEXT must: be + declared as an array of 32-bit integer values +**** +-- +endif::VK_EXT_fragment_coverage_mask[] + [[interfaces-builtin-variables-sampleposition]] [open,refpage='SamplePosition',desc='Position of a shaded sample',type='builtins'] -- diff --git a/proposals/VK_EXT_fragment_coverage_mask.adoc b/proposals/VK_EXT_fragment_coverage_mask.adoc new file mode 100644 index 000000000..54ede3e2c --- /dev/null +++ b/proposals/VK_EXT_fragment_coverage_mask.adoc @@ -0,0 +1,113 @@ +// Copyright 2026 The Khronos Group Inc. +// +// SPDX-License-Identifier: CC-BY-4.0 + += VK_EXT_fragment_coverage_mask +:toc: left +:docs: https://docs.vulkan.org/spec/latest/ +:refpages: https://docs.vulkan.org/refpages/latest/refpages/source/ +:sectnums: + +This document describes a proposal for a new fragment shader input +built-in that exposes the complete coverage mask of a fragment, regardless +of the fragment shader's execution mode. + + +== Problem Statement + +The existing +SampleMask+ input built-in is intended to provide the set +of samples that contribute to a fragment. +However, when a fragment shader executes in a per-sample shading mode - +e.g. when a fragment shader input is decorated with +SampleId+ or ++SamplePosition+ - +SampleMask+ is masked to only include the bits +corresponding to the specific sample(s) currently being processed by that +invocation. +In the case of full per-sample shading (one invocation per sample), this +results in only a single bit being set. +When a sample shading rate is specified (e.g. via Vulkan's +*minSampleShading* parameter), an invocation *may* process a subset of +samples, and +SampleMask+ will contain the corresponding subset of +bits. + +This constrained behavior makes it impossible for the shader to +determine the entire set of samples covered by the fragment. +Specifically, it is impossible to directly map the functionality of the +input coverage mask in the D3D10 API, making it very difficult to port +some D3D10 shaders to SPIR-V. +Furthermore, when per-sample shading is triggered by a sample shading +rate rather than by the use of +SampleId+ or +SamplePosition+, +those built-in variables are not available, making it impossible to +derive the full coverage mask from +SampleMask+ alone. + + +== Solution Space + +There are a few options for exposing the complete coverage mask: + + . Change the semantics of +SampleMask+ to always contain the full + coverage mask. + This is not viable - it would silently break existing shaders that + rely on the current per-invocation behavior. + . Add an execution mode that toggles +SampleMask+ between the + current behavior and the new full-coverage behavior. + This is workable but conflates two semantically distinct values into + a single name, complicating shader code that needs both, and + introducing a global mode where a per-variable distinction would be + cleaner. + . Introduce a new built-in input that always contains the full + coverage mask, leaving +SampleMask+ unchanged. + Selected. + +A new built-in is the cleanest mapping to the D3D10 input coverage mask, +keeps the per-sample semantics of +SampleMask+ intact, and lets a +shader access both values in the same invocation if needed. + + +== Proposal + +=== New SPIR-V capability and built-in + +A new SPIR-V capability, +FragmentCoverageEXT+, and a new fragment +shader input built-in, +FragmentCoverageMaskEXT+, are introduced by +the `SPV_EXT_fragment_coverage_mask` SPIR-V extension. + +A variable in the +Input+ storage class decorated with ++FragmentCoverageMaskEXT+ contains the complete set of samples +covered by the fragment, regardless of the fragment shader's execution +mode. +Bits are mapped to samples in the same manner as +SampleMask+: bit B +of mask M corresponds to sample [eq]#32 {times} M {plus} B#. + +Note that when +SampleId+ is available, fragment shaders using ++FragmentCoverageMaskEXT+ can recover the value of +SampleMask+ +from +FragmentCoverageMaskEXT+ by computing a bitwise AND with a bit +set at position +SampleId+. + +=== New Vulkan feature + +[source,c] +---- +typedef struct VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 fragmentCoverageMask; +} VkPhysicalDeviceFragmentCoverageMaskFeaturesEXT; +---- + +The *fragmentCoverageMask* feature indicates whether the +implementation supports the +FragmentCoverageEXT+ capability and the ++FragmentCoverageMaskEXT+ built-in in fragment shaders consumed by +the implementation. + + +== Issues + +=== How does this extension interact with +PostDepthCoverage+? + +*RESOLVED*: +FragmentCoverageMaskEXT+ effectively contains the union +of the +SampleMask+ values across all concurrent per-sample shader +invocations. +If the +PostDepthCoverage+ execution mode is declared (along with ++EarlyFragmentTests+), +FragmentCoverageMaskEXT+ reflects the +coverage after the early fragment tests are applied, mirroring the +behavior +SampleMask+ has under +PostDepthCoverage+. diff --git a/xml/vk.xml b/xml/vk.xml index 4d66622ef..dc9104327 100644 --- a/xml/vk.xml +++ b/xml/vk.xml @@ -5990,6 +5990,11 @@ typedef void* MTLSharedEvent_id; VkBool32 fragmentShaderPixelInterlock VkBool32 fragmentShaderShadingRateInterlock + + VkStructureType sType + void* pNext + VkBool32 fragmentCoverageMask + VkStructureType sType void* pNext @@ -32202,10 +32207,13 @@ endif::VK_KHR_internally_synchronized_queues[] - + - - + + + + + @@ -34031,6 +34039,9 @@ endif::VK_KHR_internally_synchronized_queues[] + + + @@ -34642,6 +34653,9 @@ endif::VK_KHR_internally_synchronized_queues[] + + +