diff --git a/design/Pipelines.md b/design/Pipelines.md index 4df217f061..f3c7d2ca7e 100644 --- a/design/Pipelines.md +++ b/design/Pipelines.md @@ -7,11 +7,11 @@ For example this document uses C structures when a C++ API might want to use bui ## Creating pipeline objects For type safety, compute and graphics pipeline are separate types. -To create a pipeline, a structure containing all the relevant information is passed to ```DeviceCreatePipeline```. +To create a pipeline, a structure containing all the relevant information is passed to `DeviceCreatePipeline`. ## Compute pipeline creation -To create a compute pipeline the only things needed are some shader code present in a ```ShaderModule``` object, and a ```PipelineLayout``` object describing how the pipeline interacts with the binding model. +To create a compute pipeline the only things needed are some shader code present in a `ShaderModule` object, and a `PipelineLayout` object describing how the pipeline interacts with the binding model. ```cpp struct ComputePipelineDescriptor { @@ -24,9 +24,9 @@ ComputePipeline CreateComputePipeline(Device device, const ComputePipelineDescri ``` Translation to the backing APIs would be the following: - - **D3D12**: Translates to ```ID3D12::CreateComputePipelineState```, a ```D3D12_SHADER_BYTECODE``` is created from the ```(module, entryPoint)``` pair, and the ```ID3D12RootSignature``` is equivalent to the ```PipelineLayout```. - - **Metal**: Translates to ```MTLDevice::makeComputePipelineState```, the ```MTLFunction``` is created from the ```(module, entryPoint, layout)``` tuple by adapting the generated MSL to the resource slot allocation done in ```layout```. - - **Vulkan**: Translates to ```vkCreateComputePipelines``` with one pipeline. The ```vkShaderStageInfo``` corresponds to ```(module, entryPoint)``` and the ```vkPipelineLayout``` corresponds to ```layout```. + - **D3D12**: Translates to `ID3D12::CreateComputePipelineState`, a `D3D12_SHADER_BYTECODE` is created from the `(module, entryPoint)` pair, and the `ID3D12RootSignature` is equivalent to the `PipelineLayout`. + - **Metal**: Translates to `MTLDevice::makeComputePipelineState`, the `MTLFunction` is created from the `(module, entryPoint, layout)` tuple by adapting the generated MSL to the resource slot allocation done in `layout`. + - **Vulkan**: Translates to `vkCreateComputePipelines` with one pipeline. The `vkShaderStageInfo` corresponds to `(module, entryPoint)` and the `vkPipelineLayout` corresponds to `layout`. Open questions: How do we take advantage of the pipeline caching present in D3D12 and Vulkan? @@ -34,17 +34,17 @@ Do we expose it to the application or is it done magically in the WebGPU impleme ## Render pipeline creation -Render pipelines need ```ShaderModule``` and a ```PipelineLayout``` like compute pipelines and in addition require information about: +Render pipelines need `ShaderModule` and a `PipelineLayout` like compute pipelines and in addition require information about: - Layout for vertex inputs - Layout for fragment outputs - All the fixed-function state For simplicity we assume most fixed-function state is created in separate object. -For example a ```DepthStencilState``` object would be allocated and a pointer to it would be stored in the ```RenderPipelineDescriptor```. This is part of the UX of the API and could be replaced with chained structure like Vulkan or member structure like D3D12. +For example a `DepthStencilState` object would be allocated and a pointer to it would be stored in the `RenderPipelineDescriptor`. This is part of the UX of the API and could be replaced with chained structure like Vulkan or member structure like D3D12. Mismatch: - Metal has primitive restart always enabled. - - D3D12 needs to know whether the primitive restart index is ```0xFFFF``` or ```0xFFFFFFFF``` at pipeline creation time. + - D3D12 needs to know whether the primitive restart index is `0xFFFF` or `0xFFFFFFFF` at pipeline creation time. - Metal doesn’t have a sample mask. - Vulkan can have some state like scissor and viewport set on the pipeline as an optimization on some GPUs. - Vulkan allows creating pipelines in bulk, this is not only a UX things but allows reusing some results for faster creation. @@ -75,11 +75,11 @@ RenderPipeline CreateRenderPipeline(Device device, const RenderPipelineDescripto ``` Translation to the backing APIs would be the following: - - **D3D12**: Translates to ```ID3D12::CreateGraphicsPipelineState```. ```IBStripCutValue``` will always be set. - - **Metal**: Translates to ```MTLDevice::makeRenderPipelineState``` - - **Vulkan**: Translates to ```vkCreateGraphicsPipelines```. ```VkPipelineInputAssemblyStateCreateInfo```'s ```primitiveRestartEnable``` is always set to true. All dynamic states are set on all pipelines. + - **D3D12**: Translates to `ID3D12::CreateGraphicsPipelineState`. `IBStripCutValue` will always be set. + - **Metal**: Translates to `MTLDevice::makeRenderPipelineState` + - **Vulkan**: Translates to `vkCreateGraphicsPipelines`. `VkPipelineInputAssemblyStateCreateInfo`'s `primitiveRestartEnable` is always set to true. All dynamic states are set on all pipelines. -Open question: should the type of the indices be set in ```RenderPipelineDescriptor```? If not, how is the D3D12 ```IBStripCutValue``` chosen? +Open question: should the type of the indices be set in `RenderPipelineDescriptor`? If not, how is the D3D12 `IBStripCutValue` chosen? The translation of individual members of RenderPipelineDescriptor is described below. @@ -88,7 +88,7 @@ The translation of individual members of RenderPipelineDescriptor is described b This describes how the vertex buffers are stepped through (stride, instance vs. vertex, instance divisor), and how the attributes are extracted from the buffers (buffer index, format, offset). Mismatches: - - D3D12 takes the stride along with the vertex buffers in ```ID3D12GraphicsCommandList::IASetVertexBuffers``` whereas Vulkan and Metal take it at pipeline compilation time. + - D3D12 takes the stride along with the vertex buffers in `ID3D12GraphicsCommandList::IASetVertexBuffers` whereas Vulkan and Metal take it at pipeline compilation time. - Vulkan doesn’t support a divisor for its step rate. ```cpp @@ -119,27 +119,27 @@ InputState* CreateInputState(Device* device, InputStateDescriptor* descriptor); ``` Translation to the backing APIs would be the following: - - **D3D12**: Translates to a ```D3D12_INPUT_DESC```. - Each enabled attribute corresponds to a ```D3D12_INPUT_ELEMENT_DESC``` with ```InputSlot``` being the index of the attribute. - Other members of the ```D3D12_INPUT_ELEMENT_DESC``` are translated trivially. - The stride is looked up in the pipeline state before calls to ```ID3D12GraphicsCommandList::IASetVertexBuffers```. - ```IASetVertexBuffers``` might be deferred until before a draw and vertex buffers might be invalidated by pipeline changes. - - **Metal**: Translates to a ```MTLVertexDescriptor```, with attributes corresponding to ```MTLVertexDescriptor::attributes``` and buffers corresponding to ```MTLVertexDescriptor::layouts```. - Attributes translate trivially to ```MTLVertexAttributeDescriptor``` structures and buffers to ```MTLVertexBufferLayoutDescriptor``` structures. + - **D3D12**: Translates to a `D3D12_INPUT_DESC`. + Each enabled attribute corresponds to a `D3D12_INPUT_ELEMENT_DESC` with `InputSlot` being the index of the attribute. + Other members of the `D3D12_INPUT_ELEMENT_DESC` are translated trivially. + The stride is looked up in the pipeline state before calls to `ID3D12GraphicsCommandList::IASetVertexBuffers`. + `IASetVertexBuffers` might be deferred until before a draw and vertex buffers might be invalidated by pipeline changes. + - **Metal**: Translates to a `MTLVertexDescriptor`, with attributes corresponding to `MTLVertexDescriptor::attributes` and buffers corresponding to `MTLVertexDescriptor::layouts`. + Attributes translate trivially to `MTLVertexAttributeDescriptor` structures and buffers to `MTLVertexBufferLayoutDescriptor` structures. Extra care only needs to be taken to translate a zero stride to a constant step rate. - - **Vulkan**: Translates to a ```VkPipelineVertexInputStateCreateInfo```. - Buffers translate trivially to ```VkVertexInputBindingDescription``` and attributes to ```VkVertexInputAttributeDescription```. + - **Vulkan**: Translates to a `VkPipelineVertexInputStateCreateInfo`. + Buffers translate trivially to `VkVertexInputBindingDescription` and attributes to `VkVertexInputAttributeDescription`. Open questions: -Should the vertex attributes somehow be included in the ```PipelineLayout``` so vertex buffers are treated as other resources and changed in bulk with them? +Should the vertex attributes somehow be included in the `PipelineLayout` so vertex buffers are treated as other resources and changed in bulk with them? ### Render pass / render target format -The ```RenderPass``` will contain for each subpass a list of the attachment formats for color attachments and depth-stencil attachments. -Information from the ```RenderPass``` is used to fill the following: - - **D3D12**: ```RTVFormats```, ```DSVFormats``` and ```NumRenderTargets``` in ```D3D12_GRAPHICS_PIPELINE_STATE_DESC```. - - **Metal**: ```colorAttachments[N].pixelFormat```, ```depthAttachmentPixelFormat``` and ```stencilAttachmentPixelFormat``` in ```MTLRenderPipelineDescriptor```. - - **Vulkan**: ```renderPass``` and ```subpass``` in ```VkGraphicsPipelineCreateInfo```. +The `RenderPass` will contain for each subpass a list of the attachment formats for color attachments and depth-stencil attachments. +Information from the `RenderPass` is used to fill the following: + - **D3D12**: `RTVFormats`, `DSVFormats` and `NumRenderTargets` in `D3D12_GRAPHICS_PIPELINE_STATE_DESC`. + - **Metal**: `colorAttachments[N].pixelFormat`, `depthAttachmentPixelFormat` and `stencilAttachmentPixelFormat` in `MTLRenderPipelineDescriptor`. + - **Vulkan**: `renderPass` and `subpass` in `VkGraphicsPipelineCreateInfo`. Open question: does the sample count of the pipeline state come from the RenderPass too? @@ -147,7 +147,7 @@ does the sample count of the pipeline state come from the RenderPass too? ### Primitive topology Mismatch: - - Metal and D3D12 only require “point vs. line vs. triangle” at pipeline compilation time, the exact topology is set via ```ID3D12GraphicsCommandList::IASetPrimitiveTopology``` or passed in the ```MTLRenderCommandEncoder::draw*```. + - Metal and D3D12 only require “point vs. line vs. triangle” at pipeline compilation time, the exact topology is set via `ID3D12GraphicsCommandList::IASetPrimitiveTopology` or passed in the `MTLRenderCommandEncoder::draw*`. Vulkan requires the exact topology at compilation time. - Vulkan supports triangle fans but Metal and D3D12 don’t. @@ -162,15 +162,15 @@ enum PrimitiveTopology { ``` Translation to the backing APIs would be the following: - - **D3D12 and Metal**: The primitive topology type is set on the ```D3D12_GRAPHICS_PIPELINE_STATE_DESC``` and ```MTLRenderPipelineDescriptor```. + - **D3D12 and Metal**: The primitive topology type is set on the `D3D12_GRAPHICS_PIPELINE_STATE_DESC` and `MTLRenderPipelineDescriptor`. At draw-time, the exact topology is queried from the pipeline. - - **Vulkan**: The primitive topology type is set in the ```VkGraphicsPipelineCreateInfo```. + - **Vulkan**: The primitive topology type is set in the `VkGraphicsPipelineCreateInfo`. ### Blend state Mismatch: - In Vulkan per-attachment blending and dual source blending are exposed as optional features. - ```independentBlend``` is supported almost everywhere but Adreno 4XX while ```dualSrcBlend``` is also not supported on Mali GPUs. + `independentBlend` is supported almost everywhere but Adreno 4XX while `dualSrcBlend` is also not supported on Mali GPUs. - Metal doesn’t have logic ops. ```cpp @@ -212,11 +212,11 @@ BlendState* CreateBlendState(Device* device, BlendStateDescriptor* descriptor); ``` Translation to backing APIs would be the following: - - **D3D12**: when filling the ```D3D12_GRAPHICS_PIPELINE_DESC```, ```BlendState``` will be filled with data coming from the ```BlendStates``` referenced in the ```RenderPipelineDescriptor```. - Translation from a ```BlendState``` to a ```D3D12_RENDER_TARGET_BLEND_DESC``` is trivial. - - **Metal**: the ```BlendStates``` will be used to fill all of the data for a ```MTLRenderPipelineColorAttachmentDescriptor``` but ```pixelFormat```. + - **D3D12**: when filling the `D3D12_GRAPHICS_PIPELINE_DESC`, `BlendState` will be filled with data coming from the `BlendStates` referenced in the `RenderPipelineDescriptor`. + Translation from a `BlendState` to a `D3D12_RENDER_TARGET_BLEND_DESC` is trivial. + - **Metal**: the `BlendStates` will be used to fill all of the data for a `MTLRenderPipelineColorAttachmentDescriptor` but `pixelFormat`. Translation of individual members is trivial. - - **Vulkan**: the ```BlendStates``` will be translated to elements of ```pAttachments``` in the ```VkPipelineColorBlendStateCreateInfo```. + - **Vulkan**: the `BlendStates` will be translated to elements of `pAttachments` in the `VkPipelineColorBlendStateCreateInfo`. Translation of individual members is trivial. Open question: @@ -270,13 +270,13 @@ DepthStencilState* CreateDepthStencilState(Device* device, DepthStencilDescripto ``` Translation to backing APIs would be the following: - - **D3D12**: ```DepthStencilState``` translates trivially to a ```D3D12_DEPTH_STENCIL_DESC```. - ```DepthEnable``` would be set as ```depthCompare != Always```. - - **Metal**: ```DepthStencilState``` translates trivially to ```MTLDepthStencilDescriptor``` except that front and back stencil masks have to be set to the single stencil mask value from WebGPU. + - **D3D12**: `DepthStencilState` translates trivially to a `D3D12_DEPTH_STENCIL_DESC`. + `DepthEnable` would be set as `depthCompare != Always`. + - **Metal**: `DepthStencilState` translates trivially to `MTLDepthStencilDescriptor` except that front and back stencil masks have to be set to the single stencil mask value from WebGPU. When a pipeline is bound, the corresponding depth-stencil state is bound at the same time. - - **Vulkan**: ```DepthStencilState``` translates trivially to ```VkPipelineDepthStencilStateCreateInfoxcept``` except that front and back stencil masks have to be set to the single stencil mask value from WebGPU. - ```depthTestEnable``` would be set to ```depthCompare != Always```. + - **Vulkan**: `DepthStencilState` translates trivially to `VkPipelineDepthStencilStateCreateInfoxcept` except that front and back stencil masks have to be set to the single stencil mask value from WebGPU. + `depthTestEnable` would be set to `depthCompare != Always`. Open question: -what about Vulkan’s ```VkPipelineDepthStencilStateCreateInfo::depthBoundTestEnable``` and D3D12's ```D3D12_DEPTH_STENCIL_DESC1::DepthBoundsTestEnable```? +what about Vulkan’s `VkPipelineDepthStencilStateCreateInfo::depthBoundTestEnable` and D3D12's `D3D12_DEPTH_STENCIL_DESC1::DepthBoundsTestEnable`? Should “depth test enable” be implicit or explicit?