Another try to make barriers more explicit

This commit is contained in:
CamilleLaVey
2026-09-26 02:31:24 -04:00
parent d832f5e156
commit 9d4355064b
5 changed files with 103 additions and 71 deletions
@@ -36,6 +36,24 @@ struct VertexBindings {
u32 count;
};
constexpr VkMemoryBarrier2 READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS | VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = vk::ACCESS_TRANSFER,
};
constexpr VkMemoryBarrier2 WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS,
.dstAccessMask = vk::ACCESS_BUFFER_INPUTS,
};
VkBufferCopy MakeBufferCopy(const VideoCommon::BufferCopy& copy) {
return VkBufferCopy{
.srcOffset = copy.src_offset,
@@ -482,22 +500,6 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
if (dst_buffer == VK_NULL_HANDLE || src_buffer == VK_NULL_HANDLE) {
return;
}
static constexpr VkMemoryBarrier2 READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT,
};
static constexpr VkMemoryBarrier2 WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT,
};
// Measuring a popular game, this number never exceeds the specified size once data is warmed up
boost::container::small_vector<VkBufferCopy, 8> vk_copies(copies.size());
@@ -523,14 +525,6 @@ void BufferCacheRuntime::CopyBuffer(VkBuffer dst_buffer, VkBuffer src_buffer,
}
void BufferCacheRuntime::PreCopyBarrier() {
static constexpr VkMemoryBarrier2 READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT,
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(READ_BARRIER);
@@ -538,14 +532,6 @@ void BufferCacheRuntime::PreCopyBarrier() {
}
void BufferCacheRuntime::PostCopyBarrier() {
static constexpr VkMemoryBarrier2 WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT,
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(WRITE_BARRIER);
@@ -556,22 +542,6 @@ void BufferCacheRuntime::ClearBuffer(VkBuffer dest_buffer, u32 offset, size_t si
if (dest_buffer == VK_NULL_HANDLE) {
return;
}
static constexpr VkMemoryBarrier2 READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT,
};
static constexpr VkMemoryBarrier2 WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT,
};
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([dest_buffer, offset, size, value](vk::CommandBuffer cmdbuf) {
@@ -631,19 +631,19 @@ void RasterizerVulkan::DispatchCompute() {
static constexpr VkMemoryBarrier2 READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask =
VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT,
.srcStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS,
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = vk::ACCESS_SHADER_RESOURCES,
};
static constexpr VkMemoryBarrier2 WRITE_BARRIER{
static constexpr VkMemoryBarrier2 INDIRECT_READ_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.srcStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS,
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask =
VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.dstAccessMask = VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT | vk::ACCESS_SHADER_RESOURCES,
};
const auto& qmd{kepler_compute->launch_description};
auto indirect_address = kepler_compute->GetIndirectComputeAddress();
@@ -653,15 +653,14 @@ void RasterizerVulkan::DispatchCompute() {
const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite;
const auto [buffer, offset] =
buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.RequestComputeDispatchContext();
scheduler.Record([pipeline, indirect_buffer = buffer->Handle(),
indirect_offset = offset](vk::CommandBuffer cmdbuf) {
if (!pipeline->IsBound()) {
return;
}
cmdbuf.PipelineBarrier(READ_BARRIER);
cmdbuf.PipelineBarrier(INDIRECT_READ_BARRIER);
cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset);
cmdbuf.PipelineBarrier(WRITE_BARRIER);
});
return;
}
@@ -670,14 +669,13 @@ void RasterizerVulkan::DispatchCompute() {
if (dim[0] > max_dim[0] || dim[1] > max_dim[1] || dim[2] > max_dim[2]) {
return;
}
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.RequestComputeDispatchContext();
scheduler.Record([pipeline, dim](vk::CommandBuffer cmdbuf) {
if (!pipeline->IsBound()) {
return;
}
cmdbuf.PipelineBarrier(READ_BARRIER);
cmdbuf.Dispatch(dim[0], dim[1], dim[2]);
cmdbuf.PipelineBarrier(WRITE_BARRIER);
});
// Log compute dispatch
@@ -47,6 +47,12 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
: device{device_}, state_tracker{state_tracker_},
master_semaphore{std::make_unique<MasterSemaphore>(device)},
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
if (device.IsExtTransformFeedbackSupported()) {
renderpass_write_barrier.srcStageMask |= VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT;
renderpass_write_barrier.srcAccessMask |= VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT;
renderpass_write_barrier.dstStageMask |= VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT;
renderpass_write_barrier.dstAccessMask |= VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT;
}
AcquireNewChunk();
AllocateWorkerCommandBuffer();
@@ -95,6 +101,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass
const VkClearValue* clear_values, u32 clear_value_count) {
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
const VkExtent2D render_area = framebuffer->RenderArea();
PublishComputeWrites();
state.renderpass = renderpass;
state.framebuffer = framebuffer_handle;
state.render_area = render_area;
@@ -222,6 +229,31 @@ void Scheduler::RequestRenderpass(const Framebuffer* framebuffer) {
void Scheduler::RequestOutsideRenderPassOperationContext() {
EndRenderPass();
PublishComputeWrites();
}
void Scheduler::RequestComputeDispatchContext() {
EndRenderPass();
compute_writes = true;
}
void Scheduler::PublishComputeWrites() {
if (!std::exchange(compute_writes, false)) {
return;
}
Record([](vk::CommandBuffer cmdbuf) {
static constexpr VkMemoryBarrier2 COMPUTE_WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS | vk::PIPELINE_STAGE_ATTACHMENTS |
VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask =
vk::ACCESS_BUFFER_INPUTS | vk::ACCESS_ATTACHMENTS | vk::ACCESS_TRANSFER,
};
cmdbuf.PipelineBarrier(COMPUTE_WRITE_BARRIER);
});
}
bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
@@ -420,17 +452,10 @@ void Scheduler::EndRenderPass()
Record([num_images = num_renderpass_images,
images = renderpass_images,
ranges = renderpass_image_ranges,
write_barrier = &renderpass_write_barrier,
num_memory_barriers =
static_cast<size_t>(std::exchange(renderpass_writes, false))](
vk::CommandBuffer cmdbuf) {
static constexpr VkMemoryBarrier2 WRITE_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT,
};
std::array<VkImageMemoryBarrier2, 9> barriers;
for (size_t i = 0; i < num_images; ++i) {
const VkImageSubresourceRange& range = ranges[i];
@@ -471,7 +496,7 @@ void Scheduler::EndRenderPass()
};
}
cmdbuf.EndRenderPass();
cmdbuf.PipelineBarrier(0, vk::Span(&WRITE_BARRIER, num_memory_barriers), {},
cmdbuf.PipelineBarrier(0, vk::Span(write_barrier, num_memory_barriers), {},
vk::Span(barriers.data(), num_images));
});
@@ -73,6 +73,8 @@ public:
/// of a renderpass.
void RequestOutsideRenderPassOperationContext();
void RequestComputeDispatchContext();
/// Returns true when a render pass is currently active in the scheduler state.
bool IsRenderPassActive() const {
return state.renderpass != VK_NULL_HANDLE;
@@ -319,6 +321,8 @@ private:
void EndRenderPass();
void PublishComputeWrites();
void AcquireNewChunk();
const Device& device;
@@ -343,6 +347,16 @@ private:
u64 wfi_serial = 0;
bool renderpass_writes = false;
bool renderpass_depth_writes = false;
bool compute_writes = false;
VkMemoryBarrier2 renderpass_write_barrier{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = vk::PIPELINE_STAGE_GRAPHICS_SHADERS,
.srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
.dstStageMask = vk::PIPELINE_STAGE_BUFFER_INPUTS | vk::PIPELINE_STAGE_ATTACHMENTS |
VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = vk::ACCESS_BUFFER_INPUTS | vk::ACCESS_ATTACHMENTS | vk::ACCESS_TRANSFER,
};
u32 num_renderpass_images = 0;
std::array<VkImage, 9> renderpass_images{};
std::array<VkImageSubresourceRange, 9> renderpass_image_ranges{};
@@ -158,6 +158,31 @@ inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER_
inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_HOST = VK_PIPELINE_STAGE_2_HOST_BIT;
inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_GRAPHICS_SHADERS =
VK_PIPELINE_STAGE_2_PRE_RASTERIZATION_SHADERS_BIT | VK_PIPELINE_STAGE_2_FRAGMENT_SHADER_BIT;
inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_BUFFER_INPUTS =
VK_PIPELINE_STAGE_2_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT |
PIPELINE_STAGE_GRAPHICS_SHADERS | VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT;
inline constexpr VkPipelineStageFlags2 PIPELINE_STAGE_ATTACHMENTS =
VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
inline constexpr VkAccessFlags2 ACCESS_SHADER_RESOURCES =
VK_ACCESS_2_UNIFORM_READ_BIT | VK_ACCESS_2_SHADER_READ_BIT | VK_ACCESS_2_SHADER_WRITE_BIT;
inline constexpr VkAccessFlags2 ACCESS_BUFFER_INPUTS =
VK_ACCESS_2_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_2_INDEX_READ_BIT |
VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT | ACCESS_SHADER_RESOURCES;
inline constexpr VkAccessFlags2 ACCESS_ATTACHMENTS =
VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
inline constexpr VkAccessFlags2 ACCESS_TRANSFER =
VK_ACCESS_2_TRANSFER_READ_BIT | VK_ACCESS_2_TRANSFER_WRITE_BIT;
/// Table holding Vulkan instance function pointers.
struct InstanceDispatch {