diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h index e10a6d00..726b56f7 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VkTextureManager.h @@ -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; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 09cb7525..7bc95d08 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -620,7 +620,30 @@ namespace MobileGL::MG_Backend::DirectVulkan { Uint32 level = 0, layer = 0, layers = 1; }; Vector 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 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, diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererPresent.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererPresent.cpp index a6090a22..f835ef1f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererPresent.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRendererPresent.cpp @@ -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 diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.cpp index 95eaae32..ad5d3439 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.cpp @@ -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(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(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(std::min(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); diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.cpp index 305edf25..d773d5f7 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.cpp @@ -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) {