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[] + + +