[Perf] (MG_Backend/DirectVulkan): defer window clears into the next window pass

- A whole-window glClear with no pass open on the window waits for the next
  window pass, which loads it with LOAD_OP_CLEAR; other uses run it first
- The first window pass after a present loads undefined content as DONT_CARE
This commit is contained in:
BZLZHH
2026-11-01 13:42:44 +08:00
parent d6fd101d2a
commit 6c59b0391c
5 changed files with 282 additions and 78 deletions
@@ -132,6 +132,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
private:
VkTextureManager& m_manager;
};
// True inside a PendingClearHold: the image being resolved is an attachment of the pass
// being prepared, which takes pending clears as its load op.
Bool PendingClearsHeld() const { return m_pendingClearHold != 0; }
// A whole-subresource clear that was recorded but has not run yet (VulkanRenderer::DeferWireClear).
struct WirePendingClear {
Uint32 level = 0, layer = 0, layers = 1;
@@ -620,7 +620,30 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 level = 0, layer = 0, layers = 1;
};
Vector<WireFoldClear> m_wireFoldClears;
// Attachments of the pass being prepared whose content is undefined: a default-framebuffer
// image first used since its present. Their load op becomes DONT_CARE (WireClearVariant).
Vector<Uint32> m_wireDiscardLoads;
static Bool WireImageCanDeferClear(const WireImage& image);
// A whole-image clear of the default framebuffer, held for the next pass that draws into
// the window (its load op) the way WirePendingClear holds a texture's. Any other use of the
// window images, and the present, run it first (MaterializeDefaultWirePendingClear).
struct DefaultWirePendingClear {
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
Uint32 swapchainImageIndex = 0;
Bool color = false;
VkImageAspectFlags depthStencil = 0;
ClearAttachmentPayload colorPayload{};
ClearAttachmentPayload depthStencilPayload{};
Bool Pending() const { return color || depthStencil != 0; }
};
DefaultWirePendingClear m_defaultWirePendingClear;
GLbitfield DeferDefaultWireClear(const MG_Pipe::MGPFramebufferState& fbo, const ClearAttachmentPayload& payload,
GLint drawbuffer);
void MaterializeDefaultWirePendingClear();
void FoldDefaultWirePendingClear(RenderPassEntry& begun, Uint64& variantKey, VkClearValue* merged);
// Records a clear of `area` of one wire image in a single-attachment pass of its own.
void RecordWireImageClear(WireImage& image, VkImageAspectFlags aspect, const ClearAttachmentPayload& payload,
const VkRect2D& area, Bool textureSurface);
static void NoteWirePendingClear(const WireImage& image, VkImageAspectFlags aspects, const VkClearValue& value);
VkRenderPass WireClearVariant(RenderPassEntry& begun, Uint64 variantKey);
Bool BeginWireDrawPassFolding(FrameContext::FrameData& frame, RenderPassEntry& begun, Uint64 variantKey,
@@ -16,6 +16,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Present has its own submit path. Tag its last wire pass before that
// submission, rather than retaining it until a draw in the next frame.
RetireWireDrawPass();
// A window clear no pass drew after still has to reach the presented image; a frame that
// cannot be presented drops it with the rest of the frame.
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended)
m_defaultWirePendingClear = {};
else
MaterializeDefaultWirePendingClear();
#endif
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended) {
// No usable swapchain: the window was zero-area at initialization, or
@@ -9,6 +9,14 @@
#include "VulkanRendererInternal.h"
namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
// A wire pass variant key holds two clear bits per attachment from bit 0 and, from this
// bit on, one bit per attachment whose content is undefined (WireClearVariant).
constexpr Uint32 kWireDiscardBit = 32;
static_assert(2 * (MG_Pipe::kMGPipeMaxColorAttachments + 1) <= kWireDiscardBit,
"the clear bits of a wire pass variant key overlap its discard bits");
} // namespace
// GL_EXT_fragment_shading_rate's pipeline rate and combiner ops for the draw about to be
// recorded. The packed rate is log2(width) << 2 | log2(height), which is exactly the extent
// VK_KHR_fragment_shading_rate takes, and the combiner op indices follow
@@ -445,6 +453,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
WireDrawScratch& scratch, Bool passContinues, Bool& continuing) {
continuing = true;
m_wireFoldClears.clear();
m_wireDiscardLoads.clear();
if (passContinues) {
auto& store = MG_Record::ServerStagedTexture();
if (!m_wirePassMarksValid || m_wirePassMarksClearGeneration != store.GpuDirtyClearGeneration()) {
@@ -536,6 +545,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!image.isDefault && surface.Kind == MG_Pipe::kMGPipeSurfaceKindTexture)
scratch.gpuWriteMarks.push_back({image.storage, image.level, image.layer, image.layers});
if (image.isDefault) {
// A present leaves the default framebuffer undefined (VulkanRenderer::Present):
// the first pass after it has nothing to load.
const Bool defined =
aspect == VK_IMAGE_ASPECT_COLOR_BIT
? m_swapchainObject.IsImageContentDefined(image.swapchainImageIndex)
: m_swapchainObject.IsDepthStencilContentDefined(image.swapchainImageIndex);
if (!defined) m_wireDiscardLoads.push_back(static_cast<Uint32>(attachments.size() - 1));
if (aspect == VK_IMAGE_ASPECT_COLOR_BIT)
m_swapchainObject.SetImageContentDefined(image.swapchainImageIndex, true);
else
@@ -1361,9 +1377,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (entry.first == variantKey) return entry.second;
const Uint32 attachmentCount = static_cast<Uint32>(begun.wireAttachments.size());
auto descriptions = begun.wireAttachments;
// Bits 2a and 2a+1 clear attachment a (color or depth, and stencil); bit kWireDiscardBit + a
// marks its content undefined, so whatever is not cleared is not loaded either.
for (Uint32 a = 0; a < attachmentCount; ++a) {
if (variantKey >> (2 * a) & 1) descriptions[a].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
if (variantKey >> (2 * a + 1) & 1) descriptions[a].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
const Bool discard = variantKey >> (kWireDiscardBit + a) & 1;
if (variantKey >> (2 * a) & 1)
descriptions[a].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
else if (discard)
descriptions[a].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
if (variantKey >> (2 * a + 1) & 1)
descriptions[a].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
else if (discard)
descriptions[a].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
@@ -1382,6 +1407,138 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return variant;
}
// The default framebuffer's half of the deferred clears: a whole-window glClear with no pass
// open on the window waits for the next pass that draws into it, which loads it with
// LOAD_OP_CLEAR (BeginWireDrawPassFolding) instead of storing a cleared window and loading it
// back. Returns the buffers it did not take.
GLbitfield VulkanRenderer::DeferDefaultWireClear(const MG_Pipe::MGPFramebufferState& fbo,
const ClearAttachmentPayload& payload, GLint drawbuffer) {
GLbitfield remaining = payload.mask & (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (!remaining || !fbo.IsDefault || drawbuffer >= 0 || m_wirePreparationDepth != 0) return remaining;
if (GLScissorTestClips()) return remaining;
// A pass open on the window takes the clear itself (ClearWireAttachmentsOfOpenPass).
if (VkRenderPassManager::GetActiveRenderPass() != nullptr && m_wireDrawPass && m_wireDrawPassKey.isDefault)
return remaining;
GLbitfield handled = 0;
Bool color = false;
VkImageAspectFlags depthStencil = 0;
if (remaining & GL_COLOR_BUFFER_BIT) {
const Int32 slot = fbo.DrawBuffers[0];
const auto mask = MG_Pipe::gPipeInputs->GetColorMaskIndexed(0);
const Bool none = slot < 0 || (!mask.r() && !mask.g() && !mask.b() && !mask.a());
if (none)
handled |= GL_COLOR_BUFFER_BIT;
else if (mask.r() && mask.g() && mask.b() && mask.a() &&
payload.colorEncoding == ClearColorEncoding::Float) {
handled |= GL_COLOR_BUFFER_BIT;
color = true;
}
}
// A window without a depth/stencil buffer clears nothing there.
const VkFormat depthStencilFormat = m_swapchainObject.GetDepthStencilFormat();
const Bool hasDepth = depthStencilFormat != VK_FORMAT_UNDEFINED;
const Bool packed = vkuFormatIsDepthAndStencil(depthStencilFormat);
if (remaining & GL_DEPTH_BUFFER_BIT) {
handled |= GL_DEPTH_BUFFER_BIT;
if (hasDepth && MG_Pipe::gPipeInputs->GetDepthMask()) depthStencil |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (remaining & GL_STENCIL_BUFFER_BIT) {
const Uint32 mask = MG_Pipe::gPipeInputs->GetStencilState(StencilFace::Front).WriteMask & 255u;
if (mask == 0 || !packed)
handled |= GL_STENCIL_BUFFER_BIT;
else if (mask == 255u) {
handled |= GL_STENCIL_BUFFER_BIT;
depthStencil |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
}
if (!color && depthStencil == 0) return remaining & ~handled;
// The index the next drawing pass on the window will address (WireDraw.cpp's pass key).
const Uint32 index = DefaultFramebufferWriteIndex(true);
auto& pending = m_defaultWirePendingClear;
if (pending.Pending() &&
(pending.swapchain != m_swapchainObject.GetHandle() || pending.swapchainImageIndex != index))
MaterializeDefaultWirePendingClear();
pending.swapchain = m_swapchainObject.GetHandle();
pending.swapchainImageIndex = index;
if (color) {
pending.color = true;
pending.colorPayload = payload;
m_swapchainObject.SetImageContentDefined(index, true);
}
if (depthStencil & VK_IMAGE_ASPECT_DEPTH_BIT) pending.depthStencilPayload.depth = payload.depth;
if (depthStencil & VK_IMAGE_ASPECT_STENCIL_BIT) pending.depthStencilPayload.stencil = payload.stencil;
if (depthStencil) {
pending.depthStencil |= depthStencil;
m_swapchainObject.SetDepthStencilContentDefined(index, true);
}
return remaining & ~handled;
}
void VulkanRenderer::MaterializeDefaultWirePendingClear() {
if (!m_defaultWirePendingClear.Pending()) return;
const DefaultWirePendingClear pending = m_defaultWirePendingClear;
m_defaultWirePendingClear = {};
// A recreated swapchain took the images the clear was for.
if (pending.swapchain != m_swapchainObject.GetHandle() ||
pending.swapchainImageIndex >= m_swapchainObject.GetImageCount())
return;
const VkExtent2D extent = m_swapchainObject.GetExtent();
const VkRect2D area{{0, 0}, extent};
WireImage image{};
image.isDefault = true;
image.extent = extent;
image.swapchainImageIndex = pending.swapchainImageIndex;
if (pending.color) {
image.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
image.image = m_swapchainObject.GetImage(pending.swapchainImageIndex);
image.format = m_swapchainObject.GetSurfaceFormat().format;
image.layout = m_swapchainObject.GetImageLayout(pending.swapchainImageIndex);
RecordWireImageClear(image, VK_IMAGE_ASPECT_COLOR_BIT, pending.colorPayload, area, false);
}
if (pending.depthStencil && m_swapchainObject.GetDepthStencilImage(pending.swapchainImageIndex)) {
image.aspect = pending.depthStencil;
image.image = m_swapchainObject.GetDepthStencilImage(pending.swapchainImageIndex);
image.format = m_swapchainObject.GetDepthStencilFormat();
image.layout = m_swapchainObject.GetDepthStencilImageLayout(pending.swapchainImageIndex);
RecordWireImageClear(image, pending.depthStencil, pending.depthStencilPayload, area, false);
}
}
// A pending window clear (DeferDefaultWireClear) becomes the load op of the window pass that
// begins: its attachments are matched by image, since a continued pass key is not walked again.
// A clear the caller already put in `variantKey` is newer and wins.
void VulkanRenderer::FoldDefaultWirePendingClear(RenderPassEntry& begun, Uint64& variantKey, VkClearValue* merged) {
auto& pending = m_defaultWirePendingClear;
if (!pending.Pending() || !m_wireDrawPassKey.isDefault || m_wireDrawPassKey.swapchain != pending.swapchain ||
m_wireDrawPassKey.swapchainImageIndex != pending.swapchainImageIndex)
return;
const Uint32 attachmentCount =
static_cast<Uint32>(std::min<SizeT>(begun.wireAttachments.size(), m_wireDrawPassKey.attachments.size()));
const VkImage colorImage = m_swapchainObject.GetImage(pending.swapchainImageIndex);
const VkImage depthStencilImage = m_swapchainObject.GetDepthStencilImage(pending.swapchainImageIndex);
for (Uint32 a = 0; a < attachmentCount && a < MG_Pipe::kMGPipeMaxColorAttachments + 1; ++a) {
const auto& attachment = m_wireDrawPassKey.attachments[a];
if (pending.color && attachment.image == colorImage) {
if (!(variantKey >> (2 * a) & 1)) {
variantKey |= 1ull << (2 * a);
merged[a].color = MakeVkClearColorValue(pending.colorPayload, false);
}
pending.color = false;
} else if (pending.depthStencil && attachment.image == depthStencilImage) {
if ((pending.depthStencil & VK_IMAGE_ASPECT_DEPTH_BIT) && !(variantKey >> (2 * a) & 1)) {
variantKey |= 1ull << (2 * a);
merged[a].depthStencil.depth = pending.depthStencilPayload.depth;
}
if ((pending.depthStencil & VK_IMAGE_ASPECT_STENCIL_BIT) && !(variantKey >> (2 * a + 1) & 1)) {
variantKey |= 1ull << (2 * a + 1);
merged[a].depthStencil.stencil = pending.depthStencilPayload.stencil;
}
pending.depthStencil = 0;
}
}
// An aspect the pass does not attach stays pending: none of its draws write it.
}
// P15: EVERY DRAW PASS BEGINS HERE. The pending clears of its attachments
// (m_wireFoldClears, gathered by PrepareWireDrawPass) become their load op and leave the
// images; a clear the caller already put in `variantKey` (its own glClear, newer than any
@@ -1426,6 +1583,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
}
m_wireFoldClears.clear();
FoldDefaultWirePendingClear(begun, variantKey, merged);
for (const Uint32 a : m_wireDiscardLoads)
if (a < attachmentCount) variantKey |= 1ull << (kWireDiscardBit + a);
m_wireDiscardLoads.clear();
if (variantKey == 0) return VkRenderPassManager::BeginRenderPass(frame.commandBuffer, begun);
return VkRenderPassManager::BeginRenderPass(frame.commandBuffer, begun, WireClearVariant(begun, variantKey),
merged, attachmentCount);
@@ -658,6 +658,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
WireImage out{};
out.aspect = aspect;
if (fbo.IsDefault) {
// Only an attachment of the pass being prepared takes a pending window clear as its
// load op; every other use runs it first.
if (m_defaultWirePendingClear.Pending() && !m_textureManager->PendingClearsHeld())
MaterializeDefaultWirePendingClear();
out.isDefault = true;
out.extent = m_swapchainObject.GetExtent();
out.swapchainImageIndex =
@@ -1109,6 +1113,87 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return remaining & ~handled;
}
void VulkanRenderer::RecordWireImageClear(WireImage& image, VkImageAspectFlags aspect,
const ClearAttachmentPayload& payload, const VkRect2D& area,
Bool textureSurface) {
const Bool color = aspect == VK_IMAGE_ASPECT_COLOR_BIT;
const auto layout =
color ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
TransitionWireImage(image, layout);
VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
viewInfo.image = image.image;
viewInfo.viewType = image.viewType;
viewInfo.format = image.format;
viewInfo.subresourceRange = {
color ? VK_IMAGE_ASPECT_COLOR_BIT
: (image.format == VK_FORMAT_D24_UNORM_S8_UINT || image.format == VK_FORMAT_D32_SFLOAT_S8_UINT
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
: image.aspect),
image.level, 1, image.layer, image.layers};
VkImageView view = VK_NULL_HANDLE;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &view));
VkAttachmentDescription attachment{};
attachment.format = image.format;
attachment.samples = image.samples;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.initialLayout = layout;
attachment.finalLayout = layout;
VkAttachmentReference ref{0, layout};
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = color ? 1 : 0;
subpass.pColorAttachments = color ? &ref : nullptr;
subpass.pDepthStencilAttachment = color ? nullptr : &ref;
VkRenderPassCreateInfo passInfo{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
passInfo.attachmentCount = 1;
passInfo.pAttachments = &attachment;
passInfo.subpassCount = 1;
passInfo.pSubpasses = &subpass;
VkRenderPass pass = VK_NULL_HANDLE;
VK_VERIFY(vkCreateRenderPass(m_device, &passInfo, nullptr, &pass));
VkFramebufferCreateInfo fbInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
fbInfo.renderPass = pass;
fbInfo.attachmentCount = 1;
fbInfo.pAttachments = &view;
fbInfo.width = image.extent.width;
fbInfo.height = image.extent.height;
fbInfo.layers = image.layers;
VkFramebuffer fb = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &fbInfo, nullptr, &fb));
VkRenderPassBeginInfo begin{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
begin.renderPass = pass;
begin.framebuffer = fb;
begin.renderArea = area;
auto clearPayload = payload;
if (color) PreCompensateSrgbClearColor(clearPayload, image.format);
VkClearAttachment clear{};
clear.aspectMask = aspect;
clear.clearValue.color = MakeVkClearColorValue(clearPayload, false);
if (!color) clear.clearValue.depthStencil = {payload.depth, payload.stencil};
VkClearRect rect{area, 0, image.layers};
auto& frame = m_frameContext.GetCurrent();
EndActiveRenderPassOn(frame.commandBuffer);
vkCmdBeginRenderPass(frame.commandBuffer, &begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdClearAttachments(frame.commandBuffer, 1, &clear, 1, &rect);
vkCmdEndRenderPass(frame.commandBuffer);
WireRetiredObjects retired{};
retired.framebuffers.push_back(fb);
retired.renderPasses.push_back(pass);
retired.imageViews.push_back(view);
RetireWireObjects(Move(retired));
if (!image.isDefault && textureSurface)
m_textureManager->MarkWireTextureGpuWritten(image.storage, image.level, image.layer, image.layers);
if (image.isDefault) {
if (color)
m_swapchainObject.SetImageContentDefined(image.swapchainImageIndex, true);
else
m_swapchainObject.SetDepthStencilContentDefined(image.swapchainImageIndex, true);
}
}
void VulkanRenderer::ClearWireFramebuffer(const MG_Pipe::MGPFramebufferState& fbo,
const ClearAttachmentPayload& clearRequest, GLint drawbuffer) {
if (MG_Pipe::gPipeInputs->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) return;
@@ -1126,6 +1211,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
payload.mask = ClearWireFramebufferInPass(fbo, payload, drawbuffer);
if (payload.mask) payload.mask = ClearWireAttachmentsOfOpenPass(fbo, payload, drawbuffer);
if (payload.mask) payload.mask = DeferWireClear(fbo, payload, drawbuffer);
if (payload.mask) payload.mask = DeferDefaultWireClear(fbo, payload, drawbuffer);
if (payload.mask) payload.mask = ClearWireFramebufferInPass(fbo, payload, drawbuffer);
if (!payload.mask) return;
RetireWireDrawPass();
@@ -1177,82 +1263,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_swapchainObject.GetPreTransform(), &area.offset, &area.extent))
MagmaWireFatal("clear-surface-transform");
}
const Bool color = aspect == VK_IMAGE_ASPECT_COLOR_BIT;
const auto layout =
color ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
TransitionWireImage(image, layout);
VkImageViewCreateInfo viewInfo{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
viewInfo.image = image.image;
viewInfo.viewType = image.viewType;
viewInfo.format = image.format;
viewInfo.subresourceRange = {
color ? VK_IMAGE_ASPECT_COLOR_BIT
: (image.format == VK_FORMAT_D24_UNORM_S8_UINT || image.format == VK_FORMAT_D32_SFLOAT_S8_UINT
? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
: image.aspect),
image.level, 1, image.layer, image.layers};
VkImageView view = VK_NULL_HANDLE;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &view));
VkAttachmentDescription attachment{};
attachment.format = image.format;
attachment.samples = image.samples;
attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
attachment.initialLayout = layout;
attachment.finalLayout = layout;
VkAttachmentReference ref{0, layout};
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = color ? 1 : 0;
subpass.pColorAttachments = color ? &ref : nullptr;
subpass.pDepthStencilAttachment = color ? nullptr : &ref;
VkRenderPassCreateInfo passInfo{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
passInfo.attachmentCount = 1;
passInfo.pAttachments = &attachment;
passInfo.subpassCount = 1;
passInfo.pSubpasses = &subpass;
VkRenderPass pass = VK_NULL_HANDLE;
VK_VERIFY(vkCreateRenderPass(m_device, &passInfo, nullptr, &pass));
VkFramebufferCreateInfo fbInfo{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
fbInfo.renderPass = pass;
fbInfo.attachmentCount = 1;
fbInfo.pAttachments = &view;
fbInfo.width = image.extent.width;
fbInfo.height = image.extent.height;
fbInfo.layers = image.layers;
VkFramebuffer fb = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &fbInfo, nullptr, &fb));
VkRenderPassBeginInfo begin{VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
begin.renderPass = pass;
begin.framebuffer = fb;
begin.renderArea = area;
auto clearPayload = payload;
if (color) PreCompensateSrgbClearColor(clearPayload, image.format);
VkClearAttachment clear{};
clear.aspectMask = aspect;
clear.clearValue.color = MakeVkClearColorValue(clearPayload, false);
if (!color) clear.clearValue.depthStencil = {payload.depth, payload.stencil};
VkClearRect rect{area, 0, image.layers};
auto& frame = m_frameContext.GetCurrent();
EndActiveRenderPassOn(frame.commandBuffer);
vkCmdBeginRenderPass(frame.commandBuffer, &begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdClearAttachments(frame.commandBuffer, 1, &clear, 1, &rect);
vkCmdEndRenderPass(frame.commandBuffer);
WireRetiredObjects retired{};
retired.framebuffers.push_back(fb);
retired.renderPasses.push_back(pass);
retired.imageViews.push_back(view);
RetireWireObjects(Move(retired));
if (!image.isDefault && surface.Kind == MG_Pipe::kMGPipeSurfaceKindTexture)
m_textureManager->MarkWireTextureGpuWritten(image.storage, image.level, image.layer, image.layers);
if (image.isDefault) {
if (color)
m_swapchainObject.SetImageContentDefined(image.swapchainImageIndex, true);
else
m_swapchainObject.SetDepthStencilContentDefined(image.swapchainImageIndex, true);
}
RecordWireImageClear(image, aspect, payload, area, surface.Kind == MG_Pipe::kMGPipeSurfaceKindTexture);
};
if (payload.mask & GL_COLOR_BUFFER_BIT) {
for (Uint32 i = 0; i < MG_Pipe::kMGPipeMaxColorAttachments; ++i) {