MSL: Expand arrays of buffers passed as input.
Even as of Metal 2.1, MSL still doesn't support arrays of buffers directly. Therefore, we must manually expand them. In the prologue, we define arrays holding the argument pointers; these arrays are what the transpiled code ends up referencing. We might be able to do similar things for textures and samplers prior to MSL 2.0. Speaking of which, also enable texture arrays on iOS MSL 1.2.
This commit is contained in:
parent
69b034f26e
commit
3a9af9681c
6
main.cpp
6
main.cpp
@ -490,6 +490,7 @@ struct CLIArguments
|
||||
bool sso = false;
|
||||
bool support_nonzero_baseinstance = true;
|
||||
bool msl_swizzle_texture_samples = false;
|
||||
bool msl_ios = false;
|
||||
vector<PLSArg> pls_in;
|
||||
vector<PLSArg> pls_out;
|
||||
vector<Remap> remaps;
|
||||
@ -541,6 +542,8 @@ static void print_help()
|
||||
"\t[--cpp-interface-name <name>]\n"
|
||||
"\t[--msl]\n"
|
||||
"\t[--msl-version <MMmmpp>]\n"
|
||||
"\t[--msl-swizzle-texture-samples]\n"
|
||||
"\t[--msl-ios]\n"
|
||||
"\t[--hlsl]\n"
|
||||
"\t[--reflect]\n"
|
||||
"\t[--shader-model]\n"
|
||||
@ -705,6 +708,7 @@ static int main_inner(int argc, char *argv[])
|
||||
cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; });
|
||||
cbs.add("--no-420pack-extension", [&args](CLIParser &) { args.use_420pack_extension = false; });
|
||||
cbs.add("--msl-swizzle-texture-samples", [&args](CLIParser &) { args.msl_swizzle_texture_samples = true; });
|
||||
cbs.add("--msl-ios", [&args](CLIParser &) { args.msl_ios = true; });
|
||||
cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
|
||||
cbs.add("--rename-entry-point", [&args](CLIParser &parser) {
|
||||
auto old_name = parser.next_string();
|
||||
@ -825,6 +829,8 @@ static int main_inner(int argc, char *argv[])
|
||||
if (args.set_msl_version)
|
||||
msl_opts.msl_version = args.msl_version;
|
||||
msl_opts.swizzle_texture_samples = args.msl_swizzle_texture_samples;
|
||||
if (args.msl_ios)
|
||||
msl_opts.platform = CompilerMSL::Options::iOS;
|
||||
msl_comp->set_msl_options(msl_opts);
|
||||
}
|
||||
else if (args.hlsl)
|
||||
|
35
reference/opt/shaders-msl/vert/resource-arrays-leaf.ios.vert
Normal file
35
reference/opt/shaders-msl/vert/resource-arrays-leaf.ios.vert
Normal file
@ -0,0 +1,35 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct storage_block
|
||||
{
|
||||
uint4 baz;
|
||||
int2 quux;
|
||||
};
|
||||
|
||||
struct constant_block
|
||||
{
|
||||
float4 foo;
|
||||
int bar;
|
||||
};
|
||||
|
||||
vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array<texture2d<int>, 3> images [[texture(0)]])
|
||||
{
|
||||
device storage_block* storage[] =
|
||||
{
|
||||
storage_0,
|
||||
storage_1,
|
||||
};
|
||||
constant constant_block* constants[] =
|
||||
{
|
||||
constants_0,
|
||||
constants_1,
|
||||
constants_2,
|
||||
constants_3,
|
||||
};
|
||||
storage[0]->baz = uint4(constants[3]->foo);
|
||||
storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy;
|
||||
}
|
||||
|
35
reference/opt/shaders-msl/vert/resource-arrays.ios.vert
Normal file
35
reference/opt/shaders-msl/vert/resource-arrays.ios.vert
Normal file
@ -0,0 +1,35 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct storage_block
|
||||
{
|
||||
uint4 baz;
|
||||
int2 quux;
|
||||
};
|
||||
|
||||
struct constant_block
|
||||
{
|
||||
float4 foo;
|
||||
int bar;
|
||||
};
|
||||
|
||||
vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array<texture2d<int>, 3> images [[texture(0)]])
|
||||
{
|
||||
device storage_block* storage[] =
|
||||
{
|
||||
storage_0,
|
||||
storage_1,
|
||||
};
|
||||
constant constant_block* constants[] =
|
||||
{
|
||||
constants_0,
|
||||
constants_1,
|
||||
constants_2,
|
||||
constants_3,
|
||||
};
|
||||
storage[0]->baz = uint4(constants[3]->foo);
|
||||
storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy;
|
||||
}
|
||||
|
42
reference/shaders-msl/vert/resource-arrays-leaf.ios.vert
Normal file
42
reference/shaders-msl/vert/resource-arrays-leaf.ios.vert
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma clang diagnostic ignored "-Wmissing-prototypes"
|
||||
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct storage_block
|
||||
{
|
||||
uint4 baz;
|
||||
int2 quux;
|
||||
};
|
||||
|
||||
struct constant_block
|
||||
{
|
||||
float4 foo;
|
||||
int bar;
|
||||
};
|
||||
|
||||
void doWork(device storage_block* (&storage)[2], constant constant_block* (&constants)[4], thread const array<texture2d<int>, 3> images)
|
||||
{
|
||||
storage[0]->baz = uint4(constants[3]->foo);
|
||||
storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy;
|
||||
}
|
||||
|
||||
vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array<texture2d<int>, 3> images [[texture(0)]])
|
||||
{
|
||||
device storage_block* storage[] =
|
||||
{
|
||||
storage_0,
|
||||
storage_1,
|
||||
};
|
||||
constant constant_block* constants[] =
|
||||
{
|
||||
constants_0,
|
||||
constants_1,
|
||||
constants_2,
|
||||
constants_3,
|
||||
};
|
||||
doWork(storage, constants, images);
|
||||
}
|
||||
|
35
reference/shaders-msl/vert/resource-arrays.ios.vert
Normal file
35
reference/shaders-msl/vert/resource-arrays.ios.vert
Normal file
@ -0,0 +1,35 @@
|
||||
#include <metal_stdlib>
|
||||
#include <simd/simd.h>
|
||||
|
||||
using namespace metal;
|
||||
|
||||
struct storage_block
|
||||
{
|
||||
uint4 baz;
|
||||
int2 quux;
|
||||
};
|
||||
|
||||
struct constant_block
|
||||
{
|
||||
float4 foo;
|
||||
int bar;
|
||||
};
|
||||
|
||||
vertex void main0(device storage_block* storage_0 [[buffer(0)]], device storage_block* storage_1 [[buffer(1)]], constant constant_block* constants_0 [[buffer(2)]], constant constant_block* constants_1 [[buffer(3)]], constant constant_block* constants_2 [[buffer(4)]], constant constant_block* constants_3 [[buffer(5)]], array<texture2d<int>, 3> images [[texture(0)]])
|
||||
{
|
||||
device storage_block* storage[] =
|
||||
{
|
||||
storage_0,
|
||||
storage_1,
|
||||
};
|
||||
constant constant_block* constants[] =
|
||||
{
|
||||
constants_0,
|
||||
constants_1,
|
||||
constants_2,
|
||||
constants_3,
|
||||
};
|
||||
storage[0]->baz = uint4(constants[3]->foo);
|
||||
storage[1]->quux = images[2].read(uint2(int2(constants[1]->bar))).xy;
|
||||
}
|
||||
|
26
shaders-msl/vert/resource-arrays-leaf.ios.vert
Normal file
26
shaders-msl/vert/resource-arrays-leaf.ios.vert
Normal file
@ -0,0 +1,26 @@
|
||||
#version 450
|
||||
|
||||
layout(constant_id = 0) const int arraySize = 3;
|
||||
|
||||
layout(binding = 0, rgba32i) uniform iimage2D images[arraySize];
|
||||
uniform constant_block
|
||||
{
|
||||
vec4 foo;
|
||||
int bar;
|
||||
} constants[4];
|
||||
buffer storage_block
|
||||
{
|
||||
uvec4 baz;
|
||||
ivec2 quux;
|
||||
} storage[2];
|
||||
|
||||
void doWork()
|
||||
{
|
||||
storage[0].baz = uvec4(constants[3].foo);
|
||||
storage[1].quux = imageLoad(images[2], ivec2(constants[1].bar)).xy;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
doWork();
|
||||
}
|
21
shaders-msl/vert/resource-arrays.ios.vert
Normal file
21
shaders-msl/vert/resource-arrays.ios.vert
Normal file
@ -0,0 +1,21 @@
|
||||
#version 450
|
||||
|
||||
layout(constant_id = 0) const int arraySize = 3;
|
||||
|
||||
layout(binding = 0, rgba32i) uniform iimage2D images[arraySize];
|
||||
uniform constant_block
|
||||
{
|
||||
vec4 foo;
|
||||
int bar;
|
||||
} constants[4];
|
||||
buffer storage_block
|
||||
{
|
||||
uvec4 baz;
|
||||
ivec2 quux;
|
||||
} storage[2];
|
||||
|
||||
void main()
|
||||
{
|
||||
storage[0].baz = uvec4(constants[3].foo);
|
||||
storage[1].quux = imageLoad(images[2], ivec2(constants[1].bar)).xy;
|
||||
}
|
@ -5389,10 +5389,7 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
|
||||
if (!qual_mbr_name.empty())
|
||||
expr = qual_mbr_name;
|
||||
else
|
||||
{
|
||||
expr += ".";
|
||||
expr += to_member_name(*type, index);
|
||||
}
|
||||
expr += to_member_reference(maybe_get_backing_variable(base), *type, index);
|
||||
}
|
||||
|
||||
is_packed = member_is_packed_type(*type, index);
|
||||
@ -8233,6 +8230,11 @@ string CompilerGLSL::to_member_name(const SPIRType &type, uint32_t index)
|
||||
return join("_m", index);
|
||||
}
|
||||
|
||||
string CompilerGLSL::to_member_reference(const SPIRVariable *, const SPIRType &type, uint32_t index)
|
||||
{
|
||||
return join(".", to_member_name(type, index));
|
||||
}
|
||||
|
||||
void CompilerGLSL::add_member_name(SPIRType &type, uint32_t index)
|
||||
{
|
||||
auto &memb = meta[type.self].members;
|
||||
@ -9282,8 +9284,7 @@ void CompilerGLSL::branch(uint32_t from, uint32_t to)
|
||||
// Only sensible solution is to make a ladder variable, which we declare at the top of the switch block,
|
||||
// write to the ladder here, and defer the break.
|
||||
// The loop we're breaking out of must dominate the switch block, or there is no ladder breaking case.
|
||||
if (current_emitting_switch && is_loop_break(to) &&
|
||||
current_emitting_switch->loop_dominator != -1u &&
|
||||
if (current_emitting_switch && is_loop_break(to) && current_emitting_switch->loop_dominator != -1u &&
|
||||
get<SPIRBlock>(current_emitting_switch->loop_dominator).merge_block == to)
|
||||
{
|
||||
if (!current_emitting_switch->need_ladder_break)
|
||||
|
@ -454,6 +454,7 @@ protected:
|
||||
std::string enclose_expression(const std::string &expr);
|
||||
void strip_enclosed_expression(std::string &expr);
|
||||
std::string to_member_name(const SPIRType &type, uint32_t index);
|
||||
virtual std::string to_member_reference(const SPIRVariable *var, const SPIRType &type, uint32_t index);
|
||||
std::string type_to_glsl_constructor(const SPIRType &type);
|
||||
std::string argument_decl(const SPIRFunction::Parameter &arg);
|
||||
virtual std::string to_qualifiers_glsl(uint32_t id);
|
||||
|
@ -334,6 +334,20 @@ void CompilerMSL::emit_entry_point_declarations()
|
||||
type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first),
|
||||
"(", merge(args), ");");
|
||||
}
|
||||
|
||||
// Emit buffer arrays here.
|
||||
for (uint32_t array_id : buffer_arrays)
|
||||
{
|
||||
const auto &var = get<SPIRVariable>(array_id);
|
||||
const auto &type = get<SPIRType>(var.basetype);
|
||||
string name = get_name(array_id);
|
||||
statement(get_argument_address_space(var) + " " + type_to_glsl(type) + "* " + name + "[] =");
|
||||
begin_scope();
|
||||
for (uint32_t i = 0; i < type.array[0]; ++i)
|
||||
statement(name + "_" + convert_to_string(i) + ",");
|
||||
end_scope_decl();
|
||||
}
|
||||
buffer_arrays.clear();
|
||||
}
|
||||
|
||||
string CompilerMSL::compile()
|
||||
@ -3793,10 +3807,30 @@ string CompilerMSL::entry_point_args(bool append_comma)
|
||||
auto &m = meta.at(type.self);
|
||||
if (m.members.size() == 0)
|
||||
break;
|
||||
if (!ep_args.empty())
|
||||
ep_args += ", ";
|
||||
ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "& " + r.name;
|
||||
ep_args += " [[buffer(" + convert_to_string(r.index) + ")]]";
|
||||
if (!type.array.empty())
|
||||
{
|
||||
// Metal doesn't directly support this, so we must expand the
|
||||
// array. We'll declare a local array to hold these elements
|
||||
// later.
|
||||
uint32_t array_size =
|
||||
type.array_size_literal.back() ? type.array.back() : get<SPIRConstant>(type.array.back()).scalar();
|
||||
buffer_arrays.push_back(var_id);
|
||||
for (uint32_t i = 0; i < array_size; ++i)
|
||||
{
|
||||
if (!ep_args.empty())
|
||||
ep_args += ", ";
|
||||
ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "* " + r.name + "_" +
|
||||
convert_to_string(i);
|
||||
ep_args += " [[buffer(" + convert_to_string(r.index + i) + ")]]";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ep_args.empty())
|
||||
ep_args += ", ";
|
||||
ep_args += get_argument_address_space(var) + " " + type_to_glsl(type) + "& " + r.name;
|
||||
ep_args += " [[buffer(" + convert_to_string(r.index) + ")]]";
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPIRType::Sampler:
|
||||
@ -3969,6 +4003,9 @@ string CompilerMSL::argument_decl(const SPIRFunction::Parameter &arg)
|
||||
else if (is_array(type) && !type_is_image)
|
||||
{
|
||||
// Arrays of images and samplers are special cased.
|
||||
if (get<SPIRVariable>(name_id).storage == StorageClassUniform ||
|
||||
get<SPIRVariable>(name_id).storage == StorageClassStorageBuffer)
|
||||
decl += "*";
|
||||
decl += " (&";
|
||||
decl += to_expression(name_id);
|
||||
decl += ")";
|
||||
@ -4088,6 +4125,15 @@ void CompilerMSL::replace_illegal_names()
|
||||
CompilerGLSL::replace_illegal_names();
|
||||
}
|
||||
|
||||
string CompilerMSL::to_member_reference(const SPIRVariable *var, const SPIRType &type, uint32_t index)
|
||||
{
|
||||
// If this is a buffer array, we have to dereference the buffer pointers.
|
||||
if (var && (var->storage == StorageClassUniform || var->storage == StorageClassStorageBuffer) &&
|
||||
!get<SPIRType>(var->basetype).array.empty())
|
||||
return join("->", to_member_name(type, index));
|
||||
return join(".", to_member_name(type, index));
|
||||
}
|
||||
|
||||
string CompilerMSL::to_qualifiers_glsl(uint32_t id)
|
||||
{
|
||||
string quals;
|
||||
@ -4204,7 +4250,13 @@ string CompilerMSL::image_type_glsl(const SPIRType &type, uint32_t id)
|
||||
|
||||
if (!type.array.empty())
|
||||
{
|
||||
if (!msl_options.supports_msl_version(2))
|
||||
uint32_t major = 2, minor = 0;
|
||||
if (msl_options.is_ios())
|
||||
{
|
||||
major = 1;
|
||||
minor = 2;
|
||||
}
|
||||
if (!msl_options.supports_msl_version(major, minor))
|
||||
SPIRV_CROSS_THROW("MSL 2.0 or greater is required for arrays of textures.");
|
||||
|
||||
// Arrays of images in MSL must be declared with a special array<T, N> syntax ala C++11 std::array.
|
||||
|
@ -323,6 +323,7 @@ protected:
|
||||
std::string unpack_expression_type(std::string expr_str, const SPIRType &type) override;
|
||||
std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
|
||||
bool skip_argument(uint32_t id) const override;
|
||||
std::string to_member_reference(const SPIRVariable *var, const SPIRType &type, uint32_t index) override;
|
||||
std::string to_qualifiers_glsl(uint32_t id) override;
|
||||
void replace_illegal_names() override;
|
||||
void declare_undefined_values() override;
|
||||
@ -414,6 +415,7 @@ protected:
|
||||
spv::Op previous_instruction_opcode = spv::OpNop;
|
||||
|
||||
std::unordered_map<uint32_t, MSLConstexprSampler> constexpr_samplers;
|
||||
std::vector<uint32_t> buffer_arrays;
|
||||
|
||||
// OpcodeHandler that handles several MSL preprocessing operations.
|
||||
struct OpCodePreprocessor : OpcodeHandler
|
||||
|
@ -87,14 +87,26 @@ def print_msl_compiler_version():
|
||||
raise
|
||||
|
||||
def path_to_msl_standard(shader):
|
||||
if '.msl2.' in shader:
|
||||
return '-std=macos-metal2.0'
|
||||
elif '.msl21.' in shader:
|
||||
return '-std=macos-metal2.1'
|
||||
elif '.msl11.' in shader:
|
||||
return '-std=macos-metal1.1'
|
||||
if '.ios.' in shader:
|
||||
if '.msl2.' in shader:
|
||||
return '-std=ios-metal2.0'
|
||||
elif '.msl21.' in shader:
|
||||
return '-std=ios-metal2.1'
|
||||
elif '.msl11.' in shader:
|
||||
return '-std=ios-metal1.1'
|
||||
elif '.msl10.' in shader:
|
||||
return '-std=ios-metal1.0'
|
||||
else:
|
||||
return '-std=ios-metal1.2'
|
||||
else:
|
||||
return '-std=macos-metal1.2'
|
||||
if '.msl2.' in shader:
|
||||
return '-std=macos-metal2.0'
|
||||
elif '.msl21.' in shader:
|
||||
return '-std=macos-metal2.1'
|
||||
elif '.msl11.' in shader:
|
||||
return '-std=macos-metal1.1'
|
||||
else:
|
||||
return '-std=macos-metal1.2'
|
||||
|
||||
def path_to_msl_standard_cli(shader):
|
||||
if '.msl2.' in shader:
|
||||
@ -109,8 +121,10 @@ def path_to_msl_standard_cli(shader):
|
||||
def validate_shader_msl(shader, opt):
|
||||
msl_path = reference_path(shader[0], shader[1], opt)
|
||||
try:
|
||||
msl_os = 'macosx'
|
||||
# msl_os = 'iphoneos'
|
||||
if '.ios.' in msl_path:
|
||||
msl_os = 'iphoneos'
|
||||
else:
|
||||
msl_os = 'macosx'
|
||||
subprocess.check_call(['xcrun', '--sdk', msl_os, 'metal', '-x', 'metal', path_to_msl_standard(msl_path), '-Werror', '-Wno-unused-variable', msl_path])
|
||||
print('Compiled Metal shader: ' + msl_path) # display after so xcrun FNF is silent
|
||||
except OSError as oe:
|
||||
@ -139,6 +153,8 @@ def cross_compile_msl(shader, spirv, opt):
|
||||
msl_args.append(path_to_msl_standard_cli(shader))
|
||||
if '.swizzle.' in shader:
|
||||
msl_args.append('--msl-swizzle-texture-samples')
|
||||
if '.ios.' in shader:
|
||||
msl_args.append('--msl-ios')
|
||||
|
||||
subprocess.check_call(msl_args)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user