]> git.tdb.fi Git - libs/gl.git/commitdiff
Add support for storage images in Renderer and PipelineState
authorMikko Rasa <tdb@tdb.fi>
Fri, 15 Apr 2022 20:10:37 +0000 (23:10 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 15 Apr 2022 21:17:01 +0000 (00:17 +0300)
16 files changed:
extensions/arb_shader_image_load_store.glext [new file with mode: 0644]
source/backends/opengl/device_backend.cpp
source/backends/opengl/device_backend.h
source/backends/opengl/pipelinestate_backend.cpp
source/backends/vulkan/device_backend.cpp
source/backends/vulkan/pipelinestate_backend.cpp
source/backends/vulkan/pipelinestate_backend.h
source/backends/vulkan/program_backend.cpp
source/backends/vulkan/texture_backend.cpp
source/core/datatype.h
source/core/device.h
source/core/module.cpp
source/core/pipelinestate.cpp
source/core/pipelinestate.h
source/render/renderer.cpp
source/render/renderer.h

diff --git a/extensions/arb_shader_image_load_store.glext b/extensions/arb_shader_image_load_store.glext
new file mode 100644 (file)
index 0000000..591da5d
--- /dev/null
@@ -0,0 +1 @@
+extension ARB_shader_image_load_store
index db711e5101bae9036eb2edc82fb645e2ed3e3813..0fcbe07212ccb93783af6e473a7177783d9b98b2 100644 (file)
@@ -93,6 +93,7 @@ void OpenGLDevice::fill_info()
        feat.storage_texture_binding_range = lim.max_storage_texture_bindings;
 
        state.bound_tex_targets.resize(lim.max_texture_bindings);
+       state.bound_storage_textures.resize(lim.max_storage_texture_bindings);
        state.bound_uniform_blocks.resize(lim.max_uniform_bindings);
 }
 
index 491f1b3173a3aefc3a4da3d07b0123a7c69d64e6..2583d5822173af93ce0bb92882c72f5e392aa124 100644 (file)
@@ -16,6 +16,7 @@ struct OpenGLDeviceState
 {
        const OpenGLPipelineState *last_pipeline = 0;
        std::vector<int> bound_tex_targets;
+       std::vector<char> bound_storage_textures;
        std::vector<char> bound_uniform_blocks;
        unsigned restart_index = 0;
        unsigned n_clip_distances = 0;
index 3b8c6a5ae736c3f33543f9febb1c4b5a17696142..a2b8ab32717555bc329b85a515f2b58818738ff0 100644 (file)
@@ -1,5 +1,6 @@
 #include <msp/gl/extensions/arb_direct_state_access.h>
 #include <msp/gl/extensions/arb_sampler_objects.h>
+#include <msp/gl/extensions/arb_shader_image_load_store.h>
 #include <msp/gl/extensions/arb_shader_objects.h>
 #include <msp/gl/extensions/arb_uniform_buffer_object.h>
 #include <msp/gl/extensions/arb_vertex_array_object.h>
@@ -128,7 +129,7 @@ void OpenGLPipelineState::apply() const
                                                        call.func(call.location, call.size, data+call.location*16);
                                        }
                                }
-                               else if(r.type==PipelineState::TEXTURE)
+                               else if(r.type==PipelineState::SAMPLED_TEXTURE)
                                {
                                        if(ARB_direct_state_access)
                                                glBindTextureUnit(r.binding, r.texture->id);
@@ -145,6 +146,14 @@ void OpenGLPipelineState::apply() const
                                        glBindSampler(r.binding, r.sampler->id);
                                        r.sampler->refresh();
                                }
+                               else if(r.type==PipelineState::STORAGE_TEXTURE)
+                               {
+                                       static Require _req(ARB_shader_image_load_store);
+                                       GLenum gl_format = get_gl_pixelformat(r.texture->get_format());
+                                       glBindImageTexture(r.binding, r.texture->id, 0, true, 0, GL_READ_WRITE, gl_format);
+
+                                       dev_state.bound_storage_textures[r.binding] = 1;
+                               }
                        }
 
                        r.changed = false;
@@ -260,6 +269,13 @@ void OpenGLPipelineState::clear()
                                dev_state.bound_tex_targets[i] = 0;
                        }
 
+               for(unsigned i=0; i<dev_state.bound_storage_textures.size(); ++i)
+                       if(dev_state.bound_storage_textures[i])
+                       {
+                               glBindImageTexture(i, 0, 0, true, 0, GL_READ_ONLY, GL_RGBA8);
+                               dev_state.bound_storage_textures[i] = 0;
+                       }
+
                for(unsigned i=0; i<dev_state.bound_uniform_blocks.size(); ++i)
                        if(dev_state.bound_uniform_blocks[i])
                        {
index 8e3f2ab621a9acc55823d2f0c2ea22b4331a082a..9a8b930d713f6d100f3351a0a91040232c3f19f7 100644 (file)
@@ -56,7 +56,6 @@ void VulkanDevice::fill_info()
        limits.max_clip_planes = props.limits.maxClipDistances;
        limits.max_vertex_attributes = props.limits.maxVertexInputAttributes;
        limits.max_texture_bindings = props.limits.maxDescriptorSetSampledImages;
-       limits.max_storage_texture_bindings = props.limits.maxDescriptorSetStorageImages;
        limits.max_color_attachments = props.limits.maxColorAttachments;
        unsigned samples = props.limits.framebufferColorSampleCounts&props.limits.framebufferDepthSampleCounts&props.limits.framebufferStencilSampleCounts;
        if(samples&VK_SAMPLE_COUNT_64_BIT)
index f32818049ef0b906c173edc6efb0ede4247a549f..5f9e7eb9185a4e812c1826edf0e071677e2b20d7 100644 (file)
@@ -64,12 +64,13 @@ void VulkanPipelineState::update() const
                        {
                                if(r.type==PipelineState::UNIFORM_BLOCK)
                                        r.used = self.shprog->uses_uniform_block_binding(r.binding);
-                               else if(r.type==PipelineState::TEXTURE)
+                               else if(r.type==PipelineState::SAMPLED_TEXTURE || r.type==PipelineState::STORAGE_TEXTURE)
                                {
                                        r.used = self.shprog->uses_texture_binding(r.binding);
                                        if(r.mip_level>=0)
                                                r.texture->refresh_mip_views();
-                                       r.sampler->refresh();
+                                       if(r.type==PipelineState::SAMPLED_TEXTURE)
+                                               r.sampler->refresh();
                                }
                                if(r.binding>=0)
                                        changed_sets |= 1<<(r.binding>>20);
@@ -285,12 +286,14 @@ uint64_t VulkanPipelineState::compute_descriptor_set_hash(unsigned index) const
                        result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->block));
                        result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->buffer->handle));
                }
-               else if(i->type==PipelineState::TEXTURE)
+               else if(i->type==PipelineState::SAMPLED_TEXTURE)
                {
                        result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->texture->handle));
                        result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->sampler->handle));
                        result = hash_update<64>(result, i->mip_level);
                }
+               else if(i->type==PipelineState::STORAGE_TEXTURE)
+                       result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->texture->handle));
                empty = false;
        }
 
@@ -330,7 +333,7 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr
                {
                        if(i->type==PipelineState::UNIFORM_BLOCK)
                                ++n_buffers;
-                       else if(i->type==PipelineState::TEXTURE)
+                       else if(i->type==PipelineState::SAMPLED_TEXTURE || i->type==PipelineState::STORAGE_TEXTURE)
                                ++n_images;
                }
        unsigned n_writes = n_buffers+n_images;
@@ -365,19 +368,28 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr
 
                        ++buffer_ptr;
                }
-               else if(i->type==PipelineState::TEXTURE)
+               else if(i->type==PipelineState::SAMPLED_TEXTURE || i->type==PipelineState::STORAGE_TEXTURE)
                {
-                       image_ptr->sampler = handle_cast<::VkSampler>(i->sampler->handle);
                        if(i->mip_level<0)
                                image_ptr->imageView = handle_cast<::VkImageView>(i->texture->view_handle);
                        else
                                image_ptr->imageView = handle_cast<::VkImageView>(i->texture->mip_view_handles[i->mip_level]);
-                       image_ptr->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+                       if(i->type==PipelineState::SAMPLED_TEXTURE)
+                       {
+                               image_ptr->sampler = handle_cast<::VkSampler>(i->sampler->handle);
+                               image_ptr->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+                               write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+                       }
+                       else if(i->type==PipelineState::STORAGE_TEXTURE)
+                       {
+                               image_ptr->imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+                               write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+                       }
 
                        write_ptr->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
                        write_ptr->dstBinding = i->binding&0xFFFFF;
                        write_ptr->descriptorCount = 1;
-                       write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
                        write_ptr->pImageInfo = image_ptr;
 
                        ++image_ptr;
@@ -389,6 +401,15 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr
        return n_writes;
 }
 
+void VulkanPipelineState::synchronize_resources(bool discard_fb_contents) const
+{
+       const PipelineState &self = *static_cast<const PipelineState *>(this);
+
+       for(const PipelineState::BoundResource &r: self.resources)
+               if(r.type==PipelineState::STORAGE_TEXTURE)
+                       r.texture->change_layout(-1, VK_IMAGE_LAYOUT_GENERAL, false);
+}
+
 void VulkanPipelineState::apply(const VulkanCommandRecorder &vkCmd, const VulkanPipelineState *last, unsigned frame, bool negative_viewport) const
 {
        const PipelineState &self = *static_cast<const PipelineState *>(this);
index 892d8b3a12a127cc20d64b445a0a56bb07f601b1..37c96a4e2d394f967f0f75d0f0b4f802bea6bab2 100644 (file)
@@ -39,6 +39,7 @@ protected:
        VkDescriptorSetLayout get_descriptor_set_layout(unsigned) const;
        unsigned fill_descriptor_writes(unsigned, unsigned, std::vector<char> &) const;
 
+       void synchronize_resources(bool) const;
        void apply(const VulkanCommandRecorder &, const VulkanPipelineState *, unsigned, bool) const;
 };
 
index 9402226b1e68917bf00273bd10ae31ff519d3f4c..a962a78e61a5f3a9d1ccbd08310b6b15faea4f7d 100644 (file)
@@ -138,7 +138,10 @@ void VulkanProgram::finalize_uniforms()
                                bindings.emplace_back();
                                VkDescriptorSetLayoutBinding &binding = bindings.back();
                                binding.binding = u.binding&0xFFFFF;
-                               binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+                               if(is_sampled_image(u.type))
+                                       binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+                               else
+                                       binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
                                binding.descriptorCount = 1;
                                binding.stageFlags = stage_flags;
                                binding.pImmutableSamplers = 0;
index 5b73749884892aad06f69589648e4bad16b663aa..1367cc8d46c2a87a24eddb892b93b0d7cb142817 100644 (file)
@@ -49,9 +49,13 @@ void VulkanTexture::allocate()
        const Texture &self = *static_cast<const Texture *>(this);
        const VulkanFunctions &vk = device.get_functions();
 
+       VkFormat vk_format = static_cast<VkFormat>(get_vulkan_pixelformat(self.storage_fmt));
+       VkFormatProperties props;
+       vk.GetPhysicalDeviceFormatProperties(vk_format, props);
+
        VkImageCreateInfo image_info = { };
        image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
-       image_info.format = static_cast<VkFormat>(get_vulkan_pixelformat(self.storage_fmt));
+       image_info.format = vk_format;
        image_info.extent.width = 1;
        image_info.extent.height = 1;
        image_info.extent.depth = 1;
@@ -63,6 +67,9 @@ void VulkanTexture::allocate()
        image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
 
        image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_SAMPLED_BIT;
+       if(props.optimalTilingFeatures&VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)
+               image_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
+
        PixelComponents comp = get_components(self.storage_fmt);
        if(comp==DEPTH_COMPONENT || comp==STENCIL_INDEX)
                image_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
index d2739c49b6f7471ad3fed90c5e12de0ec27f4911..29789907cdb78ca9863c328251aa826419b72442 100644 (file)
@@ -101,6 +101,7 @@ inline bool is_float(DataType t) { return t&0x200; }
 inline bool is_matrix(DataType t) { return t&0xC000; }
 inline bool is_vector(DataType t) { return !is_matrix(t) && (t&0x3000); }
 inline bool is_image(DataType t) { return t&0x70000; }
+inline bool is_sampled_image(DataType t) { return t&0x100000; }
 
 inline DataType get_matrix_column_type(DataType t)
 {
index 97a28e73f6307b852c9305808be39e7cb08a3b48..8c7e92a99f0144e6d4e0f8052314239d90913191 100644 (file)
@@ -17,6 +17,7 @@ struct DeviceLimits
        unsigned max_clip_planes = 6;
        unsigned max_vertex_attributes = 16;
        unsigned max_texture_bindings = 16;
+       unsigned max_storage_texture_bindings = 8;
        unsigned max_color_attachments = 8;
        unsigned max_samples = 4;
        unsigned max_uniform_bindings = 24;
index 5475e434ac2636331c846a529bdf99fb508efb04..0870c4782c9f90d620b8ecda500d985f8bd363ab 100644 (file)
@@ -623,11 +623,13 @@ void SpirVModule::Reflection::reflect_matrix_type(CodeIterator op)
 void SpirVModule::Reflection::reflect_image_type(CodeIterator op)
 {
        TypeInfo &type = types[*(op+1)];
-       DataType sample = types[*(op+2)].type;
+       DataType sample_type = types[*(op+2)].type;
        unsigned dimensions = *(op+3);
        bool depth = *(op+4)==1;
        bool array = *(op+5);
-       type.type = static_cast<DataType>((depth*0x200000) | (array*0x80000) | ((dimensions+1)<<16) | sample);
+       bool sampled = *(op+7)==1;
+       type.type = static_cast<DataType>((depth*0x200000) | (sampled*0x100000) | (array*0x80000) |
+               ((dimensions+1)<<16) | sample_type);
 }
 
 void SpirVModule::Reflection::reflect_sampled_image_type(CodeIterator op)
index 5ad51502010a1b1c2a2c37e7dd21b2a680dbdf2f..4dc54e2403ffea9288375df8dbfa09eb01f50283 100644 (file)
@@ -70,11 +70,21 @@ void PipelineState::set_texture(unsigned binding, const Texture *tex, int level,
        if(level>=0 && !can_bind_tex_level(level))
                throw invalid_operation("PipelineState::set_texture");
 
+       set_texture_resource(binding, tex, level, samp);
+}
+
+void PipelineState::set_storage_texture(unsigned binding, const Texture *tex)
+{
+       set_texture_resource(binding, tex, 0, 0);
+}
+
+void PipelineState::set_texture_resource(unsigned binding, const Texture *tex, int level, const Sampler *samp)
+{
        auto i = lower_bound_member(resources, static_cast<int>(binding), &BoundResource::binding);
        if(i==resources.end() || i->binding!=static_cast<int>(binding))
                i = resources.insert(i, BoundResource(binding));
 
-       ResourceType type = (tex ? TEXTURE : NO_RESOURCE);
+       ResourceType type = (tex ? samp ? SAMPLED_TEXTURE : STORAGE_TEXTURE : NO_RESOURCE);
        if(i->type!=type || tex!=i->texture || level!=i->mip_level || samp!=i->sampler)
        {
                i->type = type;
@@ -82,7 +92,7 @@ void PipelineState::set_texture(unsigned binding, const Texture *tex, int level,
                i->sampler = samp;
                i->mip_level = level;
                i->changed = true;
-               i->used = (tex && samp);
+               i->used = tex;
                changes |= RESOURCES;
        }
 }
index e7fdfdc83ff6e17be21b35e0c01b0e8b4c02fcb2..965bc15f16f11e5dbcfef30068dad621cee0a3a3 100644 (file)
@@ -36,7 +36,8 @@ private:
        {
                NO_RESOURCE,
                UNIFORM_BLOCK,
-               TEXTURE
+               SAMPLED_TEXTURE,
+               STORAGE_TEXTURE
        };
 
        struct BoundResource
@@ -98,6 +99,10 @@ public:
        void set_uniform_block(int, const UniformBlock *);
        void set_texture(unsigned, const Texture *, const Sampler *);
        void set_texture(unsigned, const Texture *, int, const Sampler *);
+       void set_storage_texture(unsigned, const Texture *);
+private:
+       void set_texture_resource(unsigned, const Texture *, int, const Sampler *);
+public:
        void set_vertex_setup(const VertexSetup *);
        void set_primitive_type(PrimitiveType);
        void set_front_face(FaceWinding);
index f2fb002e73bcbae24ab54f0b7ab16c6b42a6e520..828efd70ab0695dfc8773dd478061e0eb826aebf 100644 (file)
@@ -181,6 +181,12 @@ void Renderer::set_texture(Tag tag, const Texture *tex, int level, const Sampler
        set_resource(texture_stack, state.texture_count, tag, { tex, samp, level });
 }
 
+void Renderer::set_storage_texture(Tag tag, const Texture *tex)
+{
+       State &state = get_state();
+       set_resource(texture_stack, state.texture_count, tag, { tex, 0, 0 });
+}
+
 template<typename T>
 void Renderer::set_resource(vector<BoundResource<T>> &stack, unsigned &count, Tag tag, const T &res)
 {
@@ -413,7 +419,12 @@ void Renderer::apply_state()
                        if(t.binding<0 || shprog_changed)
                                t.binding = state.shprog->get_uniform_binding(t.tag);
                        if(t.binding>=0)
-                               ps.set_texture(t.binding, t.resource.texture, t.resource.level, t.resource.sampler);
+                       {
+                               if(t.resource.sampler)
+                                       ps.set_texture(t.binding, t.resource.texture, t.resource.level, t.resource.sampler);
+                               else
+                                       ps.set_storage_texture(t.binding, t.resource.texture);
+                       }
                }
 
        static const DepthTest default_depth_test;
index 9335164b38477993d9202db61f9f140e67cfb232..fb460cf07824cfea911c651d897c2aa88bce053b 100644 (file)
@@ -197,6 +197,7 @@ public:
 
        void set_texture(Tag, const Texture *, const Sampler * = 0);
        void set_texture(Tag, const Texture *, int, const Sampler * = 0);
+       void set_storage_texture(Tag, const Texture *);
 
 private:
        template<typename T>