Address some regressions with TFB + SSBO's due to WFI removal

This commit is contained in:
CamilleLaVey
2026-09-25 18:05:12 -04:00
parent bc121efc19
commit d2d2220b24
9 changed files with 125 additions and 15 deletions
+20
View File
@@ -137,6 +137,22 @@ public:
content_serial = ++next_content_serial;
}
[[nodiscard]] bool HasDrawHazard(u64 pass, u64 wfi, DAddr addr, u64 size) const noexcept {
return draw_write_pass == pass && draw_write_wfi < wfi && addr < draw_write_end &&
addr + size > draw_write_begin;
}
void MarkDrawWrite(u64 pass, u64 wfi, DAddr addr, u64 size) noexcept {
if (draw_write_pass != pass) {
draw_write_pass = pass;
draw_write_begin = addr;
draw_write_end = addr + size;
}
draw_write_wfi = wfi;
draw_write_begin = (std::min)(draw_write_begin, addr);
draw_write_end = (std::max)(draw_write_end, addr + size);
}
private:
static inline u64 next_content_serial = 0;
@@ -147,6 +163,10 @@ private:
size_t size_bytes = 0;
u64 write_tick = 0;
u64 content_serial = ++next_content_serial;
u64 draw_write_pass = 0;
u64 draw_write_wfi = 0;
DAddr draw_write_begin = 0;
DAddr draw_write_end = 0;
};
} // namespace VideoCommon
@@ -366,6 +366,13 @@ void BufferCache<P>::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
template <class P>
void BufferCache<P>::UpdateGraphicsBuffers(bool is_indexed) {
if constexpr (!IS_OPENGL) {
draw_writes.clear();
draw_pass = runtime.RenderPassSerial();
draw_wfi = runtime.WaitForIdleSerial();
draw_hazard = false;
recording_draw = true;
}
ReclaimInline();
do {
channel_state->has_deleted_buffers = false;
@@ -373,6 +380,24 @@ void BufferCache<P>::UpdateGraphicsBuffers(bool is_indexed) {
} while (channel_state->has_deleted_buffers);
}
template <class P>
bool BufferCache<P>::TakeDrawHazard() noexcept {
return std::exchange(draw_hazard, false);
}
template <class P>
void BufferCache<P>::CommitDrawWrites() {
recording_draw = false;
if (draw_writes.empty()) {
return;
}
const u64 pass = runtime.RenderPassSerial();
for (const DrawWrite& write : draw_writes) {
slot_buffers[write.buffer_id].MarkDrawWrite(pass, draw_wfi, write.device_addr, write.size);
}
runtime.MarkRenderPassWrites();
}
template <class P>
void BufferCache<P>::UpdateComputeBuffers() {
ReclaimInline();
@@ -1550,6 +1575,9 @@ void BufferCache<P>::MarkWrittenBuffer(BufferId buffer_id, DAddr device_addr, u3
Buffer& buffer = slot_buffers[buffer_id];
buffer.setWriteTick(runtime.CurrentTick());
buffer.MarkContentModified();
if (recording_draw) {
draw_writes.push_back({buffer_id, device_addr, size});
}
}
memory_tracker.MarkRegionAsGpuModified(device_addr, size);
gpu_modified_ranges.Add(device_addr, size);
@@ -1770,6 +1798,9 @@ void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
template <class P>
bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size) {
if constexpr (!IS_OPENGL) {
draw_hazard |= buffer.HasDrawHazard(draw_pass, draw_wfi, device_addr, size);
}
upload_copies.clear();
u64 total_size_bytes = 0;
u64 largest_copy = 0;
@@ -260,6 +260,10 @@ public:
void UpdateComputeBuffers();
[[nodiscard]] bool TakeDrawHazard() noexcept;
void CommitDrawWrites();
void BindHostGeometryBuffers(bool is_indexed);
void BindHostStageBuffers(size_t stage);
@@ -520,6 +524,17 @@ private:
boost::container::small_vector<BufferCopy, 4> upload_copies;
struct DrawWrite {
BufferId buffer_id;
DAddr device_addr;
u32 size;
};
boost::container::small_vector<DrawWrite, 8> draw_writes;
u64 draw_pass = 0;
u64 draw_wfi = 0;
bool draw_hazard = false;
bool recording_draw = false;
MemoryTracker memory_tracker;
Common::RangeSet<DAddr> uncommitted_gpu_modified_ranges;
Common::RangeSet<DAddr> gpu_modified_ranges;
@@ -440,6 +440,18 @@ u64 BufferCacheRuntime::CurrentTick() {
return scheduler.GetMasterSemaphore().CurrentTick();
}
u64 BufferCacheRuntime::RenderPassSerial() const noexcept {
return scheduler.ActiveRenderPassSerial();
}
u64 BufferCacheRuntime::WaitForIdleSerial() const noexcept {
return scheduler.WaitForIdleSerial();
}
void BufferCacheRuntime::MarkRenderPassWrites() noexcept {
scheduler.MarkRenderPassWrites();
}
u64 BufferCacheRuntime::KnownGpuTick() {
return scheduler.GetMasterSemaphore().KnownGpuTick();
}
@@ -112,6 +112,12 @@ public:
u64 CurrentTick();
u64 RenderPassSerial() const noexcept;
u64 WaitForIdleSerial() const noexcept;
void MarkRenderPassWrites() noexcept;
u64 KnownGpuTick();
void Wait(u64 buffer_tick);
@@ -595,7 +595,11 @@ bool GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
}
}
if (buffer_cache.TakeDrawHazard()) {
scheduler.RequestOutsideRenderPassOperationContext();
}
scheduler.RequestRenderpass(texture_cache.GetFramebuffer());
buffer_cache.CommitDrawWrites();
if (!is_built.load(std::memory_order::relaxed)) {
// Wait for the pipeline to be built
scheduler.Record([this](vk::CommandBuffer) {
@@ -894,6 +894,7 @@ void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size,
void RasterizerVulkan::WaitForIdle() {
query_cache.NotifyWFI();
scheduler.NotifyWaitForIdle();
fence_manager.SignalOrdering();
}
+13 -15
View File
@@ -98,6 +98,7 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer, VkRenderPass
state.renderpass = renderpass;
state.framebuffer = framebuffer_handle;
state.render_area = render_area;
++renderpass_serial;
if (GPU::Logging::IsActive() && Settings::values.gpu_log_vulkan_calls.GetValue()) {
const std::string render_pass_info =
@@ -417,8 +418,17 @@ void Scheduler::EndRenderPass()
Record([num_images = num_renderpass_images,
images = renderpass_images,
ranges = renderpass_image_ranges,
has_transform_feedback = device.IsExtTransformFeedbackSupported()](
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];
@@ -459,20 +469,8 @@ void Scheduler::EndRenderPass()
};
}
cmdbuf.EndRenderPass();
cmdbuf.PipelineBarrier(0, {}, {}, vk::Span(barriers.data(), num_images));
if (has_transform_feedback) {
static constexpr VkMemoryBarrier2 XFB_OUTPUT_BARRIER{
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2,
.pNext = nullptr,
.srcStageMask = VK_PIPELINE_STAGE_2_TRANSFORM_FEEDBACK_BIT_EXT,
.srcAccessMask = VK_ACCESS_2_TRANSFORM_FEEDBACK_WRITE_BIT_EXT,
.dstStageMask = VK_PIPELINE_STAGE_2_VERTEX_INPUT_BIT
| VK_PIPELINE_STAGE_2_TRANSFER_BIT,
.dstAccessMask = VK_ACCESS_2_VERTEX_ATTRIBUTE_READ_BIT
| VK_ACCESS_2_TRANSFER_READ_BIT,
};
cmdbuf.PipelineBarrier(XFB_OUTPUT_BARRIER);
}
cmdbuf.PipelineBarrier(0, vk::Span(&WRITE_BARRIER, num_memory_barriers), {},
vk::Span(barriers.data(), num_images));
});
state.renderpass = VkRenderPass{};
@@ -9,6 +9,7 @@
#include <condition_variable>
#include <cstddef>
#include <functional>
#include <limits>
#include <memory>
#include <thread>
#include <utility>
@@ -77,6 +78,25 @@ public:
return state.renderpass != VK_NULL_HANDLE;
}
u64 ActiveRenderPassSerial() const noexcept {
if (state.renderpass) {
return renderpass_serial;
}
return (std::numeric_limits<u64>::max)();
}
u64 WaitForIdleSerial() const noexcept {
return wfi_serial;
}
void NotifyWaitForIdle() noexcept {
++wfi_serial;
}
void MarkRenderPassWrites() noexcept {
renderpass_writes = true;
}
/// Update the pipeline to the current execution context.
bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline);
@@ -311,6 +331,9 @@ private:
State state;
u64 renderpass_serial = 0;
u64 wfi_serial = 0;
bool renderpass_writes = false;
u32 num_renderpass_images = 0;
std::array<VkImage, 9> renderpass_images{};
std::array<VkImageSubresourceRange, 9> renderpass_image_ranges{};