mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Fix] (MG_Backend/DirectVulkan): fix missing clear when (texture-FBO attach -> clear -> detach) occurs
This commit is contained in:
@@ -173,6 +173,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
desc.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
ClearAttachmentPayload clearPayload{};
|
||||
Bool hasClear = m_clearManager.GetPendingClear(texture, clearPayload);
|
||||
VkImageLayout trackedColorLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
desc.loadOp = hasClear ?
|
||||
VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||
VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
@@ -197,9 +198,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const auto& swapchainViews = m_swapchainObject.GetImageViews();
|
||||
MOBILEGL_ASSERT(swapchainImageIndex < swapchainViews.size(),
|
||||
"GetOrCreateRenderPass: swapchain image index out of range");
|
||||
desc.initialLayout = hasClear ?
|
||||
VK_IMAGE_LAYOUT_UNDEFINED :
|
||||
m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
||||
trackedColorLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||
.target = TrackedAttachmentTarget::SwapchainColor,
|
||||
.swapchainImageIndex = swapchainImageIndex,
|
||||
@@ -210,12 +209,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
textureResources[i] = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
||||
MOBILEGL_ASSERT(textureResources[i],
|
||||
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i);
|
||||
MOBILEGL_ASSERT(hasClear || textureResources[i]->layout != VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
"GetOrCreateRenderPass: color attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD (attachment=%d)",
|
||||
texture->GetExternalIndex(), i);
|
||||
desc.initialLayout = hasClear ?
|
||||
VK_IMAGE_LAYOUT_UNDEFINED :
|
||||
textureResources[i]->layout;
|
||||
trackedColorLayout = textureResources[i]->layout;
|
||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||
.target = TrackedAttachmentTarget::Texture,
|
||||
.texture = texture,
|
||||
@@ -224,6 +218,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
attachmentViews[i] = textureResources[i]->view;
|
||||
}
|
||||
|
||||
if (!hasClear && trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||
MGLOG_W("GetOrCreateRenderPass: color attachment textureId=%d starts with undefined layout and no clear; "
|
||||
"using LOAD_OP_DONT_CARE",
|
||||
texture->GetExternalIndex());
|
||||
desc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
}
|
||||
desc.initialLayout = (hasClear || trackedColorLayout == VK_IMAGE_LAYOUT_UNDEFINED) ?
|
||||
VK_IMAGE_LAYOUT_UNDEFINED :
|
||||
trackedColorLayout;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
static constexpr VkPipelineStageFlags kGraphicsSampledReadStages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
|
||||
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
||||
switch (layout) {
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
@@ -90,6 +92,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
if (IsValidSampledImageLayout(resource->layout)) {
|
||||
return true;
|
||||
}
|
||||
if (resource->layout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||
MGLOG_W("TransitionTextureForSampling: textureId=%d is still in VK_IMAGE_LAYOUT_UNDEFINED before sampling",
|
||||
texture.GetExternalIndex());
|
||||
}
|
||||
|
||||
VkImageLayout targetLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
@@ -120,7 +126,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
const Bool ok = TransitionImageLayout(commandBuffer, resource->image, resource->layout, targetLayout, srcStageMask,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, srcAccessMask,
|
||||
kGraphicsSampledReadStages, srcAccessMask,
|
||||
VK_ACCESS_SHADER_READ_BIT, resource->aspect);
|
||||
MOBILEGL_ASSERT(ok, "TransitionTextureForSampling: transition failed for textureId=%d", texture.GetExternalIndex());
|
||||
return ok;
|
||||
@@ -378,7 +384,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
Bool ok = TransitionImageLayout(commandBuffer, outResource.image,
|
||||
outResource.layout,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ?
|
||||
kGraphicsSampledReadStages :
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
outResource.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL ? VK_ACCESS_SHADER_READ_BIT : 0,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
@@ -404,7 +412,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
outResource.layout,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
kGraphicsSampledReadStages,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT,
|
||||
aspectMask);
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
outSrcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
break;
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
outSrcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
outSrcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
outSrcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
break;
|
||||
default:
|
||||
@@ -763,7 +763,8 @@ void main() {
|
||||
MGLOG_D("SetupDraw: sampled textureId=%d layout(before)=%s(%d)",
|
||||
sampledTexture->GetExternalIndex(), VkImageLayoutToString(textureResource->layout),
|
||||
static_cast<Int>(textureResource->layout));
|
||||
if (!IsValidSampledImageLayout(textureResource->layout)) {
|
||||
if (m_clearManager->HasPendingClear(sampledTexture) ||
|
||||
!IsValidSampledImageLayout(textureResource->layout)) {
|
||||
needSampledTextureTransitions = true;
|
||||
break;
|
||||
}
|
||||
@@ -779,6 +780,9 @@ void main() {
|
||||
if (!sampledTexture) {
|
||||
continue;
|
||||
}
|
||||
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *sampledTexture);
|
||||
MOBILEGL_ASSERT(clearReady, "%s: MaterializePendingClearForTexture failed for textureId=%d",
|
||||
__func__, sampledTexture->GetExternalIndex());
|
||||
const Bool ready = m_textureManager->TransitionTextureForSampling(frame.commandBuffer, *sampledTexture);
|
||||
MOBILEGL_ASSERT(ready, "%s: TransitionTextureForSampling failed for textureId=%d",
|
||||
__func__, sampledTexture->GetExternalIndex());
|
||||
@@ -850,6 +854,71 @@ void main() {
|
||||
m_clearManager->QueueClear(mask, payload, *fbo);
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
|
||||
MG_State::GLState::ITextureObject& texture) {
|
||||
ClearAttachmentPayload clearPayload{};
|
||||
if (!m_clearManager->GetPendingClear(&texture, clearPayload)) {
|
||||
return true;
|
||||
}
|
||||
MOBILEGL_ASSERT(VkRenderPassManager::GetActiveRenderPass() == nullptr,
|
||||
"MaterializePendingClearForTexture requires no active render pass");
|
||||
|
||||
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(texture);
|
||||
MOBILEGL_ASSERT(resource != nullptr,
|
||||
"MaterializePendingClearForTexture: SyncTextureAndGetDescriptor failed for textureId=%d",
|
||||
texture.GetExternalIndex());
|
||||
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags srcAccessMask = 0;
|
||||
GetImageTransitionSourceState(resource->layout, srcStageMask, srcAccessMask);
|
||||
|
||||
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||
commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
resource->aspect);
|
||||
MOBILEGL_ASSERT(ok,
|
||||
"MaterializePendingClearForTexture: failed to transition textureId=%d to TRANSFER_DST",
|
||||
texture.GetExternalIndex());
|
||||
|
||||
VkImageSubresourceRange subresourceRange{};
|
||||
subresourceRange.aspectMask = resource->aspect;
|
||||
subresourceRange.baseMipLevel = 0;
|
||||
subresourceRange.levelCount = 1;
|
||||
subresourceRange.baseArrayLayer = 0;
|
||||
subresourceRange.layerCount = 1;
|
||||
|
||||
VkImageLayout sampledLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
||||
VkClearColorValue clearValue{};
|
||||
clearValue.float32[0] = clearPayload.color.x();
|
||||
clearValue.float32[1] = clearPayload.color.y();
|
||||
clearValue.float32[2] = clearPayload.color.z();
|
||||
clearValue.float32[3] = clearPayload.color.w();
|
||||
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
&clearValue, 1, &subresourceRange);
|
||||
} else {
|
||||
VkClearDepthStencilValue clearValue{};
|
||||
clearValue.depth = clearPayload.depth;
|
||||
clearValue.stencil = clearPayload.stencil;
|
||||
vkCmdClearDepthStencilImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
&clearValue, 1, &subresourceRange);
|
||||
sampledLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
|
||||
}
|
||||
|
||||
ok = VkTextureManager::TransitionImageLayout(
|
||||
commandBuffer, resource->image, resource->layout, sampledLayout,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, resource->aspect);
|
||||
MOBILEGL_ASSERT(ok,
|
||||
"MaterializePendingClearForTexture: failed to transition textureId=%d to sampled layout",
|
||||
texture.GetExternalIndex());
|
||||
|
||||
m_clearManager->PopPendingClear(&texture);
|
||||
MGLOG_D("MaterializePendingClearForTexture: textureId=%d pending clear materialized",
|
||||
texture.GetExternalIndex());
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool VulkanRenderer::TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame,
|
||||
MG_State::GLState::FramebufferObject& readFbo,
|
||||
MG_State::GLState::FramebufferObject& drawFbo,
|
||||
@@ -881,6 +950,10 @@ void main() {
|
||||
const auto& attachment = readFbo.GetAttachment(readFbo.GetReadBuffer());
|
||||
auto sourceTexture = attachment.GetTexture();
|
||||
MOBILEGL_ASSERT(sourceTexture != nullptr, "TryBlitToDefaultFramebufferWithShader: source texture is null");
|
||||
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *sourceTexture);
|
||||
MOBILEGL_ASSERT(clearReady,
|
||||
"TryBlitToDefaultFramebufferWithShader: failed to materialize pending clear for textureId=%d",
|
||||
sourceTexture->GetExternalIndex());
|
||||
const Bool ready = m_textureManager->TransitionTextureForSampling(frame.commandBuffer, *sourceTexture);
|
||||
if (!ready) {
|
||||
MGLOG_E("BlitFramebuffer skipped: failed to transition source textureId=%d for sampling",
|
||||
@@ -1025,6 +1098,16 @@ void main() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!readIsDefaultFbo) {
|
||||
const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer());
|
||||
auto sourceTexture = sourceAttachment.GetTexture();
|
||||
MOBILEGL_ASSERT(sourceTexture != nullptr, "BlitFramebuffer: source texture attachment is null");
|
||||
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *sourceTexture);
|
||||
MOBILEGL_ASSERT(clearReady,
|
||||
"BlitFramebuffer: failed to materialize pending clear for source textureId=%d",
|
||||
sourceTexture->GetExternalIndex());
|
||||
}
|
||||
|
||||
VkImageLayout srcLayout = readIsDefaultFbo
|
||||
? m_swapchainObject.GetImageLayout(m_imageIndexAcquired)
|
||||
: *srcBinding.trackedLayout;
|
||||
|
||||
@@ -190,6 +190,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
|
||||
GLenum filter);
|
||||
Bool MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
|
||||
MG_State::GLState::ITextureObject& texture);
|
||||
VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry);
|
||||
|
||||
void ShutdownSwapchain();
|
||||
|
||||
Reference in New Issue
Block a user