From: Mikko Rasa Date: Sun, 16 Jul 2023 20:02:29 +0000 (+0300) Subject: Add storage block support to the shader compiler X-Git-Url: https://git.tdb.fi/?a=commitdiff_plain;h=53e81067b92351621c0ba972e4a710986212ff9d;p=libs%2Fgl.git Add storage block support to the shader compiler At the moment extended alignment (std140 layout) is used for storage blocks. Supporting base alignment (std430) has some complications because structs may need to be duplicated as base and extended versions. --- diff --git a/source/glsl/features.cpp b/source/glsl/features.cpp index d66226e3..4dc43390 100644 --- a/source/glsl/features.cpp +++ b/source/glsl/features.cpp @@ -28,6 +28,7 @@ Features Features::from_api_version(GraphicsApi api, const Version &ver) features.arb_gpu_shader5 = (ver>=Version(4, 0)); features.arb_separate_shader_objects = (ver>=Version(4, 1)); features.arb_uniform_buffer_object = (ver>=Version(3, 2)); + features.arb_shader_storage_buffer_object = (ver>=Version(4, 2)); features.ext_gpu_shader4 = (ver>=Version(2, 1)); features.ext_texture_array = (ver>=Version(3, 0)); features.uniform_binding_range = (ver>=Version(4, 3) ? 84 : ver>=Version(4, 0) ? 60 : @@ -35,6 +36,7 @@ Features Features::from_api_version(GraphicsApi api, const Version &ver) features.texture_binding_range = (ver>=Version(4, 3) ? 96 : ver>=Version(4, 0) ? 80 : ver>=Version(3, 2) ? 48 : ver>=Version(1, 4) ? 32 : 16); features.storage_texture_binding_range = 8; + features.storage_buffer_binding_range = 8; break; case OPENGL_ES: if(ver.major==2) @@ -48,18 +50,21 @@ Features Features::from_api_version(GraphicsApi api, const Version &ver) features.arb_gpu_shader5 = (ver>=Version(3, 2)); features.arb_separate_shader_objects = (ver>=Version(3, 1)); features.arb_uniform_buffer_object = (ver>=Version(3, 0)); + features.arb_shader_storage_buffer_object = (ver>=Version(3, 1)); features.ext_gpu_shader4 = (ver>=Version(3, 0)); features.ext_texture_array = (ver>=Version(3, 0)); features.uniform_binding_range = (ver>=Version(3, 2) ? 72 : ver>=Version(3, 1) ? 36 : 24); features.texture_binding_range = (ver>=Version(3, 2) ? 96 : ver>=Version(3, 1) ? 48 : ver>=Version(3, 0) ? 32 : 8); features.storage_texture_binding_range = 4; + features.storage_buffer_binding_range = 4; break; case VULKAN: features.glsl_version = Version(4, 60); features.uniform_binding_range = 72; features.texture_binding_range = 96; features.storage_texture_binding_range = 24; + features.storage_buffer_binding_range = 24; break; default: throw invalid_argument("Features::from_api_version"); diff --git a/source/glsl/features.h b/source/glsl/features.h index a92fbf61..5596f0da 100644 --- a/source/glsl/features.h +++ b/source/glsl/features.h @@ -18,12 +18,14 @@ struct MSPGL_API Features bool arb_gpu_shader5 = false; bool arb_separate_shader_objects = false; bool arb_uniform_buffer_object = false; + bool arb_shader_storage_buffer_object = false; bool ext_gpu_shader4 = false; bool ext_texture_array = false; unsigned constant_id_range = 0x80000000U; unsigned uniform_binding_range = 24; unsigned texture_binding_range = 16; unsigned storage_texture_binding_range = 8; + unsigned storage_buffer_binding_range = 8; static Features from_api_version(GraphicsApi, const Version &); static Features latest(GraphicsApi); diff --git a/source/glsl/finalize.cpp b/source/glsl/finalize.cpp index 91d2e44c..fc6370ee 100644 --- a/source/glsl/finalize.cpp +++ b/source/glsl/finalize.cpp @@ -73,7 +73,10 @@ void LocationAllocator::apply(Module &module, const Features &f, bool a) allocate_locations("uniform"); for(VariableDeclaration *b: unbound_blocks) - bind_uniform(b->layout, b->block_declaration->block_name, features.uniform_binding_range); + { + unsigned range = (b->interface=="buffer" ? features.storage_buffer_binding_range : features.uniform_binding_range); + bind_uniform(b->layout, b->block_declaration->block_name, range); + } for(VariableDeclaration *t: unbound_textures) { const TypeDeclaration *base_type = get_ultimate_base_type(t->type_declaration); @@ -234,7 +237,7 @@ void LocationAllocator::visit(VariableDeclaration &var) unplaced_variables.push_back(&var); } - if(var.interface=="uniform") + if(var.interface=="uniform" || var.interface=="buffer") { if(var.block_declaration) { @@ -621,9 +624,18 @@ bool StructuralFeatureConverter::supports_interface_blocks(const string &iface) { if(iface=="uniform") return check_version(Version(3, 0)); + else if(iface=="buffer") + return check_version(Version(3, 10)); else return check_version(Version(3, 20)); } + else if(iface=="buffer") + { + if(check_version(Version(4, 30))) + return true; + else + return check_extension(&Features::arb_shader_storage_buffer_object); + } else if(check_version(Version(1, 50))) return true; else if(iface=="uniform") diff --git a/source/glsl/output.cpp b/source/glsl/output.cpp index d28de156..2b82ddbc 100644 --- a/source/glsl/output.cpp +++ b/source/glsl/output.cpp @@ -35,6 +35,8 @@ string Formatter::apply(Stage &s) append("#extension GL_ARB_separate_shader_objects: require\n"); if(s.required_features.arb_uniform_buffer_object) append("#extension GL_ARB_uniform_buffer_object: require\n"); + if(s.required_features.arb_shader_storage_buffer_object) + append("#extension GL_ARB_shader_storage_buffer_object: require\n"); if(s.required_features.ext_gpu_shader4) append("#extension GL_EXT_gpu_shader4: require\n"); if(s.required_features.ext_texture_array) diff --git a/source/glsl/parser.cpp b/source/glsl/parser.cpp index 615d75d4..db607894 100644 --- a/source/glsl/parser.cpp +++ b/source/glsl/parser.cpp @@ -171,7 +171,7 @@ bool Parser::check(const string &token) bool Parser::is_interface_qualifier(const string &token) { - return (token=="uniform" || token=="in" || token=="out"); + return (token=="uniform" || token=="in" || token=="out" || token=="buffer"); } bool Parser::is_sampling_qualifier(const string &token) @@ -308,7 +308,11 @@ RefPtr Parser::parse_global_declaration() { string next = tokenizer.peek_token(1); if(is_type(next) || is_qualifier(next)) + { + if(token=="buffer") + throw parse_error(tokenizer.get_location(), token, "buffer block declaration"); return parse_variable_declaration(); + } else { RefPtr iface_strct = parse_interface_block(); diff --git a/source/glsl/spirv.cpp b/source/glsl/spirv.cpp index b0855feb..a9cb302d 100644 --- a/source/glsl/spirv.cpp +++ b/source/glsl/spirv.cpp @@ -163,6 +163,8 @@ SpirVGenerator::StorageClass SpirVGenerator::get_interface_storage(const string return STORAGE_OUTPUT; else if(iface=="uniform") return (block ? STORAGE_UNIFORM : STORAGE_UNIFORM_CONSTANT); + else if(iface=="buffer") + return STORAGE_BUFFER; else if(iface.empty()) return STORAGE_PRIVATE; else @@ -445,7 +447,7 @@ SpirVGenerator::Id SpirVGenerator::get_variable_type_id(const VariableDeclaratio if(basic->kind==BasicTypeDeclaration::ARRAY) { if(!var.array_size) - throw logic_error("array without size"); + return get_array_type_id(*basic->base_type, 0, basic->extended_alignment); SetFlag set_const(constant_expression); r_expression_result_id = 0; diff --git a/source/glsl/spirvconstants.h b/source/glsl/spirvconstants.h index 2e952e05..5a7b876e 100644 --- a/source/glsl/spirvconstants.h +++ b/source/glsl/spirvconstants.h @@ -206,7 +206,8 @@ enum SpirVStorageClass STORAGE_OUTPUT = 3, STORAGE_PRIVATE = 6, STORAGE_FUNCTION = 7, - STORAGE_PUSH_CONSTANT = 9 + STORAGE_PUSH_CONSTANT = 9, + STORAGE_BUFFER = 12 }; enum SpirVDecoration diff --git a/source/glsl/validate.cpp b/source/glsl/validate.cpp index f6b11ef2..304bcd9e 100644 --- a/source/glsl/validate.cpp +++ b/source/glsl/validate.cpp @@ -120,7 +120,7 @@ void DeclarationValidator::visit(Layout &layout) } else if(iface_block) { - allowed = (iface_block->interface=="uniform"); + allowed = (iface_block->interface=="uniform" || iface_block->interface=="buffer"); err_descr = "non-uniform interface block"; } } @@ -394,8 +394,16 @@ void DeclarationValidator::visit(VariableDeclaration &var) error(var, "Type 'bool' not allowed on interface variable"); } - if(var.array && !var.array_size) - error(var, "Array must have a size"); + if(var.array) + { + if(iface_block && iface_block->interface=="buffer") + { + if(&var!=iface_block->block_declaration->members.body.back().get()) + error(var, "Unsized array is only allowed at the end of a storage block"); + } + else if(!var.array_size) + error(var, "Array must have a size"); + } if(var.init_expression) { diff --git a/tests/glsl/bad_runtime_sized_array.glsl b/tests/glsl/bad_runtime_sized_array.glsl new file mode 100644 index 00000000..c05afbd8 --- /dev/null +++ b/tests/glsl/bad_runtime_sized_array.glsl @@ -0,0 +1,16 @@ +buffer Data +{ + vec4 array[]; + float value; +}; + +#pragma MSP stage(vertex) +layout(location=0) in int i; +void main() +{ + gl_Position = array[i]; +} + +/* Expected error: +:3: Unsized array is only allowed at the end of a storage block +*/ diff --git a/tests/glsl/runtime_sized_array_in_storage_block.glsl b/tests/glsl/runtime_sized_array_in_storage_block.glsl new file mode 100644 index 00000000..9d17e1b0 --- /dev/null +++ b/tests/glsl/runtime_sized_array_in_storage_block.glsl @@ -0,0 +1,24 @@ +buffer Data +{ + vec4 array[]; +}; + +#pragma MSP stage(vertex) +layout(location=0) in int i; +void main() +{ + gl_Position = array[i]; +} + +/* Expected output: vertex +buffer Data +{ + vec4 array[]; +}; +layout(location=0) in int i; +void main() +{ + gl_Position = array[i]; + gl_Position.z = gl_Position.z*2.0-gl_Position.w; +} +*/