]> git.tdb.fi Git - libs/gl.git/commitdiff
Refactor Synchronizer to deal with individual mipmap levels
authorMikko Rasa <tdb@tdb.fi>
Tue, 30 Nov 2021 21:30:27 +0000 (23:30 +0200)
committerMikko Rasa <tdb@tdb.fi>
Tue, 30 Nov 2021 21:43:45 +0000 (23:43 +0200)
Some algorithms read from one mipmap level and write to another.  On the
other hand, there's currently no need for layer granularity.

12 files changed:
source/backends/vulkan/buffer_backend.cpp
source/backends/vulkan/commands_backend.cpp
source/backends/vulkan/framebuffer_backend.cpp
source/backends/vulkan/framebuffer_backend.h
source/backends/vulkan/synchronizer.cpp
source/backends/vulkan/synchronizer.h
source/backends/vulkan/texture1d_backend.cpp
source/backends/vulkan/texture2d_backend.cpp
source/backends/vulkan/texture3d_backend.cpp
source/backends/vulkan/texture_backend.cpp
source/backends/vulkan/texture_backend.h
source/backends/vulkan/texturecube_backend.cpp

index 29dd15ef363fa26fe36794e3d1a877bc821cb996..9290b42aefe2fff301735eaabf0684f4f7b2f327 100644 (file)
@@ -52,7 +52,7 @@ void VulkanBuffer::allocate()
 
 void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d)
 {
-       device.get_synchronizer().access(handle, off, sz);
+       device.get_synchronizer().write_buffer(handle, off, sz);
 
        void *staging = device.get_transfer_queue().prepare_transfer(sz, [this, off, sz](VkCommandBuffer cmd_buf, VkBuffer staging_buf, size_t src_off){
                const VulkanFunctions &vk = device.get_functions();
index 6f5703f60d1c98ac7e9563dc8360638d082b11fb..d55a4dfdbad3095bc253f57820f9bfa37a10c775 100644 (file)
@@ -85,7 +85,7 @@ void VulkanCommands::begin_render_pass(bool clear, const ClearValue *clear_value
                if(dynamic_cast<const SwapChainTexture *>(framebuffer->get_attachment(i)))
                        to_present = true;
        if(!to_present)
-               framebuffer->synchronize(clear);
+               framebuffer->prepare_image_layouts(clear);
        VkRenderPass render_pass = device.get_pipeline_cache().get_render_pass(framebuffer->get_format(), clear, !clear_values, to_present);
 
        framebuffer->refresh();
index 9320fffb176832e87cf8878bd0c2d034d54effca..2ebd8b33d067fb6844a237e7bf6e38995d7b446b 100644 (file)
@@ -82,10 +82,10 @@ void VulkanFramebuffer::update(unsigned) const
                set_vulkan_object_name();
 }
 
-void VulkanFramebuffer::synchronize(bool discard) const
+void VulkanFramebuffer::prepare_image_layouts(bool discard) const
 {
        for(const Framebuffer::Attachment &a: static_cast<const Framebuffer *>(this)->attachments)
-               a.tex->synchronize(a.layer, get_vulkan_attachment_layout(get_components(a.tex->get_format())), discard);
+               a.tex->change_layout(0, a.level, get_vulkan_attachment_layout(get_components(a.tex->get_format())), discard);
 }
 
 void VulkanFramebuffer::set_debug_name(const string &name)
index 9a616875e8f888f8515fdec02202e97e738fcffa..b990d307db6dbf4c119917553a3c56073f208c11 100644 (file)
@@ -31,7 +31,7 @@ protected:
        void update(unsigned) const;
        void require_complete() const { }
 
-       void synchronize(bool = false) const;
+       void prepare_image_layouts(bool = false) const;
 
        void set_debug_name(const std::string &);
        void set_vulkan_object_name() const;
index c2c245a5a094cec43f7a3ec7a45970361ac64a64..3ec04026a09ae2e2617c3461e4a746544f0ab119 100644 (file)
@@ -1,6 +1,7 @@
 #include <msp/core/algorithm.h>
 #include "buffer.h"
 #include "device.h"
+#include "error.h"
 #include "texture.h"
 #include "synchronizer.h"
 #include "vulkan.h"
@@ -14,12 +15,12 @@ Synchronizer::Synchronizer(Device &d):
        device(d)
 { }
 
-void Synchronizer::access(VkBuffer buffer, size_t offset, size_t size)
+void Synchronizer::write_buffer(VkBuffer buffer, size_t offset, size_t size)
 {
-       auto i = find_member(buffer_accesses, buffer, &BufferAccess::buffer);
-       if(i==buffer_accesses.end())
+       auto i = lower_bound_member(buffer_accesses, buffer, &BufferAccess::buffer);
+       if(i==buffer_accesses.end() || i->buffer!=buffer)
        {
-               i = buffer_accesses.emplace(buffer_accesses.end());
+               i = buffer_accesses.emplace(i);
                i->buffer = buffer;
                i->offset = offset;
                i->size = size;
@@ -35,23 +36,79 @@ void Synchronizer::access(VkBuffer buffer, size_t offset, size_t size)
        i->pending_write = true;
 }
 
-void Synchronizer::access(VkImage image, unsigned aspect, int layer, unsigned layout, bool discard)
+void Synchronizer::split_image_mipmap(VkImage image, unsigned aspect, unsigned n_levels)
 {
-       auto i = find_member(image_accesses, image, &ImageAccess::image);
-       if(i==image_accesses.end())
+       if(!n_levels)
+               throw invalid_argument("Synchronizer::split_image_mipmap");
+
+       auto i = lower_bound_member(image_accesses, image, &ImageAccess::image);
+       if(i!=image_accesses.end() && i->image==image && i->level>=0)
+               return;
+
+       if(i!=image_accesses.end() && i->image==image && i->level==-1)
+       {
+               i = image_accesses.insert(i, n_levels-1, *i);
+               for(unsigned j=0; j<n_levels; ++i, ++j)
+                       i->level = j;
+       }
+       else
+       {
+               ImageAccess access;
+               access.image = image;
+               access.aspect = aspect;
+               access.current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+               access.pending_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+               if(i->level==-2)
+                       i = image_accesses.erase(i);
+
+               for(unsigned j=0; j<n_levels; ++i, ++j)
+                       if(i==image_accesses.end() || i->image!=image || i->level>static_cast<int>(j))
+                       {
+                               i = image_accesses.insert(i, access);
+                               i->level = j;
+                       }
+       }
+}
+
+void Synchronizer::change_image_layout(VkImage image, unsigned aspect, int level, unsigned layout, bool discard)
+{
+       auto i = lower_bound_member(image_accesses, image, &ImageAccess::image);
+
+       if(level>=0)
+       {
+               if(i==image_accesses.end() || i->image!=image)
+               {
+                       i = image_accesses.emplace(i);
+                       i->image = image;
+                       i->level = -2;
+                       ++i;
+               }
+               else if(i->level==-1)
+                       throw invalid_operation("Synchronizer::change_image_layout");
+               else
+               {
+                       for(; (i!=image_accesses.end() && i->image==image && i->level<level); ++i) ;
+               }
+       }
+       else if(i!=image_accesses.end() && i->image==image && i->level==-2)
+               throw invalid_operation("Synchronizer::change_image_layout");
+
+       if(i==image_accesses.end() || i->image!=image || (level>=0 && i->level!=level))
        {
-               i = image_accesses.emplace(image_accesses.end());
+               i = image_accesses.emplace(i);
                i->image = image;
                i->aspect = aspect;
-               i->layer = layer;
+               i->level = (level<0 ? -1 : level);
                i->current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
        }
 
-       if(discard)
-               i->current_layout = VK_IMAGE_LAYOUT_UNDEFINED;
-       if(layer!=i->layer)
-               i->layer = -1;
-       i->pending_layout = layout;
+       for(; (i!=image_accesses.end() && i->image==image && (level<0 || i->level==level)); ++i)
+       {
+               if(discard)
+                       i->current_layout = VK_IMAGE_LAYOUT_UNDEFINED;
+               i->pending_layout = layout;
+       }
 }
 
 void Synchronizer::reset()
@@ -108,7 +165,7 @@ void Synchronizer::barrier(VkCommandBuffer command_buffer)
        image_barriers.reserve(image_accesses.size());
        for(const ImageAccess &i: image_accesses)
        {
-               if(i.pending_layout==i.current_layout)
+               if(i.level==-2 || i.pending_layout==i.current_layout)
                        continue;
 
                image_barriers.emplace_back(VkImageMemoryBarrier{ });
@@ -123,18 +180,10 @@ void Synchronizer::barrier(VkCommandBuffer command_buffer)
                barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
                barrier.image = handle_cast<::VkImage>(i.image);
                barrier.subresourceRange.aspectMask = i.aspect;
-               barrier.subresourceRange.baseMipLevel = 0;
-               barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
-               if(i.layer>=0)
-               {
-                       barrier.subresourceRange.baseArrayLayer = i.layer;
-                       barrier.subresourceRange.layerCount = 1;
-               }
-               else
-               {
-                       barrier.subresourceRange.baseArrayLayer = 0;
-                       barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
-               }
+               barrier.subresourceRange.baseMipLevel = max(i.level, 0);
+               barrier.subresourceRange.levelCount = (i.level<0 ? VK_REMAINING_MIP_LEVELS : 1);
+               barrier.subresourceRange.baseArrayLayer = 0;
+               barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
 
                if(i.current_layout!=VK_IMAGE_LAYOUT_UNDEFINED)
                        src_stage |= (is_write_layout(i.current_layout) ? image_write_stages : image_read_stages);
@@ -163,10 +212,29 @@ void Synchronizer::barrier(VkCommandBuffer command_buffer)
                }
        }
 
+       bool sparse_levels = false;
        for(auto i=image_accesses.begin(); i!=image_accesses.end(); )
        {
-               if(i->pending_layout==VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+               if(i->level==-2)
+               {
+                       sparse_levels = true;
+                       ++i;
+               }
+               else if(i->pending_layout==VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+               {
+                       VkImage image = i->image;
                        i = image_accesses.erase(i);
+                       if(i->image!=image)
+                       {
+                               if(sparse_levels)
+                               {
+                                       auto j = prev(i);
+                                       if(j->level==-2)
+                                               i = image_accesses.erase(j);
+                               }
+                               sparse_levels = false;
+                       }
+               }
                else
                {
                        i->current_layout = i->pending_layout;
index 48699540f32b302fb8b376abd3993a2947d60848..78c3443ed0761d32e19326b6fd7473602a832e6d 100644 (file)
@@ -15,7 +15,7 @@ private:
        {
                VkImage image = 0;
                unsigned aspect;
-               int layer = -1;
+               int level = -1;
                unsigned current_layout;
                unsigned pending_layout;
        };
@@ -36,8 +36,9 @@ private:
 public:
        Synchronizer(Device &);
 
-       void access(VkBuffer, std::size_t, std::size_t);
-       void access(VkImage, unsigned, int, unsigned, bool);
+       void write_buffer(VkBuffer, std::size_t, std::size_t);
+       void split_image_mipmap(VkImage, unsigned, unsigned);
+       void change_image_layout(VkImage, unsigned, int, unsigned, bool);
        void reset();
        void barrier(VkCommandBuffer);
 
index aaa3c385bb41e030d4a3ab6c39d7f66d871e6d13..91c6252a85d1544597f7a273e3e7e40a0fa8a381 100644 (file)
@@ -27,7 +27,7 @@ void VulkanTexture1D::sub_image(unsigned level, int x, unsigned wd, const void *
        const Texture1D &self = *static_cast<const Texture1D *>(this);
 
        unsigned level_size = self.get_level_size(level);
-       synchronize(-1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && wd==level_size));
+       change_layout(self.levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && wd==level_size));
 
        size_t data_size = wd*get_pixel_size(storage_fmt);
        void *staging = device.get_transfer_queue().prepare_transfer(data_size,
index 2d6ee131056b8975512b8a0b82c1e1e66cbb4145..d3db86d3ae27efe214ec2acd6586e96350ad4599 100644 (file)
@@ -28,7 +28,7 @@ void VulkanTexture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsig
        const Texture2D &self = *static_cast<const Texture2D *>(this);
 
        auto level_size = self.get_level_size(level);
-       synchronize(-1, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && y==0 && wd==level_size.x && ht==level_size.y));
+       change_layout(self.levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && y==0 && wd==level_size.x && ht==level_size.y));
 
        size_t data_size = wd*ht*get_pixel_size(storage_fmt);
        void *staging = device.get_transfer_queue().prepare_transfer(data_size,
index 7a11b35e2eec04d1e84378f4fa892fe4619cd836..03a957344e11083fb0bfdcb0ac8e2eb051aa28a7 100644 (file)
@@ -33,8 +33,8 @@ void VulkanTexture3D::sub_image(unsigned level, int x, int y, int z, unsigned wd
        const Texture3D &self = *static_cast<const Texture3D *>(this);
 
        auto level_size = self.get_level_size(level);
-       int layer = (is_array() && dp==1 ? z : -1);
-       synchronize(layer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && y==0 && z==0 && wd==level_size.x && ht==level_size.y && dp==level_size.z));
+       bool discard = (x==0 && y==0 && z==0 && wd==level_size.x && ht==level_size.y && dp==level_size.z);
+       change_layout(self.levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, discard);
 
        size_t data_size = wd*ht*dp*get_pixel_size(storage_fmt);
        void *staging = device.get_transfer_queue().prepare_transfer(data_size,
index 06c4a622341955b1064539afe11b369c0620b9e6..f1e8b85bab162e32926db81f78ced507752e5d04 100644 (file)
@@ -73,7 +73,7 @@ void VulkanTexture::allocate()
                memory_id = device.get_allocator().allocate(handle, DEVICE_MEMORY);
 
                // Trigger a layout transition if the image is used before uploading data.
-               synchronize(-1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true);
+               change_layout(0, -1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true);
        }
 
        VkImageViewCreateInfo view_info = { };
@@ -105,10 +105,12 @@ void VulkanTexture::generate_mipmap()
        throw logic_error("VulkanTexture::generate_mipmap is unimplemented");
 }
 
-void VulkanTexture::synchronize(int layer, unsigned layout, bool discard) const
+void VulkanTexture::change_layout(unsigned n_levels, int level, unsigned layout, bool discard) const
 {
        unsigned aspect = get_vulkan_aspect(get_components(static_cast<const Texture *>(this)->storage_fmt));
-       device.get_synchronizer().access(handle, aspect, layer, layout, discard);
+       if(n_levels>0)
+               device.get_synchronizer().split_image_mipmap(handle, aspect, n_levels);
+       device.get_synchronizer().change_image_layout(handle, aspect, level, layout, discard);
 }
 
 void VulkanTexture::set_debug_name(const string &name)
index 4784ae9a9b5104d8f69f121fc204f2ed8db23869..123c6e4b278efd1d9f3325c351737241800f9d49 100644 (file)
@@ -32,7 +32,7 @@ protected:
 
        void generate_mipmap();
 
-       void synchronize(int, unsigned, bool = false) const;
+       void change_layout(unsigned, int, unsigned, bool) const;
 
        void set_debug_name(const std::string &);
        void set_vulkan_object_names() const;
index fe528921f8c99b135537e4ae397f2978cabb62cf..38348f34b8636abd0b09e0e3c478fd694abccaea 100644 (file)
@@ -30,7 +30,7 @@ void VulkanTextureCube::sub_image(unsigned face, unsigned level, int x, int y, u
        const TextureCube &self = *static_cast<const TextureCube *>(this);
 
        unsigned level_size = self.get_level_size(level);
-       synchronize(face, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && y==0 && wd==level_size && ht==level_size));
+       change_layout(self.levels, level, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (x==0 && y==0 && wd==level_size && ht==level_size));
 
        size_t data_size = wd*ht*get_pixel_size(storage_fmt);
        void *staging = device.get_transfer_queue().prepare_transfer(data_size,