]> git.tdb.fi Git - libs/gl.git/commitdiff
Add storage block support to the shader compiler
authorMikko Rasa <tdb@tdb.fi>
Sun, 16 Jul 2023 20:02:29 +0000 (23:02 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 16 Jul 2023 23:15:44 +0000 (02:15 +0300)
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.

source/glsl/features.cpp
source/glsl/features.h
source/glsl/finalize.cpp
source/glsl/output.cpp
source/glsl/parser.cpp
source/glsl/spirv.cpp
source/glsl/spirvconstants.h
source/glsl/validate.cpp
tests/glsl/bad_runtime_sized_array.glsl [new file with mode: 0644]
tests/glsl/runtime_sized_array_in_storage_block.glsl [new file with mode: 0644]

index d66226e327fee4a384b7d2eabb60cf0a21a98c02..4dc4339096166438b6386e31aa2bfb313175d4b6 100644 (file)
@@ -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");
index a92fbf6140c0740accfc4232447aa727d18c74a5..5596f0da3eed5350f375585b811d97111b44bda9 100644 (file)
@@ -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);
index 91d2e44c52560fd6ff5fde842e9ef1d3d9f29076..fc6370eec4a5fb0eb3024293b3348ffb537642f2 100644 (file)
@@ -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")
index d28de156c5a881fede9fb116052370102338c80a..2b82ddbcc352510917fe38e2f6cc3c7e102745d8 100644 (file)
@@ -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)
index 615d75d426a38e1e2e83dabf09fee6edd23b78e7..db60789443bdd59ce10b13ae9550b20fd60f804f 100644 (file)
@@ -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<Statement> 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<StructDeclaration> iface_strct = parse_interface_block();
index b0855feb63ac121123422987b91b47dd57d64af9..a9cb302db9558b18cac6f0e3b3077f070138616d 100644 (file)
@@ -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;
index 2e952e058bde58b4cc21eca56e745244666959b6..5a7b876ed8b353319d8729104cf66577f109fe5a 100644 (file)
@@ -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
index f6b11ef22e34f8e019a43c5ff040eb8f05283c08..304bcd9ec4ecdd9afd61c754a48bdf238995737c 100644 (file)
@@ -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 (file)
index 0000000..c05afbd
--- /dev/null
@@ -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:
+<test>: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 (file)
index 0000000..9d17e1b
--- /dev/null
@@ -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;
+}
+*/