Remove deferred clears

This commit is contained in:
CamilleLaVey
2026-09-30 00:47:14 -04:00
parent 63fc150635
commit 41d8da1612
11 changed files with 122 additions and 159 deletions
@@ -75,8 +75,6 @@ public:
void Finish();
void FlushDeferredClear() {}
StagingBufferMap UploadStagingBuffer(size_t size);
StagingBufferMap DownloadStagingBuffer(size_t size, bool deferred = false);
@@ -476,6 +476,23 @@ void RecordShaderReadBarrier(Scheduler& scheduler, const ImageView& image_view)
});
}
void DiscardCoveredAttachments(Scheduler& scheduler, const Framebuffer* framebuffer,
const Region2D& region, bool writes_stencil) {
const VkExtent2D area = framebuffer->RenderArea();
if ((std::min)(region.start.x, region.end.x) > 0 ||
(std::min)(region.start.y, region.end.y) > 0 ||
(std::max)(region.start.x, region.end.x) < static_cast<s32>(area.width) ||
(std::max)(region.start.y, region.end.y) < static_cast<s32>(area.height) ||
framebuffer->Attachments().layers != 1) {
return;
}
u32 attachments = 1u | Scheduler::DEPTH_ATTACHMENT_BIT;
if (writes_stencil) {
attachments |= Scheduler::STENCIL_ATTACHMENT_BIT;
}
scheduler.OverrideLoadOps(framebuffer, attachments, VK_ATTACHMENT_LOAD_OP_DONT_CARE, {});
}
[[nodiscard]] VkSampleCountFlagBits SampleCountFlag(u32 num_samples) {
switch (num_samples) {
case 2:
@@ -661,6 +678,7 @@ void BlitImageHelper::BlitImpl(const Framebuffer* dst_framebuffer,
}
RecordShaderReadBarrier(scheduler, src_image_view);
DiscardCoveredAttachments(scheduler, dst_framebuffer, dst_region, blit_stencil);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([this, dst_region, src_region, pipeline, layout, sampler, src_view,
src_stencil_view, blit_stencil](vk::CommandBuffer cmdbuf) {
@@ -1067,8 +1085,13 @@ void BlitImageHelper::Convert(VkPipeline pipeline, const Framebuffer* dst_frameb
const VkImageView src_view = src_image_view.Handle(Shader::TextureType::Color2D);
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent = GetConversionExtent(src_image_view);
const Region2D region{
.start = {0, 0},
.end = {static_cast<s32>(extent.width), static_cast<s32>(extent.height)},
};
RecordShaderReadBarrier(scheduler, src_image_view);
DiscardCoveredAttachments(scheduler, dst_framebuffer, region, false);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_view, extent, this](vk::CommandBuffer cmdbuf) {
const VkOffset2D offset{
@@ -1112,8 +1135,13 @@ void BlitImageHelper::ConvertDepthStencil(VkPipeline pipeline, const Framebuffer
const VkImageView src_stencil_view = src_image_view.StencilView();
const VkSampler sampler = *nearest_sampler;
const VkExtent2D extent = GetConversionExtent(src_image_view);
const Region2D region{
.start = {0, 0},
.end = {static_cast<s32>(extent.width), static_cast<s32>(extent.height)},
};
RecordShaderReadBarrier(scheduler, src_image_view);
DiscardCoveredAttachments(scheduler, dst_framebuffer, region, false);
scheduler.RequestRenderpass(dst_framebuffer);
scheduler.Record([pipeline, layout, sampler, src_depth_view, src_stencil_view, extent,
this](vk::CommandBuffer cmdbuf) {
@@ -143,6 +143,9 @@ struct AttachmentAccess {
AttachmentAccess MakeAttachmentAccess(const Maxwell& regs) {
AttachmentAccess access{};
if (regs.rasterize_enable == 0) {
return access;
}
for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
size_t mask_index = index;
if (regs.color_mask_common) {
@@ -1402,6 +1402,10 @@ void QueryCacheRuntime::ResumeHostConditionalRendering() {
impl->is_hcr_running = true;
}
bool QueryCacheRuntime::IsHostConditionalRenderingSet() const noexcept {
return impl->hcr_is_set;
}
void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object,
bool is_equal) {
{
@@ -48,6 +48,8 @@ public:
void ResumeHostConditionalRendering();
[[nodiscard]] bool IsHostConditionalRenderingSet() const noexcept;
bool HostConditionalRenderingCompareValue(VideoCommon::LookupData object_1, bool qc_dirty);
bool HostConditionalRenderingCompareValues(VideoCommon::LookupData object_1,
@@ -445,15 +445,10 @@ void RasterizerVulkan::Clear(u32 layer_count) {
const Framebuffer* const framebuffer = texture_cache.GetFramebuffer();
const VkExtent2D render_area = framebuffer->RenderArea();
constexpr bool ENABLE_DEFERRED_CLEAR = true;
const bool color_full_channels = regs.clear_surface.R && regs.clear_surface.G &&
regs.clear_surface.B && regs.clear_surface.A;
const bool stencil_partial = use_stencil && framebuffer->HasAspectStencilBit() &&
regs.stencil_front_mask != 0xFF && regs.stencil_front_mask != 0;
const bool ds_used = use_depth || use_stencil;
const bool ds_deferrable =
!ds_used || ((!framebuffer->HasAspectDepthBit() || use_depth) &&
(!framebuffer->HasAspectStencilBit() || use_stencil) && !stencil_partial);
u32 up_scale = 1;
u32 down_shift = 0;
if (texture_cache.IsRescaling()) {
@@ -519,17 +514,16 @@ void RasterizerVulkan::Clear(u32 layer_count) {
clear_rect.rect.offset.x == 0 && clear_rect.rect.offset.y == 0 &&
clear_rect.rect.extent.width >= render_area.width &&
clear_rect.rect.extent.height >= render_area.height;
const bool can_defer_clear = ENABLE_DEFERRED_CLEAR && (!regs.clear_control.use_scissor || clear_covers_render_area) &&
regs.clear_surface.layer == 0 &&
layer_count >= framebuffer->Attachments().layers &&
!scheduler.IsRenderPassActive() &&
(!use_color || color_full_channels) && ds_deferrable;
if (!can_defer_clear) {
const bool clear_on_load = (!regs.clear_control.use_scissor || clear_covers_render_area) &&
regs.clear_surface.layer == 0 &&
layer_count >= framebuffer->Attachments().layers &&
!query_cache_runtime.IsHostConditionalRenderingSet();
const auto request_renderpass = [&] {
scheduler.RequestRenderpass(framebuffer);
query_cache.NotifySegment(true);
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
maxwell3d->regs.zpass_pixel_count_enable);
}
};
UpdateViewportsState(regs);
const u32 color_attachment = regs.clear_surface.RT;
@@ -549,26 +543,26 @@ void RasterizerVulkan::Clear(u32 layer_count) {
clear_value.color.int32[i] = s32(f32(s64(int_size - 1) << 1) * (regs.clear_color[i] - 0.5f));
}
if (color_full_channels) {
if (can_defer_clear) {
scheduler.DeferColorClear(framebuffer, color_attachment, clear_value);
} else {
scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
const VkClearAttachment attachment{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.colorAttachment = color_attachment,
.clearValue = clear_value,
};
cmdbuf.ClearAttachments(attachment, clear_rect);
});
}
} else {
if (!color_full_channels) {
u8 color_mask = u8(regs.clear_surface.R | regs.clear_surface.G << 1 | regs.clear_surface.B << 2 | regs.clear_surface.A << 3);
Region2D dst_region = {
Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
Offset2D{.x = clear_rect.rect.offset.x + s32(clear_rect.rect.extent.width),
.y = clear_rect.rect.offset.y + s32(clear_rect.rect.extent.height)}};
request_renderpass();
blit_image.ClearColor(framebuffer, color_mask, regs.clear_color, dst_region);
} else if (!clear_on_load ||
!scheduler.OverrideLoadOps(framebuffer, 1u << color_attachment,
VK_ATTACHMENT_LOAD_OP_CLEAR, clear_value)) {
request_renderpass();
scheduler.Record([color_attachment, clear_value, clear_rect](vk::CommandBuffer cmdbuf) {
const VkClearAttachment attachment{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.colorAttachment = color_attachment,
.clearValue = clear_value,
};
cmdbuf.ClearAttachments(attachment, clear_rect);
});
}
}
@@ -576,41 +570,46 @@ void RasterizerVulkan::Clear(u32 layer_count) {
return;
}
VkImageAspectFlags aspect_flags = 0;
u32 ds_attachments = 0;
if (use_depth && framebuffer->HasAspectDepthBit()) {
aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT;
ds_attachments |= Scheduler::DEPTH_ATTACHMENT_BIT;
}
if (use_stencil && framebuffer->HasAspectStencilBit()) {
aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
ds_attachments |= Scheduler::STENCIL_ATTACHMENT_BIT;
}
if (aspect_flags == 0) {
return;
}
if (use_stencil && framebuffer->HasAspectStencilBit() && regs.stencil_front_mask != 0xFF &&
regs.stencil_front_mask != 0) {
if (stencil_partial) {
Region2D dst_region = {
Offset2D{.x = clear_rect.rect.offset.x, .y = clear_rect.rect.offset.y},
Offset2D{.x = clear_rect.rect.offset.x + s32(clear_rect.rect.extent.width),
.y = clear_rect.rect.offset.y + s32(clear_rect.rect.extent.height)}};
request_renderpass();
blit_image.ClearDepthStencil(framebuffer, use_depth, regs.clear_depth,
u8(regs.stencil_front_mask), regs.clear_stencil,
regs.stencil_front_func_mask, dst_region);
} else if (can_defer_clear) {
VkClearValue ds_value{};
ds_value.depthStencil.depth = regs.clear_depth;
ds_value.depthStencil.stencil = regs.clear_stencil;
scheduler.DeferDepthStencilClear(framebuffer, ds_value);
} else {
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
VkClearAttachment attachment;
attachment.aspectMask = aspect_flags;
attachment.colorAttachment = 0;
attachment.clearValue.depthStencil.depth = clear_depth;
attachment.clearValue.depthStencil.stencil = clear_stencil;
cmdbuf.ClearAttachments(attachment, clear_rect);
});
return;
}
VkClearValue ds_value{};
ds_value.depthStencil.depth = regs.clear_depth;
ds_value.depthStencil.stencil = regs.clear_stencil;
if (clear_on_load && scheduler.OverrideLoadOps(framebuffer, ds_attachments,
VK_ATTACHMENT_LOAD_OP_CLEAR, ds_value)) {
return;
}
request_renderpass();
scheduler.Record([ds_value, clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
const VkClearAttachment attachment{
.aspectMask = aspect_flags,
.colorAttachment = 0,
.clearValue = ds_value,
};
cmdbuf.ClearAttachments(attachment, clear_rect);
});
}
void RasterizerVulkan::DispatchCompute() {
@@ -1014,7 +1013,7 @@ void RasterizerVulkan::FlushWork() {
draw_counter = 0;
return;
}
if ((draw_counter & CHECK_MASK) == CHECK_MASK) {
if ((draw_counter & CHECK_MASK) == CHECK_MASK && !scheduler.IsRenderPassActive()) {
scheduler.DispatchWork();
}
}
+38 -81
View File
@@ -120,11 +120,12 @@ void Scheduler::DispatchWork() {
}
}
void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer,
const RenderingAttachments& attachments) {
void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer) {
const RenderingAttachments& attachments = framebuffer->Attachments();
PublishComputeWrites();
state.framebuffer_id = framebuffer->Id();
++renderpass_serial;
renderpass_pristine = true;
attachments_touched = 0;
attachments_written = 0;
@@ -149,91 +150,48 @@ void Scheduler::BeginRenderPassImpl(const Framebuffer* framebuffer,
framebuffer->MarkResolveShadowsUpToDate();
}
void Scheduler::RealizeDeferredClear() {
if (deferred_clear.framebuffer == nullptr) {
return;
}
const DeferredClear dc = deferred_clear;
deferred_clear = {};
RenderingAttachments attachments = dc.framebuffer->Attachments();
for (u32 slot = 0; slot < attachments.num_colors; ++slot) {
if ((dc.color_clear_mask & (1u << slot)) == 0) {
continue;
}
VkRenderingAttachmentInfo& attachment = attachments.colors[slot];
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment.clearValue = dc.color_values[slot];
if (dc.framebuffer->DiscardsMsaaColor()) {
attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
}
if (dc.depth_stencil) {
for (VkRenderingAttachmentInfo* const attachment :
{&attachments.depth, &attachments.stencil}) {
attachment->loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachment->clearValue = dc.depth_stencil_value;
if (dc.framebuffer->DiscardsMsaaDepthStencil()) {
attachment->storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
}
}
EndRenderPass();
BeginRenderPassImpl(dc.framebuffer, attachments);
if (dc.depth_stencil) {
attachments_written |= DEPTH_ATTACHMENT_BIT | STENCIL_ATTACHMENT_BIT;
}
}
bool Scheduler::DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot,
const VkClearValue& value) {
if (IsRenderPassActive()) {
return false;
}
if (deferred_clear.framebuffer != nullptr && deferred_clear.framebuffer != framebuffer) {
RealizeDeferredClear();
EndRenderPass();
}
deferred_clear.framebuffer = framebuffer;
deferred_clear.color_clear_mask |= 1u << rt_slot;
deferred_clear.color_values[rt_slot] = value;
return true;
}
bool Scheduler::DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value) {
if (IsRenderPassActive()) {
return false;
}
if (deferred_clear.framebuffer != nullptr && deferred_clear.framebuffer != framebuffer) {
RealizeDeferredClear();
EndRenderPass();
}
deferred_clear.framebuffer = framebuffer;
deferred_clear.depth_stencil = true;
deferred_clear.depth_stencil_value = value;
return true;
}
void Scheduler::FlushDeferredClear() {
if (deferred_clear.framebuffer == nullptr) {
return;
}
RealizeDeferredClear();
EndRenderPass();
}
void Scheduler::RequestRenderpass(const Framebuffer* framebuffer, u32 touched, u32 written) {
if (deferred_clear.framebuffer == framebuffer) {
RealizeDeferredClear();
} else if (framebuffer->Id() != state.framebuffer_id) {
// Ends any active pass and realizes a deferred clear
if (framebuffer->Id() != state.framebuffer_id) {
EndRenderPass();
BeginRenderPassImpl(framebuffer, framebuffer->Attachments());
BeginRenderPassImpl(framebuffer);
}
renderpass_pristine = false;
attachments_touched |= touched;
attachments_written |= written;
}
bool Scheduler::OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments,
VkAttachmentLoadOp load_op, const VkClearValue& value) {
if (framebuffer->Id() != state.framebuffer_id) {
EndRenderPass();
BeginRenderPassImpl(framebuffer);
}
if (!renderpass_pristine || recorded_attachments == nullptr) {
return false;
}
const auto set_load_op = [&](VkRenderingAttachmentInfo& attachment, bool discards_msaa) {
attachment.loadOp = load_op;
attachment.clearValue = value;
if (discards_msaa && load_op == VK_ATTACHMENT_LOAD_OP_CLEAR) {
attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
}
};
for (u32 slot = 0; slot < recorded_attachments->num_colors; ++slot) {
if ((attachments & (1u << slot)) != 0) {
set_load_op(recorded_attachments->colors[slot], framebuffer->DiscardsMsaaColor());
}
}
if ((attachments & DEPTH_ATTACHMENT_BIT) != 0) {
set_load_op(recorded_attachments->depth, framebuffer->DiscardsMsaaDepthStencil());
}
if ((attachments & STENCIL_ATTACHMENT_BIT) != 0) {
set_load_op(recorded_attachments->stencil, framebuffer->DiscardsMsaaDepthStencil());
}
attachments_touched |= attachments;
attachments_written |= attachments;
return true;
}
void Scheduler::RequestOutsideRenderPassOperationContext() {
EndRenderPass();
PublishComputeWrites();
@@ -457,7 +415,6 @@ void Scheduler::EndPendingOperations() {
void Scheduler::EndRenderPass()
{
RealizeDeferredClear();
if (state.framebuffer_id == 0) {
return;
}
+4 -24
View File
@@ -66,14 +66,8 @@ public:
void RequestRenderpass(const Framebuffer* framebuffer, u32 touched = ALL_ATTACHMENTS,
u32 written = ALL_ATTACHMENTS);
/// Defers a full-attachment color clear so it becomes the next render pass.
bool DeferColorClear(const Framebuffer* framebuffer, u32 rt_slot, const VkClearValue& value);
/// Defers a full depth/stencil clear so it becomes the next render pass.
bool DeferDepthStencilClear(const Framebuffer* framebuffer, const VkClearValue& value);
/// Realizes any pending deferred clear before its framebuffer can be moved or freed.
void FlushDeferredClear();
bool OverrideLoadOps(const Framebuffer* framebuffer, u32 attachments,
VkAttachmentLoadOp load_op, const VkClearValue& value);
/// Requests the current execution context to be able to execute operations only allowed outside
/// of a renderpass.
@@ -302,20 +296,7 @@ private:
bool descriptor_buffer_bound = false;
};
struct DeferredClear {
const Framebuffer* framebuffer = nullptr;
u32 color_clear_mask = 0;
std::array<VkClearValue, 8> color_values{};
bool depth_stencil = false;
VkClearValue depth_stencil_value{};
};
/// Begins a render pass for the given framebuffer, optionally with clear values.
void BeginRenderPassImpl(const Framebuffer* framebuffer,
const RenderingAttachments& attachments);
/// If a deferred clear is pending.
void RealizeDeferredClear();
void BeginRenderPassImpl(const Framebuffer* framebuffer);
void WorkerThread(std::stop_token stop_token);
@@ -344,8 +325,6 @@ private:
vk::CommandBuffer current_cmdbuf;
vk::CommandBuffer current_upload_cmdbuf;
DeferredClear deferred_clear;
std::unique_ptr<CommandChunk> chunk;
std::function<void()> on_submit;
@@ -354,6 +333,7 @@ private:
u64 renderpass_serial = 0;
u64 wfi_serial = 0;
bool renderpass_writes = false;
bool renderpass_pristine = false;
bool compute_writes = false;
u32 attachments_touched = 0;
u32 attachments_written = 0;
@@ -1884,10 +1884,6 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const {
return device.CanReportMemoryUsage();
}
void TextureCacheRuntime::FlushDeferredClear() {
scheduler.FlushDeferredClear();
}
VkImage TextureCacheRuntime::AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci) {
const MsaaScratchKey key{
.format = image_ci.format,
@@ -58,8 +58,6 @@ public:
void TickFrame();
void FlushDeferredClear();
u64 GetDeviceLocalMemory() const;
u64 GetDeviceMemoryUsage() const;
@@ -453,7 +453,6 @@ FramebufferId TextureCache<P>::GetFramebufferId(const RenderTargets& key) {
return id ? &slot_image_views[id] : nullptr;
});
ImageView* const depth_buffer = key.depth_buffer_id ? &slot_image_views[key.depth_buffer_id] : nullptr;
runtime.FlushDeferredClear();
framebuffer_id = slot_framebuffers.insert(runtime, color_buffers, depth_buffer, key);
return framebuffer_id;
}
@@ -2236,7 +2235,6 @@ void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> rem
template <class P>
void TextureCache<P>::RemoveFramebuffers(std::span<const ImageViewId> removed_views) {
runtime.FlushDeferredClear();
auto it = framebuffers.begin();
while (it != framebuffers.end()) {
if (it->first.Contains(removed_views)) {