Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/KhronosGroup/SPIRV-Cross.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChip Davis <chip@holochip.com>2022-10-03 09:33:50 +0300
committerChip Davis <chip@holochip.com>2022-10-19 00:58:59 +0300
commita17108718015a58cb9e3e9c792ff49202498f6ce (patch)
tree928309d45685a10de0d51ff88288616cd1cfe6a9
parentf09ba2777714871bddb70d049878af34b94fa54d (diff)
MSL: Support "raw" buffer input in tessellation evaluation shaders.
Using vertex-style stage input is complex, and it doesn't support nesting of structures or arrays. By using raw buffer input instead, we get this support "for free," and everything becomes much simpler. Arguably, this is the way I should've done this in the first place. Eventually, I'd like to make this the default, and then remove the option altogether. (And I still need to do that with `multi_patch_workgroup`...) Should help fix 66 tests in the Vulkan CTS, under the following trees: - `dEQP-VK.pipeline.*.interface_matching.*` - `dEQP-VK.tessellation.user_defined_io.*` - `dEQP-VK.clipping.user_defined.*`
-rw-r--r--CMakeLists.txt2
-rw-r--r--main.cpp14
-rw-r--r--reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese12
-rw-r--r--reference/opt/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese72
-rw-r--r--reference/opt/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese38
-rw-r--r--reference/opt/shaders-msl/tese/input-types.raw-tess-in.tese70
-rw-r--r--reference/opt/shaders-msl/tese/quad.domain.tese12
-rw-r--r--reference/opt/shaders-msl/tese/quad.tese12
-rw-r--r--reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese12
-rw-r--r--reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese66
-rw-r--r--reference/opt/shaders-msl/tese/read-tess-level-in-func.msl2.tese8
-rw-r--r--reference/opt/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese64
-rw-r--r--reference/opt/shaders-msl/tese/triangle-tess-level.tese8
-rw-r--r--reference/opt/shaders-msl/tese/water_tess.raw-tess-in.tese46
-rw-r--r--reference/shaders-msl-no-opt/asm/tese/copy-tess-level.asm.msl2.tese12
-rw-r--r--reference/shaders-msl-no-opt/tese/builtin-input-automatic-attribute-assignment.tese12
-rw-r--r--reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese12
-rw-r--r--reference/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese73
-rw-r--r--reference/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese39
-rw-r--r--reference/shaders-msl/tese/input-types.raw-tess-in.tese81
-rw-r--r--reference/shaders-msl/tese/quad.domain.tese12
-rw-r--r--reference/shaders-msl/tese/quad.tese12
-rw-r--r--reference/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese12
-rw-r--r--reference/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese72
-rw-r--r--reference/shaders-msl/tese/read-tess-level-in-func.msl2.tese8
-rw-r--r--reference/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese70
-rw-r--r--reference/shaders-msl/tese/triangle-tess-level.tese8
-rw-r--r--reference/shaders-msl/tese/water_tess.raw-tess-in.tese77
-rw-r--r--shaders-msl/tese/in-array-of-struct.raw-tess-in.tese19
-rw-r--r--shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese31
-rw-r--r--shaders-msl/tese/input-types.raw-tess-in.tese75
-rw-r--r--shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese17
-rw-r--r--shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese17
-rw-r--r--shaders-msl/tese/water_tess.raw-tess-in.tese65
-rw-r--r--spirv_cross.cpp5
-rw-r--r--spirv_cross.hpp1
-rw-r--r--spirv_cross_c.cpp8
-rw-r--r--spirv_cross_c.h5
-rw-r--r--spirv_msl.cpp436
-rw-r--r--spirv_msl.hpp13
-rwxr-xr-xtest_shaders.py2
41 files changed, 1390 insertions, 240 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf14c8d5..b28fe0ae 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -332,7 +332,7 @@ if (SPIRV_CROSS_STATIC)
endif()
set(spirv-cross-abi-major 0)
-set(spirv-cross-abi-minor 50)
+set(spirv-cross-abi-minor 51)
set(spirv-cross-abi-patch 0)
if (SPIRV_CROSS_SHARED)
diff --git a/main.cpp b/main.cpp
index 7f7bda4b..0c84ac2a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -654,6 +654,7 @@ struct CLIArguments
bool msl_enable_frag_stencil_ref_builtin = true;
uint32_t msl_enable_frag_output_mask = 0xffffffff;
bool msl_enable_clip_distance_user_varying = true;
+ bool msl_raw_buffer_tese_input = false;
bool msl_multi_patch_workgroup = false;
bool msl_vertex_for_tessellation = false;
uint32_t msl_additional_fixed_sample_mask = 0xffffffff;
@@ -886,12 +887,15 @@ static void print_help_msl()
"\t\t<format> can be 'any32', 'any16', 'u16', 'u8', or 'other', to indicate a 32-bit opaque value, 16-bit opaque value, 16-bit unsigned integer, 8-bit unsigned integer, "
"or other-typed variable. <size> is the vector length of the variable, which must be greater than or equal to that declared in the shader.\n"
"\t\tUseful if shader stage interfaces don't match up, as pipeline creation might otherwise fail.\n"
+ "\t[--msl-raw-buffer-tese-input]:\n\t\tUse raw buffers for tessellation evaluation input.\n"
+ "\t\tThis allows the use of nested structures and arrays.\n"
+ "\t\tIn a future version of SPIRV-Cross, this will become the default.\n"
"\t[--msl-multi-patch-workgroup]:\n\t\tUse the new style of tessellation control processing, where multiple patches are processed per workgroup.\n"
- "\t\tThis should increase throughput by ensuring all the GPU's SIMD lanes are occupied, but it is not compatible with the old style.\n"
- "\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n"
- "\t\tIn a future version of SPIRV-Cross, this will become the default.\n"
+ "\t\tThis should increase throughput by ensuring all the GPU's SIMD lanes are occupied, but it is not compatible with the old style.\n"
+ "\t\tIn addition, this style also passes input variables in buffers directly instead of using vertex attribute processing.\n"
+ "\t\tIn a future version of SPIRV-Cross, this will become the default.\n"
"\t[--msl-vertex-for-tessellation]:\n\t\tWhen handling a vertex shader, marks it as one that will be used with a new-style tessellation control shader.\n"
- "\t\tThe vertex shader is output to MSL as a compute kernel which outputs vertices to the buffer in the order they are received, rather than in index order as with --msl-capture-output normally.\n"
+ "\t\tThe vertex shader is output to MSL as a compute kernel which outputs vertices to the buffer in the order they are received, rather than in index order as with --msl-capture-output normally.\n"
"\t[--msl-additional-fixed-sample-mask <mask>]:\n"
"\t\tSet an additional fixed sample mask. If the shader outputs a sample mask, then the final sample mask will be a bitwise AND of the two.\n"
"\t[--msl-arrayed-subpass-input]:\n\t\tAssume that images of dimension SubpassData have multiple layers. Layered input attachments are accessed relative to BuiltInLayer.\n"
@@ -1170,6 +1174,7 @@ static string compile_iteration(const CLIArguments &args, std::vector<uint32_t>
msl_opts.enable_frag_stencil_ref_builtin = args.msl_enable_frag_stencil_ref_builtin;
msl_opts.enable_frag_output_mask = args.msl_enable_frag_output_mask;
msl_opts.enable_clip_distance_user_varying = args.msl_enable_clip_distance_user_varying;
+ msl_opts.raw_buffer_tese_input = args.msl_raw_buffer_tese_input;
msl_opts.multi_patch_workgroup = args.msl_multi_patch_workgroup;
msl_opts.vertex_for_tessellation = args.msl_vertex_for_tessellation;
msl_opts.additional_fixed_sample_mask = args.msl_additional_fixed_sample_mask;
@@ -1645,6 +1650,7 @@ static int main_inner(int argc, char *argv[])
output.vecsize = parser.next_uint();
args.msl_shader_outputs.push_back(output);
});
+ cbs.add("--msl-raw-buffer-tese-input", [&args](CLIParser &) { args.msl_raw_buffer_tese_input = true; });
cbs.add("--msl-multi-patch-workgroup", [&args](CLIParser &) { args.msl_multi_patch_workgroup = true; });
cbs.add("--msl-vertex-for-tessellation", [&args](CLIParser &) { args.msl_vertex_for_tessellation = true; });
cbs.add("--msl-additional-fixed-sample-mask",
diff --git a/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese b/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese
index 2245cb98..bfa96f9c 100644
--- a/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese
+++ b/reference/opt/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese
@@ -60,12 +60,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
out.gl_Position = float4(fma(gl_TessCoord.x * gl_TessLevelInner[0], gl_TessLevelOuter[0], ((1.0 - gl_TessCoord.x) * gl_TessLevelInner[0]) * gl_TessLevelOuter[2]), fma(gl_TessCoord.y * gl_TessLevelInner[1], gl_TessLevelOuter[1], ((1.0 - gl_TessCoord.y) * gl_TessLevelInner[1]) * gl_TessLevelOuter[3]), 0.0, 1.0);
return out;
diff --git a/reference/opt/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese b/reference/opt/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese
new file mode 100644
index 00000000..e1f1f3c9
--- /dev/null
+++ b/reference/opt/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese
@@ -0,0 +1,72 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+template<typename T, size_t Num>
+struct spvUnsafeArray
+{
+ T elements[Num ? Num : 1];
+
+ thread T& operator [] (size_t pos) thread
+ {
+ return elements[pos];
+ }
+ constexpr const thread T& operator [] (size_t pos) const thread
+ {
+ return elements[pos];
+ }
+
+ device T& operator [] (size_t pos) device
+ {
+ return elements[pos];
+ }
+ constexpr const device T& operator [] (size_t pos) const device
+ {
+ return elements[pos];
+ }
+
+ constexpr const constant T& operator [] (size_t pos) const constant
+ {
+ return elements[pos];
+ }
+
+ threadgroup T& operator [] (size_t pos) threadgroup
+ {
+ return elements[pos];
+ }
+ constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
+ {
+ return elements[pos];
+ }
+};
+
+struct _35
+{
+ float dummy;
+ float4 variableInStruct;
+};
+
+struct main0_out
+{
+ float outResult [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ spvUnsafeArray<_35, 3> testStructArray;
+};
+
+[[ patch(triangle, 0) ]] vertex main0_out main0(float3 gl_TessCoord [[position_in_patch]], uint gl_PrimitiveID [[patch_id]], const device main0_in* spvIn [[buffer(22)]])
+{
+ main0_out out = {};
+ const device main0_in* gl_in = &spvIn[gl_PrimitiveID * 0];
+ out.gl_Position = float4((gl_TessCoord.xy * 2.0) - float2(1.0), 0.0, 1.0);
+ out.outResult = ((float(abs(gl_in[0].testStructArray[2].variableInStruct.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].testStructArray[2].variableInStruct.y - (-9.0)) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].testStructArray[2].variableInStruct.z - 3.0) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].testStructArray[2].variableInStruct.w - 7.0) < 0.001000000047497451305389404296875);
+ return out;
+}
+
diff --git a/reference/opt/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese b/reference/opt/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese
new file mode 100644
index 00000000..d81d44b0
--- /dev/null
+++ b/reference/opt/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese
@@ -0,0 +1,38 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct t35
+{
+ float2 m0;
+ float4 m1;
+};
+
+struct t36
+{
+ float2 m0;
+ t35 m1;
+};
+
+struct main0_out
+{
+ float v80 [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float2 v40_m0;
+ t35 v40_m1;
+};
+
+[[ patch(triangle, 0) ]] vertex main0_out main0(float3 gl_TessCoord [[position_in_patch]], uint gl_PrimitiveID [[patch_id]], const device main0_in* spvIn [[buffer(22)]])
+{
+ main0_out out = {};
+ const device main0_in* gl_in = &spvIn[gl_PrimitiveID * 0];
+ out.gl_Position = float4((gl_TessCoord.xy * 2.0) - float2(1.0), 0.0, 1.0);
+ out.v80 = ((float(abs(gl_in[0].v40_m1.m1.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].v40_m1.m1.y - (-9.0)) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].v40_m1.m1.z - 3.0) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].v40_m1.m1.w - 7.0) < 0.001000000047497451305389404296875);
+ return out;
+}
+
diff --git a/reference/opt/shaders-msl/tese/input-types.raw-tess-in.tese b/reference/opt/shaders-msl/tese/input-types.raw-tess-in.tese
new file mode 100644
index 00000000..e918f5b6
--- /dev/null
+++ b/reference/opt/shaders-msl/tese/input-types.raw-tess-in.tese
@@ -0,0 +1,70 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct Block
+{
+ float4 a;
+ float4 b;
+};
+
+struct PatchBlock
+{
+ float4 a;
+ float4 b;
+};
+
+struct Foo
+{
+ float4 a;
+ float4 b;
+};
+
+struct main0_out
+{
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 vColor;
+ float4 blocks_a;
+ float4 blocks_b;
+ Foo vFoos;
+};
+
+struct main0_patchIn
+{
+ float4 vColors;
+ float4 patch_block_a;
+ float4 patch_block_b;
+ Foo vFoo;
+};
+
+[[ patch(quad, 0) ]] vertex main0_out main0(uint gl_PrimitiveID [[patch_id]], const device main0_patchIn* spvPatchIn [[buffer(20)]], const device main0_in* spvIn [[buffer(22)]])
+{
+ main0_out out = {};
+ PatchBlock patch_block = {};
+ const device main0_in* gl_in = &spvIn[gl_PrimitiveID * 0];
+ const device main0_patchIn& patchIn = spvPatchIn[gl_PrimitiveID];
+ patch_block.a = patchIn.patch_block_a;
+ patch_block.b = patchIn.patch_block_b;
+ out.gl_Position = gl_in[0].blocks_a;
+ out.gl_Position += gl_in[0].blocks_b;
+ out.gl_Position += gl_in[1].blocks_a;
+ out.gl_Position += gl_in[1].blocks_b;
+ out.gl_Position += patch_block.a;
+ out.gl_Position += patch_block.b;
+ out.gl_Position += gl_in[0].vColor;
+ out.gl_Position += gl_in[1].vColor;
+ out.gl_Position += patchIn.vColors;
+ out.gl_Position += patchIn.vFoo.a;
+ out.gl_Position += patchIn.vFoo.b;
+ out.gl_Position += gl_in[0].vFoos.a;
+ out.gl_Position += gl_in[0].vFoos.b;
+ out.gl_Position += gl_in[1].vFoos.a;
+ out.gl_Position += gl_in[1].vFoos.b;
+ return out;
+}
+
diff --git a/reference/opt/shaders-msl/tese/quad.domain.tese b/reference/opt/shaders-msl/tese/quad.domain.tese
index 6d844054..81c4aa49 100644
--- a/reference/opt/shaders-msl/tese/quad.domain.tese
+++ b/reference/opt/shaders-msl/tese/quad.domain.tese
@@ -60,12 +60,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
gl_TessCoord.y = 1.0 - gl_TessCoord.y;
out.gl_Position = float4(fma(gl_TessCoord.x * gl_TessLevelInner[0], gl_TessLevelOuter[0], ((1.0 - gl_TessCoord.x) * gl_TessLevelInner[0]) * gl_TessLevelOuter[2]), fma(gl_TessCoord.y * gl_TessLevelInner[1], gl_TessLevelOuter[3], ((1.0 - gl_TessCoord.y) * gl_TessLevelInner[1]) * gl_TessLevelOuter[1]), 0.0, 1.0);
diff --git a/reference/opt/shaders-msl/tese/quad.tese b/reference/opt/shaders-msl/tese/quad.tese
index 2245cb98..bfa96f9c 100644
--- a/reference/opt/shaders-msl/tese/quad.tese
+++ b/reference/opt/shaders-msl/tese/quad.tese
@@ -60,12 +60,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
out.gl_Position = float4(fma(gl_TessCoord.x * gl_TessLevelInner[0], gl_TessLevelOuter[0], ((1.0 - gl_TessCoord.x) * gl_TessLevelInner[0]) * gl_TessLevelOuter[2]), fma(gl_TessCoord.y * gl_TessLevelInner[1], gl_TessLevelOuter[1], ((1.0 - gl_TessCoord.y) * gl_TessLevelInner[1]) * gl_TessLevelOuter[3]), 0.0, 1.0);
return out;
diff --git a/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese b/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese
index 251cb2fa..44bdd5ff 100644
--- a/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese
+++ b/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese
@@ -60,12 +60,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
out.gl_Position = float4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]) + float2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
return out;
}
diff --git a/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese b/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese
new file mode 100644
index 00000000..65d2fd94
--- /dev/null
+++ b/reference/opt/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese
@@ -0,0 +1,66 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+template<typename T, size_t Num>
+struct spvUnsafeArray
+{
+ T elements[Num ? Num : 1];
+
+ thread T& operator [] (size_t pos) thread
+ {
+ return elements[pos];
+ }
+ constexpr const thread T& operator [] (size_t pos) const thread
+ {
+ return elements[pos];
+ }
+
+ device T& operator [] (size_t pos) device
+ {
+ return elements[pos];
+ }
+ constexpr const device T& operator [] (size_t pos) const device
+ {
+ return elements[pos];
+ }
+
+ constexpr const constant T& operator [] (size_t pos) const constant
+ {
+ return elements[pos];
+ }
+
+ threadgroup T& operator [] (size_t pos) threadgroup
+ {
+ return elements[pos];
+ }
+ constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
+ {
+ return elements[pos];
+ }
+};
+
+struct main0_out
+{
+ float4 gl_Position [[position]];
+};
+
+[[ patch(quad, 0) ]] vertex main0_out main0(uint gl_PrimitiveID [[patch_id]], const device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]])
+{
+ main0_out out = {};
+ spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
+ spvUnsafeArray<float, 2> gl_TessLevelInner = {};
+ gl_TessLevelOuter[0] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0];
+ gl_TessLevelOuter[1] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1];
+ gl_TessLevelOuter[2] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2];
+ gl_TessLevelOuter[3] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3];
+ gl_TessLevelInner[0] = spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0];
+ gl_TessLevelInner[1] = spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1];
+ out.gl_Position = float4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]) + float2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
+ return out;
+}
+
diff --git a/reference/opt/shaders-msl/tese/read-tess-level-in-func.msl2.tese b/reference/opt/shaders-msl/tese/read-tess-level-in-func.msl2.tese
index bb2e1e59..decaca3d 100644
--- a/reference/opt/shaders-msl/tese/read-tess-level-in-func.msl2.tese
+++ b/reference/opt/shaders-msl/tese/read-tess-level-in-func.msl2.tese
@@ -59,10 +59,10 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
- gl_TessLevelOuter[0] = patchIn.gl_TessLevel.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevel.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevel.z;
- gl_TessLevelInner[0] = patchIn.gl_TessLevel.w;
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevel[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevel[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevel[2];
+ gl_TessLevelInner[0] = patchIn.gl_TessLevel[3];
out.gl_Position = float4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]) + float2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
return out;
}
diff --git a/reference/opt/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese b/reference/opt/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese
new file mode 100644
index 00000000..0b555ed0
--- /dev/null
+++ b/reference/opt/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese
@@ -0,0 +1,64 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+template<typename T, size_t Num>
+struct spvUnsafeArray
+{
+ T elements[Num ? Num : 1];
+
+ thread T& operator [] (size_t pos) thread
+ {
+ return elements[pos];
+ }
+ constexpr const thread T& operator [] (size_t pos) const thread
+ {
+ return elements[pos];
+ }
+
+ device T& operator [] (size_t pos) device
+ {
+ return elements[pos];
+ }
+ constexpr const device T& operator [] (size_t pos) const device
+ {
+ return elements[pos];
+ }
+
+ constexpr const constant T& operator [] (size_t pos) const constant
+ {
+ return elements[pos];
+ }
+
+ threadgroup T& operator [] (size_t pos) threadgroup
+ {
+ return elements[pos];
+ }
+ constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
+ {
+ return elements[pos];
+ }
+};
+
+struct main0_out
+{
+ float4 gl_Position [[position]];
+};
+
+[[ patch(triangle, 0) ]] vertex main0_out main0(uint gl_PrimitiveID [[patch_id]], const device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]])
+{
+ main0_out out = {};
+ spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
+ spvUnsafeArray<float, 2> gl_TessLevelInner = {};
+ gl_TessLevelOuter[0] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0];
+ gl_TessLevelOuter[1] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1];
+ gl_TessLevelOuter[2] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2];
+ gl_TessLevelInner[0] = spvTessLevel[gl_PrimitiveID].insideTessellationFactor;
+ out.gl_Position = float4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]) + float2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
+ return out;
+}
+
diff --git a/reference/opt/shaders-msl/tese/triangle-tess-level.tese b/reference/opt/shaders-msl/tese/triangle-tess-level.tese
index 6930e147..86ccc4f0 100644
--- a/reference/opt/shaders-msl/tese/triangle-tess-level.tese
+++ b/reference/opt/shaders-msl/tese/triangle-tess-level.tese
@@ -59,10 +59,10 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevel.w;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevel.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevel.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevel.z;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevel[3];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevel[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevel[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevel[2];
out.gl_Position = float4((gl_TessCoord.x * gl_TessLevelInner[0]) * gl_TessLevelOuter[0], (gl_TessCoord.y * gl_TessLevelInner[0]) * gl_TessLevelOuter[1], (gl_TessCoord.z * gl_TessLevelInner[0]) * gl_TessLevelOuter[2], 1.0);
return out;
}
diff --git a/reference/opt/shaders-msl/tese/water_tess.raw-tess-in.tese b/reference/opt/shaders-msl/tese/water_tess.raw-tess-in.tese
new file mode 100644
index 00000000..d4441c2e
--- /dev/null
+++ b/reference/opt/shaders-msl/tese/water_tess.raw-tess-in.tese
@@ -0,0 +1,46 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct UBO
+{
+ float4x4 uMVP;
+ float4 uScale;
+ float2 uInvScale;
+ float3 uCamPos;
+ float2 uPatchSize;
+ float2 uInvHeightmapSize;
+};
+
+struct main0_out
+{
+ float3 vWorld [[user(locn0)]];
+ float4 vGradNormalTex [[user(locn1)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_patchIn
+{
+ float2 vOutPatchPosBase;
+ float4 vPatchLods;
+};
+
+[[ patch(quad, 0) ]] vertex main0_out main0(constant UBO& _31 [[buffer(0)]], texture2d<float> uHeightmapDisplacement [[texture(0)]], sampler uHeightmapDisplacementSmplr [[sampler(0)]], float2 gl_TessCoordIn [[position_in_patch]], uint gl_PrimitiveID [[patch_id]], const device main0_patchIn* spvPatchIn [[buffer(20)]])
+{
+ main0_out out = {};
+ const device main0_patchIn& patchIn = spvPatchIn[gl_PrimitiveID];
+ float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
+ float2 _202 = fma(gl_TessCoord.xy, _31.uPatchSize, patchIn.vOutPatchPosBase);
+ float2 _216 = mix(patchIn.vPatchLods.yx, patchIn.vPatchLods.zw, float2(gl_TessCoord.x));
+ float _223 = mix(_216.x, _216.y, gl_TessCoord.y);
+ float _225 = floor(_223);
+ float2 _141 = _31.uInvHeightmapSize * exp2(_225);
+ out.vGradNormalTex = float4(fma(_202, _31.uInvHeightmapSize, _31.uInvHeightmapSize * 0.5), (_202 * _31.uInvHeightmapSize) * _31.uScale.zw);
+ float3 _256 = mix(uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, fma(_202, _31.uInvHeightmapSize, _141 * 0.5), level(_225)).xyz, uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, fma(_202, _31.uInvHeightmapSize, _141 * 1.0), level(_225 + 1.0)).xyz, float3(_223 - _225));
+ float2 _171 = fma(_202, _31.uScale.xy, _256.yz);
+ out.vWorld = float3(_171.x, _256.x, _171.y);
+ out.gl_Position = _31.uMVP * float4(out.vWorld, 1.0);
+ return out;
+}
+
diff --git a/reference/shaders-msl-no-opt/asm/tese/copy-tess-level.asm.msl2.tese b/reference/shaders-msl-no-opt/asm/tese/copy-tess-level.asm.msl2.tese
index e34fd66e..7fa0f5a4 100644
--- a/reference/shaders-msl-no-opt/asm/tese/copy-tess-level.asm.msl2.tese
+++ b/reference/shaders-msl-no-opt/asm/tese/copy-tess-level.asm.msl2.tese
@@ -61,12 +61,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
out.out_var_CUSTOM_VALUE = float4(gl_TessLevelOuter[0] + gl_TessLevelInner[0], gl_TessLevelOuter[1] + gl_TessLevelInner[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]);
return out;
}
diff --git a/reference/shaders-msl-no-opt/tese/builtin-input-automatic-attribute-assignment.tese b/reference/shaders-msl-no-opt/tese/builtin-input-automatic-attribute-assignment.tese
index 02581a3e..15f04e73 100644
--- a/reference/shaders-msl-no-opt/tese/builtin-input-automatic-attribute-assignment.tese
+++ b/reference/shaders-msl-no-opt/tese/builtin-input-automatic-attribute-assignment.tese
@@ -68,12 +68,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
out.gl_Position = (((((float4(1.0) + patchIn.FragColor) + patchIn.gl_in[0].FragColors) + patchIn.gl_in[1].FragColors) + float4(gl_TessLevelInner[0])) + float4(gl_TessLevelOuter[int(gl_PrimitiveID) & 1])) + patchIn.gl_in[0].gl_Position;
return out;
}
diff --git a/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese b/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese
index b6bdf415..7fd48b4f 100644
--- a/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese
+++ b/reference/shaders-msl/asm/tese/unnamed-builtin-array.asm.tese
@@ -60,12 +60,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
out.gl_Position = float4(((gl_TessCoord.x * gl_TessLevelInner[0]) * gl_TessLevelOuter[0]) + (((1.0 - gl_TessCoord.x) * gl_TessLevelInner[0]) * gl_TessLevelOuter[2]), ((gl_TessCoord.y * gl_TessLevelInner[1]) * gl_TessLevelOuter[1]) + (((1.0 - gl_TessCoord.y) * gl_TessLevelInner[1]) * gl_TessLevelOuter[3]), 0.0, 1.0);
return out;
diff --git a/reference/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese b/reference/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese
new file mode 100644
index 00000000..44e495fb
--- /dev/null
+++ b/reference/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese
@@ -0,0 +1,73 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+template<typename T, size_t Num>
+struct spvUnsafeArray
+{
+ T elements[Num ? Num : 1];
+
+ thread T& operator [] (size_t pos) thread
+ {
+ return elements[pos];
+ }
+ constexpr const thread T& operator [] (size_t pos) const thread
+ {
+ return elements[pos];
+ }
+
+ device T& operator [] (size_t pos) device
+ {
+ return elements[pos];
+ }
+ constexpr const device T& operator [] (size_t pos) const device
+ {
+ return elements[pos];
+ }
+
+ constexpr const constant T& operator [] (size_t pos) const constant
+ {
+ return elements[pos];
+ }
+
+ threadgroup T& operator [] (size_t pos) threadgroup
+ {
+ return elements[pos];
+ }
+ constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
+ {
+ return elements[pos];
+ }
+};
+
+struct _35
+{
+ float dummy;
+ float4 variableInStruct;
+};
+
+struct main0_out
+{
+ float outResult [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ spvUnsafeArray<_35, 3> testStructArray;
+};
+
+[[ patch(triangle, 0) ]] vertex main0_out main0(float3 gl_TessCoord [[position_in_patch]], uint gl_PrimitiveID [[patch_id]], const device main0_in* spvIn [[buffer(22)]])
+{
+ main0_out out = {};
+ const device main0_in* gl_in = &spvIn[gl_PrimitiveID * 0];
+ out.gl_Position = float4((gl_TessCoord.xy * 2.0) - float2(1.0), 0.0, 1.0);
+ float result = ((float(abs(gl_in[0].testStructArray[2].variableInStruct.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].testStructArray[2].variableInStruct.y - (-9.0)) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].testStructArray[2].variableInStruct.z - 3.0) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].testStructArray[2].variableInStruct.w - 7.0) < 0.001000000047497451305389404296875);
+ out.outResult = result;
+ return out;
+}
+
diff --git a/reference/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese b/reference/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese
new file mode 100644
index 00000000..23c2cc3e
--- /dev/null
+++ b/reference/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese
@@ -0,0 +1,39 @@
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct t35
+{
+ float2 m0;
+ float4 m1;
+};
+
+struct t36
+{
+ float2 m0;
+ t35 m1;
+};
+
+struct main0_out
+{
+ float v80 [[user(locn0)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float2 v40_m0;
+ t35 v40_m1;
+};
+
+[[ patch(triangle, 0) ]] vertex main0_out main0(float3 gl_TessCoord [[position_in_patch]], uint gl_PrimitiveID [[patch_id]], const device main0_in* spvIn [[buffer(22)]])
+{
+ main0_out out = {};
+ const device main0_in* gl_in = &spvIn[gl_PrimitiveID * 0];
+ out.gl_Position = float4((gl_TessCoord.xy * 2.0) - float2(1.0), 0.0, 1.0);
+ float v34 = ((float(abs(gl_in[0].v40_m1.m1.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(gl_in[0].v40_m1.m1.y - (-9.0)) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].v40_m1.m1.z - 3.0) < 0.001000000047497451305389404296875)) * float(abs(gl_in[0].v40_m1.m1.w - 7.0) < 0.001000000047497451305389404296875);
+ out.v80 = v34;
+ return out;
+}
+
diff --git a/reference/shaders-msl/tese/input-types.raw-tess-in.tese b/reference/shaders-msl/tese/input-types.raw-tess-in.tese
new file mode 100644
index 00000000..52952220
--- /dev/null
+++ b/reference/shaders-msl/tese/input-types.raw-tess-in.tese
@@ -0,0 +1,81 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct Block
+{
+ float4 a;
+ float4 b;
+};
+
+struct PatchBlock
+{
+ float4 a;
+ float4 b;
+};
+
+struct Foo
+{
+ float4 a;
+ float4 b;
+};
+
+struct main0_out
+{
+ float4 gl_Position [[position]];
+};
+
+struct main0_in
+{
+ float4 vColor;
+ float4 blocks_a;
+ float4 blocks_b;
+ Foo vFoos;
+};
+
+struct main0_patchIn
+{
+ float4 vColors;
+ float4 patch_block_a;
+ float4 patch_block_b;
+ Foo vFoo;
+};
+
+static inline __attribute__((always_inline))
+void set_from_function(thread float4& gl_Position, const device main0_in* thread & gl_in, thread PatchBlock& patch_block, const device float4& vColors, const device Foo& vFoo)
+{
+ gl_Position = gl_in[0].blocks_a;
+ gl_Position += gl_in[0].blocks_b;
+ gl_Position += gl_in[1].blocks_a;
+ gl_Position += gl_in[1].blocks_b;
+ gl_Position += patch_block.a;
+ gl_Position += patch_block.b;
+ gl_Position += gl_in[0].vColor;
+ gl_Position += gl_in[1].vColor;
+ gl_Position += vColors;
+ Foo foo = vFoo;
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+ foo = gl_in[0].vFoos;
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+ foo = gl_in[1].vFoos;
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+}
+
+[[ patch(quad, 0) ]] vertex main0_out main0(uint gl_PrimitiveID [[patch_id]], const device main0_patchIn* spvPatchIn [[buffer(20)]], const device main0_in* spvIn [[buffer(22)]])
+{
+ main0_out out = {};
+ PatchBlock patch_block = {};
+ const device main0_in* gl_in = &spvIn[gl_PrimitiveID * 0];
+ const device main0_patchIn& patchIn = spvPatchIn[gl_PrimitiveID];
+ patch_block.a = patchIn.patch_block_a;
+ patch_block.b = patchIn.patch_block_b;
+ set_from_function(out.gl_Position, gl_in, patch_block, patchIn.vColors, patchIn.vFoo);
+ return out;
+}
+
diff --git a/reference/shaders-msl/tese/quad.domain.tese b/reference/shaders-msl/tese/quad.domain.tese
index dc5c7e40..10cdf5f1 100644
--- a/reference/shaders-msl/tese/quad.domain.tese
+++ b/reference/shaders-msl/tese/quad.domain.tese
@@ -60,12 +60,12 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
gl_TessCoord.y = 1.0 - gl_TessCoord.y;
out.gl_Position = float4(((gl_TessCoord.x * gl_TessLevelInner[0]) * gl_TessLevelOuter[0]) + (((1.0 - gl_TessCoord.x) * gl_TessLevelInner[0]) * gl_TessLevelOuter[2]), ((gl_TessCoord.y * gl_TessLevelInner[1]) * gl_TessLevelOuter[3]) + (((1.0 - gl_TessCoord.y) * gl_TessLevelInner[1]) * gl_TessLevelOuter[1]), 0.0, 1.0);
diff --git a/reference/shaders-msl/tese/quad.tese b/reference/shaders-msl/tese/quad.tese
index 0b48661e..e0c79443 100644
--- a/reference/shaders-msl/tese/quad.tese
+++ b/reference/shaders-msl/tese/quad.tese
@@ -66,12 +66,12 @@ void set_position(thread float4& gl_Position, thread float3& gl_TessCoord, threa
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
set_position(out.gl_Position, gl_TessCoord, gl_TessLevelInner, gl_TessLevelOuter);
return out;
diff --git a/reference/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese b/reference/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese
index 14d2565e..7048546c 100644
--- a/reference/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese
+++ b/reference/shaders-msl/tese/read-tess-level-in-func-quad.msl2.tese
@@ -66,12 +66,12 @@ float4 read_tess_levels(thread spvUnsafeArray<float, 4>& gl_TessLevelOuter, thre
main0_out out = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
- gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter.z;
- gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter.w;
- gl_TessLevelInner[0] = patchIn.gl_TessLevelInner.x;
- gl_TessLevelInner[1] = patchIn.gl_TessLevelInner.y;
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevelOuter[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevelOuter[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevelOuter[2];
+ gl_TessLevelOuter[3] = patchIn.gl_TessLevelOuter[3];
+ gl_TessLevelInner[0] = patchIn.gl_TessLevelInner[0];
+ gl_TessLevelInner[1] = patchIn.gl_TessLevelInner[1];
out.gl_Position = read_tess_levels(gl_TessLevelOuter, gl_TessLevelInner);
return out;
}
diff --git a/reference/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese b/reference/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese
new file mode 100644
index 00000000..f8f81b75
--- /dev/null
+++ b/reference/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese
@@ -0,0 +1,72 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+template<typename T, size_t Num>
+struct spvUnsafeArray
+{
+ T elements[Num ? Num : 1];
+
+ thread T& operator [] (size_t pos) thread
+ {
+ return elements[pos];
+ }
+ constexpr const thread T& operator [] (size_t pos) const thread
+ {
+ return elements[pos];
+ }
+
+ device T& operator [] (size_t pos) device
+ {
+ return elements[pos];
+ }
+ constexpr const device T& operator [] (size_t pos) const device
+ {
+ return elements[pos];
+ }
+
+ constexpr const constant T& operator [] (size_t pos) const constant
+ {
+ return elements[pos];
+ }
+
+ threadgroup T& operator [] (size_t pos) threadgroup
+ {
+ return elements[pos];
+ }
+ constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
+ {
+ return elements[pos];
+ }
+};
+
+struct main0_out
+{
+ float4 gl_Position [[position]];
+};
+
+static inline __attribute__((always_inline))
+float4 read_tess_levels(thread spvUnsafeArray<float, 4>& gl_TessLevelOuter, thread spvUnsafeArray<float, 2>& gl_TessLevelInner)
+{
+ return float4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]) + float2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
+}
+
+[[ patch(quad, 0) ]] vertex main0_out main0(uint gl_PrimitiveID [[patch_id]], const device MTLQuadTessellationFactorsHalf* spvTessLevel [[buffer(26)]])
+{
+ main0_out out = {};
+ spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
+ spvUnsafeArray<float, 2> gl_TessLevelInner = {};
+ gl_TessLevelOuter[0] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0];
+ gl_TessLevelOuter[1] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1];
+ gl_TessLevelOuter[2] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2];
+ gl_TessLevelOuter[3] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[3];
+ gl_TessLevelInner[0] = spvTessLevel[gl_PrimitiveID].insideTessellationFactor[0];
+ gl_TessLevelInner[1] = spvTessLevel[gl_PrimitiveID].insideTessellationFactor[1];
+ out.gl_Position = read_tess_levels(gl_TessLevelOuter, gl_TessLevelInner);
+ return out;
+}
+
diff --git a/reference/shaders-msl/tese/read-tess-level-in-func.msl2.tese b/reference/shaders-msl/tese/read-tess-level-in-func.msl2.tese
index f2fd49e9..432ad7cc 100644
--- a/reference/shaders-msl/tese/read-tess-level-in-func.msl2.tese
+++ b/reference/shaders-msl/tese/read-tess-level-in-func.msl2.tese
@@ -65,10 +65,10 @@ float4 read_tess_levels(thread spvUnsafeArray<float, 4>& gl_TessLevelOuter, thre
main0_out out = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
- gl_TessLevelOuter[0] = patchIn.gl_TessLevel.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevel.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevel.z;
- gl_TessLevelInner[0] = patchIn.gl_TessLevel.w;
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevel[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevel[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevel[2];
+ gl_TessLevelInner[0] = patchIn.gl_TessLevel[3];
out.gl_Position = read_tess_levels(gl_TessLevelOuter, gl_TessLevelInner);
return out;
}
diff --git a/reference/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese b/reference/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese
new file mode 100644
index 00000000..5be7c401
--- /dev/null
+++ b/reference/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese
@@ -0,0 +1,70 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#pragma clang diagnostic ignored "-Wmissing-braces"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+template<typename T, size_t Num>
+struct spvUnsafeArray
+{
+ T elements[Num ? Num : 1];
+
+ thread T& operator [] (size_t pos) thread
+ {
+ return elements[pos];
+ }
+ constexpr const thread T& operator [] (size_t pos) const thread
+ {
+ return elements[pos];
+ }
+
+ device T& operator [] (size_t pos) device
+ {
+ return elements[pos];
+ }
+ constexpr const device T& operator [] (size_t pos) const device
+ {
+ return elements[pos];
+ }
+
+ constexpr const constant T& operator [] (size_t pos) const constant
+ {
+ return elements[pos];
+ }
+
+ threadgroup T& operator [] (size_t pos) threadgroup
+ {
+ return elements[pos];
+ }
+ constexpr const threadgroup T& operator [] (size_t pos) const threadgroup
+ {
+ return elements[pos];
+ }
+};
+
+struct main0_out
+{
+ float4 gl_Position [[position]];
+};
+
+static inline __attribute__((always_inline))
+float4 read_tess_levels(thread spvUnsafeArray<float, 4>& gl_TessLevelOuter, thread spvUnsafeArray<float, 2>& gl_TessLevelInner)
+{
+ return float4(gl_TessLevelOuter[0], gl_TessLevelOuter[1], gl_TessLevelOuter[2], gl_TessLevelOuter[3]) + float2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
+}
+
+[[ patch(triangle, 0) ]] vertex main0_out main0(uint gl_PrimitiveID [[patch_id]], const device MTLTriangleTessellationFactorsHalf* spvTessLevel [[buffer(26)]])
+{
+ main0_out out = {};
+ spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
+ spvUnsafeArray<float, 2> gl_TessLevelInner = {};
+ gl_TessLevelOuter[0] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[0];
+ gl_TessLevelOuter[1] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[1];
+ gl_TessLevelOuter[2] = spvTessLevel[gl_PrimitiveID].edgeTessellationFactor[2];
+ gl_TessLevelInner[0] = spvTessLevel[gl_PrimitiveID].insideTessellationFactor;
+ out.gl_Position = read_tess_levels(gl_TessLevelOuter, gl_TessLevelInner);
+ return out;
+}
+
diff --git a/reference/shaders-msl/tese/triangle-tess-level.tese b/reference/shaders-msl/tese/triangle-tess-level.tese
index 6930e147..86ccc4f0 100644
--- a/reference/shaders-msl/tese/triangle-tess-level.tese
+++ b/reference/shaders-msl/tese/triangle-tess-level.tese
@@ -59,10 +59,10 @@ struct main0_patchIn
main0_out out = {};
spvUnsafeArray<float, 2> gl_TessLevelInner = {};
spvUnsafeArray<float, 4> gl_TessLevelOuter = {};
- gl_TessLevelInner[0] = patchIn.gl_TessLevel.w;
- gl_TessLevelOuter[0] = patchIn.gl_TessLevel.x;
- gl_TessLevelOuter[1] = patchIn.gl_TessLevel.y;
- gl_TessLevelOuter[2] = patchIn.gl_TessLevel.z;
+ gl_TessLevelInner[0] = patchIn.gl_TessLevel[3];
+ gl_TessLevelOuter[0] = patchIn.gl_TessLevel[0];
+ gl_TessLevelOuter[1] = patchIn.gl_TessLevel[1];
+ gl_TessLevelOuter[2] = patchIn.gl_TessLevel[2];
out.gl_Position = float4((gl_TessCoord.x * gl_TessLevelInner[0]) * gl_TessLevelOuter[0], (gl_TessCoord.y * gl_TessLevelInner[0]) * gl_TessLevelOuter[1], (gl_TessCoord.z * gl_TessLevelInner[0]) * gl_TessLevelOuter[2], 1.0);
return out;
}
diff --git a/reference/shaders-msl/tese/water_tess.raw-tess-in.tese b/reference/shaders-msl/tese/water_tess.raw-tess-in.tese
new file mode 100644
index 00000000..bf934564
--- /dev/null
+++ b/reference/shaders-msl/tese/water_tess.raw-tess-in.tese
@@ -0,0 +1,77 @@
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+
+#include <metal_stdlib>
+#include <simd/simd.h>
+
+using namespace metal;
+
+struct UBO
+{
+ float4x4 uMVP;
+ float4 uScale;
+ float2 uInvScale;
+ float3 uCamPos;
+ float2 uPatchSize;
+ float2 uInvHeightmapSize;
+};
+
+struct main0_out
+{
+ float3 vWorld [[user(locn0)]];
+ float4 vGradNormalTex [[user(locn1)]];
+ float4 gl_Position [[position]];
+};
+
+struct main0_patchIn
+{
+ float2 vOutPatchPosBase;
+ float4 vPatchLods;
+};
+
+static inline __attribute__((always_inline))
+float2 lerp_vertex(thread const float2& tess_coord, const device float2& vOutPatchPosBase, constant UBO& v_31)
+{
+ return vOutPatchPosBase + (tess_coord * v_31.uPatchSize);
+}
+
+static inline __attribute__((always_inline))
+float2 lod_factor(thread const float2& tess_coord, const device float4& vPatchLods)
+{
+ float2 x = mix(vPatchLods.yx, vPatchLods.zw, float2(tess_coord.x));
+ float level0 = mix(x.x, x.y, tess_coord.y);
+ float floor_level = floor(level0);
+ float fract_level = level0 - floor_level;
+ return float2(floor_level, fract_level);
+}
+
+static inline __attribute__((always_inline))
+float3 sample_height_displacement(thread const float2& uv, thread const float2& off, thread const float2& lod, texture2d<float> uHeightmapDisplacement, sampler uHeightmapDisplacementSmplr)
+{
+ return mix(uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, (uv + (off * 0.5)), level(lod.x)).xyz, uHeightmapDisplacement.sample(uHeightmapDisplacementSmplr, (uv + (off * 1.0)), level(lod.x + 1.0)).xyz, float3(lod.y));
+}
+
+[[ patch(quad, 0) ]] vertex main0_out main0(constant UBO& v_31 [[buffer(0)]], texture2d<float> uHeightmapDisplacement [[texture(0)]], sampler uHeightmapDisplacementSmplr [[sampler(0)]], float2 gl_TessCoordIn [[position_in_patch]], uint gl_PrimitiveID [[patch_id]], const device main0_patchIn* spvPatchIn [[buffer(20)]])
+{
+ main0_out out = {};
+ const device main0_patchIn& patchIn = spvPatchIn[gl_PrimitiveID];
+ float3 gl_TessCoord = float3(gl_TessCoordIn.x, gl_TessCoordIn.y, 0.0);
+ float2 tess_coord = gl_TessCoord.xy;
+ float2 param = tess_coord;
+ float2 pos = lerp_vertex(param, patchIn.vOutPatchPosBase, v_31);
+ float2 param_1 = tess_coord;
+ float2 lod = lod_factor(param_1, patchIn.vPatchLods);
+ float2 tex = pos * v_31.uInvHeightmapSize;
+ pos *= v_31.uScale.xy;
+ float delta_mod = exp2(lod.x);
+ float2 off = v_31.uInvHeightmapSize * delta_mod;
+ out.vGradNormalTex = float4(tex + (v_31.uInvHeightmapSize * 0.5), tex * v_31.uScale.zw);
+ float2 param_2 = tex;
+ float2 param_3 = off;
+ float2 param_4 = lod;
+ float3 height_displacement = sample_height_displacement(param_2, param_3, param_4, uHeightmapDisplacement, uHeightmapDisplacementSmplr);
+ pos += height_displacement.yz;
+ out.vWorld = float3(pos.x, height_displacement.x, pos.y);
+ out.gl_Position = v_31.uMVP * float4(out.vWorld, 1.0);
+ return out;
+}
+
diff --git a/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese b/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese
new file mode 100644
index 00000000..a32c1309
--- /dev/null
+++ b/shaders-msl/tese/in-array-of-struct.raw-tess-in.tese
@@ -0,0 +1,19 @@
+#version 450
+#extension GL_EXT_tessellation_shader : require
+
+layout(triangles) in;
+layout(location = 0) in struct {
+ float dummy;
+ vec4 variableInStruct;
+} testStructArray[][3];
+layout(location = 0) out float outResult;
+void main(void)
+{
+ gl_Position = vec4(gl_TessCoord.xy * 2.0 - 1.0, 0.0, 1.0);
+ float result;
+ result = float(abs(testStructArray[0][2].variableInStruct.x - -4.0) < 0.001) *
+ float(abs(testStructArray[0][2].variableInStruct.y - -9.0) < 0.001) *
+ float(abs(testStructArray[0][2].variableInStruct.z - 3.0) < 0.001) *
+ float(abs(testStructArray[0][2].variableInStruct.w - 7.0) < 0.001);
+ outResult = result;
+}
diff --git a/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese b/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese
new file mode 100644
index 00000000..629415a8
--- /dev/null
+++ b/shaders-msl/tese/in-block-with-nested-struct.raw-tess-in.tese
@@ -0,0 +1,31 @@
+#version 450
+layout(triangles, ccw, equal_spacing) in;
+
+out gl_PerVertex
+{
+ vec4 gl_Position;
+ float gl_PointSize;
+ float gl_ClipDistance[1];
+ float gl_CullDistance[1];
+};
+
+struct t35
+{
+ vec2 m0;
+ vec4 m1;
+};
+
+layout(location = 0) in t36
+{
+ vec2 m0;
+ t35 m1;
+} v40[32];
+
+layout(location = 0) out float v80;
+
+void main()
+{
+ gl_Position = vec4((gl_TessCoord.xy * 2.0) - vec2(1.0), 0.0, 1.0);
+ float v34 = ((float(abs(v40[0].m1.m1.x - (-4.0)) < 0.001000000047497451305389404296875) * float(abs(v40[0].m1.m1.y - (-9.0)) < 0.001000000047497451305389404296875)) * float(abs(v40[0].m1.m1.z - 3.0) < 0.001000000047497451305389404296875)) * float(abs(v40[0].m1.m1.w - 7.0) < 0.001000000047497451305389404296875);
+ v80 = v34;
+}
diff --git a/shaders-msl/tese/input-types.raw-tess-in.tese b/shaders-msl/tese/input-types.raw-tess-in.tese
new file mode 100644
index 00000000..3157953f
--- /dev/null
+++ b/shaders-msl/tese/input-types.raw-tess-in.tese
@@ -0,0 +1,75 @@
+#version 450
+
+layout(ccw, quads, fractional_even_spacing) in;
+
+// Try to use the whole taxonomy of input methods.
+
+// Per-vertex vector.
+layout(location = 0) in vec4 vColor[];
+// Per-patch vector.
+layout(location = 1) patch in vec4 vColors;
+// Per-patch vector array.
+layout(location = 2) patch in vec4 vColorsArray[2];
+
+// I/O blocks, per patch and per control point.
+layout(location = 4) in Block
+{
+ vec4 a;
+ vec4 b;
+} blocks[];
+
+layout(location = 6) patch in PatchBlock
+{
+ vec4 a;
+ vec4 b;
+} patch_block;
+
+// Composites.
+struct Foo
+{
+ vec4 a;
+ vec4 b;
+};
+layout(location = 8) patch in Foo vFoo;
+//layout(location = 10) patch in Foo vFooArray[2]; // FIXME: Handling of array-of-struct input is broken!
+
+// Per-control point struct.
+layout(location = 14) in Foo vFoos[];
+
+void set_from_function()
+{
+ gl_Position = blocks[0].a;
+ gl_Position += blocks[0].b;
+ gl_Position += blocks[1].a;
+ gl_Position += blocks[1].b;
+ gl_Position += patch_block.a;
+ gl_Position += patch_block.b;
+ gl_Position += vColor[0];
+ gl_Position += vColor[1];
+ gl_Position += vColors;
+
+ Foo foo = vFoo;
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+
+ /*foo = vFooArray[0];
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+
+ foo = vFooArray[1];
+ gl_Position += foo.a;
+ gl_Position += foo.b;*/
+
+ foo = vFoos[0];
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+
+ foo = vFoos[1];
+ gl_Position += foo.a;
+ gl_Position += foo.b;
+}
+
+void main()
+{
+ set_from_function();
+}
diff --git a/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese b/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese
new file mode 100644
index 00000000..0c289ac5
--- /dev/null
+++ b/shaders-msl/tese/read-tess-level-in-func-quad.raw-tess-in.msl2.tese
@@ -0,0 +1,17 @@
+#version 450
+layout(quads) in;
+
+vec4 read_tess_levels()
+{
+ return vec4(
+ gl_TessLevelOuter[0],
+ gl_TessLevelOuter[1],
+ gl_TessLevelOuter[2],
+ gl_TessLevelOuter[3]) +
+ vec2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
+}
+
+void main()
+{
+ gl_Position = read_tess_levels();
+}
diff --git a/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese b/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese
new file mode 100644
index 00000000..8cf1f1a8
--- /dev/null
+++ b/shaders-msl/tese/read-tess-level-in-func.raw-tess-in.msl2.tese
@@ -0,0 +1,17 @@
+#version 450
+layout(triangles) in;
+
+vec4 read_tess_levels()
+{
+ return vec4(
+ gl_TessLevelOuter[0],
+ gl_TessLevelOuter[1],
+ gl_TessLevelOuter[2],
+ gl_TessLevelOuter[3]) +
+ vec2(gl_TessLevelInner[0], gl_TessLevelInner[1]).xyxy;
+}
+
+void main()
+{
+ gl_Position = read_tess_levels();
+}
diff --git a/shaders-msl/tese/water_tess.raw-tess-in.tese b/shaders-msl/tese/water_tess.raw-tess-in.tese
new file mode 100644
index 00000000..32d6bc93
--- /dev/null
+++ b/shaders-msl/tese/water_tess.raw-tess-in.tese
@@ -0,0 +1,65 @@
+#version 310 es
+#extension GL_EXT_tessellation_shader : require
+precision highp int;
+
+layout(cw, quads, fractional_even_spacing) in;
+
+layout(location = 0) patch in vec2 vOutPatchPosBase;
+layout(location = 1) patch in vec4 vPatchLods;
+
+layout(binding = 1, std140) uniform UBO
+{
+ mat4 uMVP;
+ vec4 uScale;
+ vec2 uInvScale;
+ vec3 uCamPos;
+ vec2 uPatchSize;
+ vec2 uInvHeightmapSize;
+};
+layout(binding = 0) uniform mediump sampler2D uHeightmapDisplacement;
+
+layout(location = 0) highp out vec3 vWorld;
+layout(location = 1) highp out vec4 vGradNormalTex;
+
+vec2 lerp_vertex(vec2 tess_coord)
+{
+ return vOutPatchPosBase + tess_coord * uPatchSize;
+}
+
+mediump vec2 lod_factor(vec2 tess_coord)
+{
+ mediump vec2 x = mix(vPatchLods.yx, vPatchLods.zw, tess_coord.x);
+ mediump float level = mix(x.x, x.y, tess_coord.y);
+ mediump float floor_level = floor(level);
+ mediump float fract_level = level - floor_level;
+ return vec2(floor_level, fract_level);
+}
+
+mediump vec3 sample_height_displacement(vec2 uv, vec2 off, mediump vec2 lod)
+{
+ return mix(
+ textureLod(uHeightmapDisplacement, uv + 0.5 * off, lod.x).xyz,
+ textureLod(uHeightmapDisplacement, uv + 1.0 * off, lod.x + 1.0).xyz,
+ lod.y);
+}
+
+void main()
+{
+ vec2 tess_coord = gl_TessCoord.xy;
+ vec2 pos = lerp_vertex(tess_coord);
+ mediump vec2 lod = lod_factor(tess_coord);
+
+ vec2 tex = pos * uInvHeightmapSize.xy;
+ pos *= uScale.xy;
+
+ mediump float delta_mod = exp2(lod.x);
+ vec2 off = uInvHeightmapSize.xy * delta_mod;
+
+ vGradNormalTex = vec4(tex + 0.5 * uInvHeightmapSize.xy, tex * uScale.zw);
+ vec3 height_displacement = sample_height_displacement(tex, off, lod);
+
+ pos += height_displacement.yz;
+ vWorld = vec3(pos.x, height_displacement.x, pos.y);
+ gl_Position = uMVP * vec4(vWorld, 1.0);
+}
+
diff --git a/spirv_cross.cpp b/spirv_cross.cpp
index e8c57720..3b8bc87b 100644
--- a/spirv_cross.cpp
+++ b/spirv_cross.cpp
@@ -2345,6 +2345,11 @@ bool Compiler::is_tessellation_shader() const
return is_tessellation_shader(get_execution_model());
}
+bool Compiler::is_tessellating_triangles() const
+{
+ return get_execution_mode_bitset().get(ExecutionModeTriangles);
+}
+
void Compiler::set_remapped_variable_state(VariableID id, bool remap_enable)
{
get<SPIRVariable>(id).remapped_variable = remap_enable;
diff --git a/spirv_cross.hpp b/spirv_cross.hpp
index 53c8b653..789010fa 100644
--- a/spirv_cross.hpp
+++ b/spirv_cross.hpp
@@ -371,6 +371,7 @@ public:
spv::ExecutionModel get_execution_model() const;
bool is_tessellation_shader() const;
+ bool is_tessellating_triangles() const;
// In SPIR-V, the compute work group size can be represented by a constant vector, in which case
// the LocalSize execution mode is ignored.
diff --git a/spirv_cross_c.cpp b/spirv_cross_c.cpp
index 011d127a..d227f342 100644
--- a/spirv_cross_c.cpp
+++ b/spirv_cross_c.cpp
@@ -715,6 +715,14 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
case SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE:
options->msl.ios_support_base_vertex_instance = value != 0;
break;
+
+ case SPVC_COMPILER_OPTION_MSL_RAW_BUFFER_TESE_INPUT:
+ options->msl.raw_buffer_tese_input = value != 0;
+ break;
+
+ case SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_INPUT_BUFFER_INDEX:
+ options->msl.shader_patch_input_buffer_index = value;
+ break;
#endif
default:
diff --git a/spirv_cross_c.h b/spirv_cross_c.h
index 89434878..a8a64d4d 100644
--- a/spirv_cross_c.h
+++ b/spirv_cross_c.h
@@ -40,7 +40,7 @@ extern "C" {
/* Bumped if ABI or API breaks backwards compatibility. */
#define SPVC_C_API_VERSION_MAJOR 0
/* Bumped if APIs or enumerations are added in a backwards compatible way. */
-#define SPVC_C_API_VERSION_MINOR 50
+#define SPVC_C_API_VERSION_MINOR 51
/* Bumped if internal implementation details change. */
#define SPVC_C_API_VERSION_PATCH 0
@@ -690,6 +690,9 @@ typedef enum spvc_compiler_option
SPVC_COMPILER_OPTION_RELAX_NAN_CHECKS = 78 | SPVC_COMPILER_OPTION_COMMON_BIT,
+ SPVC_COMPILER_OPTION_MSL_RAW_BUFFER_TESE_INPUT = 79 | SPVC_COMPILER_OPTION_MSL_BIT,
+ SPVC_COMPILER_OPTION_MSL_SHADER_PATCH_INPUT_BUFFER_INDEX = 80 | SPVC_COMPILER_OPTION_MSL_BIT,
+
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
} spvc_compiler_option;
diff --git a/spirv_msl.cpp b/spirv_msl.cpp
index b974b669..b56edf74 100644
--- a/spirv_msl.cpp
+++ b/spirv_msl.cpp
@@ -234,7 +234,8 @@ void CompilerMSL::build_implicit_builtins()
bool need_sample_pos = active_input_builtins.get(BuiltInSamplePosition);
bool need_vertex_params = capture_output_to_buffer && get_execution_model() == ExecutionModelVertex &&
!msl_options.vertex_for_tessellation;
- bool need_tesc_params = get_execution_model() == ExecutionModelTessellationControl;
+ bool need_tesc_params = is_tesc_shader();
+ bool need_tese_params = is_tese_shader() && msl_options.raw_buffer_tese_input;
bool need_subgroup_mask =
active_input_builtins.get(BuiltInSubgroupEqMask) || active_input_builtins.get(BuiltInSubgroupGeMask) ||
active_input_builtins.get(BuiltInSubgroupGtMask) || active_input_builtins.get(BuiltInSubgroupLeMask) ||
@@ -257,9 +258,9 @@ void CompilerMSL::build_implicit_builtins()
bool need_workgroup_size = msl_options.emulate_subgroups && active_input_builtins.get(BuiltInNumSubgroups);
if (need_subpass_input || need_sample_pos || need_subgroup_mask || need_vertex_params || need_tesc_params ||
- need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params || needs_sample_id ||
- needs_subgroup_invocation_id || needs_subgroup_size || has_additional_fixed_sample_mask() || need_local_invocation_index ||
- need_workgroup_size)
+ need_tese_params || need_multiview || need_dispatch_base || need_vertex_base_params || need_grid_params ||
+ needs_sample_id || needs_subgroup_invocation_id || needs_subgroup_size || has_additional_fixed_sample_mask() ||
+ need_local_invocation_index || need_workgroup_size)
{
bool has_frag_coord = false;
bool has_sample_id = false;
@@ -365,23 +366,28 @@ void CompilerMSL::build_implicit_builtins()
}
}
- if (need_tesc_params)
+ if (need_tesc_params && builtin == BuiltInInvocationId)
{
- switch (builtin)
- {
- case BuiltInInvocationId:
- builtin_invocation_id_id = var.self;
- mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self);
- has_invocation_id = true;
- break;
- case BuiltInPrimitiveId:
- builtin_primitive_id_id = var.self;
- mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self);
- has_primitive_id = true;
- break;
- default:
- break;
- }
+ builtin_invocation_id_id = var.self;
+ mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var.self);
+ has_invocation_id = true;
+ }
+
+ if ((need_tesc_params || need_tese_params) && builtin == BuiltInPrimitiveId)
+ {
+ builtin_primitive_id_id = var.self;
+ mark_implicit_builtin(StorageClassInput, BuiltInPrimitiveId, var.self);
+ has_primitive_id = true;
+ }
+
+ if (need_tese_params && builtin == BuiltInTessLevelOuter)
+ {
+ tess_level_outer_var_id = var.self;
+ }
+
+ if (need_tese_params && builtin == BuiltInTessLevelInner)
+ {
+ tess_level_inner_var_id = var.self;
}
if ((need_subgroup_mask || needs_subgroup_invocation_id) && builtin == BuiltInSubgroupLocalInvocationId)
@@ -640,7 +646,7 @@ void CompilerMSL::build_implicit_builtins()
}
if ((need_tesc_params && (msl_options.multi_patch_workgroup || !has_invocation_id || !has_primitive_id)) ||
- need_grid_params)
+ (need_tese_params && !has_primitive_id) || need_grid_params)
{
uint32_t type_ptr_id = ir.increase_bound_by(1);
@@ -653,7 +659,7 @@ void CompilerMSL::build_implicit_builtins()
auto &ptr_type = set<SPIRType>(type_ptr_id, uint_type_ptr);
ptr_type.self = get_uint_type_id();
- if (msl_options.multi_patch_workgroup || need_grid_params)
+ if ((need_tesc_params && msl_options.multi_patch_workgroup) || need_grid_params)
{
uint32_t var_id = ir.increase_bound_by(1);
@@ -674,7 +680,7 @@ void CompilerMSL::build_implicit_builtins()
mark_implicit_builtin(StorageClassInput, BuiltInInvocationId, var_id);
}
- if (need_tesc_params && !has_primitive_id)
+ if ((need_tesc_params || need_tese_params) && !has_primitive_id)
{
uint32_t var_id = ir.increase_bound_by(1);
@@ -891,11 +897,9 @@ void CompilerMSL::build_implicit_builtins()
}
// If we're returning a struct from a vertex-like entry point, we must return a position attribute.
- bool need_position =
- (get_execution_model() == ExecutionModelVertex ||
- get_execution_model() == ExecutionModelTessellationEvaluation) &&
- !capture_output_to_buffer && !get_is_rasterization_disabled() &&
- !active_output_builtins.get(BuiltInPosition);
+ bool need_position = (get_execution_model() == ExecutionModelVertex || is_tese_shader()) &&
+ !capture_output_to_buffer && !get_is_rasterization_disabled() &&
+ !active_output_builtins.get(BuiltInPosition);
if (need_position)
{
@@ -1085,7 +1089,7 @@ SPIRType &CompilerMSL::get_patch_stage_out_struct_type()
std::string CompilerMSL::get_tess_factor_struct_name()
{
- if (get_entry_point().flags.get(ExecutionModeTriangles))
+ if (is_tessellating_triangles())
return "MTLTriangleTessellationFactorsHalf";
return "MTLQuadTessellationFactorsHalf";
}
@@ -1483,10 +1487,10 @@ string CompilerMSL::compile()
stage_out_var_id = add_interface_block(StorageClassOutput);
patch_stage_out_var_id = add_interface_block(StorageClassOutput, true);
stage_in_var_id = add_interface_block(StorageClassInput);
- if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ if (is_tese_shader())
patch_stage_in_var_id = add_interface_block(StorageClassInput, true);
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader())
stage_out_ptr_var_id = add_interface_block_pointer(stage_out_var_id, StorageClassOutput);
if (is_tessellation_shader())
stage_in_ptr_var_id = add_interface_block_pointer(stage_in_var_id, StorageClassInput);
@@ -1565,8 +1569,7 @@ void CompilerMSL::preprocess_op_codes()
// Tessellation control shaders are run as compute functions in Metal, and so
// must capture their output to a buffer.
- if (get_execution_model() == ExecutionModelTessellationControl ||
- (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation))
+ if (is_tesc_shader() || (get_execution_model() == ExecutionModelVertex && msl_options.vertex_for_tessellation))
{
is_rasterization_disabled = true;
capture_output_to_buffer = true;
@@ -1906,9 +1909,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
bool is_patch = has_decoration(arg_id, DecorationPatch) || is_patch_block(*p_type);
bool is_block = has_decoration(p_type->self, DecorationBlock);
bool is_control_point_storage =
- !is_patch &&
- ((is_tessellation_shader() && var.storage == StorageClassInput) ||
- (get_execution_model() == ExecutionModelTessellationControl && var.storage == StorageClassOutput));
+ !is_patch && ((is_tessellation_shader() && var.storage == StorageClassInput) ||
+ (is_tesc_shader() && var.storage == StorageClassOutput));
bool is_patch_block_storage = is_patch && is_block && var.storage == StorageClassOutput;
bool is_builtin = is_builtin_variable(var);
bool variable_is_stage_io =
@@ -1924,8 +1926,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
if (is_redirected_to_global_stage_io)
{
- // Tessellation control shaders see inputs and per-vertex outputs as arrays.
- // Similarly, tessellation evaluation shaders see per-vertex inputs as arrays.
+ // Tessellation control shaders see inputs and per-point outputs as arrays.
+ // Similarly, tessellation evaluation shaders see per-point inputs as arrays.
// We collected them into a structure; we must pass the array of this
// structure to the function.
std::string name;
@@ -1948,10 +1950,6 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
}
}
- // Tessellation control shaders see inputs and per-vertex outputs as arrays.
- // Similarly, tessellation evaluation shaders see per-vertex inputs as arrays.
- // We collected them into a structure; we must pass the array of this
- // structure to the function.
if (var.storage == StorageClassInput)
{
auto &added_in = is_patch ? patch_added_in : control_point_added_in;
@@ -1975,6 +1973,8 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
set<SPIRVariable>(next_id, type_id, StorageClassFunction, 0, arg_id);
set_name(next_id, name);
+ if (is_tese_shader() && msl_options.raw_buffer_tese_input && var.storage == StorageClassInput)
+ set_decoration(next_id, DecorationNonWritable);
}
else if (is_builtin && has_decoration(p_type->self, DecorationBlock))
{
@@ -3111,19 +3111,10 @@ void CompilerMSL::add_plain_member_variable_to_interface_block(StorageClass stor
void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type,
SPIRVariable &var)
{
- auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
auto &var_type = get_variable_element_type(var);
BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
-
- // Force the variable to have the proper name.
- string var_name = builtin_to_glsl(builtin, StorageClassFunction);
- set_name(var.self, var_name);
-
- // We need to declare the variable early and at entry-point scope.
- entry_func.add_local_variable(var.self);
- vars_needing_early_declaration.push_back(var.self);
- bool triangles = get_execution_mode_bitset().get(ExecutionModeTriangles);
+ bool triangles = is_tessellating_triangles();
string mbr_name;
// Add a reference to the variable type to the interface struct.
@@ -3169,7 +3160,7 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
}
else
{
- mbr_name = var_name;
+ mbr_name = builtin_to_glsl(builtin, StorageClassFunction);
uint32_t type_id = build_extended_vector_type(var_type.self, builtin == BuiltInTessLevelOuter ? 4 : 2);
@@ -3189,27 +3180,49 @@ void CompilerMSL::add_tess_level_input_to_interface_block(const std::string &ib_
mark_locations(new_var_type);
}
+ add_tess_level_input(ib_var_ref, mbr_name, var);
+}
+
+void CompilerMSL::add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var)
+{
+ auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
+ BuiltIn builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
+
+ // Force the variable to have the proper name.
+ string var_name = builtin_to_glsl(builtin, StorageClassFunction);
+ set_name(var.self, var_name);
+
+ // We need to declare the variable early and at entry-point scope.
+ entry_func.add_local_variable(var.self);
+ vars_needing_early_declaration.push_back(var.self);
+ bool triangles = is_tessellating_triangles();
+
if (builtin == BuiltInTessLevelOuter)
{
- entry_func.fixup_hooks_in.push_back([=]() {
- statement(var_name, "[0] = ", ib_var_ref, ".", mbr_name, ".x;");
- statement(var_name, "[1] = ", ib_var_ref, ".", mbr_name, ".y;");
- statement(var_name, "[2] = ", ib_var_ref, ".", mbr_name, ".z;");
- if (!triangles)
- statement(var_name, "[3] = ", ib_var_ref, ".", mbr_name, ".w;");
- });
+ entry_func.fixup_hooks_in.push_back(
+ [=]()
+ {
+ statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[0];");
+ statement(var_name, "[1] = ", base_ref, ".", mbr_name, "[1];");
+ statement(var_name, "[2] = ", base_ref, ".", mbr_name, "[2];");
+ if (!triangles)
+ statement(var_name, "[3] = ", base_ref, ".", mbr_name, "[3];");
+ });
}
else
{
entry_func.fixup_hooks_in.push_back([=]() {
if (triangles)
{
- statement(var_name, "[0] = ", ib_var_ref, ".", mbr_name, ".w;");
+ if (msl_options.raw_buffer_tese_input)
+ statement(var_name, "[0] = ", base_ref, ".", mbr_name, ";");
+ else
+ statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[3];");
}
else
{
- statement(var_name, "[0] = ", ib_var_ref, ".", mbr_name, ".x;");
- statement(var_name, "[1] = ", ib_var_ref, ".", mbr_name, ".y;");
+ statement(var_name, "[0] = ", base_ref, ".", mbr_name, "[0];");
+ statement(var_name, "[1] = ", base_ref, ".", mbr_name, "[1];");
}
});
}
@@ -3220,7 +3233,8 @@ bool CompilerMSL::variable_storage_requires_stage_io(spv::StorageClass storage)
if (storage == StorageClassOutput)
return !capture_output_to_buffer;
else if (storage == StorageClassInput)
- return !(get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup);
+ return !(is_tesc_shader() && msl_options.multi_patch_workgroup) &&
+ !(is_tese_shader() && msl_options.raw_buffer_tese_input);
else
return false;
}
@@ -3474,8 +3488,7 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
// If we're redirecting a block, we might still need to access the original block
// variable if we're masking some members.
- if (masked_block && !needs_local_declaration &&
- (!is_builtin_variable(var) || get_execution_model() == ExecutionModelTessellationControl))
+ if (masked_block && !needs_local_declaration && (!is_builtin_variable(var) || is_tesc_shader()))
{
if (is_builtin_variable(var))
{
@@ -3496,8 +3509,8 @@ void CompilerMSL::add_variable_to_interface_block(StorageClass storage, const st
}
}
}
- else if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput &&
- !meta.strip_array && is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner))
+ else if (is_tese_shader() && storage == StorageClassInput && !meta.strip_array && is_builtin &&
+ (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner))
{
add_tess_level_input_to_interface_block(ib_var_ref, ib_type, var);
}
@@ -3534,8 +3547,7 @@ void CompilerMSL::fix_up_interface_member_indices(StorageClass storage, uint32_t
// Only needed for tessellation shaders and pull-model interpolants.
// Need to redirect interface indices back to variables themselves.
// For structs, each member of the struct need a separate instance.
- if (get_execution_model() != ExecutionModelTessellationControl &&
- !(get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput) &&
+ if (!is_tesc_shader() && !(is_tese_shader() && storage == StorageClassInput) &&
!(get_execution_model() == ExecutionModelFragment && storage == StorageClassInput &&
!pull_model_inputs.empty()))
return;
@@ -3624,9 +3636,8 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
// These builtins are part of the stage in/out structs.
bool is_interface_block_builtin =
- builtin_is_stage_in_out ||
- (get_execution_model() == ExecutionModelTessellationEvaluation &&
- (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner));
+ builtin_is_stage_in_out || (is_tese_shader() && !msl_options.raw_buffer_tese_input &&
+ (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner));
bool is_active = interface_variable_exists_in_entry_point(var.self);
if (is_builtin && is_active)
@@ -3722,12 +3733,29 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
}
}
}
+
+ if (is_tese_shader() && msl_options.raw_buffer_tese_input && patch && storage == StorageClassInput &&
+ (bi_type == BuiltInTessLevelOuter || bi_type == BuiltInTessLevelInner))
+ {
+ // In this case, we won't add the builtin to the interface struct,
+ // but we still need the hook to run to populate the arrays.
+ string base_ref = join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id), "]");
+ const char *mbr_name =
+ bi_type == BuiltInTessLevelOuter ? "edgeTessellationFactor" : "insideTessellationFactor";
+ add_tess_level_input(base_ref, mbr_name, var);
+ if (inputs_by_builtin.count(bi_type))
+ {
+ uint32_t locn = inputs_by_builtin[bi_type].location;
+ mark_location_as_used_by_shader(locn, type, StorageClassInput);
+ }
+ }
});
// If no variables qualify, leave.
// For patch input in a tessellation evaluation shader, the per-vertex stage inputs
// are included in a special patch control point array.
- if (vars.empty() && !(storage == StorageClassInput && patch && stage_in_var_id))
+ if (vars.empty() &&
+ !(!msl_options.raw_buffer_tese_input && storage == StorageClassInput && patch && stage_in_var_id))
return 0;
// Add a new typed variable for this interface structure.
@@ -3750,8 +3778,9 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
{
case StorageClassInput:
ib_var_ref = patch ? patch_stage_in_var_name : stage_in_var_name;
- if (get_execution_model() == ExecutionModelTessellationControl)
+ switch (get_execution_model())
{
+ case ExecutionModelTessellationControl:
// Add a hook to populate the shared workgroup memory containing the gl_in array.
entry_func.fixup_hooks_in.push_back([=]() {
// Can't use PatchVertices, PrimitiveId, or InvocationId yet; the hooks for those may not have run yet.
@@ -3777,6 +3806,33 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
statement(" return;");
}
});
+ break;
+ case ExecutionModelTessellationEvaluation:
+ if (!msl_options.raw_buffer_tese_input)
+ break;
+ if (patch)
+ {
+ entry_func.fixup_hooks_in.push_back(
+ [=]()
+ {
+ statement("const device ", to_name(ir.default_entry_point), "_", ib_var_ref, "& ", ib_var_ref,
+ " = ", patch_input_buffer_var_name, "[", to_expression(builtin_primitive_id_id),
+ "];");
+ });
+ }
+ else
+ {
+ entry_func.fixup_hooks_in.push_back(
+ [=]()
+ {
+ statement("const device ", to_name(ir.default_entry_point), "_", ib_var_ref, "* gl_in = &",
+ input_buffer_var_name, "[", to_expression(builtin_primitive_id_id), " * ",
+ get_entry_point().output_vertices, "];");
+ });
+ }
+ break;
+ default:
+ break;
}
break;
@@ -3902,29 +3958,33 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
for (auto *p_var : vars)
{
- bool strip_array =
- (get_execution_model() == ExecutionModelTessellationControl ||
- (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput)) &&
- !patch;
+ bool strip_array = (is_tesc_shader() || (is_tese_shader() && storage == StorageClassInput)) && !patch;
// Fixing up flattened stores in TESC is impossible since the memory is group shared either via
// device (not masked) or threadgroup (masked) storage classes and it's race condition city.
meta.strip_array = strip_array;
- meta.allow_local_declaration = !strip_array && !(get_execution_model() == ExecutionModelTessellationControl &&
- storage == StorageClassOutput);
+ meta.allow_local_declaration = !strip_array && !(is_tesc_shader() && storage == StorageClassOutput);
add_variable_to_interface_block(storage, ib_var_ref, ib_type, *p_var, meta);
}
- if (get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup &&
+ if (((is_tesc_shader() && msl_options.multi_patch_workgroup) ||
+ (is_tese_shader() && msl_options.raw_buffer_tese_input)) &&
storage == StorageClassInput)
{
- // For tessellation control inputs, add all outputs from the vertex shader to ensure
+ // For tessellation inputs, add all outputs from the previous stage to ensure
// the struct containing them is the correct size and layout.
for (auto &input : inputs_by_location)
{
if (location_inputs_in_use.count(input.first.location) != 0)
continue;
+ if (input.second.patch != patch)
+ continue;
+
+ // Tessellation levels have their own struct, so there's no need to add them here.
+ if (input.second.builtin == BuiltInTessLevelOuter || input.second.builtin == BuiltInTessLevelInner)
+ continue;
+
// Create a fake variable to put at the location.
uint32_t offset = ir.increase_bound_by(4);
uint32_t type_id = offset;
@@ -4007,7 +4067,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
type.vecsize = output.second.vecsize;
set<SPIRType>(type_id, type);
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader())
{
type.array.push_back(0);
type.array_size_literal.push_back(true);
@@ -4017,7 +4077,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
type.pointer = true;
type.pointer_depth++;
- type.parent_type = get_execution_model() == ExecutionModelTessellationControl ? array_type_id : type_id;
+ type.parent_type = is_tesc_shader() ? array_type_id : type_id;
type.storage = storage;
auto &ptr_type = set<SPIRType>(ptr_type_id, type);
ptr_type.self = type.parent_type;
@@ -4067,7 +4127,7 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
fix_up_interface_member_indices(storage, ib_type_id);
// For patch inputs, add one more member, holding the array of control point data.
- if (get_execution_model() == ExecutionModelTessellationEvaluation && storage == StorageClassInput && patch &&
+ if (is_tese_shader() && !msl_options.raw_buffer_tese_input && storage == StorageClassInput && patch &&
stage_in_var_id)
{
uint32_t pcp_type_id = ir.increase_bound_by(1);
@@ -4081,6 +4141,9 @@ uint32_t CompilerMSL::add_interface_block(StorageClass storage, bool patch)
set_member_name(ib_type.self, mbr_idx, "gl_in");
}
+ if (storage == StorageClassInput)
+ set_decoration(ib_var_id, DecorationNonWritable);
+
return ib_var_id;
}
@@ -4092,7 +4155,7 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla
uint32_t ib_ptr_var_id;
uint32_t next_id = ir.increase_bound_by(3);
auto &ib_type = expression_type(ib_var_id);
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader() || (is_tese_shader() && msl_options.raw_buffer_tese_input))
{
// Tessellation control per-vertex I/O is presented as an array, so we must
// do the same with our struct here.
@@ -4101,10 +4164,12 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla
ib_ptr_type.parent_type = ib_ptr_type.type_alias = ib_type.self;
ib_ptr_type.pointer = true;
ib_ptr_type.pointer_depth++;
- ib_ptr_type.storage =
- storage == StorageClassInput ?
- (msl_options.multi_patch_workgroup ? StorageClassStorageBuffer : StorageClassWorkgroup) :
- StorageClassStorageBuffer;
+ ib_ptr_type.storage = storage == StorageClassInput ?
+ ((is_tesc_shader() && msl_options.multi_patch_workgroup) ||
+ (is_tese_shader() && msl_options.raw_buffer_tese_input) ?
+ StorageClassStorageBuffer :
+ StorageClassWorkgroup) :
+ StorageClassStorageBuffer;
ir.meta[ib_ptr_type_id] = ir.meta[ib_type.self];
// To ensure that get_variable_data_type() doesn't strip off the pointer,
// which we need, use another pointer.
@@ -4118,6 +4183,8 @@ uint32_t CompilerMSL::add_interface_block_pointer(uint32_t ib_var_id, StorageCla
ib_ptr_var_id = next_id;
set<SPIRVariable>(ib_ptr_var_id, ib_ptr_ptr_type_id, StorageClassFunction, 0);
set_name(ib_ptr_var_id, storage == StorageClassInput ? "gl_in" : "gl_out");
+ if (storage == StorageClassInput)
+ set_decoration(ib_ptr_var_id, DecorationNonWritable);
}
else
{
@@ -7330,7 +7397,7 @@ bool CompilerMSL::emit_tessellation_io_load(uint32_t result_type_id, uint32_t id
auto &result_type = get<SPIRType>(result_type_id);
if (ptr_type.storage != StorageClassInput && ptr_type.storage != StorageClassOutput)
return false;
- if (ptr_type.storage == StorageClassOutput && get_execution_model() == ExecutionModelTessellationEvaluation)
+ if (ptr_type.storage == StorageClassOutput && is_tese_shader())
return false;
if (has_decoration(ptr, DecorationPatch))
@@ -7662,8 +7729,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
patch = has_decoration(ops[2], DecorationPatch) || is_patch_block(get_variable_data_type(*var));
// Should match strip_array in add_interface_block.
- flat_data = var->storage == StorageClassInput ||
- (var->storage == StorageClassOutput && get_execution_model() == ExecutionModelTessellationControl);
+ flat_data = var->storage == StorageClassInput || (var->storage == StorageClassOutput && is_tesc_shader());
// Patch inputs are treated as normal block IO variables, so they don't deal with this path at all.
if (patch && (!is_block || var->storage == StorageClassInput))
@@ -7947,8 +8013,8 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
// Don't do this if the index is a constant 1, though. We need to drop stores
// to that one.
auto *m = ir.find_meta(var ? var->self : ID(0));
- if (get_execution_model() == ExecutionModelTessellationControl && var && m &&
- m->decoration.builtin_type == BuiltInTessLevelInner && get_entry_point().flags.get(ExecutionModeTriangles))
+ if (is_tesc_shader() && var && m && m->decoration.builtin_type == BuiltInTessLevelInner &&
+ is_tessellating_triangles())
{
auto *c = maybe_get<SPIRConstant>(ops[3]);
if (c && c->scalar() == 1)
@@ -7965,7 +8031,7 @@ bool CompilerMSL::emit_tessellation_access_chain(const uint32_t *ops, uint32_t l
bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs)
{
- if (!get_entry_point().flags.get(ExecutionModeTriangles))
+ if (!is_tessellating_triangles())
return false;
// In SPIR-V, TessLevelInner always has two elements and TessLevelOuter always has
@@ -7975,7 +8041,7 @@ bool CompilerMSL::is_out_of_bounds_tessellation_level(uint32_t id_lhs)
// In Metal, however, only the first element of TessLevelInner and the first three
// of TessLevelOuter are accessible. This stems from how in Metal, the tessellation
// levels must be stored to a dedicated buffer in a particular format that depends
- // on the patch type. Therefore, in Triangles mode, any access to the second
+ // on the patch type. Therefore, in Triangles mode, any store to the second
// inner level or the fourth outer level must be dropped.
const auto *e = maybe_get<SPIRExpression>(id_lhs);
if (!e || !e->access_chain)
@@ -8017,8 +8083,7 @@ bool CompilerMSL::access_chain_needs_stage_io_builtin_translation(uint32_t base)
// Avoid overriding it back to just gl_ClipDistance.
// This can only happen in scenarios where we cannot flatten/unflatten access chains, so, the only case
// where this triggers is evaluation shader inputs.
- bool redirect_builtin = get_execution_model() == ExecutionModelTessellationEvaluation ?
- var->storage == StorageClassOutput : false;
+ bool redirect_builtin = is_tese_shader() ? var->storage == StorageClassOutput : false;
return redirect_builtin;
}
@@ -9127,7 +9192,7 @@ void CompilerMSL::emit_texture_op(const Instruction &i, bool sparse)
void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem)
{
- if (get_execution_model() != ExecutionModelGLCompute && get_execution_model() != ExecutionModelTessellationControl)
+ if (get_execution_model() != ExecutionModelGLCompute && !is_tesc_shader())
return;
uint32_t exe_scope = id_exe_scope ? evaluate_constant_u32(id_exe_scope) : uint32_t(ScopeInvocation);
@@ -9154,13 +9219,12 @@ void CompilerMSL::emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uin
string mem_flags = "";
// For tesc shaders, this also affects objects in the Output storage class.
// Since in Metal, these are placed in a device buffer, we have to sync device memory here.
- if (get_execution_model() == ExecutionModelTessellationControl ||
+ if (is_tesc_shader() ||
(mem_sem & (MemorySemanticsUniformMemoryMask | MemorySemanticsCrossWorkgroupMemoryMask)))
mem_flags += "mem_flags::mem_device";
// Fix tessellation patch function processing
- if (get_execution_model() == ExecutionModelTessellationControl ||
- (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask)))
+ if (is_tesc_shader() || (mem_sem & (MemorySemanticsSubgroupMemoryMask | MemorySemanticsWorkgroupMemoryMask)))
{
if (!mem_flags.empty())
mem_flags += " | ";
@@ -9338,7 +9402,7 @@ void CompilerMSL::emit_array_copy(const string &lhs, uint32_t lhs_id, uint32_t r
uint32_t CompilerMSL::get_physical_tess_level_array_size(spv::BuiltIn builtin) const
{
- if (get_execution_mode_bitset().get(ExecutionModeTriangles))
+ if (is_tessellating_triangles())
return builtin == BuiltInTessLevelInner ? 1 : 3;
else
return builtin == BuiltInTessLevelInner ? 2 : 4;
@@ -9372,8 +9436,7 @@ bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
return true;
}
- if (get_execution_model() == ExecutionModelTessellationControl &&
- has_decoration(id_lhs, DecorationBuiltIn))
+ if (is_tesc_shader() && has_decoration(id_lhs, DecorationBuiltIn))
{
auto builtin = BuiltIn(get_decoration(id_lhs, DecorationBuiltIn));
// Need to manually unroll the array store.
@@ -11411,8 +11474,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
// Vertex and tessellation evaluation function outputs
- if (((execution.model == ExecutionModelVertex && !msl_options.vertex_for_tessellation) ||
- execution.model == ExecutionModelTessellationEvaluation) &&
+ if (((execution.model == ExecutionModelVertex && !msl_options.vertex_for_tessellation) || is_tese_shader()) &&
type.storage == StorageClassOutput)
{
if (is_builtin)
@@ -11466,7 +11528,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
// Tessellation control function inputs
- if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassInput)
+ if (is_tesc_shader() && type.storage == StorageClassInput)
{
if (is_builtin)
{
@@ -11503,7 +11565,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
// Tessellation control function outputs
- if (execution.model == ExecutionModelTessellationControl && type.storage == StorageClassOutput)
+ if (is_tesc_shader() && type.storage == StorageClassOutput)
{
// For this type of shader, we always arrange for it to capture its
// output to a buffer. For this reason, qualifiers are irrelevant here.
@@ -11514,7 +11576,7 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
}
// Tessellation evaluation function inputs
- if (execution.model == ExecutionModelTessellationEvaluation && type.storage == StorageClassInput)
+ if (is_tese_shader() && type.storage == StorageClassInput)
{
if (is_builtin)
{
@@ -11530,6 +11592,10 @@ string CompilerMSL::member_attribute_qualifier(const SPIRType &type, uint32_t in
break;
}
}
+
+ if (msl_options.raw_buffer_tese_input)
+ return "";
+
// The special control point array must not be marked with an attribute.
if (get_type(type.member_types[index]).basetype == SPIRType::ControlPointArray)
return "";
@@ -11787,7 +11853,7 @@ uint32_t CompilerMSL::get_or_allocate_builtin_input_member_location(spv::BuiltIn
// Triangle tess level inputs are shared in one packed float4,
// mark both builtins as sharing one location.
- if (get_execution_mode_bitset().get(ExecutionModeTriangles) &&
+ if (!msl_options.raw_buffer_tese_input && is_tessellating_triangles() &&
(builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
{
builtin_to_automatic_input_location[BuiltInTessLevelInner] = loc;
@@ -11831,8 +11897,7 @@ uint32_t CompilerMSL::get_or_allocate_builtin_output_member_location(spv::BuiltI
// Triangle tess level inputs are shared in one packed float4;
// mark both builtins as sharing one location.
- if (get_execution_mode_bitset().get(ExecutionModeTriangles) &&
- (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
+ if (is_tessellating_triangles() && (builtin == BuiltInTessLevelInner || builtin == BuiltInTessLevelOuter))
{
builtin_to_automatic_output_location[BuiltInTessLevelInner] = loc;
builtin_to_automatic_output_location[BuiltInTessLevelOuter] = loc;
@@ -11874,10 +11939,9 @@ string CompilerMSL::func_type_decl(SPIRType &type)
if (execution.flags.get(ExecutionModeIsolines))
SPIRV_CROSS_THROW("Metal does not support isoline tessellation.");
if (msl_options.is_ios())
- entry_type =
- join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ") ]] vertex");
+ entry_type = join("[[ patch(", is_tessellating_triangles() ? "triangle" : "quad", ") ]] vertex");
else
- entry_type = join("[[ patch(", execution.flags.get(ExecutionModeTriangles) ? "triangle" : "quad", ", ",
+ entry_type = join("[[ patch(", is_tessellating_triangles() ? "triangle" : "quad", ", ",
execution.output_vertices, ") ]] vertex");
break;
case ExecutionModelFragment:
@@ -11901,6 +11965,16 @@ string CompilerMSL::func_type_decl(SPIRType &type)
return entry_type + " " + return_type;
}
+bool CompilerMSL::is_tesc_shader() const
+{
+ return get_execution_model() == ExecutionModelTessellationControl;
+}
+
+bool CompilerMSL::is_tese_shader() const
+{
+ return get_execution_model() == ExecutionModelTessellationEvaluation;
+}
+
bool CompilerMSL::uses_explicit_early_fragment_test()
{
auto &ep_flags = get_entry_point().flags;
@@ -11972,9 +12046,21 @@ string CompilerMSL::get_type_address_space(const SPIRType &type, uint32_t id, bo
break;
case StorageClassInput:
- if (get_execution_model() == ExecutionModelTessellationControl && var &&
- var->basevariable == stage_in_ptr_var_id)
- addr_space = msl_options.multi_patch_workgroup ? "constant" : "threadgroup";
+ if (is_tesc_shader() && var && var->basevariable == stage_in_ptr_var_id)
+ addr_space = msl_options.multi_patch_workgroup ? "const device" : "threadgroup";
+ // Don't pass tessellation levels in the device AS; we load and convert them
+ // to float manually.
+ if (is_tese_shader() && msl_options.raw_buffer_tese_input && var)
+ {
+ bool is_stage_in = var->basevariable == stage_in_ptr_var_id;
+ bool is_patch_stage_in = has_decoration(var->self, DecorationPatch) ||
+ is_patch_block(get_variable_data_type(get<SPIRVariable>(var->basevariable)));
+ bool is_builtin = has_decoration(var->self, DecorationBuiltIn);
+ BuiltIn builtin = (BuiltIn)get_decoration(var->self, DecorationBuiltIn);
+ bool is_tess_level = is_builtin && (builtin == BuiltInTessLevelOuter || builtin == BuiltInTessLevelInner);
+ if (is_stage_in || (is_patch_stage_in && !is_tess_level))
+ addr_space = "const device";
+ }
if (get_execution_model() == ExecutionModelFragment && var && var->basevariable == stage_in_var_id)
addr_space = "thread";
break;
@@ -12039,12 +12125,13 @@ string CompilerMSL::entry_point_arg_stage_in()
{
string decl;
- if (get_execution_model() == ExecutionModelTessellationControl && msl_options.multi_patch_workgroup)
+ if ((is_tesc_shader() && msl_options.multi_patch_workgroup) ||
+ (is_tese_shader() && msl_options.raw_buffer_tese_input))
return decl;
// Stage-in structure
uint32_t stage_in_id;
- if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ if (is_tese_shader())
stage_in_id = patch_stage_in_var_id;
else
stage_in_id = stage_in_var_id;
@@ -12084,7 +12171,7 @@ bool CompilerMSL::is_direct_input_builtin(BuiltIn bi_type)
return false;
case BuiltInInvocationId:
case BuiltInPrimitiveId:
- return get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup;
+ return !is_tesc_shader() || !msl_options.multi_patch_workgroup;
// Tess. evaluation function in
case BuiltInTessLevelInner:
case BuiltInTessLevelOuter:
@@ -12239,7 +12326,7 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args)
" [[buffer(", msl_options.shader_output_buffer_index, ")]]");
}
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader())
{
if (!ep_args.empty())
ep_args += ", ";
@@ -12281,7 +12368,7 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args)
// a buffer to hold the per-patch data, a buffer to hold the per-patch
// tessellation levels, and a block of workgroup memory to hold the
// input control point data.
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader())
{
if (patch_stage_out_var_id)
{
@@ -12315,20 +12402,22 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args)
if (outer_factor_initializer_id && (c = maybe_get<SPIRConstant>(outer_factor_initializer_id)))
{
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
- entry_func.fixup_hooks_in.push_back([=]() {
- uint32_t components = get_execution_mode_bitset().get(ExecutionModeTriangles) ? 3 : 4;
- for (uint32_t i = 0; i < components; i++)
- {
- statement(builtin_to_glsl(BuiltInTessLevelOuter, StorageClassOutput), "[", i, "] = ",
- "half(", to_expression(c->subconstants[i]), ");");
- }
- });
+ entry_func.fixup_hooks_in.push_back(
+ [=]()
+ {
+ uint32_t components = is_tessellating_triangles() ? 3 : 4;
+ for (uint32_t i = 0; i < components; i++)
+ {
+ statement(builtin_to_glsl(BuiltInTessLevelOuter, StorageClassOutput), "[", i,
+ "] = ", "half(", to_expression(c->subconstants[i]), ");");
+ }
+ });
}
if (inner_factor_initializer_id && (c = maybe_get<SPIRConstant>(inner_factor_initializer_id)))
{
auto &entry_func = get<SPIRFunction>(ir.default_entry_point);
- if (get_execution_mode_bitset().get(ExecutionModeTriangles))
+ if (is_tessellating_triangles())
{
entry_func.fixup_hooks_in.push_back([=]() {
statement(builtin_to_glsl(BuiltInTessLevelInner, StorageClassOutput), " = ", "half(",
@@ -12364,6 +12453,36 @@ void CompilerMSL::entry_point_args_builtin(string &ep_args)
}
}
}
+ // Tessellation evaluation shaders get three additional parameters:
+ // a buffer for the per-patch data, a buffer for the per-patch
+ // tessellation levels, and a buffer for the control point data.
+ if (is_tese_shader() && msl_options.raw_buffer_tese_input)
+ {
+ if (patch_stage_in_var_id)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args +=
+ join("const device ", type_to_glsl(get_patch_stage_in_struct_type()), "* ", patch_input_buffer_var_name,
+ " [[buffer(", convert_to_string(msl_options.shader_patch_input_buffer_index), ")]]");
+ }
+
+ if (tess_level_inner_var_id || tess_level_outer_var_id)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += join("const device ", get_tess_factor_struct_name(), "* ", tess_factor_buffer_var_name,
+ " [[buffer(", convert_to_string(msl_options.shader_tess_factor_buffer_index), ")]]");
+ }
+
+ if (stage_in_var_id)
+ {
+ if (!ep_args.empty())
+ ep_args += ", ";
+ ep_args += join("const device ", type_to_glsl(get_stage_in_struct_type()), "* ", input_buffer_var_name,
+ " [[buffer(", convert_to_string(msl_options.shader_input_buffer_index), ")]]");
+ }
+ }
}
string CompilerMSL::entry_point_args_argument_buffer(bool append_comma)
@@ -12823,7 +12942,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
break;
case BuiltInInvocationId:
// This is direct-mapped without multi-patch workgroups.
- if (get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup)
+ if (!is_tesc_shader() || !msl_options.multi_patch_workgroup)
break;
entry_func.fixup_hooks_in.push_back([=]() {
@@ -12835,7 +12954,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
case BuiltInPrimitiveId:
// This is natively supported by fragment and tessellation evaluation shaders.
// In tessellation control shaders, this is direct-mapped without multi-patch workgroups.
- if (get_execution_model() != ExecutionModelTessellationControl || !msl_options.multi_patch_workgroup)
+ if (!is_tesc_shader() || !msl_options.multi_patch_workgroup)
break;
entry_func.fixup_hooks_in.push_back([=]() {
@@ -12845,7 +12964,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
});
break;
case BuiltInPatchVertices:
- if (get_execution_model() == ExecutionModelTessellationEvaluation)
+ if (is_tese_shader())
entry_func.fixup_hooks_in.push_back([=]() {
statement(builtin_type_decl(bi_type), " ", to_expression(var_id), " = ",
to_expression(patch_stage_in_var_id), ".gl_in.size();");
@@ -12868,7 +12987,7 @@ void CompilerMSL::fix_up_shader_inputs_outputs()
// Emit a fixup to account for the shifted domain. Don't do this for triangles;
// MoltenVK will just reverse the winding order instead.
- if (msl_options.tess_domain_origin_lower_left && !get_entry_point().flags.get(ExecutionModeTriangles))
+ if (msl_options.tess_domain_origin_lower_left && !is_tessellating_triangles())
{
string tc = to_expression(var_id);
entry_func.fixup_hooks_in.push_back([=]() { statement(tc, ".y = 1.0 - ", tc, ".y;"); });
@@ -13598,7 +13717,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
}
// Special case, need to override the array size here if we're using tess level as an argument.
- if (get_execution_model() == ExecutionModelTessellationControl && builtin &&
+ if (is_tesc_shader() && builtin &&
(builtin_type == BuiltInTessLevelInner || builtin_type == BuiltInTessLevelOuter))
{
uint32_t array_size = get_physical_tess_level_array_size(builtin_type);
@@ -14355,23 +14474,25 @@ bool CompilerMSL::variable_decl_is_remapped_storage(const SPIRVariable &variable
if (storage == StorageClassWorkgroup)
{
- auto model = get_execution_model();
-
// Specially masked IO block variable.
// Normally, we will never access IO blocks directly here.
// The only scenario which that should occur is with a masked IO block.
- if (model == ExecutionModelTessellationControl && variable.storage == StorageClassOutput &&
+ if (is_tesc_shader() && variable.storage == StorageClassOutput &&
has_decoration(get<SPIRType>(variable.basetype).self, DecorationBlock))
{
return true;
}
- return variable.storage == StorageClassOutput &&
- model == ExecutionModelTessellationControl &&
- is_stage_output_variable_masked(variable);
+ return variable.storage == StorageClassOutput && is_tesc_shader() && is_stage_output_variable_masked(variable);
}
else if (storage == StorageClassStorageBuffer)
{
+ // These builtins are passed directly; we don't want to use remapping
+ // for them.
+ auto builtin = (BuiltIn)get_decoration(variable.self, DecorationBuiltIn);
+ if (is_tese_shader() && is_builtin_variable(variable) && (builtin == BuiltInTessCoord || builtin == BuiltInPrimitiveId))
+ return false;
+
// We won't be able to catch writes to control point outputs here since variable
// refers to a function local pointer.
// This is fine, as there cannot be concurrent writers to that memory anyways,
@@ -15091,7 +15212,7 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
case BuiltInClipDistance:
case BuiltInCullDistance:
case BuiltInLayer:
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader())
break;
if (storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point) &&
!is_stage_output_builtin_masked(builtin))
@@ -15123,8 +15244,8 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
break;
case BuiltInTessLevelOuter:
- if (get_execution_model() == ExecutionModelTessellationControl &&
- storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point))
+ if (is_tesc_shader() && storage != StorageClassInput && current_function &&
+ (current_function->self == ir.default_entry_point))
{
return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id),
"].edgeTessellationFactor");
@@ -15132,8 +15253,8 @@ string CompilerMSL::builtin_to_glsl(BuiltIn builtin, StorageClass storage)
break;
case BuiltInTessLevelInner:
- if (get_execution_model() == ExecutionModelTessellationControl &&
- storage != StorageClassInput && current_function && (current_function->self == ir.default_entry_point))
+ if (is_tesc_shader() && storage != StorageClassInput && current_function &&
+ (current_function->self == ir.default_entry_point))
{
return join(tess_factor_buffer_var_name, "[", to_expression(builtin_primitive_id_id),
"].insideTessellationFactor");
@@ -15376,7 +15497,6 @@ string CompilerMSL::builtin_qualifier(BuiltIn builtin)
// Returns an MSL string type declaration for a SPIR-V builtin
string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id)
{
- const SPIREntryPoint &execution = get_entry_point();
switch (builtin)
{
// Vertex function in
@@ -15420,12 +15540,12 @@ string CompilerMSL::builtin_type_decl(BuiltIn builtin, uint32_t id)
// Tess. control function out
case BuiltInTessLevelInner:
- if (execution.model == ExecutionModelTessellationEvaluation)
- return !execution.flags.get(ExecutionModeTriangles) ? "float2" : "float";
+ if (is_tese_shader())
+ return (msl_options.raw_buffer_tese_input || is_tessellating_triangles()) ? "float" : "float2";
return "half";
case BuiltInTessLevelOuter:
- if (execution.model == ExecutionModelTessellationEvaluation)
- return !execution.flags.get(ExecutionModeTriangles) ? "float4" : "float";
+ if (is_tese_shader())
+ return (msl_options.raw_buffer_tese_input || is_tessellating_triangles()) ? "float" : "float4";
return "half";
// Tess. evaluation function in
@@ -16397,7 +16517,7 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr,
case BuiltInTessLevelInner:
case BuiltInTessLevelOuter:
- if (get_execution_model() == ExecutionModelTessellationControl)
+ if (is_tesc_shader())
{
expected_type = SPIRType::Half;
expected_width = 16;
@@ -16427,7 +16547,7 @@ void CompilerMSL::cast_from_variable_load(uint32_t source_id, std::string &expr,
wrap_expr += ", ";
}
- if (get_execution_mode_bitset().get(ExecutionModeTriangles))
+ if (is_tessellating_triangles())
wrap_expr += ", 0.0";
wrap_expr += " })";
diff --git a/spirv_msl.hpp b/spirv_msl.hpp
index a848f9b5..9bf7672b 100644
--- a/spirv_msl.hpp
+++ b/spirv_msl.hpp
@@ -306,6 +306,7 @@ public:
uint32_t dynamic_offsets_buffer_index = 23;
uint32_t shader_input_buffer_index = 22;
uint32_t shader_index_buffer_index = 21;
+ uint32_t shader_patch_input_buffer_index = 20;
uint32_t shader_input_wg_index = 0;
uint32_t device_index = 0;
uint32_t enable_frag_output_mask = 0xffffffff;
@@ -387,6 +388,11 @@ public:
// builtins are processed, but should result in more efficient usage of the GPU.
bool multi_patch_workgroup = false;
+ // Use storage buffers instead of vertex-style attributes for tessellation evaluation
+ // input. This may require conversion of inputs in the generated post-tessellation
+ // vertex shader, but allows the use of nested arrays.
+ bool raw_buffer_tese_input = false;
+
// If set, a vertex shader will be compiled as part of a tessellation pipeline.
// It will be translated as a compute kernel, so it can use the global invocation ID
// to index the output buffer.
@@ -820,6 +826,9 @@ protected:
std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, uint32_t physical_type_id,
bool is_packed) override;
+ bool is_tesc_shader() const;
+ bool is_tese_shader() const;
+
void preprocess_op_codes();
void localize_global_variables();
void extract_global_variables_from_functions();
@@ -876,6 +885,7 @@ protected:
const std::string &var_chain_qual,
uint32_t &location, uint32_t &var_mbr_idx);
void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
+ void add_tess_level_input(const std::string &base_ref, const std::string &mbr_name, SPIRVariable &var);
void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id);
@@ -1063,6 +1073,8 @@ protected:
VariableID patch_stage_out_var_id = 0;
VariableID stage_in_ptr_var_id = 0;
VariableID stage_out_ptr_var_id = 0;
+ VariableID tess_level_inner_var_id = 0;
+ VariableID tess_level_outer_var_id = 0;
VariableID stage_out_masked_builtin_type_id = 0;
// Handle HLSL-style 0-based vertex/instance index.
@@ -1101,6 +1113,7 @@ protected:
std::string input_wg_var_name = "gl_in";
std::string input_buffer_var_name = "spvIn";
std::string output_buffer_var_name = "spvOut";
+ std::string patch_input_buffer_var_name = "spvPatchIn";
std::string patch_output_buffer_var_name = "spvPatchOut";
std::string tess_factor_buffer_var_name = "spvTessLevel";
std::string index_buffer_var_name = "spvIndices";
diff --git a/test_shaders.py b/test_shaders.py
index 537693f1..31ec70a0 100755
--- a/test_shaders.py
+++ b/test_shaders.py
@@ -321,6 +321,8 @@ def cross_compile_msl(shader, spirv, opt, iterations, paths):
msl_args.append('1')
msl_args.append('any16')
msl_args.append('2')
+ if '.raw-tess-in.' in shader:
+ msl_args.append('--msl-raw-buffer-tese-input')
if '.for-tess.' in shader:
msl_args.append('--msl-vertex-for-tessellation')
if '.fixed-sample-mask.' in shader: