Merge branch 'master' of https://github.com/billhollings/SPIRV-Cross
This commit is contained in:
commit
b453348370
32
reference/opt/shaders-hlsl/frag/input-attachment-ms.frag
Normal file
32
reference/opt/shaders-hlsl/frag/input-attachment-ms.frag
Normal file
@ -0,0 +1,32 @@
|
||||
Texture2DMS<float4> uSubpass0 : register(t0);
|
||||
Texture2DMS<float4> uSubpass1 : register(t1);
|
||||
|
||||
static float4 gl_FragCoord;
|
||||
static int gl_SampleID;
|
||||
static float4 FragColor;
|
||||
|
||||
struct SPIRV_Cross_Input
|
||||
{
|
||||
float4 gl_FragCoord : SV_Position;
|
||||
uint gl_SampleID : SV_SampleIndex;
|
||||
};
|
||||
|
||||
struct SPIRV_Cross_Output
|
||||
{
|
||||
float4 FragColor : SV_Target0;
|
||||
};
|
||||
|
||||
void frag_main()
|
||||
{
|
||||
FragColor = (uSubpass0.Load(int2(gl_FragCoord.xy), 1) + uSubpass1.Load(int2(gl_FragCoord.xy), 2)) + uSubpass0.Load(int2(gl_FragCoord.xy), gl_SampleID);
|
||||
}
|
||||
|
||||
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||
{
|
||||
gl_FragCoord = stage_input.gl_FragCoord;
|
||||
gl_SampleID = stage_input.gl_SampleID;
|
||||
frag_main();
|
||||
SPIRV_Cross_Output stage_output;
|
||||
stage_output.FragColor = FragColor;
|
||||
return stage_output;
|
||||
}
|
29
reference/opt/shaders-hlsl/frag/input-attachment.frag
Normal file
29
reference/opt/shaders-hlsl/frag/input-attachment.frag
Normal file
@ -0,0 +1,29 @@
|
||||
Texture2D<float4> uSubpass0 : register(t0);
|
||||
Texture2D<float4> uSubpass1 : register(t1);
|
||||
|
||||
static float4 gl_FragCoord;
|
||||
static float4 FragColor;
|
||||
|
||||
struct SPIRV_Cross_Input
|
||||
{
|
||||
float4 gl_FragCoord : SV_Position;
|
||||
};
|
||||
|
||||
struct SPIRV_Cross_Output
|
||||
{
|
||||
float4 FragColor : SV_Target0;
|
||||
};
|
||||
|
||||
void frag_main()
|
||||
{
|
||||
FragColor = uSubpass0.Load(int3(int2(gl_FragCoord.xy), 0)) + uSubpass1.Load(int3(int2(gl_FragCoord.xy), 0));
|
||||
}
|
||||
|
||||
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||
{
|
||||
gl_FragCoord = stage_input.gl_FragCoord;
|
||||
frag_main();
|
||||
SPIRV_Cross_Output stage_output;
|
||||
stage_output.FragColor = FragColor;
|
||||
return stage_output;
|
||||
}
|
31
reference/opt/shaders-hlsl/vert/return-array.vert
Normal file
31
reference/opt/shaders-hlsl/vert/return-array.vert
Normal file
@ -0,0 +1,31 @@
|
||||
static const float4 _20[2] = { 10.0f.xxxx, 20.0f.xxxx };
|
||||
|
||||
static float4 gl_Position;
|
||||
static float4 vInput0;
|
||||
static float4 vInput1;
|
||||
|
||||
struct SPIRV_Cross_Input
|
||||
{
|
||||
float4 vInput0 : TEXCOORD0;
|
||||
float4 vInput1 : TEXCOORD1;
|
||||
};
|
||||
|
||||
struct SPIRV_Cross_Output
|
||||
{
|
||||
float4 gl_Position : SV_Position;
|
||||
};
|
||||
|
||||
void vert_main()
|
||||
{
|
||||
gl_Position = 10.0f.xxxx + vInput1;
|
||||
}
|
||||
|
||||
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||
{
|
||||
vInput0 = stage_input.vInput0;
|
||||
vInput1 = stage_input.vInput1;
|
||||
vert_main();
|
||||
SPIRV_Cross_Output stage_output;
|
||||
stage_output.gl_Position = gl_Position;
|
||||
return stage_output;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
@ -9,6 +11,12 @@ struct Foobar
|
||||
float b;
|
||||
};
|
||||
|
||||
constant float4 _37[3] = {float4(1.0), float4(2.0), float4(3.0)};
|
||||
constant float4 _49[2] = {float4(1.0), float4(2.0)};
|
||||
constant float4 _54[2] = {float4(8.0), float4(10.0)};
|
||||
constant float4 _55[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}};
|
||||
constant Foobar _75[2] = {{10.0, 40.0}, {90.0, 70.0}};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
int index [[user(locn0)]];
|
||||
@ -19,6 +27,20 @@ struct main0_out
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
// An overload for constant arrays.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]])
|
||||
{
|
||||
main0_out out = {};
|
||||
|
@ -1,3 +1,5 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
@ -9,6 +11,9 @@ struct Foo
|
||||
float b;
|
||||
};
|
||||
|
||||
constant float _16[4] = {1.0, 4.0, 3.0, 2.0};
|
||||
constant Foo _28[2] = {{10.0, 20.0}, {30.0, 40.0}};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
int line [[user(locn0)]];
|
||||
@ -19,6 +24,20 @@ struct main0_out
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
// An overload for constant arrays.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]])
|
||||
{
|
||||
main0_out out = {};
|
||||
|
17
reference/opt/shaders-msl/frag/input-attachment-ms.frag
Normal file
17
reference/opt/shaders-msl/frag/input-attachment-ms.frag
Normal file
@ -0,0 +1,17 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(texture2d_ms<float> uSubpass0 [[texture(0)]], texture2d_ms<float> uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), 1) + uSubpass1.read(uint2(gl_FragCoord.xy), 2)) + uSubpass0.read(uint2(gl_FragCoord.xy), gl_SampleID);
|
||||
return out;
|
||||
}
|
||||
|
17
reference/opt/shaders-msl/frag/input-attachment.frag
Normal file
17
reference/opt/shaders-msl/frag/input-attachment.frag
Normal file
@ -0,0 +1,17 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(texture2d<float> uSubpass0 [[texture(0)]], texture2d<float> uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy), 0) + uSubpass1.read(uint2(gl_FragCoord.xy), 0);
|
||||
return out;
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
fragment main0_out main0(depth2d<float> uDepth [[texture(0)]], texture2d<float> uColor [[texture(1)]], sampler uSamplerShadow [[sampler(0)]], sampler uSampler [[sampler(1)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.FragColor = uDepth.sample_compare(uSamplerShadow, float3(0.5).xy, 0.5) + uColor.sample(uSampler, float2(0.5)).x;
|
||||
return out;
|
||||
}
|
||||
|
24
reference/opt/shaders-msl/vert/return-array.vert
Normal file
24
reference/opt/shaders-msl/vert/return-array.vert
Normal file
@ -0,0 +1,24 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
constant float4 _20[2] = {float4(10.0), float4(20.0)};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 vInput1 [[attribute(1)]];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.gl_Position = float4(10.0) + in.vInput1;
|
||||
return out;
|
||||
}
|
||||
|
9
reference/opt/shaders/vert/return-array.vert
Normal file
9
reference/opt/shaders/vert/return-array.vert
Normal file
@ -0,0 +1,9 @@
|
||||
#version 310 es
|
||||
|
||||
layout(location = 1) in vec4 vInput1;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(10.0) + vInput1;
|
||||
}
|
||||
|
37
reference/shaders-hlsl/frag/input-attachment-ms.frag
Normal file
37
reference/shaders-hlsl/frag/input-attachment-ms.frag
Normal file
@ -0,0 +1,37 @@
|
||||
Texture2DMS<float4> uSubpass0 : register(t0);
|
||||
Texture2DMS<float4> uSubpass1 : register(t1);
|
||||
|
||||
static float4 gl_FragCoord;
|
||||
static int gl_SampleID;
|
||||
static float4 FragColor;
|
||||
|
||||
struct SPIRV_Cross_Input
|
||||
{
|
||||
float4 gl_FragCoord : SV_Position;
|
||||
uint gl_SampleID : SV_SampleIndex;
|
||||
};
|
||||
|
||||
struct SPIRV_Cross_Output
|
||||
{
|
||||
float4 FragColor : SV_Target0;
|
||||
};
|
||||
|
||||
float4 load_subpasses(Texture2DMS<float4> uInput)
|
||||
{
|
||||
return uInput.Load(int2(gl_FragCoord.xy), gl_SampleID);
|
||||
}
|
||||
|
||||
void frag_main()
|
||||
{
|
||||
FragColor = (uSubpass0.Load(int2(gl_FragCoord.xy), 1) + uSubpass1.Load(int2(gl_FragCoord.xy), 2)) + load_subpasses(uSubpass0);
|
||||
}
|
||||
|
||||
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||
{
|
||||
gl_FragCoord = stage_input.gl_FragCoord;
|
||||
gl_SampleID = stage_input.gl_SampleID;
|
||||
frag_main();
|
||||
SPIRV_Cross_Output stage_output;
|
||||
stage_output.FragColor = FragColor;
|
||||
return stage_output;
|
||||
}
|
34
reference/shaders-hlsl/frag/input-attachment.frag
Normal file
34
reference/shaders-hlsl/frag/input-attachment.frag
Normal file
@ -0,0 +1,34 @@
|
||||
Texture2D<float4> uSubpass0 : register(t0);
|
||||
Texture2D<float4> uSubpass1 : register(t1);
|
||||
|
||||
static float4 gl_FragCoord;
|
||||
static float4 FragColor;
|
||||
|
||||
struct SPIRV_Cross_Input
|
||||
{
|
||||
float4 gl_FragCoord : SV_Position;
|
||||
};
|
||||
|
||||
struct SPIRV_Cross_Output
|
||||
{
|
||||
float4 FragColor : SV_Target0;
|
||||
};
|
||||
|
||||
float4 load_subpasses(Texture2D<float4> uInput)
|
||||
{
|
||||
return uInput.Load(int3(int2(gl_FragCoord.xy), 0));
|
||||
}
|
||||
|
||||
void frag_main()
|
||||
{
|
||||
FragColor = uSubpass0.Load(int3(int2(gl_FragCoord.xy), 0)) + load_subpasses(uSubpass1);
|
||||
}
|
||||
|
||||
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||
{
|
||||
gl_FragCoord = stage_input.gl_FragCoord;
|
||||
frag_main();
|
||||
SPIRV_Cross_Output stage_output;
|
||||
stage_output.FragColor = FragColor;
|
||||
return stage_output;
|
||||
}
|
48
reference/shaders-hlsl/vert/return-array.vert
Normal file
48
reference/shaders-hlsl/vert/return-array.vert
Normal file
@ -0,0 +1,48 @@
|
||||
static const float4 _20[2] = { 10.0f.xxxx, 20.0f.xxxx };
|
||||
|
||||
static float4 gl_Position;
|
||||
static float4 vInput0;
|
||||
static float4 vInput1;
|
||||
|
||||
struct SPIRV_Cross_Input
|
||||
{
|
||||
float4 vInput0 : TEXCOORD0;
|
||||
float4 vInput1 : TEXCOORD1;
|
||||
};
|
||||
|
||||
struct SPIRV_Cross_Output
|
||||
{
|
||||
float4 gl_Position : SV_Position;
|
||||
};
|
||||
|
||||
void test(out float4 SPIRV_Cross_return_value[2])
|
||||
{
|
||||
SPIRV_Cross_return_value = _20;
|
||||
}
|
||||
|
||||
void test2(out float4 SPIRV_Cross_return_value[2])
|
||||
{
|
||||
float4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
SPIRV_Cross_return_value = foobar;
|
||||
}
|
||||
|
||||
void vert_main()
|
||||
{
|
||||
float4 _42[2];
|
||||
test(_42);
|
||||
float4 _44[2];
|
||||
test2(_44);
|
||||
gl_Position = _42[0] + _44[1];
|
||||
}
|
||||
|
||||
SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input)
|
||||
{
|
||||
vInput0 = stage_input.vInput0;
|
||||
vInput1 = stage_input.vInput1;
|
||||
vert_main();
|
||||
SPIRV_Cross_Output stage_output;
|
||||
stage_output.gl_Position = gl_Position;
|
||||
return stage_output;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
@ -9,11 +11,27 @@ struct D
|
||||
float b;
|
||||
};
|
||||
|
||||
constant float4 _14[4] = {float4(0.0), float4(0.0), float4(0.0), float4(0.0)};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
// An overload for constant arrays.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
fragment main0_out main0()
|
||||
{
|
||||
main0_out out = {};
|
||||
|
@ -11,6 +11,12 @@ struct Foobar
|
||||
float b;
|
||||
};
|
||||
|
||||
constant float4 _37[3] = {float4(1.0), float4(2.0), float4(3.0)};
|
||||
constant float4 _49[2] = {float4(1.0), float4(2.0)};
|
||||
constant float4 _54[2] = {float4(8.0), float4(10.0)};
|
||||
constant float4 _55[2][2] = {{float4(1.0), float4(2.0)}, {float4(8.0), float4(10.0)}};
|
||||
constant Foobar _75[2] = {{10.0, 40.0}, {90.0, 70.0}};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
int index [[user(locn0)]];
|
||||
@ -21,6 +27,20 @@ struct main0_out
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
// An overload for constant arrays.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
float4 resolve(thread const Foobar& f)
|
||||
{
|
||||
return float4(f.a + f.b);
|
||||
|
@ -1,3 +1,5 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
@ -9,6 +11,9 @@ struct Foo
|
||||
float b;
|
||||
};
|
||||
|
||||
constant float _16[4] = {1.0, 4.0, 3.0, 2.0};
|
||||
constant Foo _28[2] = {{10.0, 20.0}, {30.0, 40.0}};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
int line [[user(locn0)]];
|
||||
@ -19,6 +24,20 @@ struct main0_out
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
// An overload for constant arrays.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
fragment main0_out main0(main0_in in [[stage_in]])
|
||||
{
|
||||
main0_out out = {};
|
||||
|
24
reference/shaders-msl/frag/input-attachment-ms.frag
Normal file
24
reference/shaders-msl/frag/input-attachment-ms.frag
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
float4 load_subpasses(thread const texture2d_ms<float> uInput, thread uint& gl_SampleID, thread float4& gl_FragCoord)
|
||||
{
|
||||
return uInput.read(uint2(gl_FragCoord.xy), gl_SampleID);
|
||||
}
|
||||
|
||||
fragment main0_out main0(texture2d_ms<float> uSubpass0 [[texture(0)]], texture2d_ms<float> uSubpass1 [[texture(1)]], uint gl_SampleID [[sample_id]], float4 gl_FragCoord [[position]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.FragColor = (uSubpass0.read(uint2(gl_FragCoord.xy), 1) + uSubpass1.read(uint2(gl_FragCoord.xy), 2)) + load_subpasses(uSubpass0, gl_SampleID, gl_FragCoord);
|
||||
return out;
|
||||
}
|
||||
|
24
reference/shaders-msl/frag/input-attachment.frag
Normal file
24
reference/shaders-msl/frag/input-attachment.frag
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
float4 load_subpasses(thread const texture2d<float> uInput, thread float4& gl_FragCoord)
|
||||
{
|
||||
return uInput.read(uint2(gl_FragCoord.xy), 0);
|
||||
}
|
||||
|
||||
fragment main0_out main0(texture2d<float> uSubpass0 [[texture(0)]], texture2d<float> uSubpass1 [[texture(1)]], float4 gl_FragCoord [[position]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.FragColor = uSubpass0.read(uint2(gl_FragCoord.xy), 0) + load_subpasses(uSubpass1, gl_FragCoord);
|
||||
return out;
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float FragColor [[color(0)]];
|
||||
};
|
||||
|
||||
float sample_depth_from_function(thread const depth2d<float> uT, thread const sampler uS)
|
||||
{
|
||||
return uT.sample_compare(uS, float3(0.5).xy, float3(0.5).z);
|
||||
}
|
||||
|
||||
float sample_color_from_function(thread const texture2d<float> uT, thread const sampler uS)
|
||||
{
|
||||
return uT.sample(uS, float2(0.5)).x;
|
||||
}
|
||||
|
||||
fragment main0_out main0(depth2d<float> uDepth [[texture(0)]], texture2d<float> uColor [[texture(1)]], sampler uSamplerShadow [[sampler(0)]], sampler uSampler [[sampler(1)]])
|
||||
{
|
||||
main0_out out = {};
|
||||
out.FragColor = sample_depth_from_function(uDepth, uSamplerShadow) + sample_color_from_function(uColor, uSampler);
|
||||
return out;
|
||||
}
|
||||
|
58
reference/shaders-msl/vert/return-array.vert
Normal file
58
reference/shaders-msl/vert/return-array.vert
Normal file
@ -0,0 +1,58 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
constant float4 _20[2] = {float4(10.0), float4(20.0)};
|
||||
|
||||
struct main0_in
|
||||
{
|
||||
float4 vInput1 [[attribute(1)]];
|
||||
float4 vInput0 [[attribute(0)]];
|
||||
};
|
||||
|
||||
struct main0_out
|
||||
{
|
||||
float4 gl_Position [[position]];
|
||||
};
|
||||
|
||||
// Implementation of an array copy function to cover GLSL's ability to copy an array via assignment.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
// An overload for constant arrays.
|
||||
template<typename T, uint N>
|
||||
void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])
|
||||
{
|
||||
for (uint i = 0; i < N; dst[i] = src[i], i++);
|
||||
}
|
||||
|
||||
void test(thread float4 (&SPIRV_Cross_return_value)[2])
|
||||
{
|
||||
spvArrayCopyConstant(SPIRV_Cross_return_value, _20);
|
||||
}
|
||||
|
||||
void test2(thread float4 (&SPIRV_Cross_return_value)[2], thread float4& vInput0, thread float4& vInput1)
|
||||
{
|
||||
float4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
spvArrayCopy(SPIRV_Cross_return_value, foobar);
|
||||
}
|
||||
|
||||
vertex main0_out main0(main0_in in [[stage_in]])
|
||||
{
|
||||
main0_out out = {};
|
||||
float4 _42[2];
|
||||
test(_42);
|
||||
float4 _44[2];
|
||||
test2(_44, in.vInput0, in.vInput1);
|
||||
out.gl_Position = _42[0] + _44[1];
|
||||
return out;
|
||||
}
|
||||
|
23
reference/shaders/vert/return-array.vert
Normal file
23
reference/shaders/vert/return-array.vert
Normal file
@ -0,0 +1,23 @@
|
||||
#version 310 es
|
||||
|
||||
layout(location = 0) in vec4 vInput0;
|
||||
layout(location = 1) in vec4 vInput1;
|
||||
|
||||
vec4[2] test()
|
||||
{
|
||||
return vec4[](vec4(10.0), vec4(20.0));
|
||||
}
|
||||
|
||||
vec4[2] test2()
|
||||
{
|
||||
vec4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
return foobar;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
||||
|
15
shaders-hlsl/frag/input-attachment-ms.frag
Normal file
15
shaders-hlsl/frag/input-attachment-ms.frag
Normal file
@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0;
|
||||
layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
|
||||
vec4 load_subpasses(mediump subpassInputMS uInput)
|
||||
{
|
||||
return subpassLoad(uInput, gl_SampleID);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + load_subpasses(uSubpass0);
|
||||
}
|
16
shaders-hlsl/frag/input-attachment.frag
Normal file
16
shaders-hlsl/frag/input-attachment.frag
Normal file
@ -0,0 +1,16 @@
|
||||
#version 310 es
|
||||
precision mediump float;
|
||||
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0;
|
||||
layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
|
||||
vec4 load_subpasses(mediump subpassInput uInput)
|
||||
{
|
||||
return subpassLoad(uInput);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = subpassLoad(uSubpass0) + load_subpasses(uSubpass1);
|
||||
}
|
22
shaders-hlsl/vert/return-array.vert
Normal file
22
shaders-hlsl/vert/return-array.vert
Normal file
@ -0,0 +1,22 @@
|
||||
#version 310 es
|
||||
|
||||
layout(location = 0) in vec4 vInput0;
|
||||
layout(location = 1) in vec4 vInput1;
|
||||
|
||||
vec4[2] test()
|
||||
{
|
||||
return vec4[](vec4(10.0), vec4(20.0));
|
||||
}
|
||||
|
||||
vec4[2] test2()
|
||||
{
|
||||
vec4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
return foobar;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
15
shaders-msl/frag/input-attachment-ms.frag
Normal file
15
shaders-msl/frag/input-attachment-ms.frag
Normal file
@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInputMS uSubpass0;
|
||||
layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInputMS uSubpass1;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
|
||||
vec4 load_subpasses(mediump subpassInputMS uInput)
|
||||
{
|
||||
return subpassLoad(uInput, gl_SampleID);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2) + load_subpasses(uSubpass0);
|
||||
}
|
16
shaders-msl/frag/input-attachment.frag
Normal file
16
shaders-msl/frag/input-attachment.frag
Normal file
@ -0,0 +1,16 @@
|
||||
#version 310 es
|
||||
precision mediump float;
|
||||
|
||||
layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInput uSubpass0;
|
||||
layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInput uSubpass1;
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
|
||||
vec4 load_subpasses(mediump subpassInput uInput)
|
||||
{
|
||||
return subpassLoad(uInput);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = subpassLoad(uSubpass0) + load_subpasses(uSubpass1);
|
||||
}
|
22
shaders-msl/frag/sample-depth-separate-image-sampler.frag
Normal file
22
shaders-msl/frag/sample-depth-separate-image-sampler.frag
Normal file
@ -0,0 +1,22 @@
|
||||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0) uniform texture2D uDepth;
|
||||
layout(set = 0, binding = 1) uniform texture2D uColor;
|
||||
layout(set = 0, binding = 2) uniform sampler uSampler;
|
||||
layout(set = 0, binding = 3) uniform samplerShadow uSamplerShadow;
|
||||
layout(location = 0) out float FragColor;
|
||||
|
||||
float sample_depth_from_function(texture2D uT, samplerShadow uS)
|
||||
{
|
||||
return texture(sampler2DShadow(uT, uS), vec3(0.5));
|
||||
}
|
||||
|
||||
float sample_color_from_function(texture2D uT, sampler uS)
|
||||
{
|
||||
return texture(sampler2D(uT, uS), vec2(0.5)).x;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = sample_depth_from_function(uDepth, uSamplerShadow) + sample_color_from_function(uColor, uSampler);
|
||||
}
|
22
shaders-msl/vert/return-array.vert
Normal file
22
shaders-msl/vert/return-array.vert
Normal file
@ -0,0 +1,22 @@
|
||||
#version 310 es
|
||||
|
||||
layout(location = 0) in vec4 vInput0;
|
||||
layout(location = 1) in vec4 vInput1;
|
||||
|
||||
vec4[2] test()
|
||||
{
|
||||
return vec4[](vec4(10.0), vec4(20.0));
|
||||
}
|
||||
|
||||
vec4[2] test2()
|
||||
{
|
||||
vec4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
return foobar;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
22
shaders/vert/return-array.vert
Normal file
22
shaders/vert/return-array.vert
Normal file
@ -0,0 +1,22 @@
|
||||
#version 310 es
|
||||
|
||||
layout(location = 0) in vec4 vInput0;
|
||||
layout(location = 1) in vec4 vInput1;
|
||||
|
||||
vec4[2] test()
|
||||
{
|
||||
return vec4[](vec4(10.0), vec4(20.0));
|
||||
}
|
||||
|
||||
vec4[2] test2()
|
||||
{
|
||||
vec4 foobar[2];
|
||||
foobar[0] = vInput0;
|
||||
foobar[1] = vInput1;
|
||||
return foobar;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = test()[0] + test2()[1];
|
||||
}
|
@ -3752,11 +3752,13 @@ bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage)
|
||||
return flags & (1ull << builtin);
|
||||
}
|
||||
|
||||
void Compiler::analyze_sampler_comparison_states()
|
||||
void Compiler::analyze_image_and_sampler_usage()
|
||||
{
|
||||
CombinedImageSamplerUsageHandler handler(*this);
|
||||
traverse_all_reachable_opcodes(get<SPIRFunction>(entry_point), handler);
|
||||
comparison_samplers = move(handler.comparison_samplers);
|
||||
comparison_images = move(handler.comparison_images);
|
||||
need_subpass_input = handler.need_subpass_input;
|
||||
}
|
||||
|
||||
bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length)
|
||||
@ -3777,6 +3779,14 @@ bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint
|
||||
return true;
|
||||
}
|
||||
|
||||
void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_images(uint32_t image)
|
||||
{
|
||||
// Traverse the variable dependency hierarchy and tag everything in its path with comparison images.
|
||||
comparison_images.insert(image);
|
||||
for (auto &img : dependency_hierarchy[image])
|
||||
add_hierarchy_to_comparison_images(img);
|
||||
}
|
||||
|
||||
void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_samplers(uint32_t sampler)
|
||||
{
|
||||
// Traverse the variable dependency hierarchy and tag everything in its path with comparison samplers.
|
||||
@ -3796,6 +3806,12 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_
|
||||
if (length < 3)
|
||||
return false;
|
||||
dependency_hierarchy[args[1]].insert(args[2]);
|
||||
|
||||
// Ideally defer this to OpImageRead, but then we'd need to track loaded IDs.
|
||||
// If we load an image, we're going to use it and there is little harm in declaring an unused gl_FragCoord.
|
||||
auto &type = compiler.get<SPIRType>(args[0]);
|
||||
if (type.image.dim == DimSubpassData)
|
||||
need_subpass_input = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3808,6 +3824,10 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_
|
||||
auto &type = compiler.get<SPIRType>(result_type);
|
||||
if (type.image.depth)
|
||||
{
|
||||
// This image must be a depth image.
|
||||
uint32_t image = args[2];
|
||||
add_hierarchy_to_comparison_images(image);
|
||||
|
||||
// This sampler must be a SamplerComparisionState, and not a regular SamplerState.
|
||||
uint32_t sampler = args[3];
|
||||
add_hierarchy_to_comparison_samplers(sampler);
|
||||
|
@ -720,8 +720,12 @@ protected:
|
||||
// SPIR-V does not support this distinction, so we must keep track of this information outside the type system.
|
||||
// There might be unrelated IDs found in this set which do not correspond to actual variables.
|
||||
// This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs.
|
||||
// Similar is implemented for images, as well as if subpass inputs are needed.
|
||||
std::unordered_set<uint32_t> comparison_samplers;
|
||||
void analyze_sampler_comparison_states();
|
||||
std::unordered_set<uint32_t> comparison_images;
|
||||
bool need_subpass_input = false;
|
||||
|
||||
void analyze_image_and_sampler_usage();
|
||||
struct CombinedImageSamplerUsageHandler : OpcodeHandler
|
||||
{
|
||||
CombinedImageSamplerUsageHandler(Compiler &compiler_)
|
||||
@ -734,9 +738,12 @@ protected:
|
||||
Compiler &compiler;
|
||||
|
||||
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> dependency_hierarchy;
|
||||
std::unordered_set<uint32_t> comparison_images;
|
||||
std::unordered_set<uint32_t> comparison_samplers;
|
||||
|
||||
void add_hierarchy_to_comparison_samplers(uint32_t sampler);
|
||||
void add_hierarchy_to_comparison_images(uint32_t sampler);
|
||||
bool need_subpass_input = false;
|
||||
};
|
||||
|
||||
void make_constant_null(uint32_t id, uint32_t type);
|
||||
|
111
spirv_glsl.cpp
111
spirv_glsl.cpp
@ -376,7 +376,7 @@ string CompilerGLSL::compile()
|
||||
find_static_extensions();
|
||||
fixup_image_load_store_access();
|
||||
update_active_builtins();
|
||||
analyze_sampler_comparison_states();
|
||||
analyze_image_and_sampler_usage();
|
||||
|
||||
uint32_t pass_count = 0;
|
||||
do
|
||||
@ -1040,7 +1040,8 @@ uint32_t CompilerGLSL::type_to_packed_size(const SPIRType &type, uint64_t flags,
|
||||
return size;
|
||||
}
|
||||
|
||||
bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing)
|
||||
bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing,
|
||||
uint32_t start_offset, uint32_t end_offset)
|
||||
{
|
||||
// This is very tricky and error prone, but try to be exhaustive and correct here.
|
||||
// SPIR-V doesn't directly say if we're using std430 or std140.
|
||||
@ -1079,6 +1080,10 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin
|
||||
uint32_t alignment = max(packed_alignment, pad_alignment);
|
||||
offset = (offset + alignment - 1) & ~(alignment - 1);
|
||||
|
||||
// Field is not in the specified range anymore and we can ignore any further fields.
|
||||
if (offset >= end_offset)
|
||||
break;
|
||||
|
||||
// The next member following a struct member is aligned to the base alignment of the struct that came before.
|
||||
// GL 4.5 spec, 7.6.2.2.
|
||||
if (memb_type.basetype == SPIRType::Struct)
|
||||
@ -1086,27 +1091,31 @@ bool CompilerGLSL::buffer_is_packing_standard(const SPIRType &type, BufferPackin
|
||||
else
|
||||
pad_alignment = 1;
|
||||
|
||||
// We only care about offsets in std140, std430, etc ...
|
||||
// For EnhancedLayout variants, we have the flexibility to choose our own offsets.
|
||||
if (!packing_has_flexible_offset(packing))
|
||||
// Only care about packing if we are in the given range
|
||||
if (offset >= start_offset)
|
||||
{
|
||||
uint32_t actual_offset = type_struct_member_offset(type, i);
|
||||
if (actual_offset != offset) // This cannot be the packing we're looking for.
|
||||
// We only care about offsets in std140, std430, etc ...
|
||||
// For EnhancedLayout variants, we have the flexibility to choose our own offsets.
|
||||
if (!packing_has_flexible_offset(packing))
|
||||
{
|
||||
uint32_t actual_offset = type_struct_member_offset(type, i);
|
||||
if (actual_offset != offset) // This cannot be the packing we're looking for.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify array stride rules.
|
||||
if (!memb_type.array.empty() && type_to_packed_array_stride(memb_type, member_flags, packing) !=
|
||||
type_struct_member_array_stride(type, i))
|
||||
return false;
|
||||
|
||||
// Verify that sub-structs also follow packing rules.
|
||||
// We cannot use enhanced layouts on substructs, so they better be up to spec.
|
||||
auto substruct_packing = packing_to_substruct_packing(packing);
|
||||
|
||||
if (!memb_type.member_types.empty() && !buffer_is_packing_standard(memb_type, substruct_packing))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify array stride rules.
|
||||
if (!memb_type.array.empty() &&
|
||||
type_to_packed_array_stride(memb_type, member_flags, packing) != type_struct_member_array_stride(type, i))
|
||||
return false;
|
||||
|
||||
// Verify that sub-structs also follow packing rules.
|
||||
// We cannot use enhanced layouts on substructs, so they better be up to spec.
|
||||
auto substruct_packing = packing_to_substruct_packing(packing);
|
||||
|
||||
if (!memb_type.member_types.empty() && !buffer_is_packing_standard(memb_type, substruct_packing))
|
||||
return false;
|
||||
|
||||
// Bump size.
|
||||
offset += packed_size;
|
||||
}
|
||||
@ -5362,9 +5371,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
length -= 3;
|
||||
|
||||
auto &callee = get<SPIRFunction>(func);
|
||||
auto &return_type = get<SPIRType>(callee.return_type);
|
||||
bool pure = function_is_pure(callee);
|
||||
|
||||
bool callee_has_out_variables = false;
|
||||
bool emit_return_value_as_argument = false;
|
||||
|
||||
// Invalidate out variables passed to functions since they can be OpStore'd to.
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
@ -5378,12 +5389,25 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
flush_variable_declaration(arg[i]);
|
||||
}
|
||||
|
||||
if (!return_type.array.empty() && !backend.can_return_array)
|
||||
{
|
||||
callee_has_out_variables = true;
|
||||
emit_return_value_as_argument = true;
|
||||
}
|
||||
|
||||
if (!pure)
|
||||
register_impure_function_call();
|
||||
|
||||
string funexpr;
|
||||
vector<string> arglist;
|
||||
funexpr += to_name(func) + "(";
|
||||
|
||||
if (emit_return_value_as_argument)
|
||||
{
|
||||
statement(type_to_glsl(return_type), " ", to_name(id), type_to_array_glsl(return_type), ";");
|
||||
arglist.push_back(to_name(id));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
{
|
||||
// Do not pass in separate images or samplers if we're remapping
|
||||
@ -5418,7 +5442,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
// Check for function call constraints.
|
||||
check_function_call_constraints(arg, length);
|
||||
|
||||
if (get<SPIRType>(result_type).basetype != SPIRType::Void)
|
||||
if (return_type.basetype != SPIRType::Void)
|
||||
{
|
||||
// If the function actually writes to an out variable,
|
||||
// take the conservative route and do not forward.
|
||||
@ -5430,7 +5454,13 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
|
||||
bool forward = args_will_forward(id, arg, length, pure) && !callee_has_out_variables && pure &&
|
||||
(forced_temporaries.find(id) == end(forced_temporaries));
|
||||
|
||||
emit_op(result_type, id, funexpr, forward);
|
||||
if (emit_return_value_as_argument)
|
||||
{
|
||||
statement(funexpr, ";");
|
||||
set<SPIRExpression>(id, to_name(id), result_type, true);
|
||||
}
|
||||
else
|
||||
emit_op(result_type, id, funexpr, forward);
|
||||
|
||||
// Function calls are implicit loads from all variables in question.
|
||||
// Set dependencies for them.
|
||||
@ -7070,7 +7100,7 @@ string CompilerGLSL::variable_decl(const SPIRType &type, const string &name, uin
|
||||
// Emit a structure member. Subclasses may override to modify output,
|
||||
// or to dynamically add a padding member if needed.
|
||||
void CompilerGLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||
const string &qualifier)
|
||||
const string &qualifier, uint32_t)
|
||||
{
|
||||
auto &membertype = get<SPIRType>(member_type_id);
|
||||
|
||||
@ -7393,7 +7423,9 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type, uint32_t /* id */)
|
||||
require_extension("GL_EXT_texture_array");
|
||||
res += "Array";
|
||||
}
|
||||
if (type.image.depth)
|
||||
|
||||
// "Shadow" state in GLSL only exists for samplers and combined image samplers.
|
||||
if (((type.basetype == SPIRType::SampledImage) || (type.basetype == SPIRType::Sampler)) && type.image.depth)
|
||||
res += "Shadow";
|
||||
|
||||
return res;
|
||||
@ -7640,6 +7672,7 @@ void CompilerGLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
|
||||
auto &type = get<SPIRType>(func.return_type);
|
||||
decl += flags_to_precision_qualifiers_glsl(type, return_flags);
|
||||
decl += type_to_glsl(type);
|
||||
decl += type_to_array_glsl(type);
|
||||
decl += " ";
|
||||
|
||||
if (func.self == entry_point)
|
||||
@ -7902,7 +7935,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t to)
|
||||
// so just use "self" here.
|
||||
loop_dominator = from;
|
||||
}
|
||||
else if (from_block.loop_dominator != -1u)
|
||||
else if (from_block.loop_dominator != SPIRBlock::NoDominator)
|
||||
{
|
||||
loop_dominator = from_block.loop_dominator;
|
||||
}
|
||||
@ -8448,9 +8481,26 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
||||
|
||||
if (block.return_value)
|
||||
{
|
||||
// OpReturnValue can return Undef, so don't emit anything for this case.
|
||||
if (ids.at(block.return_value).get_type() != TypeUndef)
|
||||
statement("return ", to_expression(block.return_value), ";");
|
||||
auto &type = expression_type(block.return_value);
|
||||
if (!type.array.empty() && !backend.can_return_array)
|
||||
{
|
||||
// If we cannot return arrays, we will have a special out argument we can write to instead.
|
||||
// The backend is responsible for setting this up, and redirection the return values as appropriate.
|
||||
if (ids.at(block.return_value).get_type() != TypeUndef)
|
||||
emit_array_copy("SPIRV_Cross_return_value", block.return_value);
|
||||
|
||||
if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
|
||||
block.loop_dominator != SPIRBlock::NoDominator)
|
||||
{
|
||||
statement("return;");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// OpReturnValue can return Undef, so don't emit anything for this case.
|
||||
if (ids.at(block.return_value).get_type() != TypeUndef)
|
||||
statement("return ", to_expression(block.return_value), ";");
|
||||
}
|
||||
}
|
||||
// If this block is the very final block and not called from control flow,
|
||||
// we do not need an explicit return which looks out of place. Just end the function here.
|
||||
@ -8458,7 +8508,9 @@ void CompilerGLSL::emit_block_chain(SPIRBlock &block)
|
||||
// but we actually need a return here ...
|
||||
else if (!block_is_outside_flow_control_from_block(get<SPIRBlock>(current_function->entry_block), block) ||
|
||||
block.loop_dominator != SPIRBlock::NoDominator)
|
||||
{
|
||||
statement("return;");
|
||||
}
|
||||
break;
|
||||
|
||||
case SPIRBlock::Kill:
|
||||
@ -8576,3 +8628,8 @@ uint32_t CompilerGLSL::mask_relevant_memory_semantics(uint32_t semantics)
|
||||
MemorySemanticsWorkgroupMemoryMask | MemorySemanticsUniformMemoryMask |
|
||||
MemorySemanticsCrossWorkgroupMemoryMask | MemorySemanticsSubgroupMemoryMask);
|
||||
}
|
||||
|
||||
void CompilerGLSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
|
||||
{
|
||||
statement(lhs, " = ", to_expression(rhs_id), ";");
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#define SPIRV_CROSS_GLSL_HPP
|
||||
|
||||
#include "spirv_cross.hpp"
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
@ -202,7 +203,7 @@ protected:
|
||||
virtual std::string type_to_glsl(const SPIRType &type, uint32_t id = 0);
|
||||
virtual std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage);
|
||||
virtual void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||
const std::string &qualifier = "");
|
||||
const std::string &qualifier = "", uint32_t base_offset = 0);
|
||||
virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
|
||||
virtual std::string constant_expression(const SPIRConstant &c);
|
||||
std::string constant_op_expression(const SPIRConstantOp &cop);
|
||||
@ -330,6 +331,7 @@ protected:
|
||||
bool allow_precision_qualifiers = false;
|
||||
bool can_swizzle_scalar = false;
|
||||
bool force_gl_in_out_block = false;
|
||||
bool can_return_array = true;
|
||||
} backend;
|
||||
|
||||
void emit_struct(SPIRType &type);
|
||||
@ -423,8 +425,10 @@ protected:
|
||||
std::string layout_for_variable(const SPIRVariable &variable);
|
||||
std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id);
|
||||
virtual bool skip_argument(uint32_t id) const;
|
||||
virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id);
|
||||
|
||||
bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing);
|
||||
bool buffer_is_packing_standard(const SPIRType &type, BufferPackingStandard packing, uint32_t start_offset = 0,
|
||||
uint32_t end_offset = std::numeric_limits<uint32_t>::max());
|
||||
uint32_t type_to_packed_base_size(const SPIRType &type, BufferPackingStandard packing);
|
||||
uint32_t type_to_packed_alignment(const SPIRType &type, uint64_t flags, BufferPackingStandard packing);
|
||||
uint32_t type_to_packed_array_stride(const SPIRType &type, uint64_t flags, BufferPackingStandard packing);
|
||||
|
194
spirv_hlsl.cpp
194
spirv_hlsl.cpp
@ -260,8 +260,9 @@ string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type)
|
||||
else
|
||||
SPIRV_CROSS_THROW("Sampler buffers must be either sampled or unsampled. Cannot deduce in runtime.");
|
||||
case DimSubpassData:
|
||||
// This should be implemented same way as desktop GL. Fetch on a 2D texture based on int2(SV_Position).
|
||||
SPIRV_CROSS_THROW("Subpass data support is not yet implemented for HLSL"); // TODO
|
||||
dim = "2D";
|
||||
typed_load = false;
|
||||
break;
|
||||
default:
|
||||
SPIRV_CROSS_THROW("Invalid dimension.");
|
||||
}
|
||||
@ -779,7 +780,8 @@ std::string CompilerHLSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClas
|
||||
case BuiltInNumWorkgroups:
|
||||
{
|
||||
if (!num_workgroups_builtin)
|
||||
SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. Cannot emit code for this builtin.");
|
||||
SPIRV_CROSS_THROW("NumWorkgroups builtin is used, but remap_num_workgroups_builtin() was not called. "
|
||||
"Cannot emit code for this builtin.");
|
||||
|
||||
auto &var = get<SPIRVariable>(num_workgroups_builtin);
|
||||
auto &type = get<SPIRType>(var.basetype);
|
||||
@ -1459,7 +1461,7 @@ string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index)
|
||||
}
|
||||
|
||||
void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||
const string &qualifier)
|
||||
const string &qualifier, uint32_t base_offset)
|
||||
{
|
||||
auto &membertype = get<SPIRType>(member_type_id);
|
||||
|
||||
@ -1475,9 +1477,12 @@ void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type
|
||||
qualifiers = to_interpolation_qualifiers(memberflags);
|
||||
|
||||
string packing_offset;
|
||||
if (has_decoration(type.self, DecorationCPacked) && has_member_decoration(type.self, index, DecorationOffset))
|
||||
bool is_push_constant = type.storage == StorageClassPushConstant;
|
||||
|
||||
if ((has_decoration(type.self, DecorationCPacked) || is_push_constant) &&
|
||||
has_member_decoration(type.self, index, DecorationOffset))
|
||||
{
|
||||
uint32_t offset = memb[index].offset;
|
||||
uint32_t offset = memb[index].offset - base_offset;
|
||||
if (offset & 3)
|
||||
SPIRV_CROSS_THROW("Cannot pack on tighter bounds than 4 bytes in HLSL.");
|
||||
|
||||
@ -1557,7 +1562,58 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
|
||||
|
||||
void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
|
||||
{
|
||||
emit_buffer_block(var);
|
||||
if (root_constants_layout.empty())
|
||||
{
|
||||
emit_buffer_block(var);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto &layout : root_constants_layout)
|
||||
{
|
||||
auto &type = get<SPIRType>(var.basetype);
|
||||
|
||||
if (buffer_is_packing_standard(type, BufferPackingHLSLCbufferPackOffset, layout.start, layout.end))
|
||||
set_decoration(type.self, DecorationCPacked);
|
||||
else
|
||||
SPIRV_CROSS_THROW(
|
||||
"root constant cbuffer cannot be expressed with either HLSL packing layout or packoffset.");
|
||||
|
||||
flattened_structs.insert(var.self);
|
||||
type.member_name_cache.clear();
|
||||
add_resource_name(var.self);
|
||||
auto &memb = meta[type.self].members;
|
||||
|
||||
statement("cbuffer SPIRV_CROSS_RootConstant_", to_name(var.self),
|
||||
to_resource_register('b', layout.binding, layout.space));
|
||||
begin_scope();
|
||||
|
||||
// Index of the next field in the generated root constant constant buffer
|
||||
auto constant_index = 0u;
|
||||
|
||||
// Iterate over all member of the push constant and check which of the fields
|
||||
// fit into the given root constant layout.
|
||||
for (auto i = 0u; i < memb.size(); i++)
|
||||
{
|
||||
const auto offset = memb[i].offset;
|
||||
if (layout.start <= offset && offset < layout.end)
|
||||
{
|
||||
const auto &member = type.member_types[i];
|
||||
|
||||
add_member_name(type, constant_index);
|
||||
auto backup_name = get_member_name(type.self, i);
|
||||
auto member_name = to_member_name(type, i);
|
||||
set_member_name(type.self, constant_index,
|
||||
sanitize_underscores(join(to_name(type.self), "_", member_name)));
|
||||
emit_struct_member(type, member, i, "", layout.start);
|
||||
set_member_name(type.self, constant_index, backup_name);
|
||||
|
||||
constant_index++;
|
||||
}
|
||||
}
|
||||
|
||||
end_scope_decl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string CompilerHLSL::to_sampler_expression(uint32_t id)
|
||||
@ -1608,9 +1664,17 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
|
||||
string decl;
|
||||
|
||||
auto &type = get<SPIRType>(func.return_type);
|
||||
decl += flags_to_precision_qualifiers_glsl(type, return_flags);
|
||||
decl += type_to_glsl(type);
|
||||
decl += " ";
|
||||
if (type.array.empty())
|
||||
{
|
||||
decl += flags_to_precision_qualifiers_glsl(type, return_flags);
|
||||
decl += type_to_glsl(type);
|
||||
decl += " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
// We cannot return arrays in HLSL, so "return" through an out variable.
|
||||
decl = "void ";
|
||||
}
|
||||
|
||||
if (func.self == entry_point)
|
||||
{
|
||||
@ -1628,6 +1692,19 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
|
||||
decl += to_name(func.self);
|
||||
|
||||
decl += "(";
|
||||
|
||||
if (!type.array.empty())
|
||||
{
|
||||
// Fake array returns by writing to an out array instead.
|
||||
decl += "out ";
|
||||
decl += type_to_glsl(type);
|
||||
decl += " ";
|
||||
decl += "SPIRV_Cross_return_value";
|
||||
decl += type_to_array_glsl(type);
|
||||
if (!func.arguments.empty())
|
||||
decl += ", ";
|
||||
}
|
||||
|
||||
for (auto &arg : func.arguments)
|
||||
{
|
||||
// Might change the variable name if it already exists in this function.
|
||||
@ -2316,24 +2393,24 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
|
||||
if (!has_decoration(var.self, DecorationBinding))
|
||||
return "";
|
||||
|
||||
auto &type = get<SPIRType>(var.basetype);
|
||||
const char *space = nullptr;
|
||||
const auto &type = get<SPIRType>(var.basetype);
|
||||
char space = '\0';
|
||||
|
||||
switch (type.basetype)
|
||||
{
|
||||
case SPIRType::SampledImage:
|
||||
space = "t"; // SRV
|
||||
space = 't'; // SRV
|
||||
break;
|
||||
|
||||
case SPIRType::Image:
|
||||
if (type.image.sampled == 2)
|
||||
space = "u"; // UAV
|
||||
if (type.image.sampled == 2 && type.image.dim != DimSubpassData)
|
||||
space = 'u'; // UAV
|
||||
else
|
||||
space = "t"; // SRV
|
||||
space = 't'; // SRV
|
||||
break;
|
||||
|
||||
case SPIRType::Sampler:
|
||||
space = "s";
|
||||
space = 's';
|
||||
break;
|
||||
|
||||
case SPIRType::Struct:
|
||||
@ -2345,15 +2422,15 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
|
||||
{
|
||||
uint64_t flags = get_buffer_block_flags(var);
|
||||
bool is_readonly = (flags & (1ull << DecorationNonWritable)) != 0;
|
||||
space = is_readonly ? "t" : "u"; // UAV
|
||||
space = is_readonly ? 't' : 'u'; // UAV
|
||||
}
|
||||
else if (has_decoration(type.self, DecorationBlock))
|
||||
space = "b"; // Constant buffers
|
||||
space = 'b'; // Constant buffers
|
||||
}
|
||||
else if (storage == StorageClassPushConstant)
|
||||
space = "b"; // Constant buffers
|
||||
space = 'b'; // Constant buffers
|
||||
else if (storage == StorageClassStorageBuffer)
|
||||
space = "u"; // UAV
|
||||
space = 'u'; // UAV
|
||||
|
||||
break;
|
||||
}
|
||||
@ -2364,12 +2441,8 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
|
||||
if (!space)
|
||||
return "";
|
||||
|
||||
// shader model 5.1 supports space
|
||||
if (options.shader_model >= 51)
|
||||
return join(" : register(", space, get_decoration(var.self, DecorationBinding), ", space",
|
||||
get_decoration(var.self, DecorationDescriptorSet), ")");
|
||||
else
|
||||
return join(" : register(", space, get_decoration(var.self, DecorationBinding), ")");
|
||||
return to_resource_register(space, get_decoration(var.self, DecorationBinding),
|
||||
get_decoration(var.self, DecorationDescriptorSet));
|
||||
}
|
||||
|
||||
string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
|
||||
@ -2378,11 +2451,16 @@ string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var)
|
||||
if (!has_decoration(var.self, DecorationBinding))
|
||||
return "";
|
||||
|
||||
return to_resource_register('s', get_decoration(var.self, DecorationBinding),
|
||||
get_decoration(var.self, DecorationDescriptorSet));
|
||||
}
|
||||
|
||||
string CompilerHLSL::to_resource_register(char space, uint32_t binding, uint32_t space_set)
|
||||
{
|
||||
if (options.shader_model >= 51)
|
||||
return join(" : register(s", get_decoration(var.self, DecorationBinding), ", space",
|
||||
get_decoration(var.self, DecorationDescriptorSet), ")");
|
||||
return join(" : register(", space, binding, ", space", space_set, ")");
|
||||
else
|
||||
return join(" : register(s", get_decoration(var.self, DecorationBinding), ")");
|
||||
return join(" : register(", space, binding, ")");
|
||||
}
|
||||
|
||||
void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var)
|
||||
@ -3406,21 +3484,52 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
uint32_t result_type = ops[0];
|
||||
uint32_t id = ops[1];
|
||||
auto *var = maybe_get_backing_variable(ops[2]);
|
||||
auto imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]");
|
||||
auto &type = expression_type(ops[2]);
|
||||
bool subpass_data = type.image.dim == DimSubpassData;
|
||||
bool pure = false;
|
||||
|
||||
// The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
|
||||
// except that the underlying type changes how the data is interpreted.
|
||||
if (var)
|
||||
imgexpr = remap_swizzle(get<SPIRType>(result_type),
|
||||
image_format_to_components(get<SPIRType>(var->basetype).image.format), imgexpr);
|
||||
string imgexpr;
|
||||
|
||||
if (subpass_data)
|
||||
{
|
||||
if (options.shader_model < 40)
|
||||
SPIRV_CROSS_THROW("Subpass loads are not supported in HLSL shader model 2/3.");
|
||||
|
||||
// Similar to GLSL, implement subpass loads using texelFetch.
|
||||
if (type.image.ms)
|
||||
{
|
||||
uint32_t operands = ops[4];
|
||||
if (operands != ImageOperandsSampleMask || instruction.length != 6)
|
||||
SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
|
||||
uint32_t sample = ops[5];
|
||||
imgexpr = join(to_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
|
||||
}
|
||||
else
|
||||
imgexpr = join(to_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
|
||||
|
||||
pure = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]");
|
||||
// The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
|
||||
// except that the underlying type changes how the data is interpreted.
|
||||
if (var && !subpass_data)
|
||||
imgexpr = remap_swizzle(get<SPIRType>(result_type),
|
||||
image_format_to_components(get<SPIRType>(var->basetype).image.format), imgexpr);
|
||||
}
|
||||
|
||||
if (var && var->forwardable)
|
||||
{
|
||||
bool forward = forced_temporaries.find(id) == end(forced_temporaries);
|
||||
auto &e = emit_op(result_type, id, imgexpr, forward);
|
||||
e.loaded_from = var->self;
|
||||
if (forward)
|
||||
var->dependees.push_back(id);
|
||||
|
||||
if (!pure)
|
||||
{
|
||||
e.loaded_from = var->self;
|
||||
if (forward)
|
||||
var->dependees.push_back(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
emit_op(result_type, id, imgexpr, false);
|
||||
@ -3746,9 +3855,14 @@ string CompilerHLSL::compile()
|
||||
backend.can_swizzle_scalar = true;
|
||||
backend.can_declare_struct_inline = false;
|
||||
backend.can_declare_arrays_inline = false;
|
||||
backend.can_return_array = false;
|
||||
|
||||
update_active_builtins();
|
||||
analyze_sampler_comparison_states();
|
||||
analyze_image_and_sampler_usage();
|
||||
|
||||
// Subpass input needs SV_Position.
|
||||
if (need_subpass_input)
|
||||
active_input_builtins |= 1ull << BuiltInFragCoord;
|
||||
|
||||
uint32_t pass_count = 0;
|
||||
do
|
||||
|
@ -29,6 +29,18 @@ struct HLSLVertexAttributeRemap
|
||||
uint32_t location;
|
||||
std::string semantic;
|
||||
};
|
||||
// Specifying a root constant (d3d12) or push constant range (vulkan).
|
||||
//
|
||||
// `start` and `end` denotes the range of the root constant in bytes.
|
||||
// Both values need to be multiple of 4.
|
||||
struct RootConstants
|
||||
{
|
||||
uint32_t start;
|
||||
uint32_t end;
|
||||
|
||||
uint32_t binding;
|
||||
uint32_t space;
|
||||
};
|
||||
|
||||
class CompilerHLSL : public CompilerGLSL
|
||||
{
|
||||
@ -61,6 +73,15 @@ public:
|
||||
options = opts;
|
||||
}
|
||||
|
||||
// Optionally specify a custom root constant layout.
|
||||
//
|
||||
// Push constants ranges will be split up according to the
|
||||
// layout specified.
|
||||
void set_root_constant_layouts(std::vector<RootConstants> layout)
|
||||
{
|
||||
root_constants_layout = std::move(layout);
|
||||
}
|
||||
|
||||
// Compiles and remaps vertex attributes at specific locations to a fixed semantic.
|
||||
// The default is TEXCOORD# where # denotes location.
|
||||
// Matrices are unrolled to vectors with notation ${SEMANTIC}_#, where # denotes row.
|
||||
@ -113,6 +134,7 @@ private:
|
||||
std::string to_sampler_expression(uint32_t id);
|
||||
std::string to_resource_binding(const SPIRVariable &var);
|
||||
std::string to_resource_binding_sampler(const SPIRVariable &var);
|
||||
std::string to_resource_register(char space, uint32_t binding, uint32_t set);
|
||||
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
|
||||
void emit_access_chain(const Instruction &instruction);
|
||||
void emit_load(const Instruction &instruction);
|
||||
@ -121,8 +143,8 @@ private:
|
||||
void emit_store(const Instruction &instruction);
|
||||
void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op);
|
||||
|
||||
void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||
const std::string &qualifier) override;
|
||||
void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index, const std::string &qualifier,
|
||||
uint32_t base_offset = 0) override;
|
||||
|
||||
const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;
|
||||
|
||||
@ -173,6 +195,10 @@ private:
|
||||
std::string to_semantic(uint32_t vertex_location);
|
||||
|
||||
uint32_t num_workgroups_builtin = 0;
|
||||
|
||||
// Custom root constant layout, which should be emitted
|
||||
// when translating push constant ranges.
|
||||
std::vector<RootConstants> root_constants_layout;
|
||||
};
|
||||
}
|
||||
|
||||
|
232
spirv_msl.cpp
232
spirv_msl.cpp
@ -19,6 +19,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <assert.h>
|
||||
|
||||
using namespace spv;
|
||||
using namespace spirv_cross;
|
||||
@ -52,6 +53,57 @@ CompilerMSL::CompilerMSL(const uint32_t *ir, size_t word_count, MSLVertexAttr *p
|
||||
resource_bindings.push_back(&p_res_bindings[i]);
|
||||
}
|
||||
|
||||
void CompilerMSL::build_implicit_builtins()
|
||||
{
|
||||
if (need_subpass_input)
|
||||
{
|
||||
bool has_frag_coord = false;
|
||||
|
||||
for (auto &id : ids)
|
||||
{
|
||||
if (id.get_type() != TypeVariable)
|
||||
continue;
|
||||
|
||||
auto &var = id.get<SPIRVariable>();
|
||||
|
||||
if (var.storage == StorageClassInput &&
|
||||
meta[var.self].decoration.builtin &&
|
||||
meta[var.self].decoration.builtin_type == BuiltInFragCoord)
|
||||
{
|
||||
builtin_frag_coord_id = var.self;
|
||||
has_frag_coord = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_frag_coord)
|
||||
{
|
||||
uint32_t offset = increase_bound_by(3);
|
||||
uint32_t type_id = offset;
|
||||
uint32_t type_ptr_id = offset + 1;
|
||||
uint32_t var_id = offset + 2;
|
||||
|
||||
// Create gl_FragCoord.
|
||||
SPIRType vec4_type;
|
||||
vec4_type.basetype = SPIRType::Float;
|
||||
vec4_type.vecsize = 4;
|
||||
set<SPIRType>(type_id, vec4_type);
|
||||
|
||||
SPIRType vec4_type_ptr;
|
||||
vec4_type_ptr = vec4_type;
|
||||
vec4_type_ptr.pointer = true;
|
||||
vec4_type_ptr.parent_type = type_id;
|
||||
vec4_type_ptr.storage = StorageClassInput;
|
||||
auto &ptr_type = set<SPIRType>(type_ptr_id, vec4_type_ptr);
|
||||
ptr_type.self = type_id;
|
||||
|
||||
set<SPIRVariable>(var_id, type_ptr_id, StorageClassInput);
|
||||
set_decoration(var_id, DecorationBuiltIn, BuiltInFragCoord);
|
||||
builtin_frag_coord_id = var_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string CompilerMSL::compile()
|
||||
{
|
||||
// Force a classic "C" locale, reverts when function returns
|
||||
@ -60,7 +112,7 @@ string CompilerMSL::compile()
|
||||
// Do not deal with GLES-isms like precision, older extensions and such.
|
||||
CompilerGLSL::options.vulkan_semantics = true;
|
||||
CompilerGLSL::options.es = false;
|
||||
CompilerGLSL::options.version = 120;
|
||||
CompilerGLSL::options.version = 450;
|
||||
backend.float_literal_suffix = false;
|
||||
backend.uint32_t_literal_suffix = true;
|
||||
backend.basic_int_type = "int";
|
||||
@ -72,6 +124,9 @@ string CompilerMSL::compile()
|
||||
backend.use_typed_initializer_list = true;
|
||||
backend.native_row_major_matrix = false;
|
||||
backend.flexible_member_array_supported = false;
|
||||
backend.can_declare_arrays_inline = false;
|
||||
backend.can_return_array = false;
|
||||
backend.boolean_mix_support = false;
|
||||
|
||||
replace_illegal_names();
|
||||
|
||||
@ -79,6 +134,9 @@ string CompilerMSL::compile()
|
||||
struct_member_padding.clear();
|
||||
|
||||
update_active_builtins();
|
||||
analyze_image_and_sampler_usage();
|
||||
build_implicit_builtins();
|
||||
|
||||
fixup_image_load_store_access();
|
||||
|
||||
set_enabled_interface_variables(get_active_interface_variables());
|
||||
@ -271,12 +329,21 @@ void CompilerMSL::extract_global_variables_from_function(uint32_t func_id, std::
|
||||
switch (op)
|
||||
{
|
||||
case OpLoad:
|
||||
case OpInBoundsAccessChain:
|
||||
case OpAccessChain:
|
||||
{
|
||||
uint32_t base_id = ops[2];
|
||||
if (global_var_ids.find(base_id) != global_var_ids.end())
|
||||
added_arg_ids.insert(base_id);
|
||||
|
||||
auto &type = get<SPIRType>(ops[0]);
|
||||
if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData)
|
||||
{
|
||||
// Implicitly reads gl_FragCoord.
|
||||
assert(builtin_frag_coord_id != 0);
|
||||
added_arg_ids.insert(builtin_frag_coord_id);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OpFunctionCall:
|
||||
@ -941,11 +1008,19 @@ void CompilerMSL::emit_custom_functions()
|
||||
|
||||
case SPVFuncImplArrayCopy:
|
||||
statement("// Implementation of an array copy function to cover GLSL's ability to copy an array via "
|
||||
"assignment. ");
|
||||
statement("template<typename T>");
|
||||
statement("void spvArrayCopy(thread T* dst, thread const T* src, uint count)");
|
||||
"assignment.");
|
||||
statement("template<typename T, uint N>");
|
||||
statement("void spvArrayCopy(thread T (&dst)[N], thread const T (&src)[N])");
|
||||
begin_scope();
|
||||
statement("for (uint i = 0; i < count; *dst++ = *src++, i++);");
|
||||
statement("for (uint i = 0; i < N; dst[i] = src[i], i++);");
|
||||
end_scope();
|
||||
statement("");
|
||||
|
||||
statement("// An overload for constant arrays.");
|
||||
statement("template<typename T, uint N>");
|
||||
statement("void spvArrayCopyConstant(thread T (&dst)[N], constant T (&src)[N])");
|
||||
begin_scope();
|
||||
statement("for (uint i = 0; i < N; dst[i] = src[i], i++);");
|
||||
end_scope();
|
||||
statement("");
|
||||
break;
|
||||
@ -1163,6 +1238,34 @@ void CompilerMSL::declare_undefined_values()
|
||||
statement("");
|
||||
}
|
||||
|
||||
void CompilerMSL::declare_constant_arrays()
|
||||
{
|
||||
// MSL cannot declare arrays inline (except when declaring a variable), so we must move them out to
|
||||
// global constants directly, so we are able to use constants as variable expressions.
|
||||
bool emitted = false;
|
||||
|
||||
for (auto &id : ids)
|
||||
{
|
||||
if (id.get_type() == TypeConstant)
|
||||
{
|
||||
auto &c = id.get<SPIRConstant>();
|
||||
if (c.specialization)
|
||||
continue;
|
||||
|
||||
auto &type = get<SPIRType>(c.constant_type);
|
||||
if (!type.array.empty())
|
||||
{
|
||||
auto name = to_name(c.self);
|
||||
statement("constant ", variable_decl(type, name), " = ", constant_expression(c), ";");
|
||||
emitted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emitted)
|
||||
statement("");
|
||||
}
|
||||
|
||||
void CompilerMSL::emit_resources()
|
||||
{
|
||||
// Output non-interface structs. These include local function structs
|
||||
@ -1199,6 +1302,7 @@ void CompilerMSL::emit_resources()
|
||||
}
|
||||
}
|
||||
|
||||
declare_constant_arrays();
|
||||
declare_undefined_values();
|
||||
|
||||
// Output interface structs.
|
||||
@ -1452,11 +1556,15 @@ void CompilerMSL::emit_instruction(const Instruction &instruction)
|
||||
{
|
||||
// Mark that this shader reads from this image
|
||||
uint32_t img_id = ops[2];
|
||||
auto *p_var = maybe_get_backing_variable(img_id);
|
||||
if (p_var && has_decoration(p_var->self, DecorationNonReadable))
|
||||
auto &type = expression_type(img_id);
|
||||
if (type.image.dim != DimSubpassData)
|
||||
{
|
||||
unset_decoration(p_var->self, DecorationNonReadable);
|
||||
force_recompile = true;
|
||||
auto *p_var = maybe_get_backing_variable(img_id);
|
||||
if (p_var && has_decoration(p_var->self, DecorationNonReadable))
|
||||
{
|
||||
unset_decoration(p_var->self, DecorationNonReadable);
|
||||
force_recompile = true;
|
||||
}
|
||||
}
|
||||
|
||||
emit_texture_op(instruction);
|
||||
@ -1773,27 +1881,44 @@ bool CompilerMSL::maybe_emit_input_struct_assignment(uint32_t id_lhs, uint32_t i
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompilerMSL::emit_array_copy(const string &lhs, uint32_t rhs_id)
|
||||
{
|
||||
// Assignment from an array initializer is fine.
|
||||
if (ids[rhs_id].get_type() == TypeConstant)
|
||||
statement("spvArrayCopyConstant(", lhs, ", ", to_expression(rhs_id), ");");
|
||||
else
|
||||
statement("spvArrayCopy(", lhs, ", ", to_expression(rhs_id), ");");
|
||||
}
|
||||
|
||||
// Since MSL does not allow arrays to be copied via simple variable assignment,
|
||||
// if the LHS and RHS represent an assignment of an entire array, it must be
|
||||
// implemented by calling an array copy function.
|
||||
// Returns whether the struct assignment was emitted.
|
||||
bool CompilerMSL::maybe_emit_array_assignment(uint32_t id_lhs, uint32_t id_rhs)
|
||||
{
|
||||
// Assignment from an array initializer is fine.
|
||||
if (ids[id_rhs].get_type() == TypeConstant)
|
||||
return false;
|
||||
|
||||
// We only care about assignments of an entire array
|
||||
auto &type = expression_type(id_rhs);
|
||||
if (type.array.size() == 0)
|
||||
return false;
|
||||
|
||||
auto *var = maybe_get<SPIRVariable>(id_lhs);
|
||||
if (ids[id_rhs].get_type() == TypeConstant && var && var->deferred_declaration)
|
||||
{
|
||||
// Special case, if we end up declaring a variable when assigning the constant array,
|
||||
// we can avoid the copy by directly assigning the constant expression.
|
||||
// This is likely necessary to be able to use a variable as a true look-up table, as it is unlikely
|
||||
// the compiler will be able to optimize the spvArrayCopy() into a constant LUT.
|
||||
// After a variable has been declared, we can no longer assign constant arrays in MSL unfortunately.
|
||||
statement(to_expression(id_lhs), " = ", constant_expression(get<SPIRConstant>(id_rhs)), ";");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure the LHS variable has been declared
|
||||
auto *p_v_lhs = maybe_get_backing_variable(id_lhs);
|
||||
if (p_v_lhs)
|
||||
flush_variable_declaration(p_v_lhs->self);
|
||||
|
||||
statement("spvArrayCopy(", to_expression(id_lhs), ", ", to_expression(id_rhs), ", ", to_array_size(type, 0), ");");
|
||||
emit_array_copy(to_expression(id_lhs), id_rhs);
|
||||
register_write(id_lhs);
|
||||
|
||||
return true;
|
||||
@ -1979,12 +2104,32 @@ void CompilerMSL::emit_function_prototype(SPIRFunction &func, uint64_t)
|
||||
processing_entry_point = (func.self == entry_point);
|
||||
|
||||
auto &type = get<SPIRType>(func.return_type);
|
||||
decl += func_type_decl(type);
|
||||
|
||||
if (type.array.empty())
|
||||
{
|
||||
decl += func_type_decl(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We cannot return arrays in MSL, so "return" through an out variable.
|
||||
decl = "void";
|
||||
}
|
||||
|
||||
decl += " ";
|
||||
decl += to_name(func.self);
|
||||
|
||||
decl += "(";
|
||||
|
||||
if (!type.array.empty())
|
||||
{
|
||||
// Fake arrays returns by writing to an out array instead.
|
||||
decl += "thread ";
|
||||
decl += type_to_glsl(type);
|
||||
decl += " (&SPIRV_Cross_return_value)";
|
||||
decl += type_to_array_glsl(type);
|
||||
if (!func.arguments.empty())
|
||||
decl += ", ";
|
||||
}
|
||||
|
||||
if (processing_entry_point)
|
||||
{
|
||||
decl += entry_point_args(!func.arguments.empty());
|
||||
@ -2095,6 +2240,13 @@ string CompilerMSL::to_function_args(uint32_t img, const SPIRType &imgtype, bool
|
||||
|
||||
break;
|
||||
|
||||
case DimSubpassData:
|
||||
if (imgtype.image.ms)
|
||||
tex_coords = "uint2(gl_FragCoord.xy)";
|
||||
else
|
||||
tex_coords = join("uint2(gl_FragCoord.xy), 0");
|
||||
break;
|
||||
|
||||
case Dim2D:
|
||||
if (coord_type.vecsize > 2)
|
||||
tex_coords += ".xy";
|
||||
@ -2433,7 +2585,7 @@ void CompilerMSL::emit_fixup()
|
||||
|
||||
// Emit a structure member, padding and packing to maintain the correct memeber alignments.
|
||||
void CompilerMSL::emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||
const string &qualifier)
|
||||
const string &qualifier, uint32_t)
|
||||
{
|
||||
auto &membertype = get<SPIRType>(member_type_id);
|
||||
|
||||
@ -2650,7 +2802,7 @@ string CompilerMSL::func_type_decl(SPIRType &type)
|
||||
{
|
||||
auto &execution = get_entry_point();
|
||||
// The regular function return type. If not processing the entry point function, that's all we need
|
||||
string return_type = type_to_glsl(type);
|
||||
string return_type = type_to_glsl(type) + type_to_array_glsl(type);
|
||||
if (!processing_entry_point)
|
||||
return return_type;
|
||||
|
||||
@ -2659,7 +2811,7 @@ string CompilerMSL::func_type_decl(SPIRType &type)
|
||||
{
|
||||
auto &so_var = get<SPIRVariable>(stage_out_var_id);
|
||||
auto &so_type = get<SPIRType>(so_var.basetype);
|
||||
return_type = type_to_glsl(so_type);
|
||||
return_type = type_to_glsl(so_type) + type_to_array_glsl(type);
|
||||
}
|
||||
|
||||
// Prepend a entry type, based on the execution model
|
||||
@ -2938,7 +3090,7 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
if (is_array(type))
|
||||
{
|
||||
decl += " (&";
|
||||
decl += to_name(var.self);
|
||||
decl += to_expression(var.self);
|
||||
decl += ")";
|
||||
decl += type_to_array_glsl(type);
|
||||
}
|
||||
@ -2946,12 +3098,12 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
{
|
||||
decl += "&";
|
||||
decl += " ";
|
||||
decl += to_name(var.self);
|
||||
decl += to_expression(var.self);
|
||||
}
|
||||
else
|
||||
{
|
||||
decl += " ";
|
||||
decl += to_name(var.self);
|
||||
decl += to_expression(var.self);
|
||||
}
|
||||
|
||||
return decl;
|
||||
@ -3140,8 +3292,9 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id)
|
||||
|
||||
// Bypass pointers because we need the real image struct
|
||||
auto &img_type = get<SPIRType>(type.self).image;
|
||||
bool shadow_image = comparison_images.count(id) != 0;
|
||||
|
||||
if (img_type.depth)
|
||||
if (img_type.depth || shadow_image)
|
||||
{
|
||||
switch (img_type.dim)
|
||||
{
|
||||
@ -3171,6 +3324,7 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id)
|
||||
break;
|
||||
case DimBuffer:
|
||||
case Dim2D:
|
||||
case DimSubpassData:
|
||||
img_type_name += (img_type.ms ? "texture2d_ms" : (img_type.arrayed ? "texture2d_array" : "texture2d"));
|
||||
break;
|
||||
case Dim3D:
|
||||
@ -3192,7 +3346,7 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id)
|
||||
// For unsampled images, append the sample/read/write access qualifier.
|
||||
// For kernel images, the access qualifier my be supplied directly by SPIR-V.
|
||||
// Otherwise it may be set based on whether the image is read from or written to within the shader.
|
||||
if (type.basetype == SPIRType::Image && type.image.sampled == 2)
|
||||
if (type.basetype == SPIRType::Image && type.image.sampled == 2 && type.image.dim != DimSubpassData)
|
||||
{
|
||||
switch (img_type.access)
|
||||
{
|
||||
@ -3555,16 +3709,38 @@ CompilerMSL::SPVFuncImpl CompilerMSL::OpCodePreprocessor::get_spv_func_impl(Op o
|
||||
case OpFMod:
|
||||
return SPVFuncImplMod;
|
||||
|
||||
case OpFunctionCall:
|
||||
{
|
||||
auto &return_type = compiler.get<SPIRType>(args[0]);
|
||||
if (!return_type.array.empty())
|
||||
return SPVFuncImplArrayCopy;
|
||||
else
|
||||
return SPVFuncImplNone;
|
||||
}
|
||||
|
||||
case OpStore:
|
||||
{
|
||||
// Get the result type of the RHS. Since this is run as a pre-processing stage,
|
||||
// we must extract the result type directly from the Instruction, rather than the ID.
|
||||
uint32_t id_rhs = args[1];
|
||||
uint32_t type_id_rhs = result_types[id_rhs];
|
||||
if ((compiler.ids[id_rhs].get_type() != TypeConstant) && type_id_rhs &&
|
||||
compiler.is_array(compiler.get<SPIRType>(type_id_rhs)))
|
||||
return SPVFuncImplArrayCopy;
|
||||
|
||||
const SPIRType *type = nullptr;
|
||||
if (compiler.ids[id_rhs].get_type() != TypeNone)
|
||||
{
|
||||
// Could be a constant, or similar.
|
||||
type = &compiler.expression_type(id_rhs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Or ... an expression.
|
||||
if (result_types[id_rhs] != 0)
|
||||
type = &compiler.get<SPIRType>(result_types[id_rhs]);
|
||||
}
|
||||
|
||||
if (type && compiler.is_array(*type))
|
||||
return SPVFuncImplArrayCopy;
|
||||
else
|
||||
return SPVFuncImplNone;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,7 @@ protected:
|
||||
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
|
||||
void emit_fixup() override;
|
||||
void emit_struct_member(const SPIRType &type, uint32_t member_type_id, uint32_t index,
|
||||
const std::string &qualifier = "") override;
|
||||
const std::string &qualifier = "", uint32_t base_offset = 0) override;
|
||||
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||
std::string image_type_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
|
||||
@ -208,6 +208,7 @@ protected:
|
||||
std::string to_qualifiers_glsl(uint32_t id) override;
|
||||
void replace_illegal_names() override;
|
||||
void declare_undefined_values() override;
|
||||
void declare_constant_arrays();
|
||||
bool is_non_native_row_major_matrix(uint32_t id) override;
|
||||
bool member_is_non_native_row_major_matrix(const SPIRType &type, uint32_t index) override;
|
||||
std::string convert_row_major_matrix(std::string exp_str, const SPIRType &exp_type, bool is_packed) override;
|
||||
@ -266,6 +267,9 @@ protected:
|
||||
void add_pragma_line(const std::string &line);
|
||||
void add_typedef_line(const std::string &line);
|
||||
void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
|
||||
void emit_array_copy(const std::string &lhs, uint32_t rhs_id) override;
|
||||
void build_implicit_builtins();
|
||||
uint32_t builtin_frag_coord_id = 0;
|
||||
|
||||
Options options;
|
||||
std::set<SPVFuncImpl> spv_function_implementations;
|
||||
|
Loading…
Reference in New Issue
Block a user