SPIRV-Cross/spirv_hlsl.hpp

329 lines
13 KiB
C++
Raw Normal View History

/*
2020-01-16 14:24:37 +00:00
* Copyright 2016-2020 Robert Konrad
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SPIRV_HLSL_HPP
#define SPIRV_HLSL_HPP
#include "spirv_glsl.hpp"
#include <utility>
namespace SPIRV_CROSS_NAMESPACE
{
// Interface which remaps vertex inputs to a fixed semantic name to make linking easier.
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;
};
// 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.
HLSL_BINDING_AUTO_PUSH_CONSTANT_BIT = 1 << 0,
// cbuffer resources will be declared as CBVs (b-space) without a register() declaration.
// A register will be automatically assigned, but must be reflected in D3D-land.
HLSL_BINDING_AUTO_CBV_BIT = 1 << 1,
// All SRVs (t-space) will be declared without a register() declaration.
HLSL_BINDING_AUTO_SRV_BIT = 1 << 2,
// All UAVs (u-space) will be declared without a register() declaration.
HLSL_BINDING_AUTO_UAV_BIT = 1 << 3,
// All samplers (s-space) will be declared without a register() declaration.
HLSL_BINDING_AUTO_SAMPLER_BIT = 1 << 4,
// No resources will be declared with register().
HLSL_BINDING_AUTO_ALL = 0x7fffffff
};
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;
};
2016-08-14 20:02:38 +00:00
class CompilerHLSL : public CompilerGLSL
{
public:
2016-08-17 22:51:12 +00:00
struct Options
{
uint32_t shader_model = 30; // TODO: map ps_4_0_level_9_0,... somehow
// Allows the PointSize builtin, and ignores it, as PointSize is not supported in HLSL.
bool point_size_compat = false;
// Allows the PointCoord builtin, returns float2(0.5, 0.5), as PointCoord is not supported in HLSL.
bool point_coord_compat = false;
// If true, the backend will assume that VertexIndex and InstanceIndex will need to apply
// a base offset, and you will need to fill in a cbuffer with offsets.
// Set to false if you know you will never use base instance or base vertex
// functionality as it might remove an internal cbuffer.
bool support_nonzero_base_vertex_base_instance = false;
// Forces a storage buffer to always be declared as UAV, even if the readonly decoration is used.
// By default, a readonly storage buffer will be declared as ByteAddressBuffer (SRV) instead.
bool force_storage_buffer_as_uav = false;
// Forces any storage image type marked as NonWritable to be considered an SRV instead.
// For this to work with function call parameters, NonWritable must be considered to be part of the type system
// so that NonWritable image arguments are also translated to Texture rather than RWTexture.
bool nonwritable_uav_texture_as_srv = false;
2016-08-17 22:51:12 +00:00
};
explicit CompilerHLSL(std::vector<uint32_t> spirv_)
: CompilerGLSL(std::move(spirv_))
{
2016-08-14 20:02:38 +00:00
}
2016-08-17 22:51:12 +00:00
CompilerHLSL(const uint32_t *ir_, size_t size)
: CompilerGLSL(ir_, size)
{
}
explicit CompilerHLSL(const ParsedIR &ir_)
: CompilerGLSL(ir_)
{
}
explicit CompilerHLSL(ParsedIR &&ir_)
: CompilerGLSL(std::move(ir_))
{
}
const Options &get_hlsl_options() const
{
return hlsl_options;
}
void set_hlsl_options(const Options &opts)
{
hlsl_options = opts;
2016-08-17 22:51:12 +00:00
}
// 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);
// 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.
// $SEMANTIC is either TEXCOORD# or a semantic name specified here.
void add_vertex_attribute_remap(const HLSLVertexAttributeRemap &vertex_attributes);
2016-08-14 20:02:38 +00:00
std::string compile() override;
2018-02-05 09:27:42 +00:00
// This is a special HLSL workaround for the NumWorkGroups builtin.
// This does not exist in HLSL, so the calling application must create a dummy cbuffer in
// which the application will store this builtin.
// The cbuffer layout will be:
// cbuffer SPIRV_Cross_NumWorkgroups : register(b#, space#) { uint3 SPIRV_Cross_NumWorkgroups_count; };
// This must be called before compile().
// The function returns 0 if NumWorkGroups builtin is not statically used in the shader from the current entry point.
// If non-zero, this returns the variable ID of a cbuffer which corresponds to
// the cbuffer declared above. By default, no binding or descriptor set decoration is set,
// so the calling application should declare explicit bindings on this ID before calling compile().
VariableID remap_num_workgroups_builtin();
2018-02-05 09:27:42 +00:00
// 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;
2016-08-14 20:02:38 +00:00
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);
std::string image_type_hlsl_modern(const SPIRType &type, uint32_t id);
std::string image_type_hlsl_legacy(const SPIRType &type, uint32_t id);
void emit_function_prototype(SPIRFunction &func, const Bitset &return_flags) override;
2016-08-14 20:02:38 +00:00
void emit_hlsl_entry_point();
void emit_header() override;
void emit_resources();
void declare_undefined_values() override;
2016-08-14 20:02:38 +00:00
void emit_interface_block_globally(const SPIRVariable &type);
void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set<uint32_t> &active_locations);
void emit_builtin_inputs_in_struct();
void emit_builtin_outputs_in_struct();
2016-08-14 21:09:06 +00:00
void emit_texture_op(const Instruction &i) override;
void emit_instruction(const Instruction &instruction) override;
2017-01-23 13:49:32 +00:00
void emit_glsl_op(uint32_t result_type, uint32_t result_id, uint32_t op, const uint32_t *args,
uint32_t count) override;
void emit_buffer_block(const SPIRVariable &type) override;
void emit_push_constant_block(const SPIRVariable &var) override;
void emit_uniform(const SPIRVariable &var) override;
void emit_modern_uniform(const SPIRVariable &var);
void emit_legacy_uniform(const SPIRVariable &var);
void emit_specialization_constants_and_structs();
void emit_composite_constants();
void emit_fixup() override;
2017-12-06 10:01:32 +00:00
std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override;
std::string layout_for_member(const SPIRType &type, uint32_t index) override;
std::string to_interpolation_qualifiers(const Bitset &flags) override;
2017-03-24 13:13:59 +00:00
std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
MSL: Add support for sampler Y'CbCr conversion. This change introduces functions and in one case, a class, to support the `VK_KHR_sampler_ycbcr_conversion` extension. Except in the case of GBGR8 and BGRG8 formats, for which Metal natively supports implicit chroma reconstruction, we're on our own here. We have to do everything ourselves. Much of the complexity comes from the need to support multiple planes, which must now be passed to functions that use the corresponding combined image-samplers. The rest is from the actual Y'CbCr conversion itself, which requires additional post-processing of the sample retrieved from the image. Passing sampled images to a function was a particular problem. To support this, I've added a new class which is emitted to MSL shaders that pass sampled images with Y'CbCr conversions attached around. It can handle sampled images with or without Y'CbCr conversion. This is an awful abomination that should not exist, but I'm worried that there's some shader out there which does this. This support requires Metal 2.0 to work properly, because it uses default-constructed texture objects, which were only added in MSL 2. I'm not even going to get into arrays of combined image-samplers--that's a whole other can of worms. They are deliberately unsupported in this change. I've taken the liberty of refactoring the support for texture swizzling while I'm at it. It's now treated as a post-processing step similar to Y'CbCr conversion. I'd like to think this is cleaner than having everything in `to_function_name()`/`to_function_args()`. It still looks really hairy, though. I did, however, get rid of the explicit type arguments to `spvGatherSwizzle()`/`spvGatherCompareSwizzle()`. Update the C API. In addition to supporting this new functionality, add some compiler options that I added in previous changes, but for which I neglected to update the C API.
2019-08-02 20:11:19 +00:00
std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id) override;
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(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;
2017-08-10 15:12:48 +00:00
void emit_access_chain(const Instruction &instruction);
void emit_load(const Instruction &instruction);
void read_access_chain(std::string *expr, const std::string &lhs, const SPIRAccessChain &chain);
void read_access_chain_struct(const std::string &lhs, const SPIRAccessChain &chain);
void read_access_chain_array(const std::string &lhs, const SPIRAccessChain &chain);
void write_access_chain(const SPIRAccessChain &chain, uint32_t value, const SmallVector<uint32_t> &composite_chain);
2020-01-08 13:27:34 +00:00
void write_access_chain_struct(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain);
void write_access_chain_array(const SPIRAccessChain &chain, uint32_t value,
const SmallVector<uint32_t> &composite_chain);
std::string write_access_chain_value(uint32_t value, const SmallVector<uint32_t> &composite_chain, bool enclose);
2017-08-10 15:12:48 +00:00
void emit_store(const Instruction &instruction);
2017-10-20 12:56:37 +00:00
void emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op);
void emit_subgroup_op(const Instruction &i) override;
2018-06-25 08:33:13 +00:00
void emit_block_hints(const SPIRBlock &block) override;
2016-08-17 22:51:12 +00:00
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;
void replace_illegal_names() override;
Options hlsl_options;
// TODO: Refactor this to be more similar to MSL, maybe have some common system in place?
bool requires_op_fmod = false;
2017-11-27 13:24:30 +00:00
bool requires_fp16_packing = false;
bool requires_uint2_packing = false;
bool requires_explicit_fp16_packing = false;
2017-11-27 13:44:21 +00:00
bool requires_unorm8_packing = false;
bool requires_snorm8_packing = false;
bool requires_unorm16_packing = false;
bool requires_snorm16_packing = false;
2017-11-29 10:33:44 +00:00
bool requires_bitfield_insert = false;
bool requires_bitfield_extract = false;
bool requires_inverse_2x2 = false;
bool requires_inverse_3x3 = false;
bool requires_inverse_4x4 = false;
bool requires_scalar_reflect = false;
bool requires_scalar_refract = false;
bool requires_scalar_faceforward = false;
uint64_t required_textureSizeVariants = 0;
void require_texture_query_variant(const SPIRType &type);
enum TextureQueryVariantDim
{
Query1D = 0,
Query1DArray,
Query2D,
Query2DArray,
Query3D,
QueryBuffer,
QueryCube,
QueryCubeArray,
Query2DMS,
Query2DMSArray,
QueryDimCount
};
enum TextureQueryVariantType
{
QueryTypeFloat = 0,
QueryTypeInt = 16,
QueryTypeUInt = 32,
QueryTypeCount = 3
};
enum BitcastType
{
TypeNormal,
TypePackUint2x32,
TypeUnpackUint64
};
BitcastType get_bitcast_type(uint32_t result_type, uint32_t op0);
void emit_builtin_variables();
bool require_output = false;
bool require_input = false;
SmallVector<HLSLVertexAttributeRemap> remap_vertex_attributes;
uint32_t type_to_consumed_locations(const SPIRType &type) const;
void emit_io_block(const SPIRVariable &var);
std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc);
2018-02-05 09:27:42 +00:00
uint32_t num_workgroups_builtin = 0;
HLSLBindingFlags resource_binding_flags = 0;
// Custom root constant layout, which should be emitted
// when translating push constant ranges.
std::vector<RootConstants> root_constants_layout;
void validate_shader_model();
std::string get_unique_identifier();
uint32_t unique_identifier_count = 0;
std::unordered_map<StageSetBinding, std::pair<HLSLResourceBinding, bool>, InternalHasher> resource_bindings;
void remap_hlsl_resource_binding(HLSLBindingFlagBits type, uint32_t &desc_set, uint32_t &binding);
2016-08-14 20:02:38 +00:00
};
} // namespace SPIRV_CROSS_NAMESPACE
#endif