Merged with upstream

This commit is contained in:
Bill Hollings 2016-07-24 22:40:20 -07:00
commit 7c255dde4d
28 changed files with 639 additions and 91 deletions

View File

@ -196,10 +196,14 @@ static void print_resources(const Compiler &compiler, const char *tag, const vec
bool is_push_constant = compiler.get_storage_class(res.id) == StorageClassPushConstant;
bool is_block = (compiler.get_decoration_mask(type.self) &
((1ull << DecorationBlock) | (1ull << DecorationBufferBlock))) != 0;
uint32_t fallback_id = !is_push_constant && is_block ? res.type_id : res.id;
uint32_t fallback_id = !is_push_constant && is_block ? res.base_type_id : res.id;
fprintf(stderr, " ID %03u : %s", res.id,
!res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str());
string array;
for (auto arr : type.array)
array = join("[", arr ? convert_to_string(arr) : "", "]") + array;
fprintf(stderr, " ID %03u : %s%s", res.id,
!res.name.empty() ? res.name.c_str() : compiler.get_fallback_name(fallback_id).c_str(), array.c_str());
if (mask & (1ull << DecorationLocation))
fprintf(stderr, " (Location : %u)", compiler.get_decoration(res.id, DecorationLocation));
@ -304,7 +308,7 @@ static void print_push_constant_resources(const Compiler &compiler, const vector
fprintf(stderr, "==================\n\n");
for (auto &range : ranges)
{
const auto &name = compiler.get_member_name(block.type_id, range.index);
const auto &name = compiler.get_member_name(block.base_type_id, range.index);
fprintf(stderr, "Member #%3u (%s): Offset: %4u, Range: %4u\n", range.index,
!name.empty() ? name.c_str() : compiler.get_fallback_member_name(range.index).c_str(),

View File

@ -1,13 +1,13 @@
#version 310 es
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(binding = 0, std430) buffer _3
layout(binding = 0, std430) restrict buffer _3
{
ivec4 _0;
uvec4 _1;
} _5;
layout(binding = 1, std430) buffer _4
layout(binding = 1, std430) restrict buffer _4
{
uvec4 _0;
ivec4 _1;

View File

@ -0,0 +1,15 @@
#version 450
in float v0;
in float v1;
out float FragColor;
void main()
{
float a = v0;
float b = v1;
float _17 = a;
a = v1;
FragColor = ((_17 + b) * b);
}

View File

@ -28,6 +28,7 @@ uvec2 workaround_mix(uvec2 a, uvec2 b, bvec2 sel)
{
_137 = a.x;
}
uint _147 = _137;
if (sel.y)
{
_148 = b.y;
@ -36,7 +37,7 @@ uvec2 workaround_mix(uvec2 a, uvec2 b, bvec2 sel)
{
_148 = a.y;
}
return uvec2(_137, _148);
return uvec2(_147, _148);
}
vec2 alias(vec2 i, vec2 N)

View File

@ -21,7 +21,8 @@ void main()
uint j;
for (;;)
{
int _40 = k + 1;
int _39 = k;
int _40 = _39 + 1;
k = _40;
if ((_40 < 10))
{

View File

@ -0,0 +1,47 @@
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(binding = 0, rgba32f) uniform readonly writeonly image2D uImg00;
layout(binding = 1, rgba16f) uniform readonly writeonly image2D uImg01;
layout(binding = 2, rg32f) uniform readonly writeonly image2D uImg02;
layout(binding = 3, rg16f) uniform readonly writeonly image2D uImg03;
layout(binding = 4, r11f_g11f_b10f) uniform readonly writeonly image2D uImg04;
layout(binding = 5, r32f) uniform readonly writeonly image2D uImg05;
layout(binding = 6, r16f) uniform readonly writeonly image2D uImg06;
layout(binding = 7, rgba16) uniform readonly writeonly image2D uImg07;
layout(binding = 8, rgb10_a2) uniform readonly writeonly image2D uImg08;
layout(binding = 9, rgba8) uniform readonly writeonly image2D uImg09;
layout(binding = 10, rg16) uniform readonly writeonly image2D uImg10;
layout(binding = 11, rg8) uniform readonly writeonly image2D uImg11;
layout(binding = 12, r16) uniform readonly writeonly image2D uImg12;
layout(binding = 13, r8) uniform readonly writeonly image2D uImg13;
layout(binding = 14, rgba16_snorm) uniform readonly writeonly image2D uImg14;
layout(binding = 15, rgba8_snorm) uniform readonly writeonly image2D uImg15;
layout(binding = 16, rg16_snorm) uniform readonly writeonly image2D uImg16;
layout(binding = 17, rg8_snorm) uniform readonly writeonly image2D uImg17;
layout(binding = 18, r16_snorm) uniform readonly writeonly image2D uImg18;
layout(binding = 19, r8_snorm) uniform readonly writeonly image2D uImg19;
layout(binding = 20, rgba32i) uniform readonly writeonly iimage2D uImage20;
layout(binding = 21, rgba16i) uniform readonly writeonly iimage2D uImage21;
layout(binding = 22, rgba8i) uniform readonly writeonly iimage2D uImage22;
layout(binding = 23, rg32i) uniform readonly writeonly iimage2D uImage23;
layout(binding = 24, rg16i) uniform readonly writeonly iimage2D uImage24;
layout(binding = 25, rg8i) uniform readonly writeonly iimage2D uImage25;
layout(binding = 26, r32i) uniform readonly writeonly iimage2D uImage26;
layout(binding = 27, r16i) uniform readonly writeonly iimage2D uImage27;
layout(binding = 28, r8i) uniform readonly writeonly iimage2D uImage28;
layout(binding = 29, rgba32ui) uniform readonly writeonly uimage2D uImage29;
layout(binding = 30, rgba16ui) uniform readonly writeonly uimage2D uImage30;
layout(binding = 31, rgb10_a2ui) uniform readonly writeonly uimage2D uImage31;
layout(binding = 32, rgba8ui) uniform readonly writeonly uimage2D uImage32;
layout(binding = 33, rg32ui) uniform readonly writeonly uimage2D uImage33;
layout(binding = 34, rg16ui) uniform readonly writeonly uimage2D uImage34;
layout(binding = 35, rg8ui) uniform readonly writeonly uimage2D uImage35;
layout(binding = 36, r32ui) uniform readonly writeonly uimage2D uImage36;
layout(binding = 37, r16ui) uniform readonly writeonly uimage2D uImage37;
layout(binding = 38, r8ui) uniform readonly writeonly uimage2D uImage38;
void main()
{
}

View File

@ -0,0 +1,13 @@
#version 450
layout(binding = 0, rgba8) uniform image2DMS uImage;
layout(binding = 1, rgba8) uniform image2DMSArray uImageArray;
void main()
{
vec4 a = imageLoad(uImage, ivec2(1, 2), 2);
vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4), 3);
imageStore(uImage, ivec2(2, 3), 1, a);
imageStore(uImageArray, ivec3(2, 3, 7), 1, b);
}

View File

@ -0,0 +1,11 @@
#version 450
layout(binding = 0) uniform sampler2D uSampler;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vec4(float(textureQueryLevels(uSampler)));
}

View File

@ -0,0 +1,12 @@
#version 450
layout(binding = 0) uniform sampler2D uSampler;
layout(location = 0) out vec4 FragColor;
layout(location = 0) in vec2 vTexCoord;
void main()
{
FragColor = textureQueryLod(uSampler, vTexCoord).xyxy;
}

View File

@ -0,0 +1,14 @@
#version 450
layout(binding = 0) uniform sampler2DMS uSampler;
layout(binding = 1) uniform sampler2DMSArray uSamplerArray;
layout(binding = 2, rgba8) uniform readonly writeonly image2DMS uImage;
layout(binding = 3, rgba8) uniform readonly writeonly image2DMSArray uImageArray;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vec4(float((((textureSamples(uSampler) + textureSamples(uSamplerArray)) + imageSamples(uImage)) + imageSamples(uImageArray))));
}

View File

@ -26,7 +26,8 @@ bool frustum_cull(vec2 p0)
float radius = (0.5 * length((bb_max - bb_min)));
vec3 f0 = vec3(dot(_41.uFrustum[0], vec4(center, 1.0)), dot(_41.uFrustum[1], vec4(center, 1.0)), dot(_41.uFrustum[2], vec4(center, 1.0)));
vec3 f1 = vec3(dot(_41.uFrustum[3], vec4(center, 1.0)), dot(_41.uFrustum[4], vec4(center, 1.0)), dot(_41.uFrustum[5], vec4(center, 1.0)));
bool _205 = any(lessThanEqual(f0, vec3((-radius))));
vec3 _199 = f0;
bool _205 = any(lessThanEqual(_199, vec3((-radius))));
bool _215;
if ((!_205))
{

View File

@ -69,6 +69,7 @@ vec2 warp_position()
{
_332 = 0u;
}
uint _342 = _332;
if ((uPosition.y < 32u))
{
_343 = mask.y;
@ -77,7 +78,7 @@ vec2 warp_position()
{
_343 = 0u;
}
rounding = uvec2(_332, _343);
rounding = uvec2(_342, _343);
lower_upper_snapped = vec4(((uPosition + rounding).xyxy & (~mask).xxyy));
return mix(lower_upper_snapped.xy, lower_upper_snapped.zw, vec2(fract_lod));
}

View File

@ -0,0 +1,14 @@
#version 310 es
precision mediump float;
precision highp int;
layout(binding = 0) uniform mediump sampler2DMS uSubpass0;
layout(binding = 1) uniform mediump sampler2DMS uSubpass1;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = (texelFetch(uSubpass0, ivec2(gl_FragCoord.xy), 1) + texelFetch(uSubpass1, ivec2(gl_FragCoord.xy), 2));
}

View File

@ -0,0 +1,14 @@
#version 310 es
precision mediump float;
precision highp int;
layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInputMS uSubpass0;
layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInputMS uSubpass1;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = (subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2));
}

View File

@ -18,9 +18,11 @@
OpDecorate %input_struct BufferBlock
OpDecorate %inputs DescriptorSet 0
OpDecorate %inputs Binding 0
OpDecorate %inputs Restrict
OpDecorate %output_struct BufferBlock
OpDecorate %outputs DescriptorSet 0
OpDecorate %outputs Binding 1
OpDecorate %outputs Restrict
%void = OpTypeVoid
%main_func = OpTypeFunction %void

View File

@ -0,0 +1,43 @@
; SPIR-V
; Version: 1.0
; Generator: Khronos Glslang Reference Front End; 1
; Bound: 28
; Schema: 0
OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %4 "main" %v0 %v1 %FragColor
OpExecutionMode %4 OriginUpperLeft
OpSource GLSL 450
OpName %4 "main"
OpName %a "a"
OpName %v0 "v0"
OpName %b "b"
OpName %v1 "v1"
OpName %FragColor "FragColor"
%2 = OpTypeVoid
%3 = OpTypeFunction %2
%float = OpTypeFloat 32
%pfloat = OpTypePointer Function %float
%9 = OpTypePointer Input %float
%v0 = OpVariable %9 Input
%v1 = OpVariable %9 Input
%25 = OpTypePointer Output %float
%FragColor = OpVariable %25 Output
%4 = OpFunction %2 None %3
%5 = OpLabel
%a = OpVariable %pfloat Function
%b = OpVariable %pfloat Function
%v0_tmp = OpLoad %float %v0
%v1_tmp = OpLoad %float %v1
OpStore %a %v0_tmp
OpStore %b %v1_tmp
%a_tmp = OpLoad %float %a
%b_tmp = OpLoad %float %b
%res = OpFAdd %float %a_tmp %b_tmp
%res1 = OpFMul %float %res %b_tmp
OpStore %a %v1_tmp
OpStore %FragColor %res1
OpReturn
OpFunctionEnd

View File

@ -0,0 +1,48 @@
#version 450
layout(local_size_x = 1) in;
layout(rgba32f, binding = 0) uniform image2D uImg00;
layout(rgba16f, binding = 1) uniform image2D uImg01;
layout(rg32f, binding = 2) uniform image2D uImg02;
layout(rg16f, binding = 3) uniform image2D uImg03;
layout(r11f_g11f_b10f, binding = 4) uniform image2D uImg04;
layout(r32f, binding = 5) uniform image2D uImg05;
layout(r16f, binding = 6) uniform image2D uImg06;
layout(rgba16, binding = 7) uniform image2D uImg07;
layout(rgb10_a2, binding = 8) uniform image2D uImg08;
layout(rgba8, binding = 9) uniform image2D uImg09;
layout(rg16, binding = 10) uniform image2D uImg10;
layout(rg8, binding = 11) uniform image2D uImg11;
layout(r16, binding = 12) uniform image2D uImg12;
layout(r8, binding = 13) uniform image2D uImg13;
layout(rgba16_snorm, binding = 14) uniform image2D uImg14;
layout(rgba8_snorm, binding = 15) uniform image2D uImg15;
layout(rg16_snorm, binding = 16) uniform image2D uImg16;
layout(rg8_snorm, binding = 17) uniform image2D uImg17;
layout(r16_snorm, binding = 18) uniform image2D uImg18;
layout(r8_snorm, binding = 19) uniform image2D uImg19;
layout(rgba32i, binding = 20) uniform iimage2D uImage20;
layout(rgba16i, binding = 21) uniform iimage2D uImage21;
layout(rgba8i, binding = 22) uniform iimage2D uImage22;
layout(rg32i, binding = 23) uniform iimage2D uImage23;
layout(rg16i, binding = 24) uniform iimage2D uImage24;
layout(rg8i, binding = 25) uniform iimage2D uImage25;
layout(r32i, binding = 26) uniform iimage2D uImage26;
layout(r16i, binding = 27) uniform iimage2D uImage27;
layout(r8i, binding = 28) uniform iimage2D uImage28;
layout(rgba32ui, binding = 29) uniform uimage2D uImage29;
layout(rgba16ui, binding = 30) uniform uimage2D uImage30;
layout(rgb10_a2ui, binding = 31) uniform uimage2D uImage31;
layout(rgba8ui, binding = 32) uniform uimage2D uImage32;
layout(rg32ui, binding = 33) uniform uimage2D uImage33;
layout(rg16ui, binding = 34) uniform uimage2D uImage34;
layout(rg8ui, binding = 35) uniform uimage2D uImage35;
layout(r32ui, binding = 36) uniform uimage2D uImage36;
layout(r16ui, binding = 37) uniform uimage2D uImage37;
layout(r8ui, binding = 38) uniform uimage2D uImage38;
void main()
{
}

View File

@ -0,0 +1,12 @@
#version 450
layout(rgba8, binding = 0) uniform image2DMS uImage;
layout(rgba8, binding = 1) uniform image2DMSArray uImageArray;
void main()
{
vec4 a = imageLoad(uImage, ivec2(1, 2), 2);
vec4 b = imageLoad(uImageArray, ivec3(1, 2, 4), 3);
imageStore(uImage, ivec2(2, 3), 1, a);
imageStore(uImageArray, ivec3(2, 3, 7), 1, b);
}

View File

@ -0,0 +1,9 @@
#version 450
layout(binding = 0) uniform sampler2D uSampler;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = vec4(textureQueryLevels(uSampler));
}

View File

@ -0,0 +1,10 @@
#version 450
layout(location = 0) in vec2 vTexCoord;
layout(binding = 0) uniform sampler2D uSampler;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = textureQueryLod(uSampler, vTexCoord).xyxy;
}

View File

@ -0,0 +1,17 @@
#version 450
layout(location = 0) out vec4 FragColor;
layout(binding = 0) uniform sampler2DMS uSampler;
layout(binding = 1) uniform sampler2DMSArray uSamplerArray;
layout(rgba8, binding = 2) uniform image2DMS uImage;
layout(rgba8, binding = 3) uniform image2DMSArray uImageArray;
void main()
{
FragColor =
vec4(
textureSamples(uSampler) +
textureSamples(uSamplerArray) +
imageSamples(uImage) +
imageSamples(uImageArray));
}

View File

@ -0,0 +1,11 @@
#version 310 es
precision mediump float;
layout(input_attachment_index = 0, set = 0, binding = 0) uniform mediump subpassInputMS uSubpass0;
layout(input_attachment_index = 1, set = 0, binding = 1) uniform mediump subpassInputMS uSubpass1;
layout(location = 0) out vec4 FragColor;
void main()
{
FragColor = subpassLoad(uSubpass0, 1) + subpassLoad(uSubpass1, 2);
}

View File

@ -258,13 +258,15 @@ struct SPIRExpression : IVariant
// If this expression will never change, we can avoid lots of temporaries
// in high level source.
// An expression being immutable can be speculative,
// it is assumed that this is true almost always.
bool immutable = false;
// If this expression has been used while invalidated.
bool used_while_invalidated = false;
// A list of a variables for which this expression was invalidated by.
std::vector<uint32_t> invalidated_by;
// A list of expressions which this expression depends on.
std::vector<uint32_t> expression_dependencies;
};
struct SPIRFunctionPrototype : IVariant

View File

@ -30,6 +30,10 @@ Instruction::Instruction(const vector<uint32_t> &spirv, uint32_t &index)
{
op = spirv[index] & 0xffff;
count = (spirv[index] >> 16) & 0xffff;
if (count == 0)
throw CompilerError("SPIR-V instructions cannot consume 0 words. Invalid SPIR-V file.");
offset = index + 1;
length = count - 1;
@ -56,7 +60,8 @@ bool Compiler::variable_storage_is_aliased(const SPIRVariable &v)
bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0;
bool image = type.basetype == SPIRType::Image;
bool counter = type.basetype == SPIRType::AtomicCounter;
return ssbo || image || counter;
bool is_restrict = (meta[v.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0;
return !is_restrict && (ssbo || image || counter);
}
bool Compiler::block_is_pure(const SPIRBlock &block)
@ -270,10 +275,7 @@ void Compiler::register_write(uint32_t chain)
void Compiler::flush_dependees(SPIRVariable &var)
{
for (auto expr : var.dependees)
{
invalid_expressions.insert(expr);
get<SPIRExpression>(expr).invalidated_by.push_back(var.self);
}
var.dependees.clear();
}
@ -348,7 +350,7 @@ bool Compiler::is_immutable(uint32_t id) const
// Anything we load from the UniformConstant address space is guaranteed to be immutable.
bool pointer_to_const = var.storage == StorageClassUniformConstant;
return pointer_to_const || var.phi_variable || var.forwardable || !expression_is_lvalue(id);
return pointer_to_const || var.phi_variable || !expression_is_lvalue(id);
}
else if (ids[id].get_type() == TypeExpression)
return get<SPIRExpression>(id).immutable;
@ -420,56 +422,56 @@ ShaderResources Compiler::get_shader_resources() const
if (var.storage == StorageClassInput)
{
if (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock))
res.stage_inputs.push_back({ var.self, type.self, meta[type.self].decoration.alias });
res.stage_inputs.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias });
else
res.stage_inputs.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.stage_inputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
// Subpass inputs
else if (var.storage == StorageClassUniformConstant && type.image.dim == DimSubpassData)
{
res.subpass_inputs.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.subpass_inputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
// Outputs
else if (var.storage == StorageClassOutput)
{
if (meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock))
res.stage_outputs.push_back({ var.self, type.self, meta[type.self].decoration.alias });
res.stage_outputs.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias });
else
res.stage_outputs.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.stage_outputs.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
// UBOs
else if (type.storage == StorageClassUniform &&
(meta[type.self].decoration.decoration_flags & (1ull << DecorationBlock)))
{
res.uniform_buffers.push_back({ var.self, type.self, meta[type.self].decoration.alias });
res.uniform_buffers.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias });
}
// SSBOs
else if (type.storage == StorageClassUniform &&
(meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)))
{
res.storage_buffers.push_back({ var.self, type.self, meta[type.self].decoration.alias });
res.storage_buffers.push_back({ var.self, var.basetype, type.self, meta[type.self].decoration.alias });
}
// Push constant blocks
else if (type.storage == StorageClassPushConstant)
{
// There can only be one push constant block, but keep the vector in case this restriction is lifted
// in the future.
res.push_constant_buffers.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.push_constant_buffers.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
// Images
else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::Image)
{
res.storage_images.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.storage_images.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
// Textures
else if (type.storage == StorageClassUniformConstant && type.basetype == SPIRType::SampledImage)
{
res.sampled_images.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.sampled_images.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
// Atomic counters
else if (type.storage == StorageClassAtomicCounter)
{
res.atomic_counters.push_back({ var.self, type.self, meta[var.self].decoration.alias });
res.atomic_counters.push_back({ var.self, var.basetype, type.self, meta[var.self].decoration.alias });
}
}
@ -2026,3 +2028,21 @@ uint32_t Compiler::get_subpass_input_remapped_components(uint32_t id) const
{
return get<SPIRVariable>(id).remapped_components;
}
void Compiler::inherit_expression_dependencies(uint32_t dst, uint32_t source_expression)
{
auto &e = get<SPIRExpression>(dst);
auto *s = maybe_get<SPIRExpression>(source_expression);
if (!s)
return;
auto &e_deps = e.expression_dependencies;
auto &s_deps = s->expression_dependencies;
// If we depend on a expression, we also depend on all sub-dependencies from source.
e_deps.push_back(source_expression);
e_deps.insert(end(e_deps), begin(s_deps), end(s_deps));
// Eliminate duplicated dependencies.
e_deps.erase(unique(begin(e_deps), end(e_deps)), end(e_deps));
}

View File

@ -36,15 +36,23 @@ struct Resource
// This is the ID of the OpVariable.
uint32_t id;
// The type of the declared resource.
// The type ID of the variable which includes arrays and all type modifications.
// This type ID is not suitable for parsing OpMemberDecoration of a struct and other decorations in general
// since these modifications typically happen on the base_type_id.
uint32_t type_id;
// The base type of the declared resource.
// This type is the base type which ignores pointers and arrays of the type_id.
// This is mostly useful to parse decorations of the underlying type.
// base_type_id can also be obtained with get_type(get_type(type_id).self).
uint32_t base_type_id;
// The declared name (OpName) of the resource.
// For Buffer blocks, the name actually reflects the externally
// visible Block name.
//
// This name can be retrieved again by using either
// get_name(id) or get_name(type_id) depending if it's a buffer block or not.
// get_name(id) or get_name(base_type_id) depending if it's a buffer block or not.
//
// This name can be an empty string in which case get_fallback_name(id) can be
// used which obtains a suitable fallback identifier for an ID.
@ -111,7 +119,7 @@ public:
void unset_decoration(uint32_t id, spv::Decoration decoration);
// Gets the SPIR-V associated with ID.
// Mostly used with Resource::type_id to parse the underlying type of a resource.
// Mostly used with Resource::type_id and Resource::base_type_id to parse the underlying type of a resource.
const SPIRType &get_type(uint32_t id) const;
// Gets the underlying storage class for an OpVariable.
@ -352,6 +360,7 @@ protected:
uint32_t increase_bound_by(uint32_t incr_amount);
bool types_are_logically_equivalent(const SPIRType &a, const SPIRType &b) const;
void inherit_expression_dependencies(uint32_t dst, uint32_t source);
private:
void parse();

View File

@ -460,7 +460,11 @@ string CompilerGLSL::layout_for_member(const SPIRType &type, uint32_t index)
const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format)
{
// Only handle GLES 3.1 compliant types for now ...
auto check_desktop = [this] {
if (options.es)
throw CompilerError("Attempting to use image format not supported in ES profile.");
};
switch (format)
{
case ImageFormatRgba32f:
@ -504,10 +508,73 @@ const char *CompilerGLSL::format_to_glsl(spv::ImageFormat format)
case ImageFormatRg16ui:
return "rg16ui";
// Desktop-only formats
case ImageFormatR11fG11fB10f:
check_desktop();
return "r11f_g11f_b10f";
case ImageFormatR16f:
check_desktop();
return "r16f";
case ImageFormatRgb10A2:
check_desktop();
return "rgb10_a2";
case ImageFormatR8:
check_desktop();
return "r8";
case ImageFormatRg8:
check_desktop();
return "rg8";
case ImageFormatR16:
check_desktop();
return "r16";
case ImageFormatRg16:
check_desktop();
return "rg16";
case ImageFormatRgba16:
check_desktop();
return "rgba16";
case ImageFormatR16Snorm:
check_desktop();
return "r16_snorm";
case ImageFormatRg16Snorm:
check_desktop();
return "rg16_snorm";
case ImageFormatRgba16Snorm:
check_desktop();
return "rgba16_snorm";
case ImageFormatR8Snorm:
check_desktop();
return "r8_snorm";
case ImageFormatRg8Snorm:
check_desktop();
return "rg8_snorm";
case ImageFormatR8ui:
check_desktop();
return "r8ui";
case ImageFormatRg8ui:
check_desktop();
return "rg8ui";
case ImageFormatR16ui:
check_desktop();
return "r16ui";
case ImageFormatRgb10a2ui:
check_desktop();
return "rgb10_a2ui";
case ImageFormatR8i:
check_desktop();
return "r8i";
case ImageFormatRg8i:
check_desktop();
return "rg8i";
case ImageFormatR16i:
check_desktop();
return "r16i";
default:
case ImageFormatUnknown:
return nullptr;
default:
return "UNSUPPORTED"; // TODO: Fill in rest.
}
}
@ -805,7 +872,8 @@ void CompilerGLSL::emit_push_constant_block_glsl(const SPIRVariable &var)
void CompilerGLSL::emit_buffer_block(const SPIRVariable &var)
{
auto &type = get<SPIRType>(var.basetype);
auto ssbo = meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock);
bool ssbo = (meta[type.self].decoration.decoration_flags & (1ull << DecorationBufferBlock)) != 0;
bool is_restrict = (meta[var.self].decoration.decoration_flags & (1ull << DecorationRestrict)) != 0;
add_resource_name(var.self);
@ -819,7 +887,7 @@ void CompilerGLSL::emit_buffer_block(const SPIRVariable &var)
else
resource_names.insert(buffer_name);
statement(layout_for_variable(var) + (ssbo ? "buffer " : "uniform ") + buffer_name);
statement(layout_for_variable(var), is_restrict ? "restrict " : "", ssbo ? "buffer " : "uniform ", buffer_name);
begin_scope();
type.member_name_cache.clear();
@ -1117,33 +1185,44 @@ string CompilerGLSL::to_func_call_arg(uint32_t id)
return to_expression(id);
}
void CompilerGLSL::handle_invalid_expression(uint32_t id)
{
auto &expr = get<SPIRExpression>(id);
// This expression has been invalidated in the past.
// Be careful with this expression next pass ...
// Used for OpCompositeInsert forwarding atm.
expr.used_while_invalidated = true;
// We tried to read an invalidated expression.
// This means we need another pass at compilation, but next time, force temporary variables so that they cannot be invalidated.
forced_temporaries.insert(id);
force_recompile = true;
}
string CompilerGLSL::to_expression(uint32_t id)
{
auto itr = invalid_expressions.find(id);
if (itr != end(invalid_expressions))
handle_invalid_expression(id);
if (ids[id].get_type() == TypeExpression)
{
// We might have a more complex chain of dependencies.
// A possible scenario is that we
//
// %1 = OpLoad
// %2 = OpDoSomething %1 %1. here %2 will have a dependency on %1.
// %3 = OpDoSomethingAgain %2 %2. Here %3 will lose the link to %1 since we don't propagate the dependencies like that.
// OpStore %1 %foo // Here we can invalidate %1, and hence all expressions which depend on %1. Only %2 will know since it's part of invalid_expressions.
// %4 = OpDoSomethingAnotherTime %3 %3 // If we forward all expressions we will see %1 expression after store, not before.
//
// However, we can propagate up a list of depended expressions when we used %2, so we can check if %2 is invalid when reading %3 after the store,
// and see that we should not forward reads of the original variable.
auto &expr = get<SPIRExpression>(id);
// This expression has been invalidated in the past.
// Be careful with this expression next pass ...
// Used for OpCompositeInsert forwarding atm.
expr.used_while_invalidated = true;
// We tried to read an invalidated expression.
// This means we need another pass at compilation, but next time, do not try to forward
// the variables which caused invalidation to happen in the first place.
for (auto var : expr.invalidated_by)
{
//fprintf(stderr, "Expression %u was invalidated due to variable %u being invalid at read time!\n", id, var);
get<SPIRVariable>(var).forwardable = false;
}
if (expr.invalidated_by.empty() && expr.loaded_from)
{
//fprintf(stderr, "Expression %u was invalidated due to variable %u being invalid at read time!\n", id, expr.loaded_from);
get<SPIRVariable>(expr.loaded_from).forwardable = false;
}
force_recompile = true;
for (uint32_t dep : expr.expression_dependencies)
if (invalid_expressions.find(dep) != end(invalid_expressions))
handle_invalid_expression(dep);
}
track_expression_read(id);
@ -1382,13 +1461,23 @@ SPIRExpression &CompilerGLSL::emit_op(uint32_t result_type, uint32_t result_id,
void CompilerGLSL::emit_unary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op)
{
emit_op(result_type, result_id, join(op, to_expression(op0)), should_forward(op0), true);
bool forward = should_forward(op0);
emit_op(result_type, result_id, join(op, to_expression(op0)), forward, true);
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
inherit_expression_dependencies(result_id, op0);
}
void CompilerGLSL::emit_binary_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op)
{
emit_op(result_type, result_id, join(to_expression(op0), " ", op, " ", to_expression(op1)),
should_forward(op0) && should_forward(op1), true);
bool forward = should_forward(op0) && should_forward(op1);
emit_op(result_type, result_id, join(to_expression(op0), " ", op, " ", to_expression(op1)), forward, true);
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
{
inherit_expression_dependencies(result_id, op0);
inherit_expression_dependencies(result_id, op1);
}
}
SPIRType CompilerGLSL::binary_op_bitcast_helper(string &cast_op0, string &cast_op1, SPIRType::BaseType &input_type,
@ -1457,14 +1546,23 @@ void CompilerGLSL::emit_binary_op_cast(uint32_t result_type, uint32_t result_id,
void CompilerGLSL::emit_unary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op)
{
emit_op(result_type, result_id, join(op, "(", to_expression(op0), ")"), should_forward(op0), false);
bool forward = should_forward(op0);
emit_op(result_type, result_id, join(op, "(", to_expression(op0), ")"), forward, false);
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
inherit_expression_dependencies(result_id, op0);
}
void CompilerGLSL::emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
const char *op)
{
emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", ", to_expression(op1), ")"),
should_forward(op0) && should_forward(op1), false);
bool forward = should_forward(op0) && should_forward(op1);
emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", ", to_expression(op1), ")"), forward, false);
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
{
inherit_expression_dependencies(result_id, op0);
inherit_expression_dependencies(result_id, op1);
}
}
void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
@ -1495,17 +1593,33 @@ void CompilerGLSL::emit_binary_func_op_cast(uint32_t result_type, uint32_t resul
void CompilerGLSL::emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
uint32_t op2, const char *op)
{
bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2);
emit_op(result_type, result_id,
join(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(op2), ")"),
should_forward(op0) && should_forward(op1) && should_forward(op2), false);
join(op, "(", to_expression(op0), ", ", to_expression(op1), ", ", to_expression(op2), ")"), forward, false);
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
{
inherit_expression_dependencies(result_id, op0);
inherit_expression_dependencies(result_id, op1);
inherit_expression_dependencies(result_id, op2);
}
}
void CompilerGLSL::emit_quaternary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1,
uint32_t op2, uint32_t op3, const char *op)
{
bool forward = should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3);
emit_op(result_type, result_id, join(op, "(", to_expression(op0), ", ", to_expression(op1), ", ",
to_expression(op2), ", ", to_expression(op3), ")"),
should_forward(op0) && should_forward(op1) && should_forward(op2) && should_forward(op3), false);
forward, false);
if (forward && forced_temporaries.find(result_id) == end(forced_temporaries))
{
inherit_expression_dependencies(result_id, op0);
inherit_expression_dependencies(result_id, op1);
inherit_expression_dependencies(result_id, op2);
inherit_expression_dependencies(result_id, op3);
}
}
string CompilerGLSL::legacy_tex_op(const std::string &op, const SPIRType &imgtype)
@ -2332,7 +2446,11 @@ string CompilerGLSL::access_chain(uint32_t base, const uint32_t *indices, uint32
bool CompilerGLSL::should_forward(uint32_t id)
{
return is_immutable(id) && !options.force_temporary;
// Immutable expression can always be forwarded.
// If not immutable, we can speculate about it by forwarding potentially mutable variables.
auto *var = maybe_get<SPIRVariable>(id);
bool forward = var ? var->forwardable : false;
return (is_immutable(id) || forward) && !options.force_temporary;
}
void CompilerGLSL::track_expression_read(uint32_t id)
@ -2606,18 +2724,12 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// If we're loading from memory that cannot be changed by the shader,
// just forward the expression directly to avoid needless temporaries.
if (should_forward(ptr))
{
set<SPIRExpression>(id, to_expression(ptr), result_type, true);
register_read(id, ptr, true);
}
else
{
// If the variable can be modified after this OpLoad, we cannot just forward the expression.
// We must read it now and store it in a temporary.
emit_op(result_type, id, to_expression(ptr), false, false);
register_read(id, ptr, false);
}
// If an expression is mutable and forwardable, we speculate that it is immutable.
bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
// Suppress usage tracking since using same expression multiple times does not imply any extra work.
emit_op(result_type, id, to_expression(ptr), forward, false, true);
register_read(id, ptr, forward);
break;
}
@ -2629,8 +2741,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
flush_variable_declaration(var->self);
// If the base is immutable, the access chain pointer must also be.
// If an expression is mutable and forwardable, we speculate that it is immutable.
auto e = access_chain(ops[2], &ops[3], length - 3, false);
auto &expr = set<SPIRExpression>(ops[1], move(e), ops[0], is_immutable(ops[2]));
auto &expr = set<SPIRExpression>(ops[1], move(e), ops[0], should_forward(ops[2]));
expr.loaded_from = ops[2];
break;
}
@ -2650,8 +2763,8 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
// For this case, we don't need to invalidate anything and emit any opcode.
if (lhs != rhs)
{
register_write(ops[0]);
statement(lhs, " = ", rhs, ";");
register_write(ops[0]);
}
}
break;
@ -3507,7 +3620,52 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
{
uint32_t result_type = ops[0];
uint32_t id = ops[1];
emit_op(result_type, id, to_expression(ops[2]), true, false);
auto &e = emit_op(result_type, id, to_expression(ops[2]), true, false);
// When using the image, we need to know which variable it is actually loaded from.
auto *var = maybe_get_backing_variable(ops[2]);
e.loaded_from = var ? var->self : 0;
break;
}
case OpImageQueryLod:
{
if (!options.es && options.version < 400)
{
require_extension("GL_ARB_texture_query_lod");
// For some reason, the ARB spec is all-caps.
BFOP(textureQueryLOD);
}
else if (options.es)
throw CompilerError("textureQueryLod not supported in ES profile.");
else
BFOP(textureQueryLod);
break;
}
case OpImageQueryLevels:
{
if (!options.es && options.version < 430)
require_extension("GL_ARB_texture_query_levels");
if (options.es)
throw CompilerError("textureQueryLevels not supported in ES profile.");
UFOP(textureQueryLevels);
break;
}
case OpImageQuerySamples:
{
auto *var = maybe_get_backing_variable(ops[2]);
if (!var)
throw CompilerError(
"Bug. OpImageQuerySamples must have a backing variable so we know if the image is sampled or not.");
auto &type = get<SPIRType>(var->basetype);
bool image = type.image.sampled == 2;
if (image)
UFOP(imageSamples);
else
UFOP(textureSamples);
break;
}
@ -3549,6 +3707,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
if (var && var->remapped_variable) // Remapped input, just read as-is without any op-code
{
if (type.image.ms)
throw CompilerError("Trying to remap multisampled image to variable, this is not possible.");
auto itr =
find_if(begin(pls_inputs), end(pls_inputs), [var](const PlsRemap &pls) { return pls.id == var->self; });
@ -3574,19 +3735,56 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
if (options.vulkan_semantics)
{
// With Vulkan semantics, use the proper Vulkan GLSL construct.
imgexpr = join("subpassLoad(", to_expression(ops[2]), ")");
if (type.image.ms)
{
uint32_t operands = ops[4];
if (operands != ImageOperandsSampleMask || length != 6)
throw CompilerError(
"Multisampled image used in OpImageRead, but unexpected operand mask was used.");
uint32_t samples = ops[5];
imgexpr = join("subpassLoad(", to_expression(ops[2]), ", ", to_expression(samples), ")");
}
else
imgexpr = join("subpassLoad(", to_expression(ops[2]), ")");
}
else
{
// Implement subpass loads via texture barrier style sampling.
imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
if (type.image.ms)
{
uint32_t operands = ops[4];
if (operands != ImageOperandsSampleMask || length != 6)
throw CompilerError(
"Multisampled image used in OpImageRead, but unexpected operand mask was used.");
uint32_t samples = ops[5];
imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), ",
to_expression(samples), ")");
}
else
{
// Implement subpass loads via texture barrier style sampling.
imgexpr = join("texelFetch(", to_expression(ops[2]), ", ivec2(gl_FragCoord.xy), 0)");
}
}
pure = true;
}
else
{
// Plain image load/store.
imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ")");
if (type.image.ms)
{
uint32_t operands = ops[4];
if (operands != ImageOperandsSampleMask || length != 6)
throw CompilerError(
"Multisampled image used in OpImageRead, but unexpected operand mask was used.");
uint32_t samples = ops[5];
imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ", ",
to_expression(samples), ")");
}
else
imgexpr = join("imageLoad(", to_expression(ops[2]), ", ", to_expression(ops[3]), ")");
pure = false;
}
@ -3612,6 +3810,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
uint32_t id = ops[1];
auto &e = set<SPIRExpression>(id, join(to_expression(ops[2]), ", ", to_expression(ops[3])), result_type, true);
// When using the pointer, we need to know which variable it is actually loaded from.
auto *var = maybe_get_backing_variable(ops[2]);
e.loaded_from = var ? var->self : 0;
break;
@ -3633,7 +3832,19 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
}
}
statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]), ");");
auto &type = expression_type(ops[0]);
if (type.image.ms)
{
uint32_t operands = ops[3];
if (operands != ImageOperandsSampleMask || length != 5)
throw CompilerError("Multisampled image used in OpImageWrite, but unexpected operand mask was used.");
uint32_t samples = ops[4];
statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(samples),
", ", to_expression(ops[2]), ");");
}
else
statement("imageStore(", to_expression(ops[0]), ", ", to_expression(ops[1]), ", ", to_expression(ops[2]),
");");
if (var && variable_storage_is_aliased(*var))
flush_all_aliased_variables();
@ -3951,7 +4162,7 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type)
}
if (type.basetype == SPIRType::Image && type.image.dim == DimSubpassData && options.vulkan_semantics)
return res + "subpassInput";
return res + "subpassInput" + (type.image.ms ? "MS" : "");
// If we're emulating subpassInput with samplers, force sampler2D
// so we don't have to specify format.
@ -3990,12 +4201,12 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type)
throw CompilerError("Only 1D, 2D, 3D, Buffer, InputTarget and Cube textures supported.");
}
if (type.image.ms)
res += "MS";
if (type.image.arrayed)
res += "Array";
if (type.image.depth)
res += "Shadow";
if (type.image.ms)
res += "MS";
return res;
}

View File

@ -347,6 +347,8 @@ protected:
void add_variable(std::unordered_set<std::string> &variables, uint32_t id);
void check_function_call_constraints(const uint32_t *args, uint32_t length);
void handle_invalid_expression(uint32_t id);
};
}

View File

@ -146,19 +146,23 @@ def regression_check(shader, glsl, update, keep):
def shader_is_vulkan(shader):
return '.vk.' in shader
def shader_is_desktop(shader):
return '.desktop.' in shader
def shader_is_spirv(shader):
return '.asm.' in shader
def test_shader(stats, shader, update, keep):
joined_path = os.path.join(shader[0], shader[1])
vulkan = shader_is_vulkan(shader[1])
desktop = shader_is_desktop(shader[1])
spirv = shader_is_spirv(shader[1])
print('Testing shader:', joined_path)
spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, spirv)
# Only test GLSL stats if we have a shader following GL semantics.
if stats and (not vulkan) and (not spirv):
if stats and (not vulkan) and (not spirv) and (not desktop):
cross_stats = get_shader_stats(glsl)
regression_check(shader, glsl, update, keep)