|
@@ -23,6 +23,41 @@ using namespace spv;
|
|
|
using namespace SPIRV_CROSS_NAMESPACE;
|
|
|
using namespace std;
|
|
|
|
|
|
+enum class ImageFormatNormalizedState
|
|
|
+{
|
|
|
+ None = 0,
|
|
|
+ Unorm = 1,
|
|
|
+ Snorm = 2
|
|
|
+};
|
|
|
+
|
|
|
+static ImageFormatNormalizedState image_format_to_normalized_state(ImageFormat fmt)
|
|
|
+{
|
|
|
+ switch (fmt)
|
|
|
+ {
|
|
|
+ case ImageFormatR8:
|
|
|
+ case ImageFormatR16:
|
|
|
+ case ImageFormatRg8:
|
|
|
+ case ImageFormatRg16:
|
|
|
+ case ImageFormatRgba8:
|
|
|
+ case ImageFormatRgba16:
|
|
|
+ case ImageFormatRgb10A2:
|
|
|
+ return ImageFormatNormalizedState::Unorm;
|
|
|
+
|
|
|
+ case ImageFormatR8Snorm:
|
|
|
+ case ImageFormatR16Snorm:
|
|
|
+ case ImageFormatRg8Snorm:
|
|
|
+ case ImageFormatRg16Snorm:
|
|
|
+ case ImageFormatRgba8Snorm:
|
|
|
+ case ImageFormatRgba16Snorm:
|
|
|
+ return ImageFormatNormalizedState::Snorm;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ImageFormatNormalizedState::None;
|
|
|
+}
|
|
|
+
|
|
|
static unsigned image_format_to_components(ImageFormat fmt)
|
|
|
{
|
|
|
switch (fmt)
|
|
@@ -395,7 +430,20 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
|
|
case SPIRType::AtomicCounter:
|
|
|
return "atomic_uint";
|
|
|
case SPIRType::Half:
|
|
|
- return "min16float";
|
|
|
+ if (hlsl_options.enable_16bit_types)
|
|
|
+ return "half";
|
|
|
+ else
|
|
|
+ return "min16float";
|
|
|
+ case SPIRType::Short:
|
|
|
+ if (hlsl_options.enable_16bit_types)
|
|
|
+ return "int16_t";
|
|
|
+ else
|
|
|
+ return "min16int";
|
|
|
+ case SPIRType::UShort:
|
|
|
+ if (hlsl_options.enable_16bit_types)
|
|
|
+ return "uint16_t";
|
|
|
+ else
|
|
|
+ return "min16uint";
|
|
|
case SPIRType::Float:
|
|
|
return "float";
|
|
|
case SPIRType::Double:
|
|
@@ -423,7 +471,11 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
|
|
case SPIRType::UInt:
|
|
|
return join("uint", type.vecsize);
|
|
|
case SPIRType::Half:
|
|
|
- return join("min16float", type.vecsize);
|
|
|
+ return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.vecsize);
|
|
|
+ case SPIRType::Short:
|
|
|
+ return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.vecsize);
|
|
|
+ case SPIRType::UShort:
|
|
|
+ return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.vecsize);
|
|
|
case SPIRType::Float:
|
|
|
return join("float", type.vecsize);
|
|
|
case SPIRType::Double:
|
|
@@ -447,7 +499,11 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type, uint32_t id)
|
|
|
case SPIRType::UInt:
|
|
|
return join("uint", type.columns, "x", type.vecsize);
|
|
|
case SPIRType::Half:
|
|
|
- return join("min16float", type.columns, "x", type.vecsize);
|
|
|
+ return join(hlsl_options.enable_16bit_types ? "half" : "min16float", type.columns, "x", type.vecsize);
|
|
|
+ case SPIRType::Short:
|
|
|
+ return join(hlsl_options.enable_16bit_types ? "int16_t" : "min16int", type.columns, "x", type.vecsize);
|
|
|
+ case SPIRType::UShort:
|
|
|
+ return join(hlsl_options.enable_16bit_types ? "uint16_t" : "min16uint", type.columns, "x", type.vecsize);
|
|
|
case SPIRType::Float:
|
|
|
return join("float", type.columns, "x", type.vecsize);
|
|
|
case SPIRType::Double:
|
|
@@ -1423,66 +1479,14 @@ void CompilerHLSL::emit_resources()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (required_textureSizeVariants != 0)
|
|
|
+ emit_texture_size_variants(required_texture_size_variants.srv, "4", false, "");
|
|
|
+ for (uint32_t norm = 0; norm < 3; norm++)
|
|
|
{
|
|
|
- static const char *types[QueryTypeCount] = { "float4", "int4", "uint4" };
|
|
|
- static const char *dims[QueryDimCount] = { "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray",
|
|
|
- "Texture3D", "Buffer", "TextureCube", "TextureCubeArray",
|
|
|
- "Texture2DMS", "Texture2DMSArray" };
|
|
|
-
|
|
|
- static const bool has_lod[QueryDimCount] = { true, true, true, true, true, false, true, true, false, false };
|
|
|
-
|
|
|
- static const char *ret_types[QueryDimCount] = {
|
|
|
- "uint", "uint2", "uint2", "uint3", "uint3", "uint", "uint2", "uint3", "uint2", "uint3",
|
|
|
- };
|
|
|
-
|
|
|
- static const uint32_t return_arguments[QueryDimCount] = {
|
|
|
- 1, 2, 2, 3, 3, 1, 2, 3, 2, 3,
|
|
|
- };
|
|
|
-
|
|
|
- for (uint32_t index = 0; index < QueryDimCount; index++)
|
|
|
+ for (uint32_t comp = 0; comp < 4; comp++)
|
|
|
{
|
|
|
- for (uint32_t type_index = 0; type_index < QueryTypeCount; type_index++)
|
|
|
- {
|
|
|
- uint32_t bit = 16 * type_index + index;
|
|
|
- uint64_t mask = 1ull << bit;
|
|
|
-
|
|
|
- if ((required_textureSizeVariants & mask) == 0)
|
|
|
- continue;
|
|
|
-
|
|
|
- statement(ret_types[index], " SPIRV_Cross_textureSize(", dims[index], "<", types[type_index],
|
|
|
- "> Tex, uint Level, out uint Param)");
|
|
|
- begin_scope();
|
|
|
- statement(ret_types[index], " ret;");
|
|
|
- switch (return_arguments[index])
|
|
|
- {
|
|
|
- case 1:
|
|
|
- if (has_lod[index])
|
|
|
- statement("Tex.GetDimensions(Level, ret.x, Param);");
|
|
|
- else
|
|
|
- {
|
|
|
- statement("Tex.GetDimensions(ret.x);");
|
|
|
- statement("Param = 0u;");
|
|
|
- }
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- if (has_lod[index])
|
|
|
- statement("Tex.GetDimensions(Level, ret.x, ret.y, Param);");
|
|
|
- else
|
|
|
- statement("Tex.GetDimensions(ret.x, ret.y, Param);");
|
|
|
- break;
|
|
|
- case 3:
|
|
|
- if (has_lod[index])
|
|
|
- statement("Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param);");
|
|
|
- else
|
|
|
- statement("Tex.GetDimensions(ret.x, ret.y, ret.z, Param);");
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- statement("return ret;");
|
|
|
- end_scope();
|
|
|
- statement("");
|
|
|
- }
|
|
|
+ static const char *qualifiers[] = { "", "unorm ", "snorm " };
|
|
|
+ static const char *vecsizes[] = { "", "2", "3", "4" };
|
|
|
+ emit_texture_size_variants(required_texture_size_variants.uav[norm][comp], vecsizes[comp], true, qualifiers[norm]);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1845,6 +1849,83 @@ void CompilerHLSL::emit_resources()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav, const char *type_qualifier)
|
|
|
+{
|
|
|
+ if (variant_mask == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
+ static const char *types[QueryTypeCount] = { "float", "int", "uint" };
|
|
|
+ static const char *dims[QueryDimCount] = { "Texture1D", "Texture1DArray", "Texture2D", "Texture2DArray",
|
|
|
+ "Texture3D", "Buffer", "TextureCube", "TextureCubeArray",
|
|
|
+ "Texture2DMS", "Texture2DMSArray" };
|
|
|
+
|
|
|
+ static const bool has_lod[QueryDimCount] = { true, true, true, true, true, false, true, true, false, false };
|
|
|
+
|
|
|
+ static const char *ret_types[QueryDimCount] = {
|
|
|
+ "uint", "uint2", "uint2", "uint3", "uint3", "uint", "uint2", "uint3", "uint2", "uint3",
|
|
|
+ };
|
|
|
+
|
|
|
+ static const uint32_t return_arguments[QueryDimCount] = {
|
|
|
+ 1, 2, 2, 3, 3, 1, 2, 3, 2, 3,
|
|
|
+ };
|
|
|
+
|
|
|
+ for (uint32_t index = 0; index < QueryDimCount; index++)
|
|
|
+ {
|
|
|
+ for (uint32_t type_index = 0; type_index < QueryTypeCount; type_index++)
|
|
|
+ {
|
|
|
+ uint32_t bit = 16 * type_index + index;
|
|
|
+ uint64_t mask = 1ull << bit;
|
|
|
+
|
|
|
+ if ((variant_mask & mask) == 0)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ statement(ret_types[index], " SPIRV_Cross_", (uav ? "image" : "texture"), "Size(", (uav ? "RW" : ""),
|
|
|
+ dims[index], "<", type_qualifier, types[type_index], vecsize_qualifier,
|
|
|
+ "> Tex, ", (uav ? "" : "uint Level, "), "out uint Param)");
|
|
|
+ begin_scope();
|
|
|
+ statement(ret_types[index], " ret;");
|
|
|
+ switch (return_arguments[index])
|
|
|
+ {
|
|
|
+ case 1:
|
|
|
+ if (has_lod[index] && !uav)
|
|
|
+ statement("Tex.GetDimensions(Level, ret.x, Param);");
|
|
|
+ else
|
|
|
+ {
|
|
|
+ statement("Tex.GetDimensions(ret.x);");
|
|
|
+ statement("Param = 0u;");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ if (has_lod[index] && !uav)
|
|
|
+ statement("Tex.GetDimensions(Level, ret.x, ret.y, Param);");
|
|
|
+ else if (!uav)
|
|
|
+ statement("Tex.GetDimensions(ret.x, ret.y, Param);");
|
|
|
+ else
|
|
|
+ {
|
|
|
+ statement("Tex.GetDimensions(ret.x, ret.y);");
|
|
|
+ statement("Param = 0u;");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ if (has_lod[index] && !uav)
|
|
|
+ statement("Tex.GetDimensions(Level, ret.x, ret.y, ret.z, Param);");
|
|
|
+ else if (!uav)
|
|
|
+ statement("Tex.GetDimensions(ret.x, ret.y, ret.z, Param);");
|
|
|
+ else
|
|
|
+ {
|
|
|
+ statement("Tex.GetDimensions(ret.x, ret.y, ret.z);");
|
|
|
+ statement("Param = 0u;");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ statement("return ret;");
|
|
|
+ end_scope();
|
|
|
+ statement("");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
string CompilerHLSL::layout_for_member(const SPIRType &type, uint32_t index)
|
|
|
{
|
|
|
auto &flags = get_member_decoration_bitset(type.self, index);
|
|
@@ -1906,7 +1987,7 @@ void CompilerHLSL::emit_buffer_block(const SPIRVariable &var)
|
|
|
if (is_uav)
|
|
|
{
|
|
|
Bitset flags = ir.get_buffer_block_flags(var);
|
|
|
- bool is_readonly = flags.get(DecorationNonWritable) && !hlsl_options.force_storage_buffer_as_uav;
|
|
|
+ bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
|
|
|
bool is_coherent = flags.get(DecorationCoherent) && !is_readonly;
|
|
|
bool is_interlocked = interlocked_resources.count(var.self) > 0;
|
|
|
const char *type_name = "ByteAddressBuffer ";
|
|
@@ -3038,7 +3119,7 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
|
|
|
if (has_decoration(type.self, DecorationBufferBlock))
|
|
|
{
|
|
|
Bitset flags = ir.get_buffer_block_flags(var);
|
|
|
- bool is_readonly = flags.get(DecorationNonWritable) && !hlsl_options.force_storage_buffer_as_uav;
|
|
|
+ bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
|
|
|
space = is_readonly ? 't' : 'u'; // UAV
|
|
|
resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT;
|
|
|
}
|
|
@@ -3057,7 +3138,7 @@ string CompilerHLSL::to_resource_binding(const SPIRVariable &var)
|
|
|
{
|
|
|
// UAV or SRV depending on readonly flag.
|
|
|
Bitset flags = ir.get_buffer_block_flags(var);
|
|
|
- bool is_readonly = flags.get(DecorationNonWritable) && !hlsl_options.force_storage_buffer_as_uav;
|
|
|
+ bool is_readonly = flags.get(DecorationNonWritable) && !is_hlsl_force_storage_buffer_as_uav(var.self);
|
|
|
space = is_readonly ? 't' : 'u';
|
|
|
resource_flags = is_readonly ? HLSL_BINDING_AUTO_SRV_BIT : HLSL_BINDING_AUTO_UAV_BIT;
|
|
|
}
|
|
@@ -3587,11 +3668,16 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
|
|
read_access_chain_struct(lhs, chain);
|
|
|
return;
|
|
|
}
|
|
|
- else if (type.width != 32)
|
|
|
- SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported.");
|
|
|
+ else if (type.width != 32 && !hlsl_options.enable_16bit_types)
|
|
|
+ SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and native 16-bit types are enabled.");
|
|
|
|
|
|
+ bool templated_load = hlsl_options.shader_model >= 62;
|
|
|
string load_expr;
|
|
|
|
|
|
+ string template_expr;
|
|
|
+ if (templated_load)
|
|
|
+ template_expr = join("<", type_to_glsl(type), ">");
|
|
|
+
|
|
|
// Load a vector or scalar.
|
|
|
if (type.columns == 1 && !chain.row_major_matrix)
|
|
|
{
|
|
@@ -3614,12 +3700,24 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
|
|
SPIRV_CROSS_THROW("Unknown vector size.");
|
|
|
}
|
|
|
|
|
|
- load_expr = join(chain.base, ".", load_op, "(", chain.dynamic_index, chain.static_index, ")");
|
|
|
+ if (templated_load)
|
|
|
+ load_op = "Load";
|
|
|
+
|
|
|
+ load_expr = join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")");
|
|
|
}
|
|
|
else if (type.columns == 1)
|
|
|
{
|
|
|
// Strided load since we are loading a column from a row-major matrix.
|
|
|
- if (type.vecsize > 1)
|
|
|
+ if (templated_load)
|
|
|
+ {
|
|
|
+ auto scalar_type = type;
|
|
|
+ scalar_type.vecsize = 1;
|
|
|
+ scalar_type.columns = 1;
|
|
|
+ template_expr = join("<", type_to_glsl(scalar_type), ">");
|
|
|
+ if (type.vecsize > 1)
|
|
|
+ load_expr += type_to_glsl(type) + "(";
|
|
|
+ }
|
|
|
+ else if (type.vecsize > 1)
|
|
|
{
|
|
|
load_expr = type_to_glsl(target_type);
|
|
|
load_expr += "(";
|
|
@@ -3628,7 +3726,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
|
|
for (uint32_t r = 0; r < type.vecsize; r++)
|
|
|
{
|
|
|
load_expr +=
|
|
|
- join(chain.base, ".Load(", chain.dynamic_index, chain.static_index + r * chain.matrix_stride, ")");
|
|
|
+ join(chain.base, ".Load", template_expr, "(", chain.dynamic_index, chain.static_index + r * chain.matrix_stride, ")");
|
|
|
if (r + 1 < type.vecsize)
|
|
|
load_expr += ", ";
|
|
|
}
|
|
@@ -3658,13 +3756,25 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
|
|
SPIRV_CROSS_THROW("Unknown vector size.");
|
|
|
}
|
|
|
|
|
|
- // Note, this loading style in HLSL is *actually* row-major, but we always treat matrices as transposed in this backend,
|
|
|
- // so row-major is technically column-major ...
|
|
|
- load_expr = type_to_glsl(target_type);
|
|
|
+ if (templated_load)
|
|
|
+ {
|
|
|
+ auto vector_type = type;
|
|
|
+ vector_type.columns = 1;
|
|
|
+ template_expr = join("<", type_to_glsl(vector_type), ">");
|
|
|
+ load_expr = type_to_glsl(type);
|
|
|
+ load_op = "Load";
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Note, this loading style in HLSL is *actually* row-major, but we always treat matrices as transposed in this backend,
|
|
|
+ // so row-major is technically column-major ...
|
|
|
+ load_expr = type_to_glsl(target_type);
|
|
|
+ }
|
|
|
load_expr += "(";
|
|
|
+
|
|
|
for (uint32_t c = 0; c < type.columns; c++)
|
|
|
{
|
|
|
- load_expr += join(chain.base, ".", load_op, "(", chain.dynamic_index,
|
|
|
+ load_expr += join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index,
|
|
|
chain.static_index + c * chain.matrix_stride, ")");
|
|
|
if (c + 1 < type.columns)
|
|
|
load_expr += ", ";
|
|
@@ -3676,13 +3786,24 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
|
|
// Pick out elements one by one ... Hopefully compilers are smart enough to recognize this pattern
|
|
|
// considering HLSL is "row-major decl", but "column-major" memory layout (basically implicit transpose model, ugh) ...
|
|
|
|
|
|
- load_expr = type_to_glsl(target_type);
|
|
|
+ if (templated_load)
|
|
|
+ {
|
|
|
+ load_expr = type_to_glsl(type);
|
|
|
+ auto scalar_type = type;
|
|
|
+ scalar_type.vecsize = 1;
|
|
|
+ scalar_type.columns = 1;
|
|
|
+ template_expr = join("<", type_to_glsl(scalar_type), ">");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ load_expr = type_to_glsl(target_type);
|
|
|
+
|
|
|
load_expr += "(";
|
|
|
+
|
|
|
for (uint32_t c = 0; c < type.columns; c++)
|
|
|
{
|
|
|
for (uint32_t r = 0; r < type.vecsize; r++)
|
|
|
{
|
|
|
- load_expr += join(chain.base, ".Load(", chain.dynamic_index,
|
|
|
+ load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index,
|
|
|
chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")");
|
|
|
|
|
|
if ((r + 1 < type.vecsize) || (c + 1 < type.columns))
|
|
@@ -3692,9 +3813,12 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
|
|
load_expr += ")";
|
|
|
}
|
|
|
|
|
|
- auto bitcast_op = bitcast_glsl_op(type, target_type);
|
|
|
- if (!bitcast_op.empty())
|
|
|
- load_expr = join(bitcast_op, "(", load_expr, ")");
|
|
|
+ if (!templated_load)
|
|
|
+ {
|
|
|
+ auto bitcast_op = bitcast_glsl_op(type, target_type);
|
|
|
+ if (!bitcast_op.empty())
|
|
|
+ load_expr = join(bitcast_op, "(", load_expr, ")");
|
|
|
+ }
|
|
|
|
|
|
if (lhs.empty())
|
|
|
{
|
|
@@ -3877,8 +4001,14 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
|
|
register_write(chain.self);
|
|
|
return;
|
|
|
}
|
|
|
- else if (type.width != 32)
|
|
|
- SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported.");
|
|
|
+ else if (type.width != 32 && !hlsl_options.enable_16bit_types)
|
|
|
+ SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported, unless SM 6.2 and native 16-bit types are enabled.");
|
|
|
+
|
|
|
+ bool templated_store = hlsl_options.shader_model >= 62;
|
|
|
+
|
|
|
+ string template_expr;
|
|
|
+ if (templated_store)
|
|
|
+ template_expr = join("<", type_to_glsl(type), ">");
|
|
|
|
|
|
if (type.columns == 1 && !chain.row_major_matrix)
|
|
|
{
|
|
@@ -3902,13 +4032,27 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
|
|
}
|
|
|
|
|
|
auto store_expr = write_access_chain_value(value, composite_chain, false);
|
|
|
- auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
- if (!bitcast_op.empty())
|
|
|
- store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
- statement(chain.base, ".", store_op, "(", chain.dynamic_index, chain.static_index, ", ", store_expr, ");");
|
|
|
+
|
|
|
+ if (!templated_store)
|
|
|
+ {
|
|
|
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
+ if (!bitcast_op.empty())
|
|
|
+ store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ store_op = "Store";
|
|
|
+ statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ", store_expr, ");");
|
|
|
}
|
|
|
else if (type.columns == 1)
|
|
|
{
|
|
|
+ if (templated_store)
|
|
|
+ {
|
|
|
+ auto scalar_type = type;
|
|
|
+ scalar_type.vecsize = 1;
|
|
|
+ scalar_type.columns = 1;
|
|
|
+ template_expr = join("<", type_to_glsl(scalar_type), ">");
|
|
|
+ }
|
|
|
+
|
|
|
// Strided store.
|
|
|
for (uint32_t r = 0; r < type.vecsize; r++)
|
|
|
{
|
|
@@ -3920,10 +4064,14 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
|
|
}
|
|
|
remove_duplicate_swizzle(store_expr);
|
|
|
|
|
|
- auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
- if (!bitcast_op.empty())
|
|
|
- store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
- statement(chain.base, ".Store(", chain.dynamic_index, chain.static_index + chain.matrix_stride * r, ", ",
|
|
|
+ if (!templated_store)
|
|
|
+ {
|
|
|
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
+ if (!bitcast_op.empty())
|
|
|
+ store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
+ }
|
|
|
+
|
|
|
+ statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index, chain.static_index + chain.matrix_stride * r, ", ",
|
|
|
store_expr, ");");
|
|
|
}
|
|
|
}
|
|
@@ -3948,18 +4096,39 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
|
|
SPIRV_CROSS_THROW("Unknown vector size.");
|
|
|
}
|
|
|
|
|
|
+ if (templated_store)
|
|
|
+ {
|
|
|
+ store_op = "Store";
|
|
|
+ auto vector_type = type;
|
|
|
+ vector_type.columns = 1;
|
|
|
+ template_expr = join("<", type_to_glsl(vector_type), ">");
|
|
|
+ }
|
|
|
+
|
|
|
for (uint32_t c = 0; c < type.columns; c++)
|
|
|
{
|
|
|
auto store_expr = join(write_access_chain_value(value, composite_chain, true), "[", c, "]");
|
|
|
- auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
- if (!bitcast_op.empty())
|
|
|
- store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
- statement(chain.base, ".", store_op, "(", chain.dynamic_index, chain.static_index + c * chain.matrix_stride,
|
|
|
+
|
|
|
+ if (!templated_store)
|
|
|
+ {
|
|
|
+ auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
+ if (!bitcast_op.empty())
|
|
|
+ store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
+ }
|
|
|
+
|
|
|
+ statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index + c * chain.matrix_stride,
|
|
|
", ", store_expr, ");");
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ if (templated_store)
|
|
|
+ {
|
|
|
+ auto scalar_type = type;
|
|
|
+ scalar_type.vecsize = 1;
|
|
|
+ scalar_type.columns = 1;
|
|
|
+ template_expr = join("<", type_to_glsl(scalar_type), ">");
|
|
|
+ }
|
|
|
+
|
|
|
for (uint32_t r = 0; r < type.vecsize; r++)
|
|
|
{
|
|
|
for (uint32_t c = 0; c < type.columns; c++)
|
|
@@ -3970,7 +4139,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
|
|
auto bitcast_op = bitcast_glsl_op(target_type, type);
|
|
|
if (!bitcast_op.empty())
|
|
|
store_expr = join(bitcast_op, "(", store_expr, ")");
|
|
|
- statement(chain.base, ".Store(", chain.dynamic_index,
|
|
|
+ statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index,
|
|
|
chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");");
|
|
|
}
|
|
|
}
|
|
@@ -4087,9 +4256,11 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
|
|
|
const char *atomic_op = nullptr;
|
|
|
|
|
|
string value_expr;
|
|
|
- if (op != OpAtomicIDecrement && op != OpAtomicIIncrement)
|
|
|
+ if (op != OpAtomicIDecrement && op != OpAtomicIIncrement && op != OpAtomicLoad && op != OpAtomicStore)
|
|
|
value_expr = to_expression(ops[op == OpAtomicCompareExchange ? 6 : 5]);
|
|
|
|
|
|
+ bool is_atomic_store = false;
|
|
|
+
|
|
|
switch (op)
|
|
|
{
|
|
|
case OpAtomicIIncrement:
|
|
@@ -4102,6 +4273,11 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
|
|
|
value_expr = "-1";
|
|
|
break;
|
|
|
|
|
|
+ case OpAtomicLoad:
|
|
|
+ atomic_op = "InterlockedAdd";
|
|
|
+ value_expr = "0";
|
|
|
+ break;
|
|
|
+
|
|
|
case OpAtomicISub:
|
|
|
atomic_op = "InterlockedAdd";
|
|
|
value_expr = join("-", enclose_expression(value_expr));
|
|
@@ -4137,6 +4313,11 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
|
|
|
atomic_op = "InterlockedExchange";
|
|
|
break;
|
|
|
|
|
|
+ case OpAtomicStore:
|
|
|
+ atomic_op = "InterlockedExchange";
|
|
|
+ is_atomic_store = true;
|
|
|
+ break;
|
|
|
+
|
|
|
case OpAtomicCompareExchange:
|
|
|
if (length < 8)
|
|
|
SPIRV_CROSS_THROW("Not enough data for opcode.");
|
|
@@ -4148,31 +4329,57 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
|
|
|
SPIRV_CROSS_THROW("Unknown atomic opcode.");
|
|
|
}
|
|
|
|
|
|
- uint32_t result_type = ops[0];
|
|
|
- uint32_t id = ops[1];
|
|
|
- forced_temporaries.insert(ops[1]);
|
|
|
+ if (is_atomic_store)
|
|
|
+ {
|
|
|
+ auto &data_type = expression_type(ops[0]);
|
|
|
+ auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
|
|
|
|
|
|
- auto &type = get<SPIRType>(result_type);
|
|
|
- statement(variable_decl(type, to_name(id)), ";");
|
|
|
+ auto &tmp_id = extra_sub_expressions[ops[0]];
|
|
|
+ if (!tmp_id)
|
|
|
+ {
|
|
|
+ tmp_id = ir.increase_bound_by(1);
|
|
|
+ emit_uninitialized_temporary_expression(get_pointee_type(data_type).self, tmp_id);
|
|
|
+ }
|
|
|
|
|
|
- auto &data_type = expression_type(ops[2]);
|
|
|
- auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
|
|
|
- SPIRType::BaseType expr_type;
|
|
|
- if (data_type.storage == StorageClassImage || !chain)
|
|
|
- {
|
|
|
- statement(atomic_op, "(", to_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
|
|
|
- expr_type = data_type.basetype;
|
|
|
+ if (data_type.storage == StorageClassImage || !chain)
|
|
|
+ {
|
|
|
+ statement(atomic_op, "(", to_expression(ops[0]), ", ", to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // RWByteAddress buffer is always uint in its underlying type.
|
|
|
+ statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", to_expression(ops[3]),
|
|
|
+ ", ", to_expression(tmp_id), ");");
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- // RWByteAddress buffer is always uint in its underlying type.
|
|
|
- expr_type = SPIRType::UInt;
|
|
|
- statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr, ", ",
|
|
|
- to_name(id), ");");
|
|
|
- }
|
|
|
+ uint32_t result_type = ops[0];
|
|
|
+ uint32_t id = ops[1];
|
|
|
+ forced_temporaries.insert(ops[1]);
|
|
|
+
|
|
|
+ auto &type = get<SPIRType>(result_type);
|
|
|
+ statement(variable_decl(type, to_name(id)), ";");
|
|
|
+
|
|
|
+ auto &data_type = expression_type(ops[2]);
|
|
|
+ auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
|
|
|
+ SPIRType::BaseType expr_type;
|
|
|
+ if (data_type.storage == StorageClassImage || !chain)
|
|
|
+ {
|
|
|
+ statement(atomic_op, "(", to_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
|
|
|
+ expr_type = data_type.basetype;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // RWByteAddress buffer is always uint in its underlying type.
|
|
|
+ expr_type = SPIRType::UInt;
|
|
|
+ statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr,
|
|
|
+ ", ", to_name(id), ");");
|
|
|
+ }
|
|
|
|
|
|
- auto expr = bitcast_expression(type, expr_type, to_name(id));
|
|
|
- set<SPIRExpression>(id, expr, result_type, true);
|
|
|
+ auto expr = bitcast_expression(type, expr_type, to_name(id));
|
|
|
+ set<SPIRExpression>(id, expr, result_type, true);
|
|
|
+ }
|
|
|
flush_all_atomic_capable_variables();
|
|
|
}
|
|
|
|
|
@@ -4796,8 +5003,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
|
|
auto result_type = ops[0];
|
|
|
auto id = ops[1];
|
|
|
|
|
|
- require_texture_query_variant(expression_type(ops[2]));
|
|
|
-
|
|
|
+ require_texture_query_variant(ops[2]);
|
|
|
auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
|
|
|
statement("uint ", dummy_samples_levels, ";");
|
|
|
|
|
@@ -4815,12 +5021,22 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
|
|
auto result_type = ops[0];
|
|
|
auto id = ops[1];
|
|
|
|
|
|
- require_texture_query_variant(expression_type(ops[2]));
|
|
|
+ require_texture_query_variant(ops[2]);
|
|
|
+ bool uav = expression_type(ops[2]).image.sampled == 2;
|
|
|
+
|
|
|
+ if (const auto *var = maybe_get_backing_variable(ops[2]))
|
|
|
+ if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable))
|
|
|
+ uav = false;
|
|
|
|
|
|
auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
|
|
|
statement("uint ", dummy_samples_levels, ";");
|
|
|
|
|
|
- auto expr = join("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
|
|
|
+ string expr;
|
|
|
+ if (uav)
|
|
|
+ expr = join("SPIRV_Cross_imageSize(", to_expression(ops[2]), ", ", dummy_samples_levels, ")");
|
|
|
+ else
|
|
|
+ expr = join("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
|
|
|
+
|
|
|
auto &restype = get<SPIRType>(ops[0]);
|
|
|
expr = bitcast_expression(restype, SPIRType::UInt, expr);
|
|
|
emit_op(result_type, id, expr, true);
|
|
@@ -4833,14 +5049,25 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
|
|
auto result_type = ops[0];
|
|
|
auto id = ops[1];
|
|
|
|
|
|
- require_texture_query_variant(expression_type(ops[2]));
|
|
|
+ require_texture_query_variant(ops[2]);
|
|
|
+ bool uav = expression_type(ops[2]).image.sampled == 2;
|
|
|
+ if (opcode == OpImageQueryLevels && uav)
|
|
|
+ SPIRV_CROSS_THROW("Cannot query levels for UAV images.");
|
|
|
+
|
|
|
+ if (const auto *var = maybe_get_backing_variable(ops[2]))
|
|
|
+ if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var->self, DecorationNonWritable))
|
|
|
+ uav = false;
|
|
|
|
|
|
// Keep it simple and do not emit special variants to make this look nicer ...
|
|
|
// This stuff is barely, if ever, used.
|
|
|
forced_temporaries.insert(id);
|
|
|
auto &type = get<SPIRType>(result_type);
|
|
|
statement(variable_decl(type, to_name(id)), ";");
|
|
|
- statement("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", to_name(id), ");");
|
|
|
+
|
|
|
+ if (uav)
|
|
|
+ statement("SPIRV_Cross_imageSize(", to_expression(ops[2]), ", ", to_name(id), ");");
|
|
|
+ else
|
|
|
+ statement("SPIRV_Cross_textureSize(", to_expression(ops[2]), ", 0u, ", to_name(id), ");");
|
|
|
|
|
|
auto &restype = get<SPIRType>(ops[0]);
|
|
|
auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id));
|
|
@@ -4967,6 +5194,8 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
|
|
case OpAtomicIAdd:
|
|
|
case OpAtomicIIncrement:
|
|
|
case OpAtomicIDecrement:
|
|
|
+ case OpAtomicLoad:
|
|
|
+ case OpAtomicStore:
|
|
|
{
|
|
|
emit_atomic(ops, instruction.length, opcode);
|
|
|
break;
|
|
@@ -5152,8 +5381,16 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void CompilerHLSL::require_texture_query_variant(const SPIRType &type)
|
|
|
+void CompilerHLSL::require_texture_query_variant(uint32_t var_id)
|
|
|
{
|
|
|
+ if (const auto *var = maybe_get_backing_variable(var_id))
|
|
|
+ var_id = var->self;
|
|
|
+
|
|
|
+ auto &type = expression_type(var_id);
|
|
|
+ bool uav = type.image.sampled == 2;
|
|
|
+ if (hlsl_options.nonwritable_uav_texture_as_srv && has_decoration(var_id, DecorationNonWritable))
|
|
|
+ uav = false;
|
|
|
+
|
|
|
uint32_t bit = 0;
|
|
|
switch (type.image.dim)
|
|
|
{
|
|
@@ -5202,11 +5439,15 @@ void CompilerHLSL::require_texture_query_variant(const SPIRType &type)
|
|
|
SPIRV_CROSS_THROW("Unsupported query type.");
|
|
|
}
|
|
|
|
|
|
+ auto norm_state = image_format_to_normalized_state(type.image.format);
|
|
|
+ auto &variant = uav ? required_texture_size_variants.uav[uint32_t(norm_state)][image_format_to_components(type.image.format) - 1] :
|
|
|
+ required_texture_size_variants.srv;
|
|
|
+
|
|
|
uint64_t mask = 1ull << bit;
|
|
|
- if ((required_textureSizeVariants & mask) == 0)
|
|
|
+ if ((variant & mask) == 0)
|
|
|
{
|
|
|
force_recompile();
|
|
|
- required_textureSizeVariants |= mask;
|
|
|
+ variant |= mask;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -5291,6 +5532,9 @@ void CompilerHLSL::validate_shader_model()
|
|
|
|
|
|
if (ir.addressing_model != AddressingModelLogical)
|
|
|
SPIRV_CROSS_THROW("Only Logical addressing model can be used with HLSL.");
|
|
|
+
|
|
|
+ if (hlsl_options.enable_16bit_types && hlsl_options.shader_model < 62)
|
|
|
+ SPIRV_CROSS_THROW("Need at least shader model 6.2 when enabling native 16-bit type support.");
|
|
|
}
|
|
|
|
|
|
string CompilerHLSL::compile()
|
|
@@ -5413,3 +5657,22 @@ CompilerHLSL::BitcastType CompilerHLSL::get_bitcast_type(uint32_t result_type, u
|
|
|
|
|
|
return BitcastType::TypeNormal;
|
|
|
}
|
|
|
+
|
|
|
+bool CompilerHLSL::is_hlsl_force_storage_buffer_as_uav(ID id) const
|
|
|
+{
|
|
|
+ if (hlsl_options.force_storage_buffer_as_uav)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ const uint32_t desc_set = get_decoration(id, spv::DecorationDescriptorSet);
|
|
|
+ const uint32_t binding = get_decoration(id, spv::DecorationBinding);
|
|
|
+
|
|
|
+ return (force_uav_buffer_bindings.find({desc_set, binding}) != force_uav_buffer_bindings.end());
|
|
|
+}
|
|
|
+
|
|
|
+void CompilerHLSL::set_hlsl_force_storage_buffer_as_uav(uint32_t desc_set, uint32_t binding)
|
|
|
+{
|
|
|
+ SetBindingPair pair = { desc_set, binding };
|
|
|
+ force_uav_buffer_bindings.insert(pair);
|
|
|
+}
|