]> git.tdb.fi Git - libs/gl.git/commitdiff
Multiplex streaming buffer contents on Vulkan
authorMikko Rasa <tdb@tdb.fi>
Sun, 19 Dec 2021 11:39:08 +0000 (13:39 +0200)
committerMikko Rasa <tdb@tdb.fi>
Mon, 20 Dec 2021 00:14:45 +0000 (02:14 +0200)
This ensures that the data stays valid until the draw commands using it
have finished executing.

16 files changed:
source/backends/opengl/buffer_backend.cpp
source/backends/opengl/buffer_backend.h
source/backends/vulkan/buffer_backend.cpp
source/backends/vulkan/buffer_backend.h
source/backends/vulkan/commands_backend.cpp
source/backends/vulkan/commands_backend.h
source/backends/vulkan/pipelinestate_backend.cpp
source/backends/vulkan/pipelinestate_backend.h
source/backends/vulkan/program_backend.cpp
source/core/buffer.cpp
source/core/buffer.h
source/core/bufferable.cpp
source/core/bufferable.h
source/render/programdata.cpp
source/render/programdata.h
source/render/renderer.cpp

index 13944a86d439231e93603b84454fceed5f781900..6417633b72a34a6866ff4b73d761fa56bd697dc5 100644 (file)
@@ -39,7 +39,7 @@ OpenGLBuffer::~OpenGLBuffer()
 
 void OpenGLBuffer::allocate()
 {
-       size_t size = static_cast<const Buffer *>(this)->size;
+       size_t size = static_cast<const Buffer *>(this)->get_total_size();
 
        if(ARB_buffer_storage)
        {
index 3d76c01a0a5d4bd1de107dc99c58aa5ae66fc454..06c128af15ddbab4f5b3479eab6548a36268be96 100644 (file)
@@ -22,6 +22,8 @@ protected:
        void allocate();
        void sub_data(size_t, size_t, const void *);
 
+       unsigned get_multiplicity() const { return 1; }
+
        bool can_map() const { return true; }
        void *map();
        bool unmap();
index 548f0a51b97e6420c2a92ff2567d275378fc79eb..5439548f886fd1d81ba0ac16181f12c4a213f28f 100644 (file)
@@ -37,7 +37,7 @@ void VulkanBuffer::allocate()
 
        VkBufferCreateInfo buffer_info = { };
        buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
-       buffer_info.size = self.size;
+       buffer_info.size = self.get_total_size();
        buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT|VK_BUFFER_USAGE_TRANSFER_DST_BIT|VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT|
                VK_BUFFER_USAGE_INDEX_BUFFER_BIT|VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
        buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -72,6 +72,12 @@ void VulkanBuffer::sub_data(size_t off, size_t sz, const void *d)
        tq.finalize_transfer(staging);
 }
 
+unsigned VulkanBuffer::get_multiplicity() const
+{
+       BufferUsage usage = static_cast<const Buffer *>(this)->usage;
+       return (usage==STREAMING ? device.get_n_frames_in_flight() : 1);
+}
+
 bool VulkanBuffer::can_map() const
 {
        return static_cast<const Buffer *>(this)->usage==STREAMING;
index 3fcb2176defd9c0a1760dc106d9c07e0f77f8f0b..5902704f83e7460fb629f8f9f874b85ab34f72e6 100644 (file)
@@ -29,6 +29,8 @@ protected:
        void allocate();
        void sub_data(std::size_t, std::size_t, const void *);
 
+       unsigned get_multiplicity() const;
+
        bool can_map() const;
        void *map();
        bool unmap();
index b7a342defb5ab741eb16f3b9c3bc06af6f8d7555..bf8ee3f13d5652c21a14db09ff1e077e016740cc 100644 (file)
@@ -31,11 +31,12 @@ VulkanCommands::~VulkanCommands()
 
 void VulkanCommands::begin_buffer(VkRenderPass render_pass)
 {
-       if(!current_pool)
+       if(frame_index>=command_pools.size())
                throw invalid_operation("VulkanCommands::begin_buffer");
 
        const VulkanFunctions &vk = device.get_functions();
 
+       CommandPool *current_pool = &command_pools[frame_index];
        if(!current_pool->in_use)
        {
                current_pool->fence.reset();
@@ -176,15 +177,15 @@ void VulkanCommands::begin_frame(unsigned index)
 {
        const VulkanFunctions &vk = device.get_functions();
 
-       unsigned pool_index = index%device.get_n_frames_in_flight();
-       if(pool_index>=command_pools.size())
+       frame_index = index%device.get_n_frames_in_flight();
+       if(frame_index>=command_pools.size())
        {
-               command_pools.reserve(pool_index+1);
-               for(unsigned i=command_pools.size(); i<pool_index+1; ++i)
+               command_pools.reserve(frame_index+1);
+               for(unsigned i=command_pools.size(); i<frame_index+1; ++i)
                        command_pools.emplace_back(device);
        }
 
-       current_pool = &command_pools[pool_index];
+       CommandPool *current_pool = &command_pools[frame_index];
        if(current_pool->in_use)
        {
                current_pool->fence.wait();
@@ -220,7 +221,7 @@ void VulkanCommands::submit_frame(Semaphore *wait_sem, Semaphore *signal_sem)
        submit_info.signalSemaphoreCount = (signal_sem ? 1 : 0);
        submit_info.pSignalSemaphores = &vk_signal_sem;
 
-       vk.QueueSubmit(1, &submit_info, current_pool->fence.handle);
+       vk.QueueSubmit(1, &submit_info, command_pools[frame_index].fence.handle);
 
        primary_buffer = 0;
 }
@@ -258,7 +259,7 @@ void VulkanCommands::draw_instanced(const Batch &batch, unsigned count)
                 begin_render_pass(false, 0);
 
        pipeline_state->refresh();
-       pipeline_state->apply(pass_buffer, fb_is_swapchain);
+       pipeline_state->apply(pass_buffer, frame_index, fb_is_swapchain);
        unsigned first_index = batch.get_offset()/batch.get_index_size();
        vk.CmdDrawIndexed(pass_buffer, batch.size(), count, first_index, 0, 0);
 }
index 3f2c3be9bbf243029028f519360c4e5523f4b642..5fcae882a26a7c053ff72595c35521d32735d1bc 100644 (file)
@@ -43,7 +43,7 @@ protected:
 
        Device &device;
        std::vector<CommandPool> command_pools;
-       CommandPool *current_pool = 0;
+       unsigned frame_index = 0;
        VkCommandBuffer primary_buffer = 0;
        VkCommandBuffer pass_buffer = 0;
        const PipelineState *pipeline_state = 0;
index a6c9d8ce6754ce28d24002900b47511f21a16a33..172386d6eee9c8bcf85d4e41dfc91b55551d1f70 100644 (file)
@@ -327,7 +327,7 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, vector<char
                        write_ptr->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
                        write_ptr->dstBinding = u.binding&0xFFFFF;
                        write_ptr->descriptorCount = 1;
-                       write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+                       write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
                        write_ptr->pBufferInfo = buffer_ptr;
 
                        ++buffer_ptr;
@@ -357,7 +357,7 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, vector<char
        return n_writes;
 }
 
-void VulkanPipelineState::apply(VkCommandBuffer command_buffer, bool negative_viewport) const
+void VulkanPipelineState::apply(VkCommandBuffer command_buffer, unsigned frame, bool negative_viewport) const
 {
        const PipelineState &self = *static_cast<const PipelineState *>(this);
        const VulkanFunctions &vk = device.get_functions();
@@ -382,7 +382,21 @@ void VulkanPipelineState::apply(VkCommandBuffer command_buffer, bool negative_vi
        }
 
        if(!descriptor_set_handles.empty())
-               vk.CmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, self.shprog->layout_handle, 0, descriptor_set_handles.size(), descriptor_set_handles.data(), 0, 0);
+       {
+               vector<uint32_t> dynamic_offsets;
+               dynamic_offsets.reserve(self.uniform_blocks.size());
+               for(const PipelineState::BoundUniformBlock &u: self.uniform_blocks)
+                       if(u.used && u.binding>=0)
+                       {
+                               if(u.buffer->get_usage()==STREAMING)
+                                       dynamic_offsets.push_back(frame*u.buffer->get_size());
+                               else
+                                       dynamic_offsets.push_back(0);
+                       }
+
+               vk.CmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, self.shprog->layout_handle,
+                       0, descriptor_set_handles.size(), descriptor_set_handles.data(), dynamic_offsets.size(), dynamic_offsets.data());
+       }
 
        VkViewport viewport = { };
        if(self.viewport)
index 19f7f902d1e7257ba4430a4dd7d7af8b9244a12d..82f664c1c9b881ae7ffafd083111317cde9f6e29 100644 (file)
@@ -33,7 +33,7 @@ protected:
        VkDescriptorSetLayout get_descriptor_set_layout(unsigned) const;
        unsigned fill_descriptor_writes(unsigned, std::vector<char> &) const;
 
-       void apply(VkCommandBuffer, bool) const;
+       void apply(VkCommandBuffer, unsigned, bool) const;
 };
 
 using PipelineStateBackend = VulkanPipelineState;
index 60e96698bd313242374911112359a01c117ba446..50d772639adbeaf83bf3fbea40be303622639c58 100644 (file)
@@ -119,7 +119,7 @@ void VulkanProgram::finalize_uniforms()
                                bindings.emplace_back();
                                VkDescriptorSetLayoutBinding &binding = bindings.back();
                                binding.binding = b.bind_point;
-                               binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+                               binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
                                binding.descriptorCount = 1;
                                binding.stageFlags = VK_SHADER_STAGE_ALL;
                                binding.pImmutableSamplers = 0;
index 4a36937e7b745d1dd32f8c1e9ce19df0766037d7..ed0c552360ac8fad2345236e2024d17455200334 100644 (file)
@@ -34,7 +34,7 @@ void Buffer::sub_data(size_t off, size_t sz, const void *d)
 {
        if(size==0)
                throw invalid_operation("Buffer::sub_data");
-       if(off>size || off+sz>size)
+       if(off>get_total_size() || off%size+sz>size)
                throw out_of_range("Buffer::sub_data");
 
        BufferBackend::sub_data(off, sz, d);
index 17678fd4b4008f68a7dd2f3745916fd237526a10..8aba95b10f08176be725bbafb0aa4b5771e6e8c8 100644 (file)
@@ -58,6 +58,8 @@ public:
        void sub_data(std::size_t, std::size_t, const void *);
 
        std::size_t get_size() const { return size; }
+       using BufferBackend::get_multiplicity;
+       std::size_t get_total_size() const { return size*get_multiplicity(); }
        BufferUsage get_usage() const { return usage; }
 
        void require_size(std::size_t) const;
index 502c270c8f6bad09861841847e1dc71d70d006c7..69588dddef301d72f0eda7128209a5fba632cc63 100644 (file)
@@ -128,14 +128,20 @@ void Bufferable::update_offset()
 
 void Bufferable::mark_dirty()
 {
-       dirty = true;
+       dirty = 0xFF;
 }
 
-void Bufferable::upload_data(char *target) const
+void Bufferable::upload_data(unsigned frame, char *target) const
 {
        if(!buffer)
                throw invalid_operation("Bufferable::upload_data");
 
+       unsigned multi_buf = buffer->get_multiplicity();
+       frame %= multi_buf;
+       uint8_t mask = 1<<frame;
+       if(!(dirty&mask))
+               return;
+
        size_t data_size = get_data_size();
        if(location_dirty)
        {
@@ -149,8 +155,17 @@ void Bufferable::upload_data(char *target) const
                copy(source, source+data_size, target);
        }
        else
-               buffer->sub_data(offset, data_size, get_data_pointer());
-       dirty = false;
+               buffer->sub_data(frame*buffer->get_size()+offset, data_size, get_data_pointer());
+       dirty &= ~mask;
+       if(!(dirty&((1<<multi_buf)-1)))
+               dirty = 0;
+}
+
+Bufferable::AsyncUpdater *Bufferable::create_async_updater() const
+{
+       if(!buffer || buffer->get_multiplicity()>1)
+               throw invalid_operation("Bufferable::create_async_updater");
+       return new AsyncUpdater(*this);
 }
 
 
@@ -168,14 +183,14 @@ Bufferable::AsyncUpdater::~AsyncUpdater()
 
 void Bufferable::AsyncUpdater::upload_data()
 {
-       bufferable.upload_data(mapped_address+bufferable.offset);
+       bufferable.upload_data(0, mapped_address+bufferable.offset);
        // Update all bufferables in the same buffer at once
        for(const Bufferable *b=bufferable.prev_in_buffer; b; b=b->prev_in_buffer)
                if(b->dirty)
-                       b->upload_data(mapped_address+b->offset);
+                       b->upload_data(0, mapped_address+b->offset);
        for(const Bufferable *b=bufferable.next_in_buffer; b; b=b->next_in_buffer)
                if(b->dirty)
-                       b->upload_data(mapped_address+b->offset);
+                       b->upload_data(0, mapped_address+b->offset);
 }
 
 } // namespace GL
index 25d29fd8f393021c6d6af4f86e624d77ec79705b..b2b25765af2ef67b88c3c9e91bb7e785ee97b1b5 100644 (file)
@@ -63,7 +63,7 @@ public:
        std::size_t get_required_buffer_size(bool = false) const;
 
        /** Uploads new data into the buffer if necessary. */
-       void refresh(unsigned f) const { if(dirty&(1<<f)) upload_data(f, 0); }
+       void refresh(unsigned f) const { if(dirty) upload_data(f, 0); }
 
        /** Returns an object which can be used to upload data to the buffer using
        mapped memory.  If data is not dirty, returns null. */
index fc9b30a99e14b5011eba96d426f555a5467012f1..f6ad56aee13b6dc0ed740180c24e1a4b32dc57e8 100644 (file)
@@ -664,16 +664,17 @@ vector<ProgramData::ProgramBlock>::const_iterator ProgramData::prepare_program(c
        return prog_begin;
 }
 
-void ProgramData::apply(const Program &prog, PipelineState &state) const
+void ProgramData::apply(const Program &prog, PipelineState &state, unsigned frame) const
 {
        auto prog_begin = prepare_program(prog);
        ReflectData::LayoutHash prog_hash = prog_begin->prog_hash;
+
        for(auto i=prog_begin+1; (i!=programs.end() && i->prog_hash==prog_hash); ++i)
                if(i->block)
                {
                        state.set_uniform_block(i->bind_point, i->block);
                        if(i->bind_point>=0)
-                               i->block->refresh();
+                               i->block->refresh(frame);
                }
 }
 
index f1c5996eb910e3e51368571472e94e6902d12bec..5d674a38a2504f8360d338f5ac74b85f3545689e 100644 (file)
@@ -258,7 +258,7 @@ private:
 public:
        /** Creates or updates UniformBlocks for a specific program if necessary,
        then sets them to the PipelineState. */
-       void apply(const Program &, PipelineState &) const;
+       void apply(const Program &, PipelineState &, unsigned) const;
 
        void set_debug_name(const std::string &);
 };
index 946f0100bbbf0a5467877e451343d643a0f13670..d3124f0da334c7439e630266db0eef7c8c4654cf 100644 (file)
@@ -247,7 +247,7 @@ void Renderer::clear(const ClearValue *values)
 void Renderer::draw(const Batch &batch)
 {
        apply_state();
-       batch.refresh();
+       batch.refresh(frame_index);
        pipeline_state.set_primitive_type(batch.get_type());
        commands.use_pipeline(&pipeline_state);
        commands.draw(batch);
@@ -256,7 +256,7 @@ void Renderer::draw(const Batch &batch)
 void Renderer::draw_instanced(const Batch &batch, unsigned count)
 {
        apply_state();
-       batch.refresh();
+       batch.refresh(frame_index);
        pipeline_state.set_primitive_type(batch.get_type());
        commands.use_pipeline(&pipeline_state);
        commands.draw_instanced(batch, count);
@@ -323,7 +323,7 @@ void Renderer::apply_state()
                        shdata_stack.erase(shdata_stack.begin()+state.shdata_count, shdata_stack.end());
                for(const BoundProgramData &d: shdata_stack)
                {
-                       d.shdata->apply(*state.shprog, pipeline_state);
+                       d.shdata->apply(*state.shprog, pipeline_state, frame_index);
                        d.generation = d.shdata->get_generation();
                }
                changed &= ~SHADER_DATA;
@@ -332,9 +332,9 @@ void Renderer::apply_state()
        if(state.vertex_setup)
        {
                if(const VertexArray *array = state.vertex_setup->get_vertex_array())
-                       array->refresh();
+                       array->refresh(frame_index);
                if(const VertexArray *array = state.vertex_setup->get_instance_array())
-                       array->refresh();
+                       array->refresh(frame_index);
        }
        pipeline_state.set_vertex_setup(state.vertex_setup);