Merge pull request #189 from KhronosGroup/flatten-multidimensional-arrays

Add support for flattening multidimensional arrays into single dimension
This commit is contained in:
Hans-Kristian Arntzen 2017-05-31 10:08:03 +02:00 committed by GitHub
commit f77d91e077
6 changed files with 159 additions and 15 deletions

View File

@ -446,6 +446,7 @@ struct CLIArguments
bool hlsl = false; bool hlsl = false;
bool hlsl_compat = false; bool hlsl_compat = false;
bool vulkan_semantics = false; bool vulkan_semantics = false;
bool flatten_multidimensional_arrays = false;
bool remove_unused = false; bool remove_unused = false;
bool cfg_analysis = true; bool cfg_analysis = true;
}; };
@ -461,6 +462,7 @@ static void print_help()
"[--separate-shader-objects]" "[--separate-shader-objects]"
"[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name " "[--pls-in format input-name] [--pls-out format output-name] [--remap source_name target_name "
"components] [--extension ext] [--entry name] [--remove-unused-variables] " "components] [--extension ext] [--entry name] [--remove-unused-variables] "
"[--flatten-multidimensional-arrays] "
"[--remap-variable-type <variable_name> <new_variable_type>]\n"); "[--remap-variable-type <variable_name> <new_variable_type>]\n");
} }
@ -583,6 +585,7 @@ int main(int argc, char *argv[])
cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; }); cbs.add("--hlsl", [&args](CLIParser &) { args.hlsl = true; });
cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; }); cbs.add("--hlsl-enable-compat", [&args](CLIParser &) { args.hlsl_compat = true; });
cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; }); cbs.add("--vulkan-semantics", [&args](CLIParser &) { args.vulkan_semantics = true; });
cbs.add("--flatten-multidimensional-arrays", [&args](CLIParser &) { args.flatten_multidimensional_arrays = true; });
cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); }); cbs.add("--extension", [&args](CLIParser &parser) { args.extensions.push_back(parser.next_string()); });
cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); }); cbs.add("--entry", [&args](CLIParser &parser) { args.entry = parser.next_string(); });
cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; }); cbs.add("--separate-shader-objects", [&args](CLIParser &) { args.sso = true; });
@ -690,6 +693,7 @@ int main(int argc, char *argv[])
opts.es = args.es; opts.es = args.es;
opts.force_temporary = args.force_temporary; opts.force_temporary = args.force_temporary;
opts.separate_shader_objects = args.sso; opts.separate_shader_objects = args.sso;
opts.flatten_multidimensional_arrays = args.flatten_multidimensional_arrays;
opts.vulkan_semantics = args.vulkan_semantics; opts.vulkan_semantics = args.vulkan_semantics;
opts.vertex.fixup_clipspace = args.fixup; opts.vertex.fixup_clipspace = args.fixup;
opts.cfg_analysis = args.cfg_analysis; opts.cfg_analysis = args.cfg_analysis;

View File

@ -0,0 +1,24 @@
#version 450
layout(binding = 0) uniform sampler2D uTextures[2 * 3 * 1];
layout(location = 1) in vec2 vUV;
layout(location = 0) out vec4 FragColor;
layout(location = 0) flat in int vIndex;
void main()
{
vec4 values3[2 * 3 * 1];
for (int z = 0; z < 2; z++)
{
for (int y = 0; y < 3; y++)
{
for (int x = 0; x < 1; x++)
{
values3[z * 3 * 1 + y * 1 + x] = texture(uTextures[z * 3 * 1 + y * 1 + x], vUV);
}
}
}
FragColor = ((values3[1 * 3 * 1 + 2 * 1 + 0]) + (values3[0 * 3 * 1 + 2 * 1 + 0])) + (values3[(vIndex + 1) * 3 * 1 + 2 * 1 + vIndex]);
}

View File

@ -0,0 +1,18 @@
#version 450
layout(location = 0) out vec4 FragColor;
layout(binding = 0) uniform sampler2D uTextures[2][3][1];
layout(location = 0) flat in int vIndex;
layout(location = 1) in vec2 vUV;
void main()
{
vec4 values3[2][3][1];
for (int z = 0; z < 2; z++)
for (int y = 0; y < 3; y++)
for (int x = 0; x < 1; x++)
values3[z][y][x] = texture(uTextures[z][y][x], vUV);
FragColor = values3[1][2][0] + values3[0][2][0] + values3[vIndex + 1][2][vIndex];
}

View File

@ -1816,10 +1816,8 @@ void CompilerGLSL::strip_enclosed_expression(string &expr)
expr.erase(begin(expr)); expr.erase(begin(expr));
} }
// Just like to_expression except that we enclose the expression inside parentheses if needed. string CompilerGLSL::enclose_expression(const string &expr)
string CompilerGLSL::to_enclosed_expression(uint32_t id)
{ {
auto expr = to_expression(id);
bool need_parens = false; bool need_parens = false;
uint32_t paren_count = 0; uint32_t paren_count = 0;
for (auto c : expr) for (auto c : expr)
@ -1848,6 +1846,12 @@ string CompilerGLSL::to_enclosed_expression(uint32_t id)
return expr; return expr;
} }
// Just like to_expression except that we enclose the expression inside parentheses if needed.
string CompilerGLSL::to_enclosed_expression(uint32_t id)
{
return enclose_expression(to_expression(id));
}
string CompilerGLSL::to_expression(uint32_t id) string CompilerGLSL::to_expression(uint32_t id)
{ {
auto itr = invalid_expressions.find(id); auto itr = invalid_expressions.find(id);
@ -3525,6 +3529,8 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
bool access_chain_is_arrayed = false; bool access_chain_is_arrayed = false;
bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base); bool row_major_matrix_needs_conversion = is_non_native_row_major_matrix(base);
bool pending_array_enclose = false;
bool dimension_flatten = false;
for (uint32_t i = 0; i < count; i++) for (uint32_t i = 0; i < count; i++)
{ {
@ -3533,14 +3539,50 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
// Arrays // Arrays
if (!type->array.empty()) if (!type->array.empty())
{ {
expr += "["; // If we are flattening multidimensional arrays, only create opening bracket on first
if (index_is_literal) // array index.
expr += convert_to_string(index); if (options.flatten_multidimensional_arrays && !pending_array_enclose)
else {
expr += to_expression(index); dimension_flatten = type->array.size() > 1;
expr += "]"; pending_array_enclose = dimension_flatten;
if (pending_array_enclose)
expr += "[";
}
assert(type->parent_type); assert(type->parent_type);
// If we are flattening multidimensional arrays, do manual stride computation.
if (options.flatten_multidimensional_arrays && dimension_flatten)
{
auto &parent_type = get<SPIRType>(type->parent_type);
if (index_is_literal)
expr += convert_to_string(index);
else
expr += to_enclosed_expression(index);
for (auto j = uint32_t(parent_type.array.size()); j; j--)
{
expr += " * ";
expr += enclose_expression(to_array_size(parent_type, j - 1));
}
if (parent_type.array.empty())
pending_array_enclose = false;
else
expr += " + ";
}
else
{
expr += "[";
if (index_is_literal)
expr += convert_to_string(index);
else
expr += to_expression(index);
}
if (!pending_array_enclose)
expr += "]";
type = &get<SPIRType>(type->parent_type); type = &get<SPIRType>(type->parent_type);
access_chain_is_arrayed = true; access_chain_is_arrayed = true;
@ -3636,6 +3678,13 @@ string CompilerGLSL::access_chain_internal(uint32_t base, const uint32_t *indice
SPIRV_CROSS_THROW("Cannot subdivide a scalar value!"); SPIRV_CROSS_THROW("Cannot subdivide a scalar value!");
} }
if (pending_array_enclose)
{
SPIRV_CROSS_THROW("Flattening of multidimensional arrays were enabled, "
"but the access chain was terminated in the middle of a multidimensional array. "
"This is not supported.");
}
if (need_transpose) if (need_transpose)
*need_transpose = row_major_matrix_needs_conversion; *need_transpose = row_major_matrix_needs_conversion;
return expr; return expr;
@ -6022,14 +6071,40 @@ string CompilerGLSL::type_to_array_glsl(const SPIRType &type)
if (type.array.empty()) if (type.array.empty())
return ""; return "";
string res; if (options.flatten_multidimensional_arrays)
for (auto i = uint32_t(type.array.size()); i; i--)
{ {
string res;
res += "["; res += "[";
res += to_array_size(type, i - 1); for (auto i = uint32_t(type.array.size()); i; i--)
{
res += enclose_expression(to_array_size(type, i - 1));
if (i > 1)
res += " * ";
}
res += "]"; res += "]";
return res;
}
else
{
if (type.array.size() > 1)
{
if (!options.es && options.version < 430)
require_extension("GL_ARB_arrays_of_arrays");
else if (options.es && options.version < 310)
SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310. "
"Try using --flatten-multidimensional-arrays or set "
"options.flatten_multidimensional_arrays to true.");
}
string res;
for (auto i = uint32_t(type.array.size()); i; i--)
{
res += "[";
res += to_array_size(type, i - 1);
res += "]";
}
return res;
} }
return res;
} }
string CompilerGLSL::image_type_glsl(const SPIRType &type) string CompilerGLSL::image_type_glsl(const SPIRType &type)
@ -6111,6 +6186,16 @@ string CompilerGLSL::image_type_glsl(const SPIRType &type)
string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type) string CompilerGLSL::type_to_glsl_constructor(const SPIRType &type)
{ {
if (type.array.size() > 1)
{
if (options.flatten_multidimensional_arrays)
SPIRV_CROSS_THROW("Cannot flatten constructors of multidimensional array constructors, e.g. float[][]().");
else if (!options.es && options.version < 430)
require_extension("GL_ARB_arrays_of_arrays");
else if (options.es && options.version < 310)
SPIRV_CROSS_THROW("Arrays of arrays not supported before ESSL version 310.");
}
auto e = type_to_glsl(type); auto e = type_to_glsl(type);
for (uint32_t i = 0; i < type.array.size(); i++) for (uint32_t i = 0; i < type.array.size(); i++)
e += "[]"; e += "[]";

View File

@ -73,6 +73,12 @@ public:
// (EXT_shader_io_blocks) which makes things a bit more fuzzy. // (EXT_shader_io_blocks) which makes things a bit more fuzzy.
bool separate_shader_objects = false; bool separate_shader_objects = false;
// Flattens multidimensional arrays, e.g. float foo[a][b][c] into single-dimensional arrays,
// e.g. float foo[a * b * c].
// This function does not change the actual SPIRType of any object.
// Only the generated code, including declarations of interface variables are changed to be single array dimension.
bool flatten_multidimensional_arrays = false;
enum Precision enum Precision
{ {
DontCare, DontCare,
@ -359,6 +365,7 @@ protected:
void append_global_func_args(const SPIRFunction &func, uint32_t index, std::vector<std::string> &arglist); void append_global_func_args(const SPIRFunction &func, uint32_t index, std::vector<std::string> &arglist);
std::string to_expression(uint32_t id); std::string to_expression(uint32_t id);
std::string to_enclosed_expression(uint32_t id); std::string to_enclosed_expression(uint32_t id);
std::string enclose_expression(const std::string &expr);
void strip_enclosed_expression(std::string &expr); void strip_enclosed_expression(std::string &expr);
std::string to_member_name(const SPIRType &type, uint32_t index); std::string to_member_name(const SPIRType &type, uint32_t index);
std::string type_to_glsl_constructor(const SPIRType &type); std::string type_to_glsl_constructor(const SPIRType &type);

View File

@ -121,7 +121,7 @@ def validate_shader(shader, vulkan):
else: else:
subprocess.check_call(['glslangValidator', shader]) subprocess.check_call(['glslangValidator', shader])
def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso): def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim):
spirv_f, spirv_path = tempfile.mkstemp() spirv_f, spirv_path = tempfile.mkstemp()
glsl_f, glsl_path = tempfile.mkstemp(suffix = os.path.basename(shader)) glsl_f, glsl_path = tempfile.mkstemp(suffix = os.path.basename(shader))
os.close(spirv_f) os.close(spirv_f)
@ -148,6 +148,8 @@ def cross_compile(shader, vulkan, spirv, invalid_spirv, eliminate, is_legacy, fl
extra_args += ['--flatten-ubo'] extra_args += ['--flatten-ubo']
if sso: if sso:
extra_args += ['--separate-shader-objects'] extra_args += ['--separate-shader-objects']
if flatten_dim:
extra_args += ['--flatten-multidimensional-arrays']
spirv_cross_path = './spirv-cross' spirv_cross_path = './spirv-cross'
subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args) subprocess.check_call([spirv_cross_path, '--entry', 'main', '--output', glsl_path, spirv_path] + extra_args)
@ -244,6 +246,9 @@ def shader_is_flatten_ubo(shader):
def shader_is_sso(shader): def shader_is_sso(shader):
return '.sso.' in shader return '.sso.' in shader
def shader_is_flatten_dimensions(shader):
return '.flatten_dim.' in shader
def test_shader(stats, shader, update, keep): def test_shader(stats, shader, update, keep):
joined_path = os.path.join(shader[0], shader[1]) joined_path = os.path.join(shader[0], shader[1])
vulkan = shader_is_vulkan(shader[1]) vulkan = shader_is_vulkan(shader[1])
@ -254,9 +259,10 @@ def test_shader(stats, shader, update, keep):
is_legacy = shader_is_legacy(shader[1]) is_legacy = shader_is_legacy(shader[1])
flatten_ubo = shader_is_flatten_ubo(shader[1]) flatten_ubo = shader_is_flatten_ubo(shader[1])
sso = shader_is_sso(shader[1]) sso = shader_is_sso(shader[1])
flatten_dim = shader_is_flatten_dimensions(shader[1])
print('Testing shader:', joined_path) print('Testing shader:', joined_path)
spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso) spirv, glsl, vulkan_glsl = cross_compile(joined_path, vulkan, is_spirv, invalid_spirv, eliminate, is_legacy, flatten_ubo, sso, flatten_dim)
# Only test GLSL stats if we have a shader following GL semantics. # Only test GLSL stats if we have a shader following GL semantics.
if stats and (not vulkan) and (not is_spirv) and (not desktop): if stats and (not vulkan) and (not is_spirv) and (not desktop):