Add support for tessellation.
At long last, tessellation comes to MoltenVK! With this change, clients
will now be able to specify tessellation shaders when creating
pipelines, and then draw tessellated patches with them.
Unfortunately, there seem to be a few gotchas with tessellation in
Metal. For one thing, tessellation pipelines in Metal are structured
very differently from Vulkan. There is no tessellation control or even
vertex stage. Instead, the tessellation evaluation shader takes the
place of the vertex function as a "post-tessellation vertex function."
The tessellation levels are supplied in a buffer to the tessellator,
which you are expected to populate. The most common way to do this is by
running a compute shader. MoltenVK thus runs the vertex shader and
tessellation control shader by themselves; a single `VkPipeline` object
then requires at least *three* `MTLPipelineState` objects.
But wait, there's more! The tessellation-control-as-compute stage uses
Metal's support for vertex-style stage input to a compute shader. But,
this support requires one to declare indexing *ahead of time*, when the
pipeline state is created. So a single `VkPipeline` object could have as
many as *five* `MTLPipelineState` objects.
Further, if there are more output than input control points for the
tessellation control stage, then later invocations may end up fetching
the wrong attributes! To get around this, this change uses index buffers
to ensure that all tessellation control shaders see the correct input.
Unfortunately, in the indexed draw case, this means that the incoming
index buffer needs to be munged.
Instancing is another pain point here. In Vulkan, as in OpenGL and
Direct3D, instancing is done in the vertex shader; but in Metal, it is
done at the tessellation evaluation stage. For this reason, only the
vertex stage of a tessellated draw supports instancing. Additional
memory is required to hold data for the extra vertices generated by
instancing. This also requires still more munging of index buffers for
indexed draws.
Indirect draws are even more painful. Because the number of vertices and
instances is unknown, storage for the maximum possible number of
vertices must be allocated. This change imposes a totally arbitrary
limit of 131072 vertices from a single draw, including all vertices
generated by instancing. On a Mac, this requires about 194-256 MB of
VRAM for all the temporary buffers.
There are some possible optimizations here. If we could prove that the
vertex shader's output doesn't depend on the instance ID, either
directly or through a per-instance attribute, then we could avoid
running the vertex and tess. control stages per instance, and take
advantage of Metal's support for tess. eval instancing. If we could
also prove that the vertex shader simply passes instance attributes
through (similarly with the tess. control shader), we could do this for
many more instanced draws as well. It should also be possible to cache
the output from the tess. control stage; if the draw comes up again, we
can then skip the vertex and tess. control stages entirely!
Fixes #56 and #501.
2019-02-18 20:56:42 -06:00
/*
* SPIRVReflection . h
*
* Copyright ( c ) 2019 Chip Davis for Codeweavers
*
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# ifndef __SPIRVReflection_h_
# define __SPIRVReflection_h_ 1
2019-05-28 17:19:43 -04:00
# include <SPIRV-Cross/spirv.hpp>
# include <SPIRV-Cross/spirv_common.hpp>
Add support for tessellation.
At long last, tessellation comes to MoltenVK! With this change, clients
will now be able to specify tessellation shaders when creating
pipelines, and then draw tessellated patches with them.
Unfortunately, there seem to be a few gotchas with tessellation in
Metal. For one thing, tessellation pipelines in Metal are structured
very differently from Vulkan. There is no tessellation control or even
vertex stage. Instead, the tessellation evaluation shader takes the
place of the vertex function as a "post-tessellation vertex function."
The tessellation levels are supplied in a buffer to the tessellator,
which you are expected to populate. The most common way to do this is by
running a compute shader. MoltenVK thus runs the vertex shader and
tessellation control shader by themselves; a single `VkPipeline` object
then requires at least *three* `MTLPipelineState` objects.
But wait, there's more! The tessellation-control-as-compute stage uses
Metal's support for vertex-style stage input to a compute shader. But,
this support requires one to declare indexing *ahead of time*, when the
pipeline state is created. So a single `VkPipeline` object could have as
many as *five* `MTLPipelineState` objects.
Further, if there are more output than input control points for the
tessellation control stage, then later invocations may end up fetching
the wrong attributes! To get around this, this change uses index buffers
to ensure that all tessellation control shaders see the correct input.
Unfortunately, in the indexed draw case, this means that the incoming
index buffer needs to be munged.
Instancing is another pain point here. In Vulkan, as in OpenGL and
Direct3D, instancing is done in the vertex shader; but in Metal, it is
done at the tessellation evaluation stage. For this reason, only the
vertex stage of a tessellated draw supports instancing. Additional
memory is required to hold data for the extra vertices generated by
instancing. This also requires still more munging of index buffers for
indexed draws.
Indirect draws are even more painful. Because the number of vertices and
instances is unknown, storage for the maximum possible number of
vertices must be allocated. This change imposes a totally arbitrary
limit of 131072 vertices from a single draw, including all vertices
generated by instancing. On a Mac, this requires about 194-256 MB of
VRAM for all the temporary buffers.
There are some possible optimizations here. If we could prove that the
vertex shader's output doesn't depend on the instance ID, either
directly or through a per-instance attribute, then we could avoid
running the vertex and tess. control stages per instance, and take
advantage of Metal's support for tess. eval instancing. If we could
also prove that the vertex shader simply passes instance attributes
through (similarly with the tess. control shader), we could do this for
many more instanced draws as well. It should also be possible to cache
the output from the tess. control stage; if the draw comes up again, we
can then skip the vertex and tess. control stages entirely!
Fixes #56 and #501.
2019-02-18 20:56:42 -06:00
# include <string>
# include <vector>
namespace mvk {
# pragma mark -
# pragma mark SPIRVTessReflectionData
/** Reflection data for a pair of tessellation shaders. This contains the information needed to construct a tessellation pipeline. */
struct SPIRVTessReflectionData {
/** The partition mode, one of SpacingEqual, SpacingFractionalEven, or SpacingFractionalOdd. */
spv : : ExecutionMode partitionMode = spv : : ExecutionModeMax ;
/** The winding order of generated triangles, one of VertexOrderCw or VertexOrderCcw. */
spv : : ExecutionMode windingOrder = spv : : ExecutionModeMax ;
/** Whether or not tessellation should produce points instead of lines or triangles. */
bool pointMode = false ;
/** The kind of patch expected as input, one of Triangles, Quads, or Isolines. */
spv : : ExecutionMode patchKind = spv : : ExecutionModeMax ;
/** The number of control points output by the tessellation control shader. */
uint32_t numControlPoints = 0 ;
} ;
# pragma mark -
# pragma mark SPIRVShaderOutputData
/** Reflection data on a single output of a shader. This contains the information needed to construct a stage-input descriptor for the next stage of a pipeline. */
struct SPIRVShaderOutput {
/** The type of the output. */
2019-03-29 11:21:19 +01:00
SPIRV_CROSS_NAMESPACE : : SPIRType : : BaseType baseType ;
Add support for tessellation.
At long last, tessellation comes to MoltenVK! With this change, clients
will now be able to specify tessellation shaders when creating
pipelines, and then draw tessellated patches with them.
Unfortunately, there seem to be a few gotchas with tessellation in
Metal. For one thing, tessellation pipelines in Metal are structured
very differently from Vulkan. There is no tessellation control or even
vertex stage. Instead, the tessellation evaluation shader takes the
place of the vertex function as a "post-tessellation vertex function."
The tessellation levels are supplied in a buffer to the tessellator,
which you are expected to populate. The most common way to do this is by
running a compute shader. MoltenVK thus runs the vertex shader and
tessellation control shader by themselves; a single `VkPipeline` object
then requires at least *three* `MTLPipelineState` objects.
But wait, there's more! The tessellation-control-as-compute stage uses
Metal's support for vertex-style stage input to a compute shader. But,
this support requires one to declare indexing *ahead of time*, when the
pipeline state is created. So a single `VkPipeline` object could have as
many as *five* `MTLPipelineState` objects.
Further, if there are more output than input control points for the
tessellation control stage, then later invocations may end up fetching
the wrong attributes! To get around this, this change uses index buffers
to ensure that all tessellation control shaders see the correct input.
Unfortunately, in the indexed draw case, this means that the incoming
index buffer needs to be munged.
Instancing is another pain point here. In Vulkan, as in OpenGL and
Direct3D, instancing is done in the vertex shader; but in Metal, it is
done at the tessellation evaluation stage. For this reason, only the
vertex stage of a tessellated draw supports instancing. Additional
memory is required to hold data for the extra vertices generated by
instancing. This also requires still more munging of index buffers for
indexed draws.
Indirect draws are even more painful. Because the number of vertices and
instances is unknown, storage for the maximum possible number of
vertices must be allocated. This change imposes a totally arbitrary
limit of 131072 vertices from a single draw, including all vertices
generated by instancing. On a Mac, this requires about 194-256 MB of
VRAM for all the temporary buffers.
There are some possible optimizations here. If we could prove that the
vertex shader's output doesn't depend on the instance ID, either
directly or through a per-instance attribute, then we could avoid
running the vertex and tess. control stages per instance, and take
advantage of Metal's support for tess. eval instancing. If we could
also prove that the vertex shader simply passes instance attributes
through (similarly with the tess. control shader), we could do this for
many more instanced draws as well. It should also be possible to cache
the output from the tess. control stage; if the draw comes up again, we
can then skip the vertex and tess. control stages entirely!
Fixes #56 and #501.
2019-02-18 20:56:42 -06:00
/** The vector size, if a vector. */
uint32_t vecWidth ;
/** The location number of the output. */
uint32_t location ;
/** Whether this is a per-patch or per-vertex output. Only meaningful for tessellation control shaders. */
bool perPatch ;
/** If this is a builtin, the kind of builtin this is. */
spv : : BuiltIn builtin ;
} ;
# pragma mark -
# pragma mark Functions
/** Given a tessellation control shader and a tessellation evaluation shader, both in SPIR-V format, returns tessellation reflection data. */
bool getTessReflectionData ( const std : : vector < uint32_t > & tesc , const std : : string & tescEntryName , const std : : vector < uint32_t > & tese , const std : : string & teseEntryName , SPIRVTessReflectionData & reflectData , std : : string & errorLog ) ;
/** Given a shader in SPIR-V format, returns output reflection data. */
bool getShaderOutputs ( const std : : vector < uint32_t > & spirv , spv : : ExecutionModel model , const std : : string & entryName , std : : vector < SPIRVShaderOutput > & outputs , std : : string & errorLog ) ;
}
# endif