Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigation: Multisample Coverage #267

Closed
Jiawei-Shao opened this issue Apr 11, 2019 · 9 comments
Closed

Investigation: Multisample Coverage #267

Jiawei-Shao opened this issue Apr 11, 2019 · 9 comments

Comments

@Jiawei-Shao
Copy link
Contributor

Jiawei-Shao commented Apr 11, 2019

Introduction

When the color attachment is multi-sampled, the explicit APIs provide the following additional configurations to affect the coverage of the samples for each pixel:

  • Sample Mask (In pipeline state object)
  • Sample Mask (In fragment shader)
  • Alpha-to-coverage

This document discusses how to expose these configurations to WebGPU.

Native APIs

D3D12

D3D12 supports specifying a 32-bit UINT as the sample mask in the structure D3D12_GRAPHICS_PIPELINE_STATE_DESC.

D3D12 supports SV_Coverage in HLSL as a mask in pixel shaders.

D3D12 supports enabling alpha-to-coverage in D3D12_BLEND_DESC. According to the document for alpha-to-coverage on MSDN, “AlphaToCoverageEnable is used to toggle if the runtime converts the .a component (alpha) of output register SV_Target0 from the pixel shader to an n-step coverage mask (given an n-sample RenderTarget). The runtime performs an AND operation of this mask with the typical sample coverage for the pixel in the primitive (in addition to the sample mask) to determine which samples to update in all the active RenderTargets”.

Note that D3D12 document mentions “if the pixel shader outputs SV_Coverage, the runtime disables alpha-to-coverage”.

D3D12 document also mentions "graphics hardware doesn't precisely specify exactly how it converts pixel shader SV_Target0.a (alpha) to a coverage mask. Hardware might perform area dithering to provide some better quantization of alpha values at the cost of spatial resolution and noise."

Metal

Metal does not support the configuration of sample mask in MTLRenderPipelineDescriptor.

According to Metal Shading Language Specification (Chapter 5.2.3.4, Chapter 5.2.3.5), Metal shading language supports an uint attribute [[sample_mask]] as the coverage mask.

Metal supports enabling alpha-to-coverage in the rendering pipeline through alphaToCoverageEnabled in MTLRenderPipelineDescriptor.

Metal document provides a Pseudo-Code for computing a coverage mask. This Pseudo-Code implies alpha-to-coverage only works for colorAttachment0.

if (alphaToCoverageEnabled) then
    alphaCoverageMask = coverageToMask(colorAttachment0.alpha);
finalCoverageMask = originalRasterizerCoverageMask & alphaCoverageMask & fragShaderSampleMaskOutput;

Metal document mentions the algorithm to convert the alpha channel of the fragment output to a coverage mask is implementation-dependent.

Vulkan

Vulkan supports configuring sample mask in the rendering pipeline by setting pSampleMask in VkPipelineMultisampleStateCreateInfo. pSampleMask is an array of uint32_t, which means Vulkan allows sample mask to have more than 32 bits.

SPIR-V supports SampleMask as a built-in variable in fragment shader.

Vulkan supports enabling alpha-to-coverage in the rendering pipeline by setting alphaToCoverageEnable in VkPipelineMultisampleStateCreateInfo to VK_TRUE.

Vulkan specification mentions alpha-to-coverage only works for “the alpha component of the fragment shader output that has a Location and Index decoration of zero”, and “if alphaToCoverageEnable is enabled, a temporary coverage value with rasterizationSamples bits is generated where each bit is determined by the fragment’s alpha value. The temporary coverage value is then ANDed with the fragment coverage value to generate a new fragment coverage value”.

Vulkan specification mentions “no specific algorithm is specified for converting the alpha value to a temporary coverage mask”.

Proposal

Here is the summary on the support of multisample coverage on D3D12, Metal and Vulkan.

D3D12 Metal Vulkan
Sample Mask (PSO) Yes No Yes
Sample Mask (shader) Yes Yes Yes
Alpha-to-coverage Yes for SV_target0 when pixel shader doesn’t output SV_Coverage Yes for colorAttachments0 (?) Yes for the fragment shader output that has a Location and Index decoration of 0

Based on the above investigations, one feasible way to support the configuration on multisample coverage in WebGPU is as follows:

  • Do not expose sample mask in pipeline state objects because Metal does not support such usage. Another choice is to re-compile the fragment shader when building pipeline state object on Metal.
  • Support sample mask in the fragment shader as it is available in HLSL, Metal shading language and SPIR-V.
  • Support alpha-to-coverage and always disable alpha-to-coverage when the fragment shader outputs sample mask. It is because on D3D12 when the pixel shader outputs SV_Coverage, the runtime always disables alpha-to-coverage.

Note that another challenge to support alpha-to-coverage in WebGPU is the algorithm of converting the alpha value to a coverage mask is implementation-dependent, which means the behaviors of alpha-to-coverage may differ among different WebGPU implementations.

@Jiawei-Shao
Copy link
Contributor Author

@Kangz @kainino0x @kvark @litherum @RafaelCintron PTAL, thanks!

@Kangz
Copy link
Contributor

Kangz commented Apr 15, 2019

LGTM, we'll have to ask IHVs if only doing the sample mask in the shader is slower than specifiying it at the pipeline level.

That the alpha-to-coverage computation isn't defined is really unfortunate. We should see if we can emulate it to be consistent but I worry that it might be slower than specifying at the pipeline level as well: looking at open-source Vulkan drivers, it seems like it is a hardware state.

@kvark
Copy link
Contributor

kvark commented Apr 22, 2019

@Jiawei-Shao thank you for the detailed investigation!

Do not expose sample mask in pipeline state objects because Metal does not support such usage. Another choice is to re-compile the fragment shader when building pipeline state object on Metal.

At first, this doesn't look like a big issue to me. The Metal backend can just provide a value to the shader to AND with, effectively producing the same result. It's not "re-compiling" per se - when using SPIRV-Cross we generate the shader code at pipeline creation anyway (not shader module creation), so by this time (and it's the first and only compilation of the shader) we know if the sample mask needs to be introduced.

There is a problem, however, that just occurred to me. In Vulkan and D3D12 we can set the sample mask for a pipeline and at the same time enable alpha-to-coverage. The output of that would be ANDed with the sample mask. If we emulate the mask with the shader logic, this would be conflicting with alpha-to-coverage (since the shader can't output both that alpha and the sample mask...). We could think of a way for hand-rolled alpha-to-coverage logic in Metal backend in this case, but anyway this seems pretty annoying.

@Jiawei-Shao
Copy link
Contributor Author

@kvark thanks for your comment!

In Vulkan and D3D12 we can set the sample mask for a pipeline and at the same time enable alpha-to-coverage. The output of that would be ANDed with the sample mask.

According to D3D12 document:

If the pixel shader outputs SV_Coverage then the runtime disables alpha-to-coverage.

So I think in WebGPU if sample mask is in the output of fragment shader, then alpha-to-coverage should always be disabled.

@kvark
Copy link
Contributor

kvark commented Apr 23, 2019

@Jiawei-Shao I think you misunderstood my concern. I'm talking about the case where the Metal backend would use SV_Coverage-like semantics internally since its pipeline state doesn't support masking. The user would still expect alpha-to-coverage to work in this case.

@Jiawei-Shao
Copy link
Contributor Author

@kvark sorry that I am still a bit puzzled about your comment.
The Metal document provides a formula to calculate the multisample coverage. According to this formula, it seems on Metal alpha-to-coverage and sample mask in the fragment shader can work together.

if (alphaToCoverageEnabled) then
    alphaCoverageMask = coverageToMask(colorAttachment0.alpha);
finalCoverageMask = originalRasterizerCoverageMask & alphaCoverageMask & fragShaderSampleMaskOutput;

@kvark
Copy link
Contributor

kvark commented Apr 23, 2019

@Jiawei-Shao great, then we have no problem with emulating the pipeline sample mask in Metal. I think we should expose it then in the API.

@grorg
Copy link
Contributor

grorg commented Apr 29, 2019

Discussed at the 29 April 2019 meeting.

Jiawei-Shao added a commit to Jiawei-Shao/gpuweb that referenced this issue May 7, 2019
…iptor

This patch adds sample mask and alpha-to-coverage state to the render
pipeline descriptor after the discussion in issue gpuweb#267.
Kangz pushed a commit that referenced this issue Jun 3, 2019
…iptor (#282)

This patch adds sample mask and alpha-to-coverage state to the render
pipeline descriptor after the discussion in issue #267.
@Kangz
Copy link
Contributor

Kangz commented Sep 2, 2021

Closing. sampleMask and alphaToCoverage have been added to WebGPU.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants