#include <msp/core/algorithm.h>
#include "buffer.h"
#include "device.h"
+#include "error.h"
#include "texture.h"
#include "synchronizer.h"
#include "vulkan.h"
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;
i->pending_write = true;
}
+void Synchronizer::split_image_mipmap(VkImage image, unsigned aspect, unsigned n_levels)
+{
+ 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(i);
+ i->image = image;
+ i->aspect = aspect;
+ i->level = (level<0 ? -1 : level);
+ i->current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ }
+
+ 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()
{
for(BufferAccess &b: buffer_accesses)
b.pending_write = false;
+ for(ImageAccess &i: image_accesses)
+ i.pending_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
void Synchronizer::barrier(VkCommandBuffer command_buffer)
{
const VulkanFunctions &vk = device.get_functions();
- if(buffer_accesses.empty())
+ if(buffer_accesses.empty() && image_accesses.empty())
return;
VkPipelineStageFlags src_stage = 0;
buffer_barriers.reserve(buffer_accesses.size());
for(BufferAccess &b: buffer_accesses)
{
+ if(b.pending_write==b.was_written)
+ continue;
+
buffer_barriers.emplace_back(VkBufferMemoryBarrier{ });
VkBufferMemoryBarrier &barrier = buffer_barriers.back();
dst_stage |= (b.pending_write ? buffer_write_stages : buffer_read_stages);
}
+ static constexpr VkPipelineStageFlags image_read_stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT|VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT|
+ VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT|VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
+ static constexpr VkPipelineStageFlags image_write_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT|
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT|VK_PIPELINE_STAGE_TRANSFER_BIT;
+
+ vector<VkImageMemoryBarrier> image_barriers;
+ image_barriers.reserve(image_accesses.size());
+ for(const ImageAccess &i: image_accesses)
+ {
+ if(i.level==-2 || i.pending_layout==i.current_layout)
+ continue;
+
+ image_barriers.emplace_back(VkImageMemoryBarrier{ });
+ VkImageMemoryBarrier &barrier = image_barriers.back();
+
+ barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ barrier.srcAccessMask = (is_write_layout(i.current_layout) ? VK_ACCESS_MEMORY_WRITE_BIT : 0);
+ barrier.dstAccessMask = (is_write_layout(i.pending_layout) ? VK_ACCESS_MEMORY_WRITE_BIT : VK_ACCESS_MEMORY_READ_BIT);
+ barrier.oldLayout = static_cast<VkImageLayout>(i.current_layout);
+ barrier.newLayout = static_cast<VkImageLayout>(i.pending_layout);
+ barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+ barrier.image = handle_cast<::VkImage>(i.image);
+ barrier.subresourceRange.aspectMask = i.aspect;
+ 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);
+ dst_stage |= (is_write_layout(i.pending_layout) ? image_write_stages : image_read_stages);
+ }
+
+ if(buffer_barriers.empty() && image_barriers.empty())
+ return;
+
if(!src_stage)
src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
if(!dst_stage)
dst_stage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
vk.CmdPipelineBarrier(command_buffer, src_stage, dst_stage, 0, 0, 0,
- buffer_barriers.size(), buffer_barriers.data(), 0, 0);
+ buffer_barriers.size(), buffer_barriers.data(), image_barriers.size(), image_barriers.data());
for(auto i=buffer_accesses.begin(); i!=buffer_accesses.end(); )
{
++i;
}
}
+
+ for(auto i=image_accesses.begin(); i!=image_accesses.end(); )
+ {
+ if(i->level!=-1)
+ {
+ auto j = i;
+ if(j->level==-2)
+ ++j;
+
+ bool remove_image = true;
+ for(; (j!=image_accesses.end() && j->image==i->image); ++j)
+ remove_image &= (j->pending_layout==VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+
+ if(remove_image)
+ i = image_accesses.erase(i, j);
+ else
+ {
+ for(; i!=j; ++i)
+ i->current_layout = i->pending_layout;
+ }
+ }
+ else if(i->pending_layout==VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
+ i = image_accesses.erase(i);
+ else
+ {
+ i->current_layout = i->pending_layout;
+ ++i;
+ }
+ }
+}
+
+bool Synchronizer::is_write_layout(unsigned layout)
+{
+ return layout==VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL || layout==VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ||
+ layout==VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
}
} // namespace GL