Support arrays which have spec constant size.

The size of an array can be a specialization constant or a spec constant
op. This complicates things quite a lot.

Reflection becomes very painful in the presence of expressions instead
of literals so add a new array which expresses this.

It is unlikely that we will need to do accurate reflection of interface
types which have specialization constant size.

SSBOs and UBOs will for now throw exception if a dynamic size is used since it
is very difficult to know the real size.
This commit is contained in:
Hans-Kristian Arntzen 2016-10-03 17:17:11 +02:00
Родитель 7e8afa872b
Коммит 5d4bb687bc
9 изменённых файлов: 122 добавлений и 51 удалений

Просмотреть файл

@ -2,6 +2,11 @@
precision mediump float; precision mediump float;
precision highp int; precision highp int;
struct Foo
{
float elems[(4 + 2)];
};
layout(location = 0) out vec4 FragColor; layout(location = 0) out vec4 FragColor;
void main() void main()
@ -46,6 +51,9 @@ void main()
mediump int c35 = int(false); mediump int c35 = int(false);
mediump uint c36 = uint(false); mediump uint c36 = uint(false);
float c37 = float(false); float c37 = float(false);
FragColor = vec4((t0 + t1)); float vec0[4][(3 + 3)];
float vec1[(3 + 2)][(4 + 5)];
Foo foo;
FragColor = (((vec4((t0 + t1)) + vec4(vec0[0][0])) + vec4(vec1[0][0])) + vec4(foo.elems[3]));
} }

Просмотреть файл

@ -2,7 +2,6 @@
precision mediump float; precision mediump float;
precision highp int; precision highp int;
layout(location = 0) out vec4 FragColor;
layout(constant_id = 1) const float _9 = 1.0; layout(constant_id = 1) const float _9 = 1.0;
layout(constant_id = 2) const float _11 = 2.0; layout(constant_id = 2) const float _11 = 2.0;
layout(constant_id = 3) const int _16 = 3; layout(constant_id = 3) const int _16 = 3;
@ -12,6 +11,13 @@ layout(constant_id = 6) const uint _35 = 6u;
layout(constant_id = 7) const bool _56 = false; layout(constant_id = 7) const bool _56 = false;
layout(constant_id = 8) const bool _57 = true; layout(constant_id = 8) const bool _57 = true;
struct Foo
{
float elems[(_25 + 2)];
};
layout(location = 0) out vec4 FragColor;
void main() void main()
{ {
float t0 = _9; float t0 = _9;
@ -54,6 +60,9 @@ void main()
mediump int c35 = int(_56); mediump int c35 = int(_56);
mediump uint c36 = uint(_56); mediump uint c36 = uint(_56);
float c37 = float(_56); float c37 = float(_56);
FragColor = vec4((t0 + t1)); float vec0[_25][(_16 + 3)];
float vec1[(_16 + 2)][(_25 + 5)];
Foo foo;
FragColor = (((vec4((t0 + t1)) + vec4(vec0[0][0])) + vec4(vec1[0][0])) + vec4(foo.elems[_16]));
} }

Просмотреть файл

@ -12,6 +12,11 @@ layout(constant_id = 7) const bool g = false;
layout(constant_id = 8) const bool h = true; layout(constant_id = 8) const bool h = true;
// glslang doesn't seem to support partial spec constants or composites yet, so only test the basics. // glslang doesn't seem to support partial spec constants or composites yet, so only test the basics.
struct Foo
{
float elems[d + 2];
};
void main() void main()
{ {
float t0 = a; float t0 = a;
@ -63,5 +68,10 @@ void main()
uint c36 = uint(g); // bool -> uint uint c36 = uint(g); // bool -> uint
float c37 = float(g); // bool -> float float c37 = float(g); // bool -> float
FragColor = vec4(t0 + t1); // Flexible sized arrays with spec constants and spec constant ops.
float vec0[d][c + 3];
float vec1[c + 2][d + 5];
Foo foo;
FragColor = vec4(t0 + t1) + vec0[0][0] + vec1[0][0] + foo.elems[c];
} }

Просмотреть файл

@ -202,9 +202,16 @@ struct SPIRType : IVariant
uint32_t vecsize = 1; uint32_t vecsize = 1;
uint32_t columns = 1; uint32_t columns = 1;
// Arrays, suport array of arrays by having a vector of array sizes. // Arrays, support array of arrays by having a vector of array sizes.
std::vector<uint32_t> array; std::vector<uint32_t> array;
// Array elements can be either specialization constants or specialization ops.
// This array determines how to interpret the array size.
// If an element is true, the element is a literal,
// otherwise, it's an expression, which must be resolved on demand.
// The actual size is not really known until runtime.
std::vector<bool> array_size_literal;
// Pointers // Pointers
bool pointer = false; bool pointer = false;
spv::StorageClass storage = spv::StorageClassGeneric; spv::StorageClass storage = spv::StorageClassGeneric;

Просмотреть файл

@ -410,8 +410,8 @@ string CompilerCPP::argument_decl(const SPIRFunction::Parameter &arg)
string variable_name = to_name(var.self); string variable_name = to_name(var.self);
remap_variable_type_name(type, variable_name, base); remap_variable_type_name(type, variable_name, base);
for (auto &array : type.array) for (uint32_t i = 0; i < type.array.size(); i++)
base = join("std::array<", base, ", ", array, ">"); base = join("std::array<", base, ", ", to_array_size(type, i), ">");
return join(constref ? "const " : "", base, " &", variable_name); return join(constref ? "const " : "", base, " &", variable_name);
} }
@ -421,16 +421,18 @@ string CompilerCPP::variable_decl(const SPIRType &type, const string &name)
string base = type_to_glsl(type); string base = type_to_glsl(type);
remap_variable_type_name(type, name, base); remap_variable_type_name(type, name, base);
bool runtime = false; bool runtime = false;
for (auto &array : type.array)
for (uint32_t i = 0; i < type.array.size(); i++)
{ {
if (array) auto &array = type.array[i];
base = join("std::array<", base, ", ", array, ">"); if (!array && type.array_size_literal[i])
else
{ {
// Avoid using runtime arrays with std::array since this is undefined. // Avoid using runtime arrays with std::array since this is undefined.
// Runtime arrays cannot be passed around as values, so this is fine. // Runtime arrays cannot be passed around as values, so this is fine.
runtime = true; runtime = true;
} }
else
base = join("std::array<", base, ", ", to_array_size(type, i), ">");
} }
base += ' '; base += ' ';
return base + name + (runtime ? "[1]" : ""); return base + name + (runtime ? "[1]" : "");

Просмотреть файл

@ -1265,7 +1265,12 @@ void Compiler::parse(const Instruction &instruction)
auto &arraybase = set<SPIRType>(id); auto &arraybase = set<SPIRType>(id);
arraybase = base; arraybase = base;
arraybase.array.push_back(get<SPIRConstant>(ops[2]).scalar());
auto *c = maybe_get<SPIRConstant>(ops[2]);
bool literal = c && !c->specialization;
arraybase.array_size_literal.push_back(literal);
arraybase.array.push_back(literal ? c->scalar() : ops[2]);
// Do NOT set arraybase.self! // Do NOT set arraybase.self!
break; break;
} }
@ -1279,6 +1284,7 @@ void Compiler::parse(const Instruction &instruction)
arraybase = base; arraybase = base;
arraybase.array.push_back(0); arraybase.array.push_back(0);
arraybase.array_size_literal.push_back(true);
// Do NOT set arraybase.self! // Do NOT set arraybase.self!
break; break;
} }

Просмотреть файл

@ -729,6 +729,7 @@ uint32_t CompilerGLSL::type_to_std430_array_stride(const SPIRType &type, uint64_
// Array stride is equal to aligned size of the underlying type. // Array stride is equal to aligned size of the underlying type.
SPIRType tmp = type; SPIRType tmp = type;
tmp.array.pop_back(); tmp.array.pop_back();
tmp.array_size_literal.pop_back();
uint32_t size = type_to_std430_size(tmp, flags); uint32_t size = type_to_std430_size(tmp, flags);
uint32_t alignment = type_to_std430_alignment(tmp, flags); uint32_t alignment = type_to_std430_alignment(tmp, flags);
return (size + alignment - 1) & ~(alignment - 1); return (size + alignment - 1) & ~(alignment - 1);
@ -737,7 +738,7 @@ uint32_t CompilerGLSL::type_to_std430_array_stride(const SPIRType &type, uint64_
uint32_t CompilerGLSL::type_to_std430_size(const SPIRType &type, uint64_t flags) uint32_t CompilerGLSL::type_to_std430_size(const SPIRType &type, uint64_t flags)
{ {
if (!type.array.empty()) if (!type.array.empty())
return type.array.back() * type_to_std430_array_stride(type, flags); return to_array_size_literal(type, type.array.size() - 1) * type_to_std430_array_stride(type, flags);
const uint32_t base_alignment = type_to_std430_base_size(type); const uint32_t base_alignment = type_to_std430_base_size(type);
uint32_t size = 0; uint32_t size = 0;
@ -1215,6 +1216,34 @@ void CompilerGLSL::emit_resources()
if (!pls_inputs.empty() || !pls_outputs.empty()) if (!pls_inputs.empty() || !pls_outputs.empty())
emit_pls(); emit_pls();
bool emitted = false;
// If emitted Vulkan GLSL,
// emit specialization constants as actual floats,
// spec op expressions will redirect to the constant name.
//
// TODO: If we have the fringe case that we create a spec constant which depends on a struct type,
// we'll have to deal with that, but there's currently no known way to express that.
if (options.vulkan_semantics)
{
for (auto &id : ids)
{
if (id.get_type() == TypeConstant)
{
auto &c = id.get<SPIRConstant>();
if (!c.specialization)
continue;
emit_specialization_constant(c);
emitted = true;
}
}
}
if (emitted)
statement("");
emitted = false;
// Output all basic struct types which are not Block or BufferBlock as these are declared inplace // Output all basic struct types which are not Block or BufferBlock as these are declared inplace
// when such variables are instantiated. // when such variables are instantiated.
for (auto &id : ids) for (auto &id : ids)
@ -1263,8 +1292,6 @@ void CompilerGLSL::emit_resources()
} }
} }
bool emitted = false;
bool skip_separate_image_sampler = !combined_image_samplers.empty() || !options.vulkan_semantics; bool skip_separate_image_sampler = !combined_image_samplers.empty() || !options.vulkan_semantics;
// Output Uniform Constants (values, samplers, images, etc). // Output Uniform Constants (values, samplers, images, etc).
@ -1338,25 +1365,6 @@ void CompilerGLSL::emit_resources()
} }
} }
// If emitted Vulkan GLSL,
// emit specialization constants as actual floats,
// spec op expressions will redirect to the constant name.
if (options.vulkan_semantics)
{
for (auto &id : ids)
{
if (id.get_type() == TypeConstant)
{
auto &c = id.get<SPIRConstant>();
if (!c.specialization)
continue;
emit_specialization_constant(c);
emitted = true;
}
}
}
if (emitted) if (emitted)
statement(""); statement("");
} }
@ -4702,6 +4710,39 @@ string CompilerGLSL::pls_decl(const PlsRemap &var)
to_name(variable.self)); to_name(variable.self));
} }
uint32_t CompilerGLSL::to_array_size_literal(const SPIRType &type, uint32_t index) const
{
assert(type.array.size() == type.array_size_literal.size());
if (!type.array_size_literal[index])
throw CompilerError("The array size is not a literal, but a specialization constant or spec constant op.");
return type.array[index];
}
string CompilerGLSL::to_array_size(const SPIRType &type, uint32_t index)
{
assert(type.array.size() == type.array_size_literal.size());
auto &size = type.array[index];
if (!type.array_size_literal[index])
return to_expression(size);
else if (size)
return convert_to_string(size);
else if (!backend.flexible_member_array_supported)
{
// For runtime-sized arrays, we can work around
// lack of standard support for this by simply having
// a single element array.
//
// Runtime length arrays must always be the last element
// in an interface block.
return "1";
}
else
return "";
}
string CompilerGLSL::type_to_array_glsl(const SPIRType &type) string CompilerGLSL::type_to_array_glsl(const SPIRType &type)
{ {
if (type.array.empty()) if (type.array.empty())
@ -4710,23 +4751,8 @@ string CompilerGLSL::type_to_array_glsl(const SPIRType &type)
string res; string res;
for (size_t i = type.array.size(); i; i--) for (size_t i = type.array.size(); i; i--)
{ {
auto &size = type.array[i - 1];
res += "["; res += "[";
if (size) res += to_array_size(type, i - 1);
{
res += convert_to_string(size);
}
else if (!backend.flexible_member_array_supported)
{
// For runtime-sized arrays, we can work around
// lack of standard support for this by simply having
// a single element array.
//
// Runtime length arrays must always be the last element
// in an interface block.
res += '1';
}
res += "]"; res += "]";
} }
return res; return res;

Просмотреть файл

@ -204,6 +204,8 @@ protected:
Options options; Options options;
std::string type_to_array_glsl(const SPIRType &type); std::string type_to_array_glsl(const SPIRType &type);
std::string to_array_size(const SPIRType &type, uint32_t index);
uint32_t to_array_size_literal(const SPIRType &type, uint32_t index) const;
std::string variable_decl(const SPIRVariable &variable); std::string variable_decl(const SPIRVariable &variable);
void add_local_variable_name(uint32_t id); void add_local_variable_name(uint32_t id);

Просмотреть файл

@ -1260,6 +1260,7 @@ SPIRType &CompilerMSL::get_pad_type(uint32_t pad_len)
ib_type.basetype = SPIRType::Char; ib_type.basetype = SPIRType::Char;
ib_type.width = 8; ib_type.width = 8;
ib_type.array.push_back(pad_len); ib_type.array.push_back(pad_len);
ib_type.array_size_literal.push_back(true);
set_decoration(ib_type.self, DecorationArrayStride, pad_len); set_decoration(ib_type.self, DecorationArrayStride, pad_len);
pad_type_ids_by_pad_len[pad_len] = pad_type_id; pad_type_ids_by_pad_len[pad_len] = pad_type_id;
@ -1616,7 +1617,7 @@ size_t CompilerMSL::get_declared_type_size(const SPIRType &type, uint64_t dec_ma
// ArrayStride is part of the array type not OpMemberDecorate. // ArrayStride is part of the array type not OpMemberDecorate.
auto &dec = meta[type.self].decoration; auto &dec = meta[type.self].decoration;
if (dec.decoration_flags & (1ull << DecorationArrayStride)) if (dec.decoration_flags & (1ull << DecorationArrayStride))
return dec.array_stride * type.array.back(); return dec.array_stride * to_array_size_literal(type, type.array.size() - 1);
else else
throw CompilerError("Type does not have ArrayStride set."); throw CompilerError("Type does not have ArrayStride set.");
} }