mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 05:38:31 +09:00
[Fix] (DirectVulkan): stop loading and carrying dead default-framebuffer content
- EGL swap semantics make the presented colour buffer's content undefined at its next acquire (EGL_BUFFER_DESTROYED, the implementation default) and every ancillary depth/stencil buffer's content undefined after ANY swap, yet the default-FBO render pass reloaded both with LOAD_OP_LOAD every frame; SwapchainObject now tracks per-image content validity (defined when a pass stores into the attachment, invalidated at present) and the render-pass manager turns an undefined attachment's tile load into LOAD_OP_DONT_CARE with initialLayout=UNDEFINED, keyed into both hashes so the cached LOAD variants cannot be hit by mistake - the default framebuffer's depth attachment is now attached ON DEMAND: a draw with depth test and stencil test both disabled (GL: a disabled test neither reads nor writes its buffer), and no pending depth/stencil clear, resolves to a depth-less pass flavour, dropping the D24S8 tile load AND store outright - MC 26.2 renders its GUI into its own FBO and only ever blits colour to the default framebuffer, so its swapchain pass carried a full-screen depth round-trip for nothing - the flavour only escalates: an active depth-full pass absorbs depth-less draws unchanged, while a depth-using draw against a depth-less pass resolves to an incompatible entry and splits, its depth loading DONT_CARE (the content was undefined all along); the depth-less flavour is folded into ComputeHash and the per-draw fast-path memo so the two flavours can never alias
This commit is contained in:
@@ -262,6 +262,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_images.resize(imageCount, VK_NULL_HANDLE);
|
m_images.resize(imageCount, VK_NULL_HANDLE);
|
||||||
VK_VERIFY(vkGetSwapchainImagesKHR(device, m_swapchain, &imageCount, m_images.data()));
|
VK_VERIFY(vkGetSwapchainImagesKHR(device, m_swapchain, &imageCount, m_images.data()));
|
||||||
m_imageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED);
|
m_imageLayouts.assign(imageCount, VK_IMAGE_LAYOUT_UNDEFINED);
|
||||||
|
// Fresh swapchain images hold garbage until a render pass stores into them.
|
||||||
|
m_imageContentDefined.assign(imageCount, false);
|
||||||
|
m_depthStencilContentDefined.assign(imageCount, false);
|
||||||
|
|
||||||
CreateImageViews(device);
|
CreateImageViews(device);
|
||||||
CreateDepthStencilResources(device, physicalDevice);
|
CreateDepthStencilResources(device, physicalDevice);
|
||||||
@@ -433,9 +436,39 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
m_images.clear();
|
m_images.clear();
|
||||||
m_imageLayouts.clear();
|
m_imageLayouts.clear();
|
||||||
|
m_imageContentDefined.clear();
|
||||||
|
m_depthStencilContentDefined.clear();
|
||||||
m_preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
m_preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool SwapchainObject::IsImageContentDefined(Uint32 index) const {
|
||||||
|
MOBILEGL_ASSERT(index < m_imageContentDefined.size(), "Swapchain image content index out of range");
|
||||||
|
return m_imageContentDefined[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainObject::SetImageContentDefined(Uint32 index, Bool defined) {
|
||||||
|
MOBILEGL_ASSERT(index < m_imageContentDefined.size(), "Swapchain image content index out of range");
|
||||||
|
m_imageContentDefined[index] = defined;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool SwapchainObject::IsDepthStencilContentDefined(Uint32 index) const {
|
||||||
|
MOBILEGL_ASSERT(index < m_depthStencilContentDefined.size(),
|
||||||
|
"Swapchain depth/stencil content index out of range");
|
||||||
|
return m_depthStencilContentDefined[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainObject::SetDepthStencilContentDefined(Uint32 index, Bool defined) {
|
||||||
|
MOBILEGL_ASSERT(index < m_depthStencilContentDefined.size(),
|
||||||
|
"Swapchain depth/stencil content index out of range");
|
||||||
|
m_depthStencilContentDefined[index] = defined;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SwapchainObject::SetAllDepthStencilContentUndefined() {
|
||||||
|
for (SizeT i = 0; i < m_depthStencilContentDefined.size(); ++i) {
|
||||||
|
m_depthStencilContentDefined[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VkImage SwapchainObject::GetImage(Uint32 index) const {
|
VkImage SwapchainObject::GetImage(Uint32 index) const {
|
||||||
MOBILEGL_ASSERT(index < m_images.size(), "Swapchain image index out of range");
|
MOBILEGL_ASSERT(index < m_images.size(), "Swapchain image index out of range");
|
||||||
return m_images[index];
|
return m_images[index];
|
||||||
|
|||||||
@@ -52,6 +52,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void SetImageLayout(Uint32 index, VkImageLayout layout);
|
void SetImageLayout(Uint32 index, VkImageLayout layout);
|
||||||
SizeT GetImageCount() const { return m_images.size(); }
|
SizeT GetImageCount() const { return m_images.size(); }
|
||||||
|
|
||||||
|
// EGL content-validity tracking for the default framebuffer. A color
|
||||||
|
// buffer's content is undefined once its image has been presented
|
||||||
|
// (EGL_BUFFER_DESTROYED swap behaviour, the implementation default),
|
||||||
|
// and every ancillary (depth/stencil) buffer's content is undefined
|
||||||
|
// after ANY swap regardless of swap behaviour (EGL 1.5 §3.10.1). The
|
||||||
|
// render-pass manager turns an undefined attachment's tile load into
|
||||||
|
// LOAD_OP_DONT_CARE. Flags start false (a fresh swapchain image holds
|
||||||
|
// garbage) and a render pass storing into an attachment sets it back
|
||||||
|
// to defined.
|
||||||
|
Bool IsImageContentDefined(Uint32 index) const;
|
||||||
|
void SetImageContentDefined(Uint32 index, Bool defined);
|
||||||
|
Bool IsDepthStencilContentDefined(Uint32 index) const;
|
||||||
|
void SetDepthStencilContentDefined(Uint32 index, Bool defined);
|
||||||
|
void SetAllDepthStencilContentUndefined();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateImageViews(VkDevice device);
|
void CreateImageViews(VkDevice device);
|
||||||
void CreateDepthStencilResources(VkDevice device, VkPhysicalDevice physicalDevice);
|
void CreateDepthStencilResources(VkDevice device, VkPhysicalDevice physicalDevice);
|
||||||
@@ -77,5 +92,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Vector<VkDeviceMemory> m_depthStencilImageMemories;
|
Vector<VkDeviceMemory> m_depthStencilImageMemories;
|
||||||
Vector<VkImageView> m_depthStencilImageViews;
|
Vector<VkImageView> m_depthStencilImageViews;
|
||||||
Vector<VkImageLayout> m_depthStencilImageLayouts;
|
Vector<VkImageLayout> m_depthStencilImageLayouts;
|
||||||
|
Vector<Bool> m_imageContentDefined;
|
||||||
|
Vector<Bool> m_depthStencilContentDefined;
|
||||||
};
|
};
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -481,7 +481,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
|
VkRenderPassManager::HashType VkRenderPassManager::ComputeHash(
|
||||||
const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear) {
|
const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex, Bool includePendingClear,
|
||||||
|
Bool includeDefaultFboDepthStencil) {
|
||||||
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
|
XXHASH_VERIFY(XXH64_reset(m_hashState, m_config.CacheVersion));
|
||||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||||
if (isDefaultFbo) {
|
if (isDefaultFbo) {
|
||||||
@@ -560,9 +561,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
attachment <= FramebufferAttachmentType::BackRight);
|
attachment <= FramebufferAttachmentType::BackRight);
|
||||||
if (isDefaultColorAttachment) {
|
if (isDefaultColorAttachment) {
|
||||||
currentLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
currentLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
||||||
|
// Content validity feeds the attachment's loadOp (see the
|
||||||
|
// creation path), so it must key the cache as well.
|
||||||
|
if (!m_swapchainObject.IsImageContentDefined(swapchainImageIndex)) {
|
||||||
|
currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
}
|
||||||
} else if (attachment == FramebufferAttachmentType::Depth ||
|
} else if (attachment == FramebufferAttachmentType::Depth ||
|
||||||
attachment == FramebufferAttachmentType::Stencil) {
|
attachment == FramebufferAttachmentType::Stencil) {
|
||||||
currentLayout = m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex);
|
currentLayout = m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex);
|
||||||
|
if (!m_swapchainObject.IsDepthStencilContentDefined(swapchainImageIndex)) {
|
||||||
|
currentLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto* textureResource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
auto* textureResource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
|
||||||
@@ -617,14 +626,49 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
combineFramebufferAttachmentObjHash(drawbuf);
|
combineFramebufferAttachmentObjHash(drawbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Depth);
|
// The depth-less default-FBO flavor omits the depth/stencil attachment
|
||||||
combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Stencil);
|
// entirely, so it must hash differently from the depth-full flavor.
|
||||||
|
const Bool depthStencilIncluded = !isDefaultFbo || includeDefaultFboDepthStencil;
|
||||||
|
XXHASH_VERIFY(XXH64_update(m_hashState, &depthStencilIncluded, sizeof(depthStencilIncluded)));
|
||||||
|
if (depthStencilIncluded) {
|
||||||
|
combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Depth);
|
||||||
|
combineFramebufferAttachmentObjHash(FramebufferAttachmentType::Stencil);
|
||||||
|
}
|
||||||
|
|
||||||
return XXH64_digest(m_hashState);
|
return XXH64_digest(m_hashState);
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderPassEntry& VkRenderPassManager::GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo,
|
RenderPassEntry& VkRenderPassManager::GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo,
|
||||||
Uint32 swapchainImageIndex) {
|
Uint32 swapchainImageIndex,
|
||||||
|
Bool drawUsesDepthStencil) {
|
||||||
|
// Resolve the default-FBO depth flavor (see the header comment): keep the
|
||||||
|
// depth attachment when the caller needs it, when a depth/stencil clear is
|
||||||
|
// pending, or when the active pass already carries it (escalate-only, so
|
||||||
|
// alternating depth-less draws never split an established depth pass).
|
||||||
|
Bool includeDefaultFboDepthStencil = true;
|
||||||
|
if (fbo.IsDefaultFramebuffer()) {
|
||||||
|
Bool activeDefaultHasDepthStencil = false;
|
||||||
|
if (const auto* active = GetActiveRenderPass()) {
|
||||||
|
Bool activeIsSwapchainPass = false;
|
||||||
|
Bool activeHasSwapchainDepthStencil = false;
|
||||||
|
for (const auto& tracked : active->trackedAttachmentLayouts) {
|
||||||
|
activeIsSwapchainPass |= tracked.target == TrackedAttachmentTarget::SwapchainColor;
|
||||||
|
activeHasSwapchainDepthStencil |=
|
||||||
|
tracked.target == TrackedAttachmentTarget::SwapchainDepthStencil;
|
||||||
|
}
|
||||||
|
activeDefaultHasDepthStencil = activeIsSwapchainPass && activeHasSwapchainDepthStencil;
|
||||||
|
}
|
||||||
|
const auto& defaultDepthAtt = fbo.GetAttachment(FramebufferAttachmentType::Depth);
|
||||||
|
const auto& defaultStencilAtt = fbo.GetAttachment(FramebufferAttachmentType::Stencil);
|
||||||
|
const Bool pendingDepthStencilClear =
|
||||||
|
(defaultDepthAtt.IsTexture() && m_clearManager.HasPendingClear(defaultDepthAtt)) ||
|
||||||
|
HasPendingRenderbufferClear(defaultDepthAtt) ||
|
||||||
|
(defaultStencilAtt.IsTexture() && m_clearManager.HasPendingClear(defaultStencilAtt)) ||
|
||||||
|
HasPendingRenderbufferClear(defaultStencilAtt);
|
||||||
|
includeDefaultFboDepthStencil =
|
||||||
|
drawUsesDepthStencil || activeDefaultHasDepthStencil || pendingDepthStencilClear;
|
||||||
|
}
|
||||||
|
|
||||||
auto hasPendingClearOnFramebuffer = [&]() -> Bool {
|
auto hasPendingClearOnFramebuffer = [&]() -> Bool {
|
||||||
const auto& drawBuffers = fbo.GetDrawBuffers();
|
const auto& drawBuffers = fbo.GetDrawBuffers();
|
||||||
for (auto attachment : drawBuffers) {
|
for (auto attachment : drawBuffers) {
|
||||||
@@ -674,6 +718,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_rpFastFboVersion == fbo.GetObjectVersion() && m_rpFastSwapchainIndex == swapchainImageIndex &&
|
m_rpFastFboVersion == fbo.GetObjectVersion() && m_rpFastSwapchainIndex == swapchainImageIndex &&
|
||||||
m_rpFastTexEpoch == m_textureManager.GetTextureImageEpoch() &&
|
m_rpFastTexEpoch == m_textureManager.GetTextureImageEpoch() &&
|
||||||
m_rpFastRbEpoch == m_renderbufferImageEpoch &&
|
m_rpFastRbEpoch == m_renderbufferImageEpoch &&
|
||||||
|
(!fbo.IsDefaultFramebuffer() || m_rpFastHadDepthStencil == includeDefaultFboDepthStencil) &&
|
||||||
m_rpFastRenderPassHash == activeRenderPass->hash && !hasPendingClearOnFramebuffer()) {
|
m_rpFastRenderPassHash == activeRenderPass->hash && !hasPendingClearOnFramebuffer()) {
|
||||||
auto activeIt = m_renderPasses.find(activeRenderPass->hash);
|
auto activeIt = m_renderPasses.find(activeRenderPass->hash);
|
||||||
if (activeIt != m_renderPasses.end()) {
|
if (activeIt != m_renderPasses.end()) {
|
||||||
@@ -682,7 +727,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false);
|
auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false, includeDefaultFboDepthStencil);
|
||||||
if (activeRenderPass != nullptr &&
|
if (activeRenderPass != nullptr &&
|
||||||
activeRenderPass->CompatibleWith(compatibilityHash) &&
|
activeRenderPass->CompatibleWith(compatibilityHash) &&
|
||||||
!hasPendingClearOnFramebuffer()) {
|
!hasPendingClearOnFramebuffer()) {
|
||||||
@@ -699,10 +744,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_rpFastTexEpoch = m_textureManager.GetTextureImageEpoch();
|
m_rpFastTexEpoch = m_textureManager.GetTextureImageEpoch();
|
||||||
m_rpFastRbEpoch = m_renderbufferImageEpoch;
|
m_rpFastRbEpoch = m_renderbufferImageEpoch;
|
||||||
m_rpFastRenderPassHash = activeRenderPass->hash;
|
m_rpFastRenderPassHash = activeRenderPass->hash;
|
||||||
|
m_rpFastHadDepthStencil = activeIt->second.hasDepthStencilAttachment;
|
||||||
activeIt->second.lastUsedFrame = m_frameCounter;
|
activeIt->second.lastUsedFrame = m_frameCounter;
|
||||||
return activeIt->second;
|
return activeIt->second;
|
||||||
}
|
}
|
||||||
auto hash = ComputeHash(fbo, swapchainImageIndex, true);
|
auto hash = ComputeHash(fbo, swapchainImageIndex, true, includeDefaultFboDepthStencil);
|
||||||
auto it = m_renderPasses.find(hash);
|
auto it = m_renderPasses.find(hash);
|
||||||
if (it != m_renderPasses.end()) {
|
if (it != m_renderPasses.end()) {
|
||||||
it->second.lastUsedFrame = m_frameCounter;
|
it->second.lastUsedFrame = m_frameCounter;
|
||||||
@@ -894,6 +940,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MOBILEGL_ASSERT(swapchainImageIndex < swapchainViews.size(),
|
MOBILEGL_ASSERT(swapchainImageIndex < swapchainViews.size(),
|
||||||
"GetOrCreateRenderPass: swapchain image index out of range");
|
"GetOrCreateRenderPass: swapchain image index out of range");
|
||||||
trackedColorLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
trackedColorLayout = m_swapchainObject.GetImageLayout(swapchainImageIndex);
|
||||||
|
// EGL: a presented color buffer's content is undefined when its
|
||||||
|
// image comes back around (EGL_BUFFER_DESTROYED, the default
|
||||||
|
// swap behaviour) - skip the tile load instead of reloading
|
||||||
|
// stale pixels nobody may rely on.
|
||||||
|
if (!hasClear && !m_swapchainObject.IsImageContentDefined(swapchainImageIndex)) {
|
||||||
|
trackedColorLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
}
|
||||||
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||||
.target = TrackedAttachmentTarget::SwapchainColor,
|
.target = TrackedAttachmentTarget::SwapchainColor,
|
||||||
.swapchainImageIndex = swapchainImageIndex,
|
.swapchainImageIndex = swapchainImageIndex,
|
||||||
@@ -976,6 +1029,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
};
|
};
|
||||||
const auto* selectedDepthStencilAttachment = isUsableDepthStencilAttachment(depthAtt) ? &depthAtt :
|
const auto* selectedDepthStencilAttachment = isUsableDepthStencilAttachment(depthAtt) ? &depthAtt :
|
||||||
(isUsableDepthStencilAttachment(stencilAtt) ? &stencilAtt : nullptr);
|
(isUsableDepthStencilAttachment(stencilAtt) ? &stencilAtt : nullptr);
|
||||||
|
// Depth-less default-FBO flavor: nothing in this pass touches depth/stencil
|
||||||
|
// and their content is undefined anyway (EGL swap), so drop the attachment
|
||||||
|
// and its whole tile load + store.
|
||||||
|
if (isDefaultFbo && !includeDefaultFboDepthStencil) {
|
||||||
|
selectedDepthStencilAttachment = nullptr;
|
||||||
|
}
|
||||||
const Bool hasDistinctDepthAndStencilAttachments =
|
const Bool hasDistinctDepthAndStencilAttachments =
|
||||||
isUsableDepthStencilAttachment(depthAtt) && isUsableDepthStencilAttachment(stencilAtt) &&
|
isUsableDepthStencilAttachment(depthAtt) && isUsableDepthStencilAttachment(stencilAtt) &&
|
||||||
!sameDepthStencilAttachmentObject(depthAtt, stencilAtt);
|
!sameDepthStencilAttachmentObject(depthAtt, stencilAtt);
|
||||||
@@ -994,6 +1053,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VkImageLayout trackedDepthLayout = isDefaultFbo ?
|
VkImageLayout trackedDepthLayout = isDefaultFbo ?
|
||||||
m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex) :
|
m_swapchainObject.GetDepthStencilImageLayout(swapchainImageIndex) :
|
||||||
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
|
// EGL 1.5 §3.10.1: every ancillary (depth/stencil) buffer's content is
|
||||||
|
// undefined after a swap, so the first default-FBO pass of a frame can
|
||||||
|
// skip the depth/stencil tile load outright.
|
||||||
|
if (isDefaultFbo && !m_swapchainObject.IsDepthStencilContentDefined(swapchainImageIndex)) {
|
||||||
|
trackedDepthLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
|
}
|
||||||
depthAttachmentDescription.flags = 0;
|
depthAttachmentDescription.flags = 0;
|
||||||
VkSampleCountFlagBits depthAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
VkSampleCountFlagBits depthAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
Int depthAttachmentId = 0;
|
Int depthAttachmentId = 0;
|
||||||
@@ -1382,11 +1447,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
case TrackedAttachmentTarget::SwapchainColor:
|
case TrackedAttachmentTarget::SwapchainColor:
|
||||||
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
||||||
s_swapchainObject->SetImageLayout(trackedAttachment.swapchainImageIndex, trackedAttachment.finalLayout);
|
s_swapchainObject->SetImageLayout(trackedAttachment.swapchainImageIndex, trackedAttachment.finalLayout);
|
||||||
|
// The pass stored into the attachment: its content is defined
|
||||||
|
// until the image is next presented.
|
||||||
|
s_swapchainObject->SetImageContentDefined(trackedAttachment.swapchainImageIndex, true);
|
||||||
break;
|
break;
|
||||||
case TrackedAttachmentTarget::SwapchainDepthStencil:
|
case TrackedAttachmentTarget::SwapchainDepthStencil:
|
||||||
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
MOBILEGL_ASSERT(s_swapchainObject != nullptr, "EndRenderPass: swapchain object is null");
|
||||||
s_swapchainObject->SetDepthStencilImageLayout(trackedAttachment.swapchainImageIndex,
|
s_swapchainObject->SetDepthStencilImageLayout(trackedAttachment.swapchainImageIndex,
|
||||||
trackedAttachment.finalLayout);
|
trackedAttachment.finalLayout);
|
||||||
|
s_swapchainObject->SetDepthStencilContentDefined(trackedAttachment.swapchainImageIndex, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
MOBILEGL_ASSERT(false, "EndRenderPass: unsupported tracked attachment target=%d",
|
MOBILEGL_ASSERT(false, "EndRenderPass: unsupported tracked attachment target=%d",
|
||||||
|
|||||||
@@ -188,8 +188,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
HashType ComputeHash(
|
HashType ComputeHash(
|
||||||
const MG_State::GLState::FramebufferObject& fbo,
|
const MG_State::GLState::FramebufferObject& fbo,
|
||||||
Uint32 swapchainImageIndex,
|
Uint32 swapchainImageIndex,
|
||||||
Bool includePendingClear = true);
|
Bool includePendingClear = true,
|
||||||
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex);
|
Bool includeDefaultFboDepthStencil = true);
|
||||||
|
// drawUsesDepthStencil: whether the operation about to run inside the pass
|
||||||
|
// reads or writes the depth/stencil buffer (depth test or stencil test
|
||||||
|
// enabled, or a depth/stencil clear). Only consulted for the DEFAULT
|
||||||
|
// framebuffer: EGL undefines its ancillary buffers at every swap, so a
|
||||||
|
// default-FBO pass whose draws provably never touch depth/stencil is
|
||||||
|
// created WITHOUT the depth attachment - on a tiler that skips the whole
|
||||||
|
// depth tile load AND store. The flavor only escalates: once a pass with
|
||||||
|
// depth is active, later depth-less draws keep using it, and a depth-using
|
||||||
|
// draw against a depth-less active pass resolves to a new (incompatible)
|
||||||
|
// entry, which the caller's compatibility check turns into a pass split;
|
||||||
|
// the new pass's depth loads DONT_CARE (content was undefined all along).
|
||||||
|
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo,
|
||||||
|
Uint32 swapchainImageIndex,
|
||||||
|
Bool drawUsesDepthStencil = true);
|
||||||
void QueueRenderbufferClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
void QueueRenderbufferClear(GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
||||||
const MG_State::GLState::FramebufferObject& drawFbo);
|
const MG_State::GLState::FramebufferObject& drawFbo);
|
||||||
void QueueRenderbufferClear(const ClearAttachmentPayload& clearPayload,
|
void QueueRenderbufferClear(const ClearAttachmentPayload& clearPayload,
|
||||||
@@ -231,6 +245,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Uint64 m_rpFastTexEpoch = 0;
|
Uint64 m_rpFastTexEpoch = 0;
|
||||||
Uint64 m_rpFastRbEpoch = 0;
|
Uint64 m_rpFastRbEpoch = 0;
|
||||||
Uint64 m_rpFastRenderPassHash = 0;
|
Uint64 m_rpFastRenderPassHash = 0;
|
||||||
|
// Whether the memoized entry carries a depth/stencil attachment; a
|
||||||
|
// default-FBO resolution whose effective depth request differs must
|
||||||
|
// miss the memo (the depth-less/depth-full flavors hash differently).
|
||||||
|
Bool m_rpFastHadDepthStencil = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct RenderbufferResource {
|
struct RenderbufferResource {
|
||||||
|
|||||||
@@ -4427,11 +4427,19 @@ void main() {
|
|||||||
static_cast<Int>(transitionedResource->layout));
|
static_cast<Int>(transitionedResource->layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* renderPassEntry = &m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired);
|
// Depth/stencil participation of THIS draw, for the default-FBO depth-less
|
||||||
|
// pass flavor (GL: a disabled depth/stencil test neither reads nor writes
|
||||||
|
// its buffer).
|
||||||
|
const Bool drawUsesDepthStencil =
|
||||||
|
MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest) ||
|
||||||
|
MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::StencilTest);
|
||||||
|
auto* renderPassEntry =
|
||||||
|
&m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired, drawUsesDepthStencil);
|
||||||
if (activeRenderPass && !activeRenderPass->CompatibleWith(*renderPassEntry)) {
|
if (activeRenderPass && !activeRenderPass->CompatibleWith(*renderPassEntry)) {
|
||||||
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
|
||||||
activeRenderPass = nullptr;
|
activeRenderPass = nullptr;
|
||||||
renderPassEntry = &m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired);
|
renderPassEntry =
|
||||||
|
&m_renderPassManager->GetOrCreateRenderPass(*drawFbo, m_imageIndexAcquired, drawUsesDepthStencil);
|
||||||
}
|
}
|
||||||
if (renderPassEntry->attachmentCount == 0 || renderPassEntry->extent.x() <= 0 || renderPassEntry->extent.y() <= 0) {
|
if (renderPassEntry->attachmentCount == 0 || renderPassEntry->extent.x() <= 0 || renderPassEntry->extent.y() <= 0) {
|
||||||
MGLOG_D("SetupDraw skipped: drawFbo=%u resolved to an empty render pass (attachmentCount=%u extent=%dx%d)",
|
MGLOG_D("SetupDraw skipped: drawFbo=%u resolved to an empty render pass (attachmentCount=%u extent=%dx%d)",
|
||||||
@@ -5453,7 +5461,10 @@ void main() {
|
|||||||
"TryBlitToDefaultFramebufferWithShader: failed to create sampled view for textureId=%d mip=%u",
|
"TryBlitToDefaultFramebufferWithShader: failed to create sampled view for textureId=%d mip=%u",
|
||||||
sourceTexture->GetExternalIndex(), srcBinding.mipLevel);
|
sourceTexture->GetExternalIndex(), srcBinding.mipLevel);
|
||||||
|
|
||||||
auto& renderPassEntry = m_renderPassManager->GetOrCreateRenderPass(drawFbo, m_imageIndexAcquired);
|
// A color-only blit never touches depth/stencil: let the default-FBO pass
|
||||||
|
// it opens skip the depth attachment (depth-less flavor).
|
||||||
|
auto& renderPassEntry =
|
||||||
|
m_renderPassManager->GetOrCreateRenderPass(drawFbo, m_imageIndexAcquired, /*drawUsesDepthStencil=*/false);
|
||||||
const Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry);
|
const Bool ok = VkRenderPassManager::BeginRenderPass(frame.commandBuffer, renderPassEntry);
|
||||||
MOBILEGL_ASSERT(ok, "%s: BeginRenderPass failed", __func__);
|
MOBILEGL_ASSERT(ok, "%s: BeginRenderPass failed", __func__);
|
||||||
|
|
||||||
@@ -7696,6 +7707,13 @@ void main() {
|
|||||||
result = VK_SUCCESS;
|
result = VK_SUCCESS;
|
||||||
}
|
}
|
||||||
VK_VERIFY(result, "Present, vkQueuePresentKHR");
|
VK_VERIFY(result, "Present, vkQueuePresentKHR");
|
||||||
|
// EGL swap semantics: the presented color buffer's content is undefined the
|
||||||
|
// next time this image is acquired (EGL_BUFFER_DESTROYED, the default swap
|
||||||
|
// behaviour), and EVERY ancillary depth/stencil buffer's content is
|
||||||
|
// undefined after any swap. The render-pass manager turns the undefined
|
||||||
|
// attachments' next tile loads into LOAD_OP_DONT_CARE.
|
||||||
|
m_swapchainObject.SetImageContentDefined(m_imageIndexAcquired, false);
|
||||||
|
m_swapchainObject.SetAllDepthStencilContentUndefined();
|
||||||
// The authoritative check, done here - after the frame is presented, before the next
|
// The authoritative check, done here - after the frame is presented, before the next
|
||||||
// acquire. This is what makes a launcher-side resolution change take effect: shrinking
|
// acquire. This is what makes a launcher-side resolution change take effect: shrinking
|
||||||
// the window's buffer (SurfaceHolder.setFixedSize) moves currentExtent, the swapchain
|
// the window's buffer (SurfaceHolder.setFixedSize) moves currentExtent, the swapchain
|
||||||
|
|||||||
Reference in New Issue
Block a user