From cc153f8d7f797988164e66df3055abe9be200c5f Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Thu, 9 Jan 2020 11:18:14 +0100 Subject: [PATCH] HLSL: Add a resource remapping API similar to MSL. Allows more flexibility of how resources are assigned without having to remap decorations. --- CMakeLists.txt | 8 ++- spirv_common.hpp | 56 ++++++++++++++++ spirv_cross_c.cpp | 74 ++++++++++++++++++++ spirv_cross_c.h | 35 +++++++++- spirv_hlsl.cpp | 82 ++++++++++++++++++++--- spirv_hlsl.hpp | 37 +++++++++- spirv_msl.cpp | 34 +--------- spirv_msl.hpp | 30 ++------- tests-other/hlsl_resource_binding.spv | Bin 0 -> 796 bytes tests-other/hlsl_resource_bindings.cpp | 89 +++++++++++++++++++++++++ 10 files changed, 375 insertions(+), 70 deletions(-) create mode 100644 tests-other/hlsl_resource_binding.spv create mode 100644 tests-other/hlsl_resource_bindings.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f9f95b72..0a1137c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,7 +312,7 @@ if (SPIRV_CROSS_STATIC) endif() set(spirv-cross-abi-major 0) -set(spirv-cross-abi-minor 21) +set(spirv-cross-abi-minor 22) set(spirv-cross-abi-patch 0) if (SPIRV_CROSS_SHARED) @@ -491,6 +491,10 @@ if (SPIRV_CROSS_CLI) target_link_libraries(spirv-cross-msl-resource-binding-test spirv-cross-c) set_target_properties(spirv-cross-msl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + add_executable(spirv-cross-hlsl-resource-binding-test tests-other/hlsl_resource_bindings.cpp) + target_link_libraries(spirv-cross-hlsl-resource-binding-test spirv-cross-c) + set_target_properties(spirv-cross-hlsl-resource-binding-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") + add_executable(spirv-cross-msl-ycbcr-conversion-test tests-other/msl_ycbcr_conversion_test.cpp) target_link_libraries(spirv-cross-msl-ycbcr-conversion-test spirv-cross-c) set_target_properties(spirv-cross-msl-ycbcr-conversion-test PROPERTIES LINK_FLAGS "${spirv-cross-link-flags}") @@ -513,6 +517,8 @@ if (SPIRV_CROSS_CLI) COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_constexpr_test.spv) add_test(NAME spirv-cross-msl-resource-binding-test COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_resource_binding.spv) + add_test(NAME spirv-cross-hlsl-resource-binding-test + COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/hlsl_resource_binding.spv) add_test(NAME spirv-cross-msl-ycbcr-conversion-test COMMAND $ ${CMAKE_CURRENT_SOURCE_DIR}/tests-other/msl_ycbcr_conversion_test.spv) add_test(NAME spirv-cross-msl-ycbcr-conversion-test-2 diff --git a/spirv_common.hpp b/spirv_common.hpp index f25486a6..b3043cf6 100644 --- a/spirv_common.hpp +++ b/spirv_common.hpp @@ -1694,6 +1694,62 @@ static inline bool opcode_is_sign_invariant(spv::Op opcode) return false; } } + +struct SetBindingPair +{ + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const SetBindingPair &other) const + { + return desc_set == other.desc_set && binding == other.binding; + } + + inline bool operator<(const SetBindingPair &other) const + { + return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); + } +}; + +struct StageSetBinding +{ + spv::ExecutionModel model; + uint32_t desc_set; + uint32_t binding; + + inline bool operator==(const StageSetBinding &other) const + { + return model == other.model && desc_set == other.desc_set && binding == other.binding; + } +}; + +struct InternalHasher +{ + inline size_t operator()(const SetBindingPair &value) const + { + // Quality of hash doesn't really matter here. + auto hash_set = std::hash()(value.desc_set); + auto hash_binding = std::hash()(value.binding); + return (hash_set * 0x10001b31) ^ hash_binding; + } + + inline size_t operator()(const StageSetBinding &value) const + { + // Quality of hash doesn't really matter here. + auto hash_model = std::hash()(value.model); + auto hash_set = std::hash()(value.desc_set); + auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set; + return (tmp_hash * 0x10001b31) ^ value.binding; + } +}; + +// Special constant used in a {MSL,HLSL}ResourceBinding desc_set +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantDescriptorSet = ~(0u); + +// Special constant used in a {MSL,HLSL}ResourceBinding binding +// element to indicate the bindings for the push constants. +static const uint32_t ResourceBindingPushConstantBinding = 0; } // namespace SPIRV_CROSS_NAMESPACE namespace std diff --git a/spirv_cross_c.cpp b/spirv_cross_c.cpp index b590fe80..f94e785d 100644 --- a/spirv_cross_c.cpp +++ b/spirv_cross_c.cpp @@ -789,6 +789,60 @@ spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler #endif } +spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + HLSLResourceBinding bind; + bind.binding = binding->binding; + bind.desc_set = binding->desc_set; + bind.stage = static_cast(binding->stage); + bind.cbv.register_binding = binding->cbv.register_binding; + bind.cbv.register_space = binding->cbv.register_space; + bind.uav.register_binding = binding->uav.register_binding; + bind.uav.register_space = binding->uav.register_space; + bind.srv.register_binding = binding->srv.register_binding; + bind.srv.register_space = binding->srv.register_space; + bind.sampler.register_binding = binding->sampler.register_binding; + bind.sampler.register_space = binding->sampler.register_space; + hlsl.add_hlsl_resource_binding(bind); + return SPVC_SUCCESS; +#else + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_ERROR_INVALID_ARGUMENT; +#endif +} + +spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, SpvExecutionModel model, unsigned set, + unsigned binding) +{ +#if SPIRV_CROSS_C_API_HLSL + if (compiler->backend != SPVC_BACKEND_HLSL) + { + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; + } + + auto &hlsl = *static_cast(compiler->compiler.get()); + return hlsl.is_hlsl_resource_binding_used(static_cast(model), set, binding) ? SPVC_TRUE : + SPVC_FALSE; +#else + (void)model; + (void)set; + (void)binding; + compiler->context->report_error("HLSL function used on a non-HLSL backend."); + return SPVC_FALSE; +#endif +} + spvc_bool spvc_compiler_msl_is_rasterization_disabled(spvc_compiler compiler) { #if SPIRV_CROSS_C_API_MSL @@ -2157,6 +2211,26 @@ void spvc_msl_resource_binding_init(spvc_msl_resource_binding *binding) #endif } +void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding) +{ +#if SPIRV_CROSS_C_API_HLSL + HLSLResourceBinding binding_default; + binding->desc_set = binding_default.desc_set; + binding->binding = binding_default.binding; + binding->cbv.register_binding = binding_default.cbv.register_binding; + binding->cbv.register_space = binding_default.cbv.register_space; + binding->srv.register_binding = binding_default.srv.register_binding; + binding->srv.register_space = binding_default.srv.register_space; + binding->uav.register_binding = binding_default.uav.register_binding; + binding->uav.register_space = binding_default.uav.register_space; + binding->sampler.register_binding = binding_default.sampler.register_binding; + binding->sampler.register_space = binding_default.sampler.register_space; + binding->stage = static_cast(binding_default.stage); +#else + memset(binding, 0, sizeof(*binding)); +#endif +} + void spvc_msl_constexpr_sampler_init(spvc_msl_constexpr_sampler *sampler) { #if SPIRV_CROSS_C_API_MSL diff --git a/spirv_cross_c.h b/spirv_cross_c.h index a3ad84be..4a8a6cf7 100644 --- a/spirv_cross_c.h +++ b/spirv_cross_c.h @@ -33,7 +33,7 @@ extern "C" { /* Bumped if ABI or API breaks backwards compatibility. */ #define SPVC_C_API_VERSION_MAJOR 0 /* Bumped if APIs or enumerations are added in a backwards compatible way. */ -#define SPVC_C_API_VERSION_MINOR 21 +#define SPVC_C_API_VERSION_MINOR 22 /* Bumped if internal implementation details change. */ #define SPVC_C_API_VERSION_PATCH 0 @@ -469,6 +469,7 @@ SPVC_PUBLIC_API void spvc_msl_sampler_ycbcr_conversion_init(spvc_msl_sampler_ycb /* Maps to C++ API. */ typedef enum spvc_hlsl_binding_flag_bits { + SPVC_HLSL_BINDING_AUTO_NONE_BIT = 0, SPVC_HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0, SPVC_HLSL_BINDING_AUTO_CBV_BIT = 1 << 1, SPVC_HLSL_BINDING_AUTO_SRV_BIT = 1 << 2, @@ -478,6 +479,31 @@ typedef enum spvc_hlsl_binding_flag_bits } spvc_hlsl_binding_flag_bits; typedef unsigned spvc_hlsl_binding_flags; +#define SPVC_HLSL_PUSH_CONSTANT_DESC_SET (~(0u)) +#define SPVC_HLSL_PUSH_CONSTANT_BINDING (0) + +/* Maps to C++ API. */ +typedef struct spvc_hlsl_resource_binding_mapping +{ + unsigned register_space; + unsigned register_binding; +} spvc_hlsl_resource_binding_mapping; + +typedef struct spvc_hlsl_resource_binding +{ + SpvExecutionModel stage; + unsigned desc_set; + unsigned binding; + + spvc_hlsl_resource_binding_mapping cbv, uav, srv, sampler; +} spvc_hlsl_resource_binding; + +/* + * Initializes the resource binding struct. + * The defaults are non-zero. + */ +SPVC_PUBLIC_API void spvc_hlsl_resource_binding_init(spvc_hlsl_resource_binding *binding); + /* Maps to the various spirv_cross::Compiler*::Option structures. See C++ API for defaults and details. */ typedef enum spvc_compiler_option { @@ -621,6 +647,13 @@ SPVC_PUBLIC_API spvc_variable_id spvc_compiler_hlsl_remap_num_workgroups_builtin SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_set_resource_binding_flags(spvc_compiler compiler, spvc_hlsl_binding_flags flags); +SPVC_PUBLIC_API spvc_result spvc_compiler_hlsl_add_resource_binding(spvc_compiler compiler, + const spvc_hlsl_resource_binding *binding); +SPVC_PUBLIC_API spvc_bool spvc_compiler_hlsl_is_resource_used(spvc_compiler compiler, + SpvExecutionModel model, + unsigned set, + unsigned binding); + /* * MSL specifics. * Maps to C++ API. diff --git a/spirv_hlsl.cpp b/spirv_hlsl.cpp index 06848321..2ae5688f 100644 --- a/spirv_hlsl.cpp +++ b/spirv_hlsl.cpp @@ -2934,16 +2934,15 @@ void CompilerHLSL::emit_texture_op(const Instruction &i) string CompilerHLSL::to_resource_binding(const SPIRVariable &var) { - // TODO: Basic implementation, might need special consideration for RW/RO structured buffers, - // RW/RO images, and so on. + const auto &type = get(var.basetype); - if (!has_decoration(var.self, DecorationBinding)) + // We can remap push constant blocks, even if they don't have any binding decoration. + if (type.storage != StorageClassPushConstant && !has_decoration(var.self, DecorationBinding)) return ""; - const auto &type = get(var.basetype); char space = '\0'; - HLSLBindingFlags resource_flags = 0; + HLSLBindingFlagBits resource_flags = HLSL_BINDING_AUTO_NONE_BIT; switch (type.basetype) { @@ -3011,8 +3010,16 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var) if (!space) return ""; - return to_resource_register(resource_flags, space, get_decoration(var.self, DecorationBinding), - get_decoration(var.self, DecorationDescriptorSet)); + uint32_t desc_set = + resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantDescriptorSet : 0u; + uint32_t binding = resource_flags == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT ? ResourceBindingPushConstantBinding : 0u; + + if (has_decoration(var.self, DecorationBinding)) + binding = get_decoration(var.self, DecorationBinding); + if (has_decoration(var.self, DecorationDescriptorSet)) + desc_set = get_decoration(var.self, DecorationDescriptorSet); + + return to_resource_register(resource_flags, space, binding, desc_set); } string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) @@ -3025,10 +3032,54 @@ string CompilerHLSL::to_resource_binding_sampler(const SPIRVariable &var) get_decoration(var.self, DecorationDescriptorSet)); } -string CompilerHLSL::to_resource_register(uint32_t flags, char space, uint32_t binding, uint32_t space_set) +void CompilerHLSL::remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding) { - if ((flags & resource_binding_flags) == 0) + auto itr = resource_bindings.find({ get_execution_model(), desc_set, binding }); + if (itr != end(resource_bindings)) { + auto &remap = itr->second; + remap.second = true; + + switch (type) + { + case HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT: + case HLSL_BINDING_AUTO_CBV_BIT: + desc_set = remap.first.cbv.register_space; + binding = remap.first.cbv.register_binding; + break; + + case HLSL_BINDING_AUTO_SRV_BIT: + desc_set = remap.first.srv.register_space; + binding = remap.first.srv.register_binding; + break; + + case HLSL_BINDING_AUTO_SAMPLER_BIT: + desc_set = remap.first.sampler.register_space; + binding = remap.first.sampler.register_binding; + break; + + case HLSL_BINDING_AUTO_UAV_BIT: + desc_set = remap.first.uav.register_space; + binding = remap.first.uav.register_binding; + break; + + default: + break; + } + } +} + +string CompilerHLSL::to_resource_register(HLSLBindingFlagBits flag, char space, uint32_t binding, uint32_t space_set) +{ + if ((flag & resource_binding_flags) == 0) + { + remap_hlsl_resource_binding(flag, space_set, binding); + + // The push constant block did not have a binding, and there were no remap for it, + // so, declare without register binding. + if (flag == HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT && space_set == ResourceBindingPushConstantDescriptorSet) + return ""; + if (hlsl_options.shader_model >= 51) return join(" : register(", space, binding, ", space", space_set, ")"); else @@ -5215,3 +5266,16 @@ string CompilerHLSL::get_unique_identifier() { return join("_", unique_identifier_count++, "ident"); } + +void CompilerHLSL::add_hlsl_resource_binding(const HLSLResourceBinding &binding) +{ + StageSetBinding tuple = { binding.stage, binding.desc_set, binding.binding }; + resource_bindings[tuple] = { binding, false }; +} + +bool CompilerHLSL::is_hlsl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const +{ + StageSetBinding tuple = { model, desc_set, binding }; + auto itr = resource_bindings.find(tuple); + return itr != end(resource_bindings) && itr->second.second; +} diff --git a/spirv_hlsl.hpp b/spirv_hlsl.hpp index 5663ec2a..2ed7c2fd 100644 --- a/spirv_hlsl.hpp +++ b/spirv_hlsl.hpp @@ -44,6 +44,8 @@ struct RootConstants // For finer control, decorations may be removed from specific resources instead with unset_decoration(). enum HLSLBindingFlagBits { + HLSL_BINDING_AUTO_NONE_BIT = 0, + // Push constant (root constant) resources will be declared as CBVs (b-space) without a register() declaration. // A register will be automatically assigned by the D3D compiler, but must therefore be reflected in D3D-land. // Push constants do not normally have a DecorationBinding set, but if they do, this can be used to ignore it. @@ -67,6 +69,28 @@ enum HLSLBindingFlagBits }; using HLSLBindingFlags = uint32_t; +// By matching stage, desc_set and binding for a SPIR-V resource, +// register bindings are set based on whether the HLSL resource is a +// CBV, UAV, SRV or Sampler. A single binding in SPIR-V might contain multiple +// resource types, e.g. COMBINED_IMAGE_SAMPLER, and SRV/Sampler bindings will be used respectively. +// On SM 5.0 and lower, register_space is ignored. +// +// To remap a push constant block which does not have any desc_set/binding associated with it, +// use ResourceBindingPushConstant{DescriptorSet,Binding} as values for desc_set/binding. +// For deeper control of push constants, set_root_constant_layouts() can be used instead. +struct HLSLResourceBinding +{ + spv::ExecutionModel stage = spv::ExecutionModelMax; + uint32_t desc_set = 0; + uint32_t binding = 0; + + struct Binding + { + uint32_t register_space = 0; + uint32_t register_binding = 0; + } cbv, uav, srv, sampler; +}; + class CompilerHLSL : public CompilerGLSL { public: @@ -145,6 +169,14 @@ public: // Controls how resource bindings are declared in the output HLSL. void set_resource_binding_flags(HLSLBindingFlags flags); + // resource is a resource binding to indicate the HLSL CBV, SRV, UAV or sampler binding + // to use for a particular SPIR-V description set + // and binding. If resource bindings are provided, + // is_hlsl_resource_binding_used() will return true after calling ::compile() if + // the set/binding combination was used by the HLSL code. + void add_hlsl_resource_binding(const HLSLResourceBinding &resource); + bool is_hlsl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; + private: std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override; std::string image_type_hlsl(const SPIRType &type, uint32_t id); @@ -178,7 +210,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(HLSLBindingFlags flags, char space, uint32_t binding, uint32_t set); + std::string to_resource_register(HLSLBindingFlagBits flag, 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); @@ -267,6 +299,9 @@ private: std::string get_unique_identifier(); uint32_t unique_identifier_count = 0; + + std::unordered_map, InternalHasher> resource_bindings; + void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding); }; } // namespace SPIRV_CROSS_NAMESPACE diff --git a/spirv_msl.cpp b/spirv_msl.cpp index 0b5e9fc5..e32e10f2 100644 --- a/spirv_msl.cpp +++ b/spirv_msl.cpp @@ -90,7 +90,7 @@ bool CompilerMSL::is_msl_vertex_attribute_used(uint32_t location) return vtx_attrs_in_use.count(location) != 0; } -bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) +bool CompilerMSL::is_msl_resource_binding_used(ExecutionModel model, uint32_t desc_set, uint32_t binding) const { StageSetBinding tuple = { model, desc_set, binding }; auto itr = resource_bindings.find(tuple); @@ -12641,35 +12641,3 @@ void CompilerMSL::analyze_argument_buffers() } } } - -bool CompilerMSL::SetBindingPair::operator==(const SetBindingPair &other) const -{ - return desc_set == other.desc_set && binding == other.binding; -} - -bool CompilerMSL::SetBindingPair::operator<(const SetBindingPair &other) const -{ - return desc_set < other.desc_set || (desc_set == other.desc_set && binding < other.binding); -} - -bool CompilerMSL::StageSetBinding::operator==(const StageSetBinding &other) const -{ - return model == other.model && desc_set == other.desc_set && binding == other.binding; -} - -size_t CompilerMSL::InternalHasher::operator()(const SetBindingPair &value) const -{ - // Quality of hash doesn't really matter here. - auto hash_set = std::hash()(value.desc_set); - auto hash_binding = std::hash()(value.binding); - return (hash_set * 0x10001b31) ^ hash_binding; -} - -size_t CompilerMSL::InternalHasher::operator()(const StageSetBinding &value) const -{ - // Quality of hash doesn't really matter here. - auto hash_model = std::hash()(value.model); - auto hash_set = std::hash()(value.desc_set); - auto tmp_hash = (hash_model * 0x10001b31) ^ hash_set; - return (tmp_hash * 0x10001b31) ^ value.binding; -} diff --git a/spirv_msl.hpp b/spirv_msl.hpp index 79b1d949..2cb9d044 100644 --- a/spirv_msl.hpp +++ b/spirv_msl.hpp @@ -218,11 +218,13 @@ struct MSLConstexprSampler // Special constant used in a MSLResourceBinding desc_set // element to indicate the bindings for the push constants. -static const uint32_t kPushConstDescSet = ~(0u); +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstDescSet = ResourceBindingPushConstantDescriptorSet; // Special constant used in a MSLResourceBinding binding // element to indicate the bindings for the push constants. -static const uint32_t kPushConstBinding = 0; +// Kinda deprecated. Just use ResourceBindingPushConstant{DescriptorSet,Binding} directly. +static const uint32_t kPushConstBinding = ResourceBindingPushConstantBinding; // Special constant used in a MSLResourceBinding binding // element to indicate the buffer binding for swizzle buffers. @@ -440,7 +442,7 @@ public: // Constexpr samplers are always assumed to be emitted. // No specific MSLResourceBinding remapping is required for constexpr samplers as long as they are remapped // by remap_constexpr_sampler(_by_binding). - bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding); + bool is_msl_resource_binding_used(spv::ExecutionModel model, uint32_t set, uint32_t binding) const; // This must only be called after a successful call to CompilerMSL::compile(). // For a variable resource ID obtained through reflection API, report the automatically assigned resource index. @@ -786,28 +788,6 @@ protected: std::set typedef_lines; SmallVector vars_needing_early_declaration; - struct SetBindingPair - { - uint32_t desc_set; - uint32_t binding; - bool operator==(const SetBindingPair &other) const; - bool operator<(const SetBindingPair &other) const; - }; - - struct StageSetBinding - { - spv::ExecutionModel model; - uint32_t desc_set; - uint32_t binding; - bool operator==(const StageSetBinding &other) const; - }; - - struct InternalHasher - { - size_t operator()(const SetBindingPair &value) const; - size_t operator()(const StageSetBinding &value) const; - }; - std::unordered_map, InternalHasher> resource_bindings; uint32_t next_metal_resource_index_buffer = 0; diff --git a/tests-other/hlsl_resource_binding.spv b/tests-other/hlsl_resource_binding.spv new file mode 100644 index 0000000000000000000000000000000000000000..c48dc49ea00c0d7753a027d29dcbc8f66247a7d4 GIT binary patch literal 796 zcmYk2%}PRH5Qe`r&9c;f%*qPQWe_bYf+&a}TeM25wUmgU6-Vn1y0NaRP0;fkk8noD znR(AUGw*!aOlu*8xlrJ@7pArvG9V!oLq3k}`_BE*c=CF5a(XPI6tXc5wmvJ0q#w+x=F51Ywwxy?^1hO;947P z?)JuY{*BG}>M6EKo(F5-HRkyy*3NJ6Um*Ib;ulG+MzL+wE)nIcFKx5B4ztJydCpr- zNMg>j_D0S9Wuo)cDZfH=mbiRtZx-8UwzsKGZk4DuF}c>hu~?P5Mr`2r9wzk`xn}=1 zQLgjOcuO_-4x0D=g5-#opz1H-YPVM38m_iySk73-<%nN{Ie!ONlY8xxSI5P_lfGSY Pd&RHVtR}HPl%M1OI}s?= literal 0 HcmV?d00001 diff --git a/tests-other/hlsl_resource_bindings.cpp b/tests-other/hlsl_resource_bindings.cpp new file mode 100644 index 00000000..1a938dac --- /dev/null +++ b/tests-other/hlsl_resource_bindings.cpp @@ -0,0 +1,89 @@ +// Testbench for HLSL resource binding APIs. +// It does not validate output at the moment, but it's useful for ad-hoc testing. + +#include +#include +#include +#include + +#define SPVC_CHECKED_CALL(x) do { \ + if ((x) != SPVC_SUCCESS) { \ + fprintf(stderr, "Failed at line %d.\n", __LINE__); \ + exit(1); \ + } \ +} while(0) + +static std::vector read_file(const char *path) +{ + long len; + FILE *file = fopen(path, "rb"); + + if (!file) + return {}; + + fseek(file, 0, SEEK_END); + len = ftell(file); + rewind(file); + + std::vector buffer(len / sizeof(SpvId)); + if (fread(buffer.data(), 1, len, file) != (size_t)len) + { + fclose(file); + return {}; + } + + fclose(file); + return buffer; +} + +int main(int argc, char **argv) +{ + if (argc != 2) + return EXIT_FAILURE; + + auto buffer = read_file(argv[1]); + if (buffer.empty()) + return EXIT_FAILURE; + + spvc_context ctx; + spvc_parsed_ir parsed_ir; + spvc_compiler compiler; + + SPVC_CHECKED_CALL(spvc_context_create(&ctx)); + SPVC_CHECKED_CALL(spvc_context_parse_spirv(ctx, buffer.data(), buffer.size(), &parsed_ir)); + SPVC_CHECKED_CALL(spvc_context_create_compiler(ctx, SPVC_BACKEND_HLSL, parsed_ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler)); + + spvc_compiler_options opts; + SPVC_CHECKED_CALL(spvc_compiler_create_compiler_options(compiler, &opts)); + SPVC_CHECKED_CALL(spvc_compiler_options_set_uint(opts, SPVC_COMPILER_OPTION_HLSL_SHADER_MODEL, 51)); + SPVC_CHECKED_CALL(spvc_compiler_install_compiler_options(compiler, opts)); + + spvc_hlsl_resource_binding binding; + spvc_hlsl_resource_binding_init(&binding); + binding.stage = SpvExecutionModelFragment; + binding.desc_set = 1; + binding.binding = 4; + binding.srv.register_space = 2; + binding.srv.register_binding = 3; + binding.sampler.register_space = 4; + binding.sampler.register_binding = 5; + SPVC_CHECKED_CALL(spvc_compiler_hlsl_add_resource_binding(compiler, &binding)); + + binding.desc_set = SPVC_HLSL_PUSH_CONSTANT_DESC_SET; + binding.binding = SPVC_HLSL_PUSH_CONSTANT_BINDING; + binding.cbv.register_space = 0; + binding.cbv.register_binding = 4; + SPVC_CHECKED_CALL(spvc_compiler_hlsl_add_resource_binding(compiler, &binding)); + + const char *str; + SPVC_CHECKED_CALL(spvc_compiler_compile(compiler, &str)); + + fprintf(stderr, "Output:\n%s\n", str); + + if (!spvc_compiler_hlsl_is_resource_used(compiler, SpvExecutionModelFragment, 1, 4)) + return EXIT_FAILURE; + + if (!spvc_compiler_hlsl_is_resource_used(compiler, SpvExecutionModelFragment, SPVC_HLSL_PUSH_CONSTANT_DESC_SET, SPVC_HLSL_PUSH_CONSTANT_BINDING)) + return EXIT_FAILURE; +} +