2017-11-17 11:14:29 -05:00
|
|
|
/*
|
|
|
|
* SPIRVToMSLConverter.h
|
|
|
|
*
|
2019-01-01 21:13:25 -05:00
|
|
|
* Copyright (c) 2014-2019 The Brenwill Workshop Ltd. (http://www.brenwill.com)
|
2017-11-17 11:14:29 -05:00
|
|
|
*
|
|
|
|
* 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 __SPIRVToMSLConverter_h_
|
|
|
|
#define __SPIRVToMSLConverter_h_ 1
|
|
|
|
|
2019-04-17 16:09:07 -04:00
|
|
|
#include <SPIRV-Cross/spirv.hpp>
|
2017-11-17 11:14:29 -05:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
#include <unordered_map>
|
|
|
|
|
|
|
|
namespace mvk {
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark SPIRVToMSLConverterContext
|
|
|
|
|
2019-05-27 17:56:44 -04:00
|
|
|
/**
|
|
|
|
* Options for converting SPIR-V to Metal Shading Language
|
|
|
|
*
|
|
|
|
* THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
|
|
|
|
* CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
|
|
|
|
*/
|
2017-11-17 11:14:29 -05:00
|
|
|
typedef struct SPIRVToMSLConverterOptions {
|
2019-04-17 16:09:07 -04:00
|
|
|
|
|
|
|
enum Platform {
|
|
|
|
iOS = 0,
|
|
|
|
macOS = 1
|
|
|
|
};
|
|
|
|
|
2018-03-19 10:58:46 -04:00
|
|
|
std::string entryPointName;
|
|
|
|
spv::ExecutionModel entryPointStage = spv::ExecutionModelMax;
|
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
|
|
|
spv::ExecutionMode tessPatchKind = spv::ExecutionModeMax;
|
2018-03-19 10:58:46 -04:00
|
|
|
|
2019-04-17 16:09:07 -04:00
|
|
|
uint32_t mslVersion = makeMSLVersion(2, 1);
|
|
|
|
Platform platform = getNativePlatform();
|
2018-06-27 18:03:30 -04:00
|
|
|
uint32_t texelBufferTextureWidth = 4096;
|
2019-05-28 17:24:14 -05:00
|
|
|
uint32_t swizzleBufferIndex = 0;
|
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
|
|
|
uint32_t indirectParamsBufferIndex = 0;
|
|
|
|
uint32_t outputBufferIndex = 0;
|
|
|
|
uint32_t patchOutputBufferIndex = 0;
|
|
|
|
uint32_t tessLevelBufferIndex = 0;
|
2019-05-28 17:24:14 -05:00
|
|
|
uint32_t bufferSizeBufferIndex = 0;
|
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
|
|
|
uint32_t inputThreadgroupMemIndex = 0;
|
|
|
|
uint32_t numTessControlPoints = 0;
|
2017-11-17 11:14:29 -05:00
|
|
|
bool shouldFlipVertexY = true;
|
|
|
|
bool isRenderingPoints = false;
|
2019-01-12 12:19:01 -05:00
|
|
|
bool shouldSwizzleTextureSamples = false;
|
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
|
|
|
bool shouldCaptureOutput = false;
|
|
|
|
bool tessDomainOriginInLowerLeft = false;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
2018-07-29 15:50:51 -04:00
|
|
|
bool isRasterizationDisabled = false;
|
2019-05-28 17:24:14 -05:00
|
|
|
bool needsSwizzleBuffer = false;
|
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
|
|
|
bool needsOutputBuffer = false;
|
|
|
|
bool needsPatchOutputBuffer = false;
|
2019-05-28 17:24:14 -05:00
|
|
|
bool needsBufferSizeBuffer = false;
|
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
|
|
|
bool needsInputThreadgroupMem = false;
|
2018-07-29 15:50:51 -04:00
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
/**
|
|
|
|
* Returns whether the specified options match this one.
|
|
|
|
* It does if all corresponding elements are equal.
|
|
|
|
*/
|
2018-03-19 10:58:46 -04:00
|
|
|
bool matches(const SPIRVToMSLConverterOptions& other) const;
|
|
|
|
|
|
|
|
bool hasEntryPoint() const {
|
|
|
|
return !entryPointName.empty() && entryPointStage != spv::ExecutionModelMax;
|
|
|
|
}
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
void setMSLVersion(uint32_t major, uint32_t minor = 0, uint32_t point = 0) {
|
|
|
|
mslVersion = makeMSLVersion(major, minor, point);
|
|
|
|
}
|
|
|
|
|
2018-03-19 10:58:46 -04:00
|
|
|
bool supportsMSLVersion(uint32_t major, uint32_t minor = 0, uint32_t point = 0) const {
|
2017-11-17 11:14:29 -05:00
|
|
|
return mslVersion >= makeMSLVersion(major, minor, point);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t makeMSLVersion(uint32_t major, uint32_t minor = 0, uint32_t patch = 0) {
|
|
|
|
return (major * 10000) + (minor * 100) + patch;
|
|
|
|
}
|
|
|
|
|
2018-12-11 18:27:01 -05:00
|
|
|
static std::string printMSLVersion(uint32_t mslVersion, bool includePatch = false);
|
|
|
|
|
2019-04-17 16:09:07 -04:00
|
|
|
static Platform getNativePlatform();
|
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
} SPIRVToMSLConverterOptions;
|
|
|
|
|
2018-11-28 11:49:24 -06:00
|
|
|
/**
|
|
|
|
* Defines the format of a vertex attribute. Currently limited to describing
|
|
|
|
* whether or not the attribute is of an 8-bit unsigned format, a 16-bit
|
|
|
|
* unsigned format, or some other format.
|
|
|
|
*/
|
|
|
|
enum class MSLVertexFormat {
|
|
|
|
Other,
|
|
|
|
UInt8,
|
|
|
|
UInt16
|
|
|
|
};
|
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
/**
|
|
|
|
* Defines MSL characteristics of a vertex attribute at a particular location.
|
|
|
|
* The isUsedByShader flag is set to true during conversion of SPIR-V to MSL
|
|
|
|
* if the shader makes use of this vertex attribute.
|
2019-05-27 17:56:44 -04:00
|
|
|
*
|
|
|
|
* THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
|
|
|
|
* CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
|
2017-11-17 11:14:29 -05:00
|
|
|
*/
|
|
|
|
typedef struct MSLVertexAttribute {
|
|
|
|
uint32_t location = 0;
|
|
|
|
uint32_t mslBuffer = 0;
|
|
|
|
uint32_t mslOffset = 0;
|
|
|
|
uint32_t mslStride = 0;
|
2018-11-28 11:49:24 -06:00
|
|
|
MSLVertexFormat format = MSLVertexFormat::Other;
|
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
|
|
|
spv::BuiltIn builtin = spv::BuiltInMax;
|
2017-11-17 11:14:29 -05:00
|
|
|
bool isPerInstance = false;
|
|
|
|
|
|
|
|
bool isUsedByShader = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the specified vertex attribute match this one.
|
|
|
|
* It does if all corresponding elements except isUsedByShader are equal.
|
|
|
|
*/
|
2018-03-19 10:58:46 -04:00
|
|
|
bool matches(const MSLVertexAttribute& other) const;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
} MSLVertexAttribute;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Matches the binding index of a MSL resource for a binding within a descriptor set.
|
|
|
|
* Taken together, the stage, desc_set and binding combine to form a reference to a resource
|
|
|
|
* descriptor used in a particular shading stage. Generally, only one of the buffer, texture,
|
|
|
|
* or sampler elements will be populated. The isUsedByShader flag is set to true during
|
|
|
|
* compilation of SPIR-V to MSL if the shader makes use of this vertex attribute.
|
2019-05-27 17:56:44 -04:00
|
|
|
*
|
|
|
|
* THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
|
|
|
|
* CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
|
2017-11-17 11:14:29 -05:00
|
|
|
*/
|
|
|
|
typedef struct MSLResourceBinding {
|
|
|
|
spv::ExecutionModel stage;
|
|
|
|
uint32_t descriptorSet = 0;
|
|
|
|
uint32_t binding = 0;
|
|
|
|
|
|
|
|
uint32_t mslBuffer = 0;
|
|
|
|
uint32_t mslTexture = 0;
|
|
|
|
uint32_t mslSampler = 0;
|
|
|
|
|
|
|
|
bool isUsedByShader = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns whether the specified resource binding match this one.
|
|
|
|
* It does if all corresponding elements except isUsedByShader are equal.
|
|
|
|
*/
|
2018-03-19 10:58:46 -04:00
|
|
|
bool matches(const MSLResourceBinding& other) const;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
} MSLResourceBinding;
|
|
|
|
|
2019-05-27 17:56:44 -04:00
|
|
|
/**
|
|
|
|
* Context passed to the SPIRVToMSLConverter to map SPIR-V descriptors to Metal resource indices.
|
|
|
|
*
|
|
|
|
* THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
|
|
|
|
* CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
|
|
|
|
*/
|
2017-11-17 11:14:29 -05:00
|
|
|
typedef struct SPIRVToMSLConverterContext {
|
|
|
|
SPIRVToMSLConverterOptions options;
|
|
|
|
std::vector<MSLVertexAttribute> vertexAttributes;
|
|
|
|
std::vector<MSLResourceBinding> resourceBindings;
|
|
|
|
|
2019-03-15 20:25:55 -04:00
|
|
|
/** Returns whether the pipeline stage being converted supports vertex attributes. */
|
|
|
|
bool stageSupportsVertexAttributes() const;
|
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
/** Returns whether the vertex attribute at the specified location is used by the shader. */
|
2018-03-19 10:58:46 -04:00
|
|
|
bool isVertexAttributeLocationUsed(uint32_t location) const;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
/** Returns whether the vertex buffer at the specified Metal binding index is used by the shader. */
|
2018-03-19 10:58:46 -04:00
|
|
|
bool isVertexBufferUsed(uint32_t mslBuffer) const;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
2019-01-21 11:32:15 -05:00
|
|
|
/** Marks all vertex attributes and resources as being used by the shader. */
|
|
|
|
void markAllAttributesAndResourcesUsed();
|
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
/**
|
|
|
|
* Returns whether this context matches the other context. It does if the respective
|
|
|
|
* options match and any vertex attributes and resource bindings used by this context
|
|
|
|
* can be found in the other context. Vertex attributes and resource bindings that are
|
|
|
|
* in the other context but are not used by the shader that created this context, are ignored.
|
|
|
|
*/
|
2018-03-19 10:58:46 -04:00
|
|
|
bool matches(const SPIRVToMSLConverterContext& other) const;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
2018-07-29 15:50:51 -04:00
|
|
|
/** Aligns certain aspects of this context with the source context. */
|
|
|
|
void alignWith(const SPIRVToMSLConverterContext& srcContext);
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
} SPIRVToMSLConverterContext;
|
|
|
|
|
2017-12-26 22:20:20 -05:00
|
|
|
/**
|
2018-07-03 13:57:53 -04:00
|
|
|
* Describes one dimension of the workgroup size of a SPIR-V entry point, including whether
|
|
|
|
* it is specialized, and if so, the value of the corresponding specialization ID, which
|
|
|
|
* is used to map to a value which will be provided when the MSL is compiled into a pipeline.
|
2019-05-27 17:56:44 -04:00
|
|
|
*
|
|
|
|
* THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
|
|
|
|
* CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
|
2018-07-03 13:57:53 -04:00
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
uint32_t size = 1;
|
|
|
|
uint32_t specializationID = 0;
|
|
|
|
bool isSpecialized = false;
|
|
|
|
} SPIRVWorkgroupSizeDimension;
|
|
|
|
|
|
|
|
/**
|
2017-12-26 22:20:20 -05:00
|
|
|
* Describes a SPIRV entry point, including the Metal function name (which may be
|
|
|
|
* different than the Vulkan entry point name if the original name was illegal in Metal),
|
2018-07-03 13:57:53 -04:00
|
|
|
* and the size of each workgroup, if the shader is a compute shader.
|
2019-05-27 17:56:44 -04:00
|
|
|
*
|
|
|
|
* THIS STRUCT IS STREAMED OUT AS PART OF THE PIEPLINE CACHE.
|
|
|
|
* CHANGES TO THIS STRUCT SHOULD BE CAPTURED IN THE STREAMING LOGIC OF THE PIPELINE CACHE.
|
2017-12-26 22:20:20 -05:00
|
|
|
*/
|
2018-07-03 13:57:53 -04:00
|
|
|
typedef struct {
|
|
|
|
std::string mtlFunctionName = "main0";
|
|
|
|
struct {
|
|
|
|
SPIRVWorkgroupSizeDimension width;
|
|
|
|
SPIRVWorkgroupSizeDimension height;
|
|
|
|
SPIRVWorkgroupSizeDimension depth;
|
|
|
|
} workgroupSize;
|
|
|
|
} SPIRVEntryPoint;
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
/** Special constant used in a MSLResourceBinding descriptorSet element to indicate the bindings for the push constants. */
|
|
|
|
static const uint32_t kPushConstDescSet = std::numeric_limits<uint32_t>::max();
|
|
|
|
|
|
|
|
/** Special constant used in a MSLResourceBinding binding element to indicate the bindings for the push constants. */
|
|
|
|
static const uint32_t kPushConstBinding = 0;
|
|
|
|
|
|
|
|
|
|
|
|
#pragma mark -
|
|
|
|
#pragma mark SPIRVToMSLConverter
|
|
|
|
|
|
|
|
/** Converts SPIR-V code to Metal Shading Language code. */
|
|
|
|
class SPIRVToMSLConverter {
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
/** Sets the SPIRV code. */
|
2019-05-10 12:21:03 -04:00
|
|
|
void setSPIRV(const std::vector<uint32_t>& spirv) { _spirv = spirv; }
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the SPIRV code from the specified array of values.
|
|
|
|
* The length parameter indicates the number of uint values to store.
|
|
|
|
*/
|
|
|
|
void setSPIRV(const uint32_t* spirvCode, size_t length);
|
|
|
|
|
|
|
|
/** Returns a reference to the SPIRV code, set by one of the setSPIRV() functions. */
|
2019-05-10 12:21:03 -04:00
|
|
|
const std::vector<uint32_t>& getSPIRV() { return _spirv; }
|
|
|
|
|
|
|
|
/** Returns whether the SPIR-V code has been set. */
|
|
|
|
bool hasSPIRV() { return !_spirv.empty(); }
|
2017-11-17 11:14:29 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts SPIR-V code, set using setSPIRV() to MSL code, which can be retrieved using getMSL().
|
|
|
|
*
|
|
|
|
* The boolean flags indicate whether the original SPIR-V code, the resulting MSL code,
|
|
|
|
* and optionally, the original GLSL (as converted from the SPIR_V), should be logged
|
|
|
|
* to the result log of this converter. This can be useful during shader debugging.
|
|
|
|
*/
|
|
|
|
bool convert(SPIRVToMSLConverterContext& context,
|
|
|
|
bool shouldLogSPIRV = false,
|
|
|
|
bool shouldLogMSL = false,
|
|
|
|
bool shouldLogGLSL = false);
|
|
|
|
|
2019-05-10 12:21:03 -04:00
|
|
|
/**
|
|
|
|
* Returns whether the most recent conversion was successful.
|
|
|
|
*
|
|
|
|
* The initial value of this property is NO. It is set to YES upon successful conversion.
|
|
|
|
*/
|
|
|
|
bool wasConverted() { return _wasConverted; }
|
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
/**
|
|
|
|
* Returns the Metal Shading Language source code most recently converted
|
|
|
|
* by the convert() function, or set directly using the setMSL() function.
|
|
|
|
*/
|
|
|
|
const std::string& getMSL() { return _msl; }
|
|
|
|
|
2018-03-19 10:58:46 -04:00
|
|
|
/** Returns information about the shader entry point. */
|
|
|
|
const SPIRVEntryPoint& getEntryPoint() { return _entryPoint; }
|
|
|
|
|
2019-06-06 11:00:20 +02:00
|
|
|
/** Sets the number of threads in a single compute kernel workgroup, per dimension. */
|
|
|
|
void setWorkgroupSize(uint32_t x, uint32_t y, uint32_t z) {
|
|
|
|
_entryPoint.workgroupSize.width.size = x;
|
|
|
|
_entryPoint.workgroupSize.height.size = y;
|
|
|
|
_entryPoint.workgroupSize.depth.size = z;
|
|
|
|
}
|
|
|
|
|
2017-11-17 11:14:29 -05:00
|
|
|
/**
|
|
|
|
* Returns a human-readable log of the most recent conversion activity.
|
|
|
|
* This may be empty if the conversion was successful.
|
|
|
|
*/
|
|
|
|
const std::string& getResultLog() { return _resultLog; }
|
|
|
|
|
|
|
|
/** Sets MSL source code. This can be used when MSL is supplied directly. */
|
2018-03-19 10:58:46 -04:00
|
|
|
void setMSL(const std::string& msl, const SPIRVEntryPoint* pEntryPoint) {
|
2017-11-17 11:14:29 -05:00
|
|
|
_msl = msl;
|
2018-03-19 10:58:46 -04:00
|
|
|
if (pEntryPoint) { _entryPoint = *pEntryPoint; }
|
2017-11-17 11:14:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void logMsg(const char* logMsg);
|
|
|
|
bool logError(const char* errMsg);
|
|
|
|
void logSPIRV(const char* opDesc);
|
|
|
|
bool validateSPIRV();
|
2018-01-08 21:44:46 -05:00
|
|
|
void writeSPIRVToFile(std::string spvFilepath);
|
2017-11-17 11:14:29 -05:00
|
|
|
void logSource(std::string& src, const char* srcLang, const char* opDesc);
|
|
|
|
|
|
|
|
std::vector<uint32_t> _spirv;
|
|
|
|
std::string _msl;
|
|
|
|
std::string _resultLog;
|
2018-03-19 10:58:46 -04:00
|
|
|
SPIRVEntryPoint _entryPoint;
|
2017-11-17 11:14:29 -05:00
|
|
|
bool _wasConverted = false;
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|