-
Notifications
You must be signed in to change notification settings - Fork 329
Description
Introduction
Multisampled anti-aliasing (MSAA) is an important graphics technique to improve the quality of rendering geometry primitives, which is well supported in D3D12, Metal and Vulkan. This report investigates how to support multisampled render targets in WebGPU.
Native APIs
D3D12
The first step to use MSAA is to create a multisampled texture. It may be implementation-dependent if we can create a multisampled texture with a given format and sample count or not.
On D3D12 we can get this information from a D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS structure after calling ID3D12Device::CheckFeatureSupport using “D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS” as its parameter “format”.
Here is the definition of D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS:
typedef struct D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS {
DXGI_FORMAT Format;
UINT SampleCount;
D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS Flags;
UINT NumQualityLevels;
} D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS;
- Format and SampleCount are input parameters to specify the format and sample count.
- NumQualityLevels specifies the number of quality levels supported for the given format and sample count. MSDN remarks that 0 quality level means the hardware does not support multisampling for the particular format and the higher the quality, the lower the performance.
- Flags is in D3D12_MULTISAMPLE_QUALITY_LEVEL_FLAGS type that indicates if the quality levels can be determined for tiled resources. We do not need to care about this flag because WebGPU does not support tiled resources right now.
D3D12 guarantees that FEATURE_LEVEL_11_0 devices must support 4xMSAA for all render target formats, and 8xMSAA for all render target formats except R32G32B32A32 formats.
To create a multisampled texture, we need to specify the member “SampleDesc” of the structure D3D12_RESOURCE_DESC used in ID3D12Device::CreateCommittedResource. The type of “SampleDesc” is DXGI_SAMPLE_DESC. Here is the definition of this structure:
typedef struct DXGI_SAMPLE_DESC {
UINT Count;
UINT Quality;
} DXGI_SAMPLE_DESC;
- Count specifies the sample count of the multisampled texture.
- Quality specifies the quality level of the multisampled texture. D3D11 defines a standard multisample pattern, which is used to specify the sample positions when Quality is specified to D3D11_STANDARD_MULTISAMPLE_PATTERN, however D3D12 does not define such enum.
We need to create a render target view on a multisampled texture before using it as a render target. The field “ViewDimension” in D3D12_RENDER_TARGET_VIEW_DESC is required be D3D12_RTV_DIMENSION_TEXTURE2DMS or D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY.
We also need to set the multisample state to the pipeline state before we can render into a multisampled texture. In the D3D12_GRAPHICS_PIPELINE_STATE_DESC structure, we need to enable MSAA by setting field “RasterizerState.MultisampleEnable” to true, and also configure its member “SampleDesc”. “SampleDesc” is also a DXGI_SAMPLE_DESC structure, whose fields “Count” and “Quality” must be the same as the multisampled texture to be rendered into. This means if MSAA is enabled all bound render targets and depth buffers must have the same sample counts and quality levels.
D3D12 does not support implicitly resolve the multisampled texture to a non-multisampled texture to present (See the “technical note” part of this document). Thus, we must achieve this purpose with ID3D12GraphicsCommandList::ResolveSubresource. We need to insert barriers to transition the usage of the source texture to D3D12_RESOURCE_STATE_RESOLVE_SOURCE and the destination texture to D3D12_RESOURCE_STATE_RESOLVE_DEST before calling ResolveSubResource.
D3D12 does not support resolving depth or stencil textures according to this document.
Here is the summary of supporting multisampled render targets and resolve operations in D3D12:
- Format and Sample Count support
FEATURE_LEVEL_11_0 devices are guaranteed to support 4xMSAA on all render target formats, and 8xMSAA on all render target formats except R32G32B32A32 formats. - Sample Positions
D3D11 defines a standard multisample pattern, but there is no such definition in D3D12. - Multisample States
We need to enable MSAA, set proper sample count and quality level in the D3D12_GRAPHICS_PIPELINE_STATE_DESC when creating the graphics pipeline. - Resolve multisample textures
- D3D12 does not support implicitly resolving a multisampled render targets in a rendering pass.
- D3D12 provides “ResolveSubResource” to resolve a multisampled resource into a non-multisampled resource.
- D3D12 does not support resolving the textures in depth or stencil formats.
Metal
On Metal we can query if a device can support the given sample count by supportsTextureSampleCount method. Metal supports 4xMSAA on all devices, and 8xMSAA on some macOS devices.
The default multisample pattern in Metal is the same as the one of D3D11. Metal begins to support programmable sample positions since Metal 2.
To create a multisampled texture on Metal, we must specify the texture type to be “MTLTextureType2DMultisample” and the sample count of the texture in the field “sampleCount” of MTLTextureDescriptor.
When creating Metal rendering pipeline, we also need to configure the MSAA states. The sampleCount value of all the render targets must match the field “sampleCount” of the class MTLRenderPipelineDescriptor used for creating the rendering pipeline.
Metal can only resolve multisampled textures in the render pass. Metal supports setting the target as the “resolveTexture” field of MTLRenderPassAttachmentDescriptor. “resolveLevel”, “resolveSlice” and “resolveDepthPlane” can be configured to specify the part of the texture as the target of the resolve action.
We are also required to specify proper store actions in the “storeAction” to enable MSAA resolve action in Metal render pass. Metal defines two values for MSAA resolve actions:
- MTLStoreActionMultisampleResolve: the multisample values are resolved into single sample values that are stored in the texture specified by the resolveTexture property. The contents of the attachment are left undefined.
- MTLStoreActionStoreAndMultisampleResolve: the valid multisample values are stored into the attachment. MTLStoreActionStoreAndMultisampleResolve is not supported on some feature sets according to the note in the “Choose Appropriate Store Action” section of this document.
Metal supports resolve action on depth and stencil attachments. We can specify depthResolveFilter and stencilResolveFilter in MTLRenderPassDepthAttachmentDescriptor and MTLRenderPassStencilAttachmentDescriptor.
Metal does not provide methods for explicit resolve action. We can only perform resolve action in a render pass.
Here is the summary of supporting multisampled render targets and resolve operations in Metal:
- Format and Sample Count support
Metal guarantees supporting 4xMSAA on all devices, and 8xMSAA on some macOS devices. - Sample Positions
The default sample pattern of Metal is the same as the one of D3D11. Metal 2 supports programmable sample positions. - Multisample States
We need to set the sample count when creating a rendering pipeline. - Resolve multisample textures
- We need to set proper storeActions and configure the resolveTexture to enable multisample resolve action in the render pass.
Metal supports two store actions for resolve action in a render pass.- MTLStoreActionMultisampleResolve leaves the contents in the multisampled attachment undefined.
- MTLStoreActionStoreAndMultisampleResolve keeps the contents valid, thus can be used to perform store-and-resolve action. MTLStoreActionStoreAndMultisampleResolve can not be supported on all feature sets.
- Metal does not provide API for explicit resolve action.
- We need to set proper storeActions and configure the resolveTexture to enable multisample resolve action in the render pass.
Vulkan
On Vulkan we can query the sample counts supported for the given image format with vkGetPhysicalDeviceImageFormatProperties. We can get the supported sample counts from the field “sampleCounts” of structure VkImageFormatProperties after calling this function.
We can query the color sample counts that are supported for all framebuffer color attachments with floating- or fixed-point formats from the “limits.framebufferColorSampleCounts” field of the VkPhysicalDeviceProperties structure after calling vkGetPhysicalDeviceProperties. We can also get the supported depth/stencil sample counts for all framebuffer depth/stencil attachments from the “limits.framebufferDepthSampleCounts” and “limits.framebufferStencilSampleCounts” fields of this structure.
Vulkan guarantees supporting 4xMSAA on all color, depth and stencil formats according to Vulkan SPEC (Table 48. Required Limits).
Vulkan devices use a standard multisample pattern if “limits.standardSampleLocations” is VK_TRUE. The Vulkan standard multisample pattern has been universally supported. The standard sample positions of Vulkan is the same as the one of D3D11.
To create a multisampled texture in Vulkan, we need to specify the sample counts in the “VkSampleCountFlagBits” field of the VkImageCreateInfo structure.
Some configurations about MSAA are required in a Vulkan pipeline according to the definition of VkGraphicsPipelineCreateInfo. Here are the fields relevant to MSAA states in this structure:
typedef struct VkGraphicsPipelineCreateInfo {
…
const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
…
VkRenderPass renderPass;
…
} VkGraphicsPipelineCreateInfo;
- pMultisampleState includes several MSAA configurations for the graphics pipeline. Here is the definition of VkPipelineMultisampleStateCreateInfo structure:
typedef struct VkPipelineMultisampleStateCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineMultisampleStateCreateFlags flags;
VkSampleCountFlagBits rasterizationSamples;
VkBool32 sampleShadingEnable;
float minSampleShading;
const VkSampleMask* pSampleMask;
VkBool32 alphaToCoverageEnable;
VkBool32 alphaToOneEnable;
} VkPipelineMultisampleStateCreateInfo;
We need to set the sample counts of the render target in the “rasterizationSamples” field. Other fields in the VkPipelineMultisampleStateCreateInfo structure will not be discussed in this report.
- renderPass is a handle to a render pass object that describes the environment in which the pipeline will be used. We should set the sample count into the “samples” field of the VkAttachmentDescription structure when creating a Vulkan attachment for the “pAttachments” field of VkRenderPassCreateInfo.
Vulkan supports resolve action on color attachments in a Vulkan subpass. Here is the definition of VkSubpassDescripton structure.
typedef struct VkSubpassDescription {
VkSubpassDescriptionFlags flags;
VkPipelineBindPoint pipelineBindPoint;
uint32_t inputAttachmentCount;
const VkAttachmentReference* pInputAttachments;
uint32_t colorAttachmentCount;
const VkAttachmentReference* pColorAttachments;
const VkAttachmentReference* pResolveAttachments;
const VkAttachmentReference* pDepthStencilAttachment;
uint32_t preserveAttachmentCount;
const uint32_t* pPreserveAttachments;
} VkSubpassDescription;
To enable resolve action, we need to configure the resolve targets by setting an array of VkAttachmentReference in the “pResolveAttachments” field of the structure, then we set the subpass created by this structure to the “pSubpasses” field of a VkRenderPassCreateInfo structure to create the render pass with resolve action enabled. Specifying the “attachment” field of the VkAttachmentReference to be VK_ATTACHMENT_UNUSED indicates the resolve on this color attachment will be skipped.
VkAttachmentDescription supports two store operations:
- VK_ATTACHMENT_STORE_OP_STORE specifies the contents generated during the render pass and within the render area are written to memory. If we use this store operation, the multisampled textures will contain valid data.
- VK_ATTACHMENT_STORE_OP_DONT_CARE specifies the contents of the attachment will be undefined inside the render area. If we use this store operation, the performance will be improved as all the original contents in the multisampled textures are treated as invalid ones.
Vulkan defines Render Pass Compatibility Rules for both pipelines and framebuffers. Vulkan requires the pipeline must only be used with an instance of any render pass compatible with the one used by the framebuffer, which means all the resolve attachments configured in the pipeline must be compatible with those in the framebuffer when we want to use them together.
Vulkan provides optimization if we do not care about the contents in the multisampled attachments:
When we create the multisampled texture, we can set VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT flag in the “usage” field of VkImageCreateInfo structure so that the memory bound to this image can be allocated with VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, which means the memory is allocated lazily. We will not expose this flag to WebGPU but it could be useful in the WebGPU implementations on Vulkan.
Vulkan provides vkCmdResolveImage to perform explicit resolve action. However its performance may be poor on some Vulkan implementations (for example, on tilling GPUs). This API does not support the resolve of depth/stencil images.
Here is the summary of supporting multisampled render targets and resolve operations in Vulkan:
- Format and Sample Count support
- We can query the color sample counts that are supported for all framebuffer color attachments with floating- or fixed-point formats from VkPhysicalDeviceProperties structure after calling vkGetPhysicalDeviceProperties.
- We can also query the sample counts supported for the given image format with vkGetPhysicalDeviceImageFormatProperties.
- Vulkan guarantees supporting 4xMSAA on all color, depth and stencil formats.
- Sample Positions
The standard multisample positions of Vulkan is the same as the one of D3D11, which has been universally supported. - Multisample States
We need to configure the MSAA states when creating Vulkan pipelines and attachments.- Vulkan requires the pipeline must only be used with an instance of any render pass compatible with the one used by the framebuffer.
- Resolve multisample textures
- Vulkan supports resolve action in the render pass. The “pResolveAttachments” field of the VkSubpassDescripton structure is used to set the destination of resolve action.
- Vulkan provides two store operations to specify if the contents in the attachments will be dropped or not.
- VK_ATTACHMENT_STORE_OP_STORE specifies the contents in the multisampled attachments will be valid ones.
- VK_ATTACHMENT_STORE_OP_DONT_CARE specifies the contents of the attachment will be undefined inside the render area.
- Vulkan provides optimization if we only care the contents in the resolving target: When we create the multisampled texture, We can set the VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT so that the memory bound to this image can be allocated lazily.
- Vulkan provides vkCmdResolveImage to perform explicit resolve action on the images in color formats. However its performance may not be good on some Vulkan implementations. This API does not support the resolve of depth/stencil images.
Proposal
Here is our proposal about support multisampled render targets and resolve operations on WebGPU.
1. Format and Sample Count support
We can say that 4xMSAA is guaranteed on all WebGPU implementations, and we need to provide APIs for queries on whether we can create a multisampled texture with given format and sample count.
2. Sample Positions
D3D11, Metal and Vulkan all define the same standard multisample pattern, however there is no such definition in D3D12.
3. Multisample States
MSAA states are required to be set in Metal, Vulkan and D3D12 rendering pipelines. Here is our proposal on WebGPURenderPipelineDescriptor with MSAA states:
// Description of a single attachment
dictionary WebGPUAttachment {
// Attachment data format
WebGPUTextureFormatEnum format;
// Number of MSAA samples
u32 samples;
// Specify if this multisampled attachment will be resolved
bool hasResolveTarget;
};
// Description of the framebuffer attachments
dictionary WebGPUAttachmentsState {
// Array of color attachments
sequence<WebGPUAttachment> colorAttachments;
// Optional depth/stencil attachment
WebGPUAttachment? depthStencilAttachment;
};
- “hasResolveTarget” indicates if this attachment will be resolved in the render pass so that on Vulkan back-ends we can create correct renderpass for Vulkan graphics pipeline.
- In the future we may need WebGPUMultisampleState in WebGPURenderPipelineDescriptor to configure more states about multisampling (for example, alpha-to-coverage).
4. Resolve Multisampled Textures
There are two methods to resolve a multisampled texture in D3D12, Metal and Vulkan: explicit resolve API and resolve in render pass:
-
Explicit resolve API
Explicit resolve API can only be supported directly in D3D12 and Vulkan. If we want to expose this feature to WebGPU, we have to implement it on Metal by starting another dummy render pass.
Note that:- Explicit resolve may have poor performance on some Vulkan implementations.
- Neither D3D12 nor Vulkan support explicit resolve on depth/stencil textures, so WebGPU should only support explicit resolve on multisampled color attachments.
-
Resolve in render pass
Resolve in render pass can only be supported directly on Vulkan and Metal. I think we should support resolve color attachments in render pass on all WebGPU implementations as it is well optimized on Metal and Vulkan, and we can easily support it on D3D12 by calling ResolveSubresource once after rendering into the multisampled render targets.Here is our proposal on supporting resolve in render pass in WebGPU.
enum WebGPUTextureDimension {
"1d",
"2d",
"2dms",
"3d",
};
dictionary WebGPURenderPassColorAttachmentDescriptor {
WebGPUTextureView attachment;
WebGPUTextureView? resolveTarget;
WebGPULoadOp loadOp;
WebGPUStoreOp storeOp;
WebGPUColor clearColor;
};
(1) “resolveTarget” specifies the resolve target for “attachment” in the render pass. “resolveTarget” can be non-null if and only if “attachment” is a multisampled texture.
(2) There is no “resolveTarget” field for depth or stencil attachments, and the field “hasResolveTarget” of "depthStencilAttachment" must be always set to false because Vulkan does not support resolve depth or stencil attachments directly in the render pass.
We have two choices to implement resolving in the render pass:
- Always accept a multisampled texture created by WebGPU program as render targets
Pros:
WebGPU program can manage the storage of the multisampled texture, including the allocation, reusing, and deletion of the texture.
Cons:
As the multisampled texture is managed by WebGPU program, we may lose some opportunities to optimize the resolve action on Metal and Vulkan:- Using MTLStoreActionMultisampleResolve on Metal and VK_ATTACHMENT_STORE_OP_DONT_CARE on Vulkan will make the contents in the multisampled attachments undefined, which is not acceptable in WebGPU.
One possible solution for this issue is that we forbid reading the data in the multisampled attachments back until valid contents are written into the texture. See discussions in issue "Either" attachment load and store ops #97 for more details. Using MTLStoreActionStoreAndMultisampleResolve in Metal and VK_ATTACHMENT_STORE_OP_STORE in Vulkan instead may gain poorer performance if we actually only care about the result of resolve. Furthermore, MTLStoreActionStoreAndMultisampleResolve is not supported on all Metal implementations. - On Vulkan back-ends, we cannot use VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT flag when creating the multisampled WebGPU texture.
- Using MTLStoreActionMultisampleResolve on Metal and VK_ATTACHMENT_STORE_OP_DONT_CARE on Vulkan will make the contents in the multisampled attachments undefined, which is not acceptable in WebGPU.
- Allow not providing external multisampled textures when resolve in the render pass is enabled
Pros:
As the multisampled texture is invisible to the WebGPU program, WebGPU implementations will be able to optimize the render pass by using some GPU features that are not exposed in WebGPU.
Cons:
As multisampled textures may consume large memory, it will adds extra complexity for WebGPU implementation to manage these multisampled resources, which seems to be against the philosophy of the design of WebGPU.
TODO
Here are the features that are related to MSAA but not covered in this report:
- Multisampled textures as sampled textures
- Copying among multisampled textures
- Multisample coverage
- Sample-rate shading