Merge pull request #165 from KhronosGroup/hlsl-separate-image-sampler

HLSL: Support for separate image/samplers.
This commit is contained in:
Hans-Kristian Arntzen 2017-05-07 13:48:35 +02:00 committed by GitHub
commit 95d4cbcd8c
20 changed files with 628 additions and 75 deletions

View File

@ -0,0 +1,44 @@
Texture2D<float4> uSampler;
SamplerState _uSampler_sampler;
Texture2D<float4> uSamplerShadow;
SamplerComparisonState _uSamplerShadow_sampler;
static float FragColor;
struct SPIRV_Cross_Output
{
float FragColor : SV_Target0;
};
float4 samp2(Texture2D<float4> s, SamplerState _s_sampler)
{
return s.Sample(_s_sampler, float2(1.0f, 1.0f)) + s.Load(int3(int2(10, 10), 0));
}
float4 samp3(Texture2D<float4> s, SamplerState _s_sampler)
{
return samp2(s, _s_sampler);
}
float samp4(Texture2D<float4> s, SamplerComparisonState _s_sampler)
{
return s.SampleCmp(_s_sampler, float3(1.0f, 1.0f, 1.0f).xy, float3(1.0f, 1.0f, 1.0f).z);
}
float samp(Texture2D<float4> s0, SamplerState _s0_sampler, Texture2D<float4> s1, SamplerComparisonState _s1_sampler)
{
return samp3(s0, _s0_sampler).x + samp4(s1, _s1_sampler);
}
void frag_main()
{
FragColor = samp(uSampler, _uSampler_sampler, uSamplerShadow, _uSamplerShadow_sampler);
}
SPIRV_Cross_Output main()
{
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -0,0 +1,40 @@
Texture2D<float4> uDepth;
SamplerComparisonState uSampler;
SamplerState uSampler1;
static float FragColor;
struct SPIRV_Cross_Output
{
float FragColor : SV_Target0;
};
float samp2(Texture2D<float4> t, SamplerComparisonState s)
{
return t.SampleCmp(s, float3(1.0f, 1.0f, 1.0f).xy, float3(1.0f, 1.0f, 1.0f).z);
}
float samp3(Texture2D<float4> t, SamplerState s)
{
return t.Sample(s, float2(1.0f, 1.0f)).x;
}
float samp(Texture2D<float4> t, SamplerComparisonState s, SamplerState s1)
{
float r0 = samp2(t, s);
float r1 = samp3(t, s1);
return r0 + r1;
}
void frag_main()
{
FragColor = samp(uDepth, uSampler, uSampler1);
}
SPIRV_Cross_Output main()
{
frag_main();
SPIRV_Cross_Output stage_output;
stage_output.FragColor = FragColor;
return stage_output;
}

View File

@ -18,6 +18,10 @@ Texture2DArray<float4> tex2dArray;
SamplerState _tex2dArray_sampler;
TextureCubeArray<float4> texCubeArray;
SamplerState _texCubeArray_sampler;
Texture2D<float4> separateTex2d;
SamplerState samplerNonDepth;
Texture2D<float4> separateTex2dDepth;
SamplerComparisonState samplerDepth;
static float texCoord1d;
static float2 texCoord2d;
@ -87,6 +91,8 @@ void frag_main()
texcolor += texCubeArray.Sample(_texCubeArray_sampler, texCoord4d);
texcolor += tex2d.Gather(_tex2d_sampler, texCoord2d, 0);
texcolor += tex2d.Load(int3(int2(1, 2), 0));
texcolor += separateTex2d.Sample(samplerNonDepth, texCoord2d);
texcolor.w += separateTex2dDepth.SampleCmp(samplerDepth, texCoord3d.xy, texCoord3d.z);
FragColor = texcolor;
}

View File

@ -0,0 +1,24 @@
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct main0_out
{
float4 FragColor [[color(0)]];
};
float4 samp(thread const texture2d<float> t, thread const sampler s)
{
return t.sample(s, float2(0.5));
}
fragment main0_out main0(texture2d<float> uDepth [[texture(0)]], sampler uSampler [[sampler(0)]])
{
main0_out out = {};
out.FragColor = samp(uDepth, uSampler);
return out;
}

View File

@ -0,0 +1,31 @@
#version 310 es
precision mediump float;
precision highp int;
uniform mediump sampler2DShadow SPIRV_Cross_CombineduDepthuSampler;
uniform mediump sampler2D SPIRV_Cross_CombineduDepthuSampler1;
layout(location = 0) out float FragColor;
float samp2(mediump sampler2DShadow SPIRV_Cross_Combinedts)
{
return texture(SPIRV_Cross_Combinedts, vec3(vec3(1.0).xy, vec3(1.0).z));
}
float samp3(mediump sampler2D SPIRV_Cross_Combinedts)
{
return texture(SPIRV_Cross_Combinedts, vec2(1.0)).x;
}
float samp(mediump sampler2DShadow SPIRV_Cross_Combinedts, mediump sampler2D SPIRV_Cross_Combinedts1)
{
float r0 = samp2(SPIRV_Cross_Combinedts);
float r1 = samp3(SPIRV_Cross_Combinedts1);
return r0 + r1;
}
void main()
{
FragColor = samp(SPIRV_Cross_CombineduDepthuSampler, SPIRV_Cross_CombineduDepthuSampler1);
}

View File

@ -0,0 +1,32 @@
#version 310 es
precision mediump float;
precision highp int;
layout(set = 0, binding = 2) uniform mediump texture2D uDepth;
layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler;
layout(set = 0, binding = 1) uniform mediump sampler uSampler1;
layout(location = 0) out float FragColor;
float samp2(mediump texture2D t, mediump samplerShadow s)
{
return texture(sampler2DShadow(t, s), vec3(vec3(1.0).xy, vec3(1.0).z));
}
float samp3(mediump texture2D t, mediump sampler s)
{
return texture(sampler2D(t, s), vec2(1.0)).x;
}
float samp(mediump texture2D t, mediump samplerShadow s, mediump sampler s1)
{
float r0 = samp2(t, s);
float r1 = samp3(t, s1);
return r0 + r1;
}
void main()
{
FragColor = samp(uDepth, uSampler, uSampler1);
}

View File

@ -0,0 +1,31 @@
#version 310 es
precision mediump float;
layout(set = 0, binding = 1) uniform mediump sampler2D uSampler;
layout(set = 0, binding = 1) uniform mediump sampler2DShadow uSamplerShadow;
layout(location = 0) out float FragColor;
vec4 samp2(sampler2D s)
{
return texture(s, vec2(1.0)) + texelFetch(s, ivec2(10), 0);
}
vec4 samp3(sampler2D s)
{
return samp2(s);
}
float samp4(mediump sampler2DShadow s)
{
return texture(s, vec3(1.0));
}
float samp(sampler2D s0, mediump sampler2DShadow s1)
{
return samp3(s0).x + samp4(s1);
}
void main()
{
FragColor = samp(uSampler, uSamplerShadow);
}

View File

@ -0,0 +1,29 @@
#version 310 es
precision mediump float;
layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler;
layout(set = 0, binding = 1) uniform mediump sampler uSampler1;
layout(set = 0, binding = 2) uniform texture2D uDepth;
layout(location = 0) out float FragColor;
float samp2(texture2D t, mediump samplerShadow s)
{
return texture(sampler2DShadow(t, s), vec3(1.0));
}
float samp3(texture2D t, mediump sampler s)
{
return texture(sampler2D(t, s), vec2(1.0)).x;
}
float samp(texture2D t, mediump samplerShadow s, mediump sampler s1)
{
float r0 = samp2(t, s);
float r1 = samp3(t, s1);
return r0 + r1;
}
void main()
{
FragColor = samp(uDepth, uSampler, uSampler1);
}

View File

@ -13,6 +13,11 @@ uniform sampler1DArray tex1dArray;
uniform sampler2DArray tex2dArray;
uniform samplerCubeArray texCubeArray;
uniform samplerShadow samplerDepth;
uniform sampler samplerNonDepth;
uniform texture2D separateTex2d;
uniform texture2D separateTex2dDepth;
in float texCoord1d;
in vec2 texCoord2d;
in vec3 texCoord3d;
@ -59,5 +64,8 @@ void main()
texcolor += texelFetch(tex2d, ivec2(1, 2), 0);
texcolor += texture(sampler2D(separateTex2d, samplerNonDepth), texCoord2d);
texcolor.a += texture(sampler2DShadow(separateTex2dDepth, samplerDepth), texCoord3d);
FragColor = texcolor;
}

View File

@ -0,0 +1,16 @@
#version 310 es
precision mediump float;
layout(set = 0, binding = 0) uniform mediump sampler uSampler;
layout(set = 0, binding = 1) uniform mediump texture2D uDepth;
layout(location = 0) out vec4 FragColor;
vec4 samp(texture2D t, mediump sampler s)
{
return texture(sampler2D(t, s), vec2(0.5));
}
void main()
{
FragColor = samp(uDepth, uSampler);
}

View File

@ -0,0 +1,29 @@
#version 310 es
precision mediump float;
layout(set = 0, binding = 0) uniform mediump samplerShadow uSampler;
layout(set = 0, binding = 1) uniform mediump sampler uSampler1;
layout(set = 0, binding = 2) uniform texture2D uDepth;
layout(location = 0) out float FragColor;
float samp2(texture2D t, mediump samplerShadow s)
{
return texture(sampler2DShadow(t, s), vec3(1.0));
}
float samp3(texture2D t, mediump sampler s)
{
return texture(sampler2D(t, s), vec2(1.0)).x;
}
float samp(texture2D t, mediump samplerShadow s, mediump sampler s1)
{
float r0 = samp2(t, s);
float r1 = samp3(t, s1);
return r0 + r1;
}
void main()
{
FragColor = samp(uDepth, uSampler, uSampler1);
}

View File

@ -186,6 +186,7 @@ enum Types
TypeExtension,
TypeExpression,
TypeConstantOp,
TypeCombinedImageSampler,
TypeUndef
};
@ -202,6 +203,25 @@ struct SPIRUndef : IVariant
uint32_t basetype;
};
// This type is only used by backends which need to access the combined image and sampler IDs separately after
// the OpSampledImage opcode.
struct SPIRCombinedImageSampler : IVariant
{
enum
{
type = TypeCombinedImageSampler
};
SPIRCombinedImageSampler(uint32_t type_, uint32_t image_, uint32_t sampler_)
: combined_type(type_)
, image(image_)
, sampler(sampler_)
{
}
uint32_t combined_type;
uint32_t image;
uint32_t sampler;
};
struct SPIRConstantOp : IVariant
{
enum
@ -555,6 +575,7 @@ struct SPIRFunction : IVariant
uint32_t sampler_id;
bool global_image;
bool global_sampler;
bool depth;
};
uint32_t return_type;

View File

@ -355,6 +355,9 @@ const SPIRType &Compiler::expression_type(uint32_t id) const
case TypeUndef:
return get<SPIRType>(get<SPIRUndef>(id).basetype);
case TypeCombinedImageSampler:
return get<SPIRType>(get<SPIRCombinedImageSampler>(id).combined_type);
default:
SPIRV_CROSS_THROW("Cannot resolve expression type.");
}
@ -2570,7 +2573,7 @@ bool Compiler::CombinedImageSamplerHandler::end_function_scope(const uint32_t *a
if (s)
sampler_id = s->self;
register_combined_image_sampler(caller, image_id, sampler_id);
register_combined_image_sampler(caller, image_id, sampler_id, param.depth);
}
}
@ -2578,13 +2581,13 @@ bool Compiler::CombinedImageSamplerHandler::end_function_scope(const uint32_t *a
}
void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIRFunction &caller, uint32_t image_id,
uint32_t sampler_id)
uint32_t sampler_id, bool depth)
{
// We now have a texture ID and a sampler ID which will either be found as a global
// or a parameter in our own function. If both are global, they will not need a parameter,
// otherwise, add it to our list.
SPIRFunction::CombinedImageSamplerParameter param = {
0u, image_id, sampler_id, true, true,
0u, image_id, sampler_id, true, true, depth,
};
auto texture_itr = find_if(begin(caller.arguments), end(caller.arguments),
@ -2628,6 +2631,7 @@ void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIR
type.basetype = SPIRType::SampledImage;
type.pointer = false;
type.storage = StorageClassGeneric;
type.image.depth = depth;
ptr_type = type;
ptr_type.pointer = true;
@ -2694,11 +2698,12 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1;
bool separate_sampler = type.basetype == SPIRType::Sampler;
if (separate_image)
SPIRV_CROSS_THROW(
"Attempting to use arrays of separate images. This is not possible to statically remap to plain GLSL.");
SPIRV_CROSS_THROW("Attempting to use arrays or structs of separate images. This is not possible to "
"statically remap to plain GLSL.");
if (separate_sampler)
SPIRV_CROSS_THROW("Attempting to use arrays of separate samplers. This is not possible to statically "
"remap to plain GLSL.");
SPIRV_CROSS_THROW(
"Attempting to use arrays or structs of separate samplers. This is not possible to statically "
"remap to plain GLSL.");
return true;
}
@ -2731,7 +2736,8 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
if (sampler)
sampler_id = sampler->self;
register_combined_image_sampler(callee, image_id, sampler_id);
auto &combined_type = compiler.get<SPIRType>(args[0]);
register_combined_image_sampler(callee, image_id, sampler_id, combined_type.image.depth);
}
}
@ -3348,3 +3354,73 @@ void Compiler::update_active_builtins()
ActiveBuiltinHandler handler(*this);
traverse_all_reachable_opcodes(get<SPIRFunction>(entry_point), handler);
}
void Compiler::analyze_sampler_comparison_states()
{
CombinedImageSamplerUsageHandler handler(*this);
traverse_all_reachable_opcodes(get<SPIRFunction>(entry_point), handler);
comparison_samplers = move(handler.comparison_samplers);
}
bool Compiler::CombinedImageSamplerUsageHandler::begin_function_scope(const uint32_t *args, uint32_t length)
{
if (length < 3)
return false;
auto &func = compiler.get<SPIRFunction>(args[2]);
const auto *arg = &args[3];
length -= 3;
for (uint32_t i = 0; i < length; i++)
{
auto &argument = func.arguments[i];
dependency_hierarchy[argument.id].insert(arg[i]);
}
return true;
}
void Compiler::CombinedImageSamplerUsageHandler::add_hierarchy_to_comparison_samplers(uint32_t sampler)
{
// Traverse the variable dependency hierarchy and tag everything in its path with comparison samplers.
comparison_samplers.insert(sampler);
for (auto &samp : dependency_hierarchy[sampler])
add_hierarchy_to_comparison_samplers(samp);
}
bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
{
switch (opcode)
{
case OpAccessChain:
case OpInBoundsAccessChain:
case OpLoad:
{
if (length < 3)
return false;
dependency_hierarchy[args[1]].insert(args[2]);
break;
}
case OpSampledImage:
{
if (length < 4)
return false;
uint32_t result_type = args[0];
auto &type = compiler.get<SPIRType>(result_type);
if (type.image.depth)
{
// This sampler must be a SamplerComparisionState, and not a regular SamplerState.
uint32_t sampler = args[3];
add_hierarchy_to_comparison_samplers(sampler);
}
return true;
}
default:
break;
}
return true;
}

View File

@ -582,7 +582,8 @@ protected:
uint32_t remap_parameter(uint32_t id);
void push_remap_parameters(const SPIRFunction &func, const uint32_t *args, uint32_t length);
void pop_remap_parameters();
void register_combined_image_sampler(SPIRFunction &caller, uint32_t texture_id, uint32_t sampler_id);
void register_combined_image_sampler(SPIRFunction &caller, uint32_t texture_id, uint32_t sampler_id,
bool depth);
};
struct ActiveBuiltinHandler : OpcodeHandler
@ -619,6 +620,29 @@ protected:
void analyze_parameter_preservation(
SPIRFunction &entry, const CFG &cfg,
const std::unordered_map<uint32_t, std::unordered_set<uint32_t>> &variable_to_blocks);
// If a variable ID or parameter ID is found in this set, a sampler is actually a shadow/comparison sampler.
// SPIR-V does not support this distinction, so we must keep track of this information outside the type system.
// There might be unrelated IDs found in this set which do not correspond to actual variables.
// This set should only be queried for the existence of samplers which are already known to be variables or parameter IDs.
std::unordered_set<uint32_t> comparison_samplers;
void analyze_sampler_comparison_states();
struct CombinedImageSamplerUsageHandler : OpcodeHandler
{
CombinedImageSamplerUsageHandler(Compiler &compiler_)
: compiler(compiler_)
{
}
bool begin_function_scope(const uint32_t *args, uint32_t length) override;
bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
Compiler &compiler;
std::unordered_map<uint32_t, std::unordered_set<uint32_t>> dependency_hierarchy;
std::unordered_set<uint32_t> comparison_samplers;
void add_hierarchy_to_comparison_samplers(uint32_t sampler);
};
};
}

View File

@ -311,6 +311,7 @@ string CompilerGLSL::compile()
find_static_extensions();
fixup_image_load_store_access();
update_active_builtins();
analyze_sampler_comparison_states();
uint32_t pass_count = 0;
do
@ -1837,6 +1838,14 @@ string CompilerGLSL::to_expression(uint32_t id)
}
}
case TypeCombinedImageSampler:
// This type should never be taken the expression of directly.
// The intention is that texture sampling functions will extract the image and samplers
// separately and take their expressions as needed.
// GLSL does not use this type because OpSampledImage immediately creates a combined image sampler
// expression ala sampler2D(texture, sampler).
SPIRV_CROSS_THROW("Combined image samplers have no default expression representation.");
default:
return to_name(id);
}
@ -5803,7 +5812,6 @@ string CompilerGLSL::to_qualifiers_glsl(uint32_t id)
string CompilerGLSL::argument_decl(const SPIRFunction::Parameter &arg)
{
// glslangValidator seems to make all arguments pointer no matter what which is rather bizarre ...
// Not sure if argument being pointer type should make the argument inout.
auto &type = expression_type(arg.id);
const char *direction = "";
@ -5815,14 +5823,37 @@ string CompilerGLSL::argument_decl(const SPIRFunction::Parameter &arg)
direction = "out ";
}
return join(direction, to_qualifiers_glsl(arg.id), variable_decl(type, to_name(arg.id)));
// We need some special consideration for samplers.
// The shadow qualifier for a sampler does not exist in SPIR-V, so the type might depend on which variable this is.
SPIRType fake_type;
const auto *tmp_type = &type;
if (type.basetype == SPIRType::Sampler)
{
tmp_type = &fake_type;
fake_type = type;
fake_type.image.depth = comparison_samplers.count(arg.id) != 0;
}
return join(direction, to_qualifiers_glsl(arg.id), variable_decl(*tmp_type, to_name(arg.id)));
}
string CompilerGLSL::variable_decl(const SPIRVariable &variable)
{
// Ignore the pointer type since GLSL doesn't have pointers.
auto &type = get<SPIRType>(variable.basetype);
auto res = join(to_qualifiers_glsl(variable.self), variable_decl(type, to_name(variable.self)));
// We need some special consideration for samplers.
// The shadow qualifier for a sampler does not exist in SPIR-V, so the type might depend on which variable this is.
SPIRType fake_type;
const auto *tmp_type = &type;
if (type.basetype == SPIRType::Sampler)
{
tmp_type = &fake_type;
fake_type = type;
fake_type.image.depth = comparison_samplers.count(variable.self) != 0;
}
auto res = join(to_qualifiers_glsl(variable.self), variable_decl(*tmp_type, to_name(variable.self)));
if (variable.loop_variable)
res += join(" = ", to_expression(variable.static_expression));
else if (variable.initializer)
@ -6002,7 +6033,11 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type)
return image_type_glsl(type);
case SPIRType::Sampler:
return "sampler";
// This is a hacky workaround. The sampler type in SPIR-V doesn't actually signal this distinction,
// but in higher level code we need it.
// The depth field is set by calling code based on the variable ID of the sampler, effectively reintroducing
// this distinction into the type system.
return type.image.depth ? "samplerShadow" : "sampler";
case SPIRType::Void:
return "void";

View File

@ -366,7 +366,7 @@ protected:
uint64_t combined_decoration_for_member(const SPIRType &type, uint32_t index);
std::string layout_for_variable(const SPIRVariable &variable);
std::string to_combined_image_sampler(uint32_t image_id, uint32_t samp_id);
bool skip_argument(uint32_t id) const;
virtual bool skip_argument(uint32_t id) const;
bool ssbo_is_std430_packing(const SPIRType &type);
uint32_t type_to_std430_base_size(const SPIRType &type);

View File

@ -43,7 +43,39 @@ static bool opcode_is_sign_invariant(Op opcode)
}
}
string CompilerHLSL::image_type_hlsl(const SPIRType &type)
string CompilerHLSL::image_type_hlsl_modern(const SPIRType &type)
{
auto &imagetype = get<SPIRType>(type.image.type);
const char *dim = nullptr;
switch (type.image.dim)
{
case Dim1D:
dim = "1D";
break;
case Dim2D:
dim = "2D";
break;
case Dim3D:
dim = "3D";
break;
case DimCube:
dim = "Cube";
break;
case DimRect:
SPIRV_CROSS_THROW("Rectangle texture support is not yet implemented for HLSL"); // TODO
case DimBuffer:
// Buffer/RWBuffer.
SPIRV_CROSS_THROW("Buffer/RWBuffer support is not yet implemented for HLSL"); // TODO
case DimSubpassData:
// This should be implemented same way as desktop GL. Fetch on a 2D texture based on int2(SV_Position).
SPIRV_CROSS_THROW("Subpass data support is not yet implemented for HLSL"); // TODO
}
uint32_t components = 4;
const char *arrayed = type.image.arrayed ? "Array" : "";
return join("Texture", dim, arrayed, "<", type_to_glsl(imagetype), components, ">");
}
string CompilerHLSL::image_type_hlsl_legacy(const SPIRType &type)
{
auto &imagetype = get<SPIRType>(type.image.type);
string res;
@ -105,17 +137,21 @@ string CompilerHLSL::image_type_hlsl(const SPIRType &type)
if (type.image.ms)
res += "MS";
if (type.image.arrayed)
{
if (is_legacy_desktop())
require_extension("GL_EXT_texture_array");
res += "Array";
}
if (type.image.depth)
res += "Shadow";
return res;
}
string CompilerHLSL::image_type_hlsl(const SPIRType &type)
{
if (options.shader_model <= 30)
return image_type_hlsl_legacy(type);
else
return image_type_hlsl_modern(type);
}
string CompilerHLSL::type_to_glsl(const SPIRType &type)
{
// Ignore the pointer type since GLSL doesn't have pointers.
@ -134,7 +170,7 @@ string CompilerHLSL::type_to_glsl(const SPIRType &type)
return image_type_hlsl(type);
case SPIRType::Sampler:
return "sampler";
return type.image.depth ? "SamplerComparisonState" : "SamplerState";
case SPIRType::Void:
return "void";
@ -856,6 +892,39 @@ void CompilerHLSL::emit_push_constant_block(const SPIRVariable &)
statement("constant block");
}
string CompilerHLSL::to_sampler_expression(uint32_t id)
{
return join("_", to_expression(id), "_sampler");
}
void CompilerHLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id)
{
set<SPIRCombinedImageSampler>(result_id, result_type, image_id, samp_id);
}
string CompilerHLSL::to_func_call_arg(uint32_t id)
{
string arg_str = CompilerGLSL::to_func_call_arg(id);
if (options.shader_model <= 30)
return arg_str;
// Manufacture automatic sampler arg if the arg is a SampledImage texture and we're in modern HLSL.
auto *var = maybe_get<SPIRVariable>(id);
if (var)
{
auto &type = get<SPIRType>(var->basetype);
// We don't have to consider combined image samplers here via OpSampledImage because
// those variables cannot be passed as arguments to functions.
// Only global SampledImage variables may be used as arguments.
if (type.basetype == SPIRType::SampledImage)
arg_str += ", " + to_sampler_expression(id);
}
return arg_str;
}
void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_flags)
{
auto &execution = get_entry_point();
@ -894,6 +963,18 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, uint64_t return_f
add_local_variable_name(arg.id);
decl += argument_decl(arg);
// Flatten a combined sampler to two separate arguments in modern HLSL.
auto &arg_type = get<SPIRType>(arg.type);
if (options.shader_model > 30 && arg_type.basetype == SPIRType::SampledImage)
{
// Manufacture automatic sampler arg for SampledImage texture
decl += ", ";
if (arg_type.basetype == SPIRType::SampledImage)
decl += join(arg_type.image.depth ? "SamplerComparisonState " : "SamplerState ",
to_sampler_expression(arg.id));
}
if (&arg != &func.arguments.back())
decl += ", ";
@ -1127,6 +1208,8 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
bool gather = false;
bool proj = false;
const uint32_t *opt = nullptr;
auto *combined_image = maybe_get<SPIRCombinedImageSampler>(img);
auto img_expr = to_expression(combined_image ? combined_image->image : img);
switch (op)
{
@ -1244,7 +1327,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
{
SPIRV_CROSS_THROW("texelFetch is not supported in HLSL shader model 2/3.");
}
texop += to_expression(img);
texop += img_expr;
texop += ".Load";
}
else
@ -1257,7 +1340,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
if (options.shader_model >= 40)
{
texop += to_expression(img);
texop += img_expr;
if (imgtype.image.depth)
texop += ".SampleCmp";
@ -1311,21 +1394,17 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
expr += texop;
expr += "(";
if (op != OpImageFetch)
if (op != OpImageFetch && (options.shader_model >= 40))
{
if (options.shader_model >= 40)
{
expr += "_";
}
expr += to_expression(img);
if (options.shader_model >= 40)
{
expr += "_sampler";
}
string sampler_expr;
if (combined_image)
sampler_expr = to_expression(combined_image->sampler);
else
sampler_expr = to_sampler_expression(img);
expr += sampler_expr;
}
bool swizz_func = backend.swizzle_is_function;
auto swizzle = [swizz_func](uint32_t comps, uint32_t in_comps) -> const char * {
auto swizzle = [](uint32_t comps, uint32_t in_comps) -> const char * {
if (comps == in_comps)
return "";
@ -1334,9 +1413,9 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
case 1:
return ".x";
case 2:
return swizz_func ? ".xy()" : ".xy";
return ".xy";
case 3:
return swizz_func ? ".xyz()" : ".xyz";
return ".xyz";
default:
return "";
}
@ -1382,9 +1461,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
if (op == OpImageFetch)
{
auto &coordtype = expression_type(coord);
stringstream str;
str << coordtype.vecsize + 1;
coord_expr = "int" + str.str() + "(" + coord_expr + ", " + to_expression(lod) + ")";
coord_expr = join("int", coordtype.vecsize + 1, "(", coord_expr, ", ", to_expression(lod), ")");
}
if (op != OpImageFetch)
@ -1455,46 +1532,62 @@ void CompilerHLSL::emit_texture_op(const Instruction &i)
emit_op(result_type, id, expr, forward, false);
}
void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
switch (type.basetype)
{
case SPIRType::SampledImage:
case SPIRType::Image:
{
statement(image_type_hlsl_modern(type), " ", to_name(var.self), ";");
if (type.basetype == SPIRType::SampledImage)
{
// For combined image samplers, also emit a combined image sampler.
if (type.image.depth)
statement("SamplerComparisonState ", to_sampler_expression(var.self), ";");
else
statement("SamplerState ", to_sampler_expression(var.self), ";");
}
break;
}
case SPIRType::Sampler:
if (comparison_samplers.count(var.self))
statement("SamplerComparisonState ", to_name(var.self), ";");
else
statement("SamplerState ", to_name(var.self), ";");
break;
default:
statement(variable_decl(var), ";");
break;
}
}
void CompilerHLSL::emit_legacy_uniform(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
switch (type.basetype)
{
case SPIRType::Sampler:
case SPIRType::Image:
SPIRV_CROSS_THROW("Separate image and samplers not supported in legacy HLSL.");
default:
statement(variable_decl(var), ";");
break;
}
}
void CompilerHLSL::emit_uniform(const SPIRVariable &var)
{
add_resource_name(var.self);
auto &type = get<SPIRType>(var.basetype);
if (options.shader_model >= 40 && type.basetype == SPIRType::SampledImage)
{
auto &imagetype = get<SPIRType>(type.image.type);
string dim;
switch (type.image.dim)
{
case Dim1D:
dim = "1D";
break;
case Dim2D:
dim = "2D";
break;
case Dim3D:
dim = "3D";
break;
case DimCube:
dim = "Cube";
break;
case DimRect:
case DimBuffer:
case DimSubpassData:
SPIRV_CROSS_THROW("Buffer texture support is not yet implemented for HLSL"); // TODO
}
string arrayed = type.image.arrayed ? "Array" : "";
statement("Texture", dim, arrayed, "<", type_to_glsl(imagetype), "4> ", to_name(var.self), ";");
if (type.image.depth)
statement("SamplerComparisonState _", to_name(var.self), "_sampler;");
else
statement("SamplerState _", to_name(var.self), "_sampler;");
}
if (options.shader_model >= 40)
emit_modern_uniform(var);
else
{
statement(variable_decl(var), ";");
}
// TODO: Separate samplers/images
emit_legacy_uniform(var);
}
string CompilerHLSL::bitcast_glsl_op(const SPIRType &out_type, const SPIRType &in_type)
@ -1845,6 +1938,7 @@ string CompilerHLSL::compile()
backend.boolean_mix_support = false;
update_active_builtins();
analyze_sampler_comparison_states();
uint32_t pass_count = 0;
do

View File

@ -61,6 +61,8 @@ public:
private:
std::string type_to_glsl(const SPIRType &type) override;
std::string image_type_hlsl(const SPIRType &type);
std::string image_type_hlsl_modern(const SPIRType &type);
std::string image_type_hlsl_legacy(const SPIRType &type);
void emit_function_prototype(SPIRFunction &func, uint64_t return_flags) override;
void emit_hlsl_entry_point();
void emit_header() override;
@ -76,9 +78,14 @@ private:
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);
std::string layout_for_member(const SPIRType &type, uint32_t index) override;
std::string to_interpolation_qualifiers(uint64_t flags) override;
std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type) override;
std::string to_func_call_arg(uint32_t id) override;
std::string to_sampler_expression(uint32_t id);
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;

View File

@ -2198,6 +2198,11 @@ size_t CompilerMSL::get_declared_type_alignment(uint32_t type_id, uint64_t dec_m
}
}
bool CompilerMSL::skip_argument(uint32_t) const
{
return false;
}
bool CompilerMSL::OpCodePreprocessor::handle(Op opcode, const uint32_t * /*args*/, uint32_t /*length*/)
{
switch (opcode)

View File

@ -196,6 +196,7 @@ protected:
void align_struct(SPIRType &ib_type);
bool is_member_packable(SPIRType &ib_type, uint32_t index);
MSLStructMemberKey get_struct_member_key(uint32_t type_id, uint32_t index);
bool skip_argument(uint32_t id) const override;
Options options;
std::unordered_map<std::string, std::string> func_name_overrides;