diff --git a/main.cpp b/main.cpp index 73d384c..7deefd6 100644 --- a/main.cpp +++ b/main.cpp @@ -487,21 +487,41 @@ struct CLIArguments static void print_help() { - fprintf(stderr, "Usage: spirv-cross [--output ] [SPIR-V file] [--es] [--no-es] " - "[--version ] [--dump-resources] [--help] [--force-temporary] " - "[--vulkan-semantics] [--flatten-ubo] [--fixup-clipspace] [--flip-vert-y] [--iterations iter] " - "[--cpp] [--cpp-interface-name ] " - "[--msl] [--msl-version ]" - "[--hlsl] [--shader-model] [--hlsl-enable-compat] " - "[--separate-shader-objects]" - "[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name " - "components] [--extension ext] [--entry name] [--stage ] [--remove-unused-variables] " - "[--flatten-multidimensional-arrays] [--no-420pack-extension] " - "[--remap-variable-type ] " - "[--rename-interface-variable ] " - "[--set-hlsl-vertex-input-semantic ] " - "[--rename-entry-point ] " + fprintf(stderr, "Usage: spirv-cross\n" + "\t[--output ]\n" + "\t[SPIR-V file]\n" + "\t[--es]\n" + "\t[--no-es]\n" + "\t[--version ]\n" + "\t[--dump-resources]\n" + "\t[--help]\n" + "\t[--force-temporary]\n" + "\t[--vulkan-semantics]\n" + "\t[--flatten-ubo]\n" + "\t[--fixup-clipspace]\n" + "\t[--flip-vert-y]\n" + "\t[--iterations iter]\n" + "\t[--cpp]\n" + "\t[--cpp-interface-name ]\n" + "\t[--msl]\n" + "\t[--msl-version ]\n" + "\t[--hlsl]\n" + "\t[--shader-model]\n" + "\t[--hlsl-enable-compat]\n" + "\t[--separate-shader-objects]\n" + "\t[--pls-in format input-name]\n" + "\t[--pls-out format output-name]\n" + "\t[--remap source_name target_name components]\n" + "\t[--extension ext]\n" + "\t[--entry name]\n" + "\t[--stage ]\n" + "\t[--remove-unused-variables]\n" + "\t[--flatten-multidimensional-arrays]\n" + "\t[--no-420pack-extension]\n" + "\t[--remap-variable-type ]\n" + "\t[--rename-interface-variable ]\n" + "\t[--set-hlsl-vertex-input-semantic ]\n" + "\t[--rename-entry-point ]\n" "\n"); } diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index 0c79892..1832ef4 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -1715,6 +1715,10 @@ void CompilerGLSL::emit_specialization_constant(const SPIRConstant &constant) } } +void CompilerGLSL::emit_entry_point_declarations() +{ +} + void CompilerGLSL::replace_illegal_names() { // clang-format off @@ -8718,6 +8722,9 @@ void CompilerGLSL::emit_function(SPIRFunction &func, const Bitset &return_flags) emit_function_prototype(func, return_flags); begin_scope(); + if (func.self == entry_point) + emit_entry_point_declarations(); + current_function = &func; auto &entry_block = get(func.entry_block); diff --git a/spirv_glsl.hpp b/spirv_glsl.hpp index 5ace45d..983f8ad 100644 --- a/spirv_glsl.hpp +++ b/spirv_glsl.hpp @@ -475,6 +475,7 @@ protected: bool check_atomic_image(uint32_t id); virtual void replace_illegal_names(); + virtual void emit_entry_point_declarations(); void replace_fragment_output(SPIRVariable &var); void replace_fragment_outputs(); diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 2b8fdd7..3b72343 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -104,6 +104,152 @@ void CompilerMSL::build_implicit_builtins() } } +static string create_sampler_address(const char *prefix, MSLSamplerAddress addr) +{ + switch (addr) + { + case MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE: + return join(prefix, "address::clamp_to_edge"); + case MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO: + return join(prefix, "address::clamp_to_zero"); + case MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER: + return join(prefix, "address::clamp_to_border"); + case MSL_SAMPLER_ADDRESS_REPEAT: + return join(prefix, "address::repeat"); + case MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT: + return join(prefix, "address::mirrored_repeat"); + default: + SPIRV_CROSS_THROW("Invalid sampler addressing mode."); + } +} + +void CompilerMSL::emit_entry_point_declarations() +{ + // FIXME: Get test coverage here ... + + // Emit constexpr samplers here. + for (auto &samp : constexpr_samplers) + { + auto &var = get(samp.first); + auto &type = get(var.basetype); + if (type.basetype == SPIRType::Sampler) + add_resource_name(samp.first); + + vector args; + auto &s = samp.second; + + if (s.coord != MSL_SAMPLER_COORD_NORMALIZED) + args.push_back("coord::pixel"); + + if (s.min_filter == s.mag_filter) + { + if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("filter::linear"); + } + else + { + if (s.min_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("min_filter::linear"); + if (s.mag_filter != MSL_SAMPLER_FILTER_NEAREST) + args.push_back("mag_filter::linear"); + } + + switch (s.mip_filter) + { + case MSL_SAMPLER_MIP_FILTER_NONE: + // Default + break; + case MSL_SAMPLER_MIP_FILTER_NEAREST: + args.push_back("mip_filter::nearest"); + break; + case MSL_SAMPLER_MIP_FILTER_LINEAR: + args.push_back("mip_filter::linear"); + break; + default: + SPIRV_CROSS_THROW("Invalid mip filter."); + } + + if (s.s_address == s.t_address && s.s_address == s.r_address) + { + if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("", s.s_address)); + } + else + { + if (s.s_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("s_", s.s_address)); + if (s.t_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("t_", s.t_address)); + if (s.r_address != MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE) + args.push_back(create_sampler_address("r_", s.r_address)); + } + + if (s.compare_enable) + { + switch (s.compare_func) + { + case MSL_SAMPLER_COMPARE_FUNC_ALWAYS: + args.push_back("compare_func::always"); + break; + case MSL_SAMPLER_COMPARE_FUNC_NEVER: + args.push_back("compare_func::never"); + break; + case MSL_SAMPLER_COMPARE_FUNC_EQUAL: + args.push_back("compare_func::equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL: + args.push_back("compare_func::not_equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_LESS: + args.push_back("compare_func::less"); + break; + case MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL: + args.push_back("compare_func::less_equal"); + break; + case MSL_SAMPLER_COMPARE_FUNC_GREATER: + args.push_back("compare_func::greater"); + break; + case MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL: + args.push_back("compare_func::greater_equal"); + break; + default: + SPIRV_CROSS_THROW("Invalid sampler compare function."); + } + } + + if (s.s_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || s.t_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER || + s.r_address == MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER) + { + switch (s.border_color) + { + case MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK: + args.push_back("border_color::opaque_black"); + break; + case MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE: + args.push_back("border_color::opaque_white"); + break; + case MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK: + args.push_back("border_color::transparent_black"); + break; + default: + SPIRV_CROSS_THROW("Invalid sampler border color."); + } + } + + if (s.anisotropy_enable) + args.push_back(join("max_anisotropy(", s.max_anisotropy, ")")); + if (s.lod_clamp_enable) + { + args.push_back( + join("lod_clamp(", convert_to_string(s.lod_clamp_min), ", ", convert_to_string(s.lod_clamp_max), ")")); + } + + statement("constexpr sampler ", + type.basetype == SPIRType::SampledImage ? to_sampler_expression(samp.first) : to_name(samp.first), + "(", merge(args), ");"); + } +} + string CompilerMSL::compile() { // Force a classic "C" locale, reverts when function returns @@ -3076,12 +3222,15 @@ string CompilerMSL::entry_point_args(bool append_comma) resources.push_back( { &id, to_name(var_id), SPIRType::Image, get_metal_resource_index(var, SPIRType::Image) }); - if (type.image.dim != DimBuffer) + if (type.image.dim != DimBuffer && constexpr_samplers.count(var_id) == 0) + { resources.push_back({ &id, to_sampler_expression(var_id), SPIRType::Sampler, get_metal_resource_index(var, SPIRType::Sampler) }); + } } - else + else if (constexpr_samplers.count(var_id) == 0) { + // constexpr samplers are not declared as resources. resources.push_back( { &id, to_name(var_id), type.basetype, get_metal_resource_index(var, type.basetype) }); } @@ -4087,3 +4236,13 @@ CompilerMSL::MemberSorter::MemberSorter(SPIRType &t, Meta &m, SortAspect sa) // Ensure enough meta info is available meta.members.resize(max(type.member_types.size(), meta.members.size())); } + +void CompilerMSL::remap_constexpr_sampler(uint32_t id, const spirv_cross::MSLConstexprSampler &sampler) +{ + auto &type = get(get(id).basetype); + if (type.basetype != SPIRType::SampledImage && type.basetype != SPIRType::Sampler) + SPIRV_CROSS_THROW("Can only remap SampledImage and Sampler type."); + if (!type.array.empty()) + SPIRV_CROSS_THROW("Can not remap array of samplers."); + constexpr_samplers[id] = sampler; +} diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 25fbc87..9708660 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -59,6 +59,73 @@ struct MSLResourceBinding bool used_by_shader = false; }; +enum MSLSamplerCoord +{ + MSL_SAMPLER_COORD_NORMALIZED, + MSL_SAMPLER_COORD_PIXEL +}; + +enum MSLSamplerFilter +{ + MSL_SAMPLER_FILTER_NEAREST, + MSL_SAMPLER_FILTER_LINEAR +}; + +enum MSLSamplerMipFilter +{ + MSL_SAMPLER_MIP_FILTER_NONE, + MSL_SAMPLER_MIP_FILTER_NEAREST, + MSL_SAMPLER_MIP_FILTER_LINEAR, +}; + +enum MSLSamplerAddress +{ + MSL_SAMPLER_ADDRESS_CLAMP_TO_ZERO, + MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE, + MSL_SAMPLER_ADDRESS_CLAMP_TO_BORDER, + MSL_SAMPLER_ADDRESS_REPEAT, + MSL_SAMPLER_ADDRESS_MIRRORED_REPEAT +}; + +enum MSLSamplerCompareFunc +{ + MSL_SAMPLER_COMPARE_FUNC_NEVER, + MSL_SAMPLER_COMPARE_FUNC_LESS, + MSL_SAMPLER_COMPARE_FUNC_LESS_EQUAL, + MSL_SAMPLER_COMPARE_FUNC_GREATER, + MSL_SAMPLER_COMPARE_FUNC_GREATER_EQUAL, + MSL_SAMPLER_COMPARE_FUNC_EQUAL, + MSL_SAMPLER_COMPARE_FUNC_NOT_EQUAL, + MSL_SAMPLER_COMPARE_FUNC_ALWAYS +}; + +enum MSLSamplerBorderColor +{ + MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_BLACK, + MSL_SAMPLER_BORDER_COLOR_OPAQUE_WHITE +}; + +struct MSLConstexprSampler +{ + MSLSamplerCoord coord = MSL_SAMPLER_COORD_NORMALIZED; + MSLSamplerFilter min_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerFilter mag_filter = MSL_SAMPLER_FILTER_NEAREST; + MSLSamplerMipFilter mip_filter = MSL_SAMPLER_MIP_FILTER_NONE; + MSLSamplerAddress s_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress t_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerAddress r_address = MSL_SAMPLER_ADDRESS_CLAMP_TO_EDGE; + MSLSamplerCompareFunc compare_func = MSL_SAMPLER_COMPARE_FUNC_NEVER; + MSLSamplerBorderColor border_color = MSL_SAMPLER_BORDER_COLOR_TRANSPARENT_BLACK; + float lod_clamp_min = 0.0f; + float lod_clamp_max = 1000.0f; + int max_anisotropy = 1; + + bool compare_enable = false; + bool lod_clamp_enable = false; + bool anisotropy_enable = false; +}; + // Tracks the type ID and member index of a struct member using MSLStructMemberKey = uint64_t; @@ -191,6 +258,14 @@ public: std::string compile(MSLConfiguration &msl_cfg, std::vector *p_vtx_attrs = nullptr, std::vector *p_res_bindings = nullptr); + // Remap a sampler with ID to a constexpr sampler. + // Older iOS targets must use constexpr samplers in certain cases (PCF), + // so a static sampler must be used. + // The sampler will not consume a binding, but be declared in the entry point as a constexpr sampler. + // This can be used on both combined image/samplers (sampler2D) or standalone samplers. + // The remapped sampler must not be an array of samplers. + void remap_constexpr_sampler(uint32_t id, const MSLConstexprSampler &sampler); + protected: void emit_instruction(const Instruction &instr) override; void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args, @@ -284,6 +359,7 @@ protected: 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(); + void emit_entry_point_declarations() override; uint32_t builtin_frag_coord_id = 0; Options msl_options; @@ -307,6 +383,8 @@ protected: std::string sampler_name_suffix = "Smplr"; spv::Op previous_instruction_opcode = spv::OpNop; + std::unordered_map constexpr_samplers; + // OpcodeHandler that handles several MSL preprocessing operations. struct OpCodePreprocessor : OpcodeHandler {