mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (MG_Backend/DirectVulkan): Fix sampled-texture/render-pass layout hazard
This commit is contained in:
@@ -10,10 +10,37 @@
|
|||||||
|
|
||||||
#include "MG_State/GLState/Core.h"
|
#include "MG_State/GLState/Core.h"
|
||||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||||
|
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
|
||||||
#include "MG_Util/ShaderTranspiler/Types.h"
|
#include "MG_Util/ShaderTranspiler/Types.h"
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
static Bool FindFramebufferAttachmentForTexture(const MG_State::GLState::FramebufferObject& framebuffer,
|
||||||
|
const MG_State::GLState::ITextureObject& texture,
|
||||||
|
FramebufferAttachmentType& outAttachment, Int& outLevel) {
|
||||||
|
const auto& attachments = framebuffer.GetAllAttachmentObjects();
|
||||||
|
for (SizeT i = 0; i < attachments.size(); ++i) {
|
||||||
|
const auto attachmentType = static_cast<FramebufferAttachmentType>(i);
|
||||||
|
if (attachmentType == FramebufferAttachmentType::None) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& attachment = attachments[i];
|
||||||
|
if (!attachment.IsTexture()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto attachedTexture = attachment.GetTexture();
|
||||||
|
if (attachedTexture && attachedTexture.get() == &texture) {
|
||||||
|
outAttachment = attachmentType;
|
||||||
|
outLevel = attachment.GetTextureLevel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||||
@@ -483,9 +510,26 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (resource == nullptr) {
|
if (resource == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MOBILEGL_ASSERT(IsValidSampledImageLayout(resource->layout),
|
if (!IsValidSampledImageLayout(resource->layout)) {
|
||||||
"ResolveSamplerDescriptor: invalid sampled image layout=%d for textureId=%d, binding=%u",
|
auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||||
static_cast<Int>(resource->layout), texture->GetExternalIndex(), binding);
|
FramebufferAttachmentType attachmentType = FramebufferAttachmentType::None;
|
||||||
|
Int attachmentLevel = 0;
|
||||||
|
if (drawFbo &&
|
||||||
|
FindFramebufferAttachmentForTexture(*drawFbo, *texture, attachmentType, attachmentLevel)) {
|
||||||
|
MOBILEGL_ASSERT(false,
|
||||||
|
"ResolveSamplerDescriptor: framebuffer feedback loop detected: textureId=%d is bound "
|
||||||
|
"for sampling at binding=%u, but is also attached to drawFbo=%u as %s (level=%d, "
|
||||||
|
"trackedLayout=%d)",
|
||||||
|
texture->GetExternalIndex(), binding, drawFbo->GetExternalIndex(),
|
||||||
|
MG_Util::ConvertFramebufferAttachmentTypeToString(attachmentType).c_str(),
|
||||||
|
attachmentLevel, static_cast<Int>(resource->layout));
|
||||||
|
}
|
||||||
|
|
||||||
|
MOBILEGL_ASSERT(false,
|
||||||
|
"ResolveSamplerDescriptor: invalid sampled image layout=%d for textureId=%d, binding=%u",
|
||||||
|
static_cast<Int>(resource->layout), texture->GetExternalIndex(), binding);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
outImageInfo = {
|
outImageInfo = {
|
||||||
.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse),
|
.sampler = m_samplerManager->GetOrCreateSampler(*samplerToUse),
|
||||||
.imageView = resource->view,
|
.imageView = resource->view,
|
||||||
|
|||||||
@@ -17,6 +17,48 @@
|
|||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
static const char* VkImageLayoutToString(VkImageLayout layout) {
|
||||||
|
switch (layout) {
|
||||||
|
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||||
|
return "VK_IMAGE_LAYOUT_UNDEFINED";
|
||||||
|
case VK_IMAGE_LAYOUT_GENERAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_GENERAL";
|
||||||
|
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||||
|
return "VK_IMAGE_LAYOUT_PRESENT_SRC_KHR";
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL";
|
||||||
|
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
|
||||||
|
return "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL";
|
||||||
|
default:
|
||||||
|
return "VK_IMAGE_LAYOUT_OTHER";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool ActiveRenderPassUsesTexture(const ActiveRenderPassInfo& activeRenderPass,
|
||||||
|
const MG_State::GLState::ITextureObject& texture) {
|
||||||
|
for (const auto& trackedAttachment : activeRenderPass.trackedAttachmentLayouts) {
|
||||||
|
if (trackedAttachment.target != TrackedAttachmentTarget::Texture) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trackedAttachment.texture == &texture) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
static Bool IsValidSampledImageLayout(VkImageLayout layout) {
|
||||||
switch (layout) {
|
switch (layout) {
|
||||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||||
@@ -416,6 +458,27 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Vector<MG_State::GLState::ITextureObject*> sampledTextures;
|
Vector<MG_State::GLState::ITextureObject*> sampledTextures;
|
||||||
Bool hasSampledTextures = m_uniformDescriptorBinder->CollectSampledTextures(program, sampledTextures);
|
Bool hasSampledTextures = m_uniformDescriptorBinder->CollectSampledTextures(program, sampledTextures);
|
||||||
MOBILEGL_ASSERT(hasSampledTextures, "%s: CollectSampledTextures failed", __func__);
|
MOBILEGL_ASSERT(hasSampledTextures, "%s: CollectSampledTextures failed", __func__);
|
||||||
|
MGLOG_D("SetupDraw: program=%u drawFbo=%u sampledTextureCount=%zu activeRenderPass=%s",
|
||||||
|
program.GetExternalIndex(), drawFbo ? drawFbo->GetExternalIndex() : 0u, sampledTextures.size(),
|
||||||
|
activeRenderPass ? "true" : "false");
|
||||||
|
Bool activeRenderPassUsesSampledTexture = false;
|
||||||
|
if (activeRenderPass != nullptr) {
|
||||||
|
for (auto* sampledTexture : sampledTextures) {
|
||||||
|
if (sampledTexture == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ActiveRenderPassUsesTexture(*activeRenderPass, *sampledTexture)) {
|
||||||
|
MGLOG_D("SetupDraw: active render pass is still using sampled textureId=%d; ending render pass before descriptor preparation",
|
||||||
|
sampledTexture->GetExternalIndex());
|
||||||
|
activeRenderPassUsesSampledTexture = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activeRenderPassUsesSampledTexture) {
|
||||||
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
|
activeRenderPass = nullptr;
|
||||||
|
}
|
||||||
Bool needSampledTextureTransitions = false;
|
Bool needSampledTextureTransitions = false;
|
||||||
for (auto* sampledTexture : sampledTextures) {
|
for (auto* sampledTexture : sampledTextures) {
|
||||||
if (!sampledTexture) {
|
if (!sampledTexture) {
|
||||||
@@ -426,6 +489,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MOBILEGL_ASSERT(textureResource != nullptr,
|
MOBILEGL_ASSERT(textureResource != nullptr,
|
||||||
"%s: SyncTextureAndGetDescriptor failed for textureId=%d",
|
"%s: SyncTextureAndGetDescriptor failed for textureId=%d",
|
||||||
__func__, sampledTexture->GetExternalIndex());
|
__func__, sampledTexture->GetExternalIndex());
|
||||||
|
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 (!IsValidSampledImageLayout(textureResource->layout)) {
|
||||||
needSampledTextureTransitions = true;
|
needSampledTextureTransitions = true;
|
||||||
break;
|
break;
|
||||||
@@ -433,6 +499,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (activeRenderPass && needSampledTextureTransitions) {
|
if (activeRenderPass && needSampledTextureTransitions) {
|
||||||
|
MGLOG_D("SetupDraw: ending active render pass before sampled texture transitions");
|
||||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
activeRenderPass = nullptr;
|
activeRenderPass = nullptr;
|
||||||
}
|
}
|
||||||
@@ -444,6 +511,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
const Bool ready = m_textureManager->TransitionTextureForSampling(frame.commandBuffer, *sampledTexture);
|
const Bool ready = m_textureManager->TransitionTextureForSampling(frame.commandBuffer, *sampledTexture);
|
||||||
MOBILEGL_ASSERT(ready, "%s: TransitionTextureForSampling failed for textureId=%d",
|
MOBILEGL_ASSERT(ready, "%s: TransitionTextureForSampling failed for textureId=%d",
|
||||||
__func__, sampledTexture->GetExternalIndex());
|
__func__, sampledTexture->GetExternalIndex());
|
||||||
|
auto* transitionedResource = m_textureManager->SyncTextureAndGetDescriptor(*sampledTexture);
|
||||||
|
MOBILEGL_ASSERT(transitionedResource != nullptr,
|
||||||
|
"%s: post-transition SyncTextureAndGetDescriptor failed for textureId=%d",
|
||||||
|
__func__, sampledTexture->GetExternalIndex());
|
||||||
|
MGLOG_D("SetupDraw: sampled textureId=%d layout(after)=%s(%d)",
|
||||||
|
sampledTexture->GetExternalIndex(), VkImageLayoutToString(transitionedResource->layout),
|
||||||
|
static_cast<Int>(transitionedResource->layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired);
|
auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired);
|
||||||
|
|||||||
Reference in New Issue
Block a user