mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (MG_Backend): scale a depth blit, keep going after one declines, and mip a 1D texture
Three DirectVulkan gaps found together.
glBlitFramebuffer's depth/stencil path refused any blit whose source and
destination extents differ, because vkCmdCopyImage cannot resize. vkCmdBlitImage
can, and VK_FILTER_NEAREST is the only filter Vulkan allows for depth/stencil
anyway - which is what the GL front end already requires. A same-size pair keeps
the cheaper copy.
Worse, that refusal and four others were `return`, not `continue`, so a
depth/stencil aspect this backend could not handle abandoned the whole function -
including the colour blit that only starts after the aspect loop. The CTS's
scaling blits therefore lost their colour as well, which is why
direct_state_access.framebuffers_blit failed all three of its checks rather than
one.
VulkanRenderer::GenerateMipmap declined GL_TEXTURE_1D. It needed nothing else:
the blit loop derives every offset from the storage extent, and a 1D texture's is
{width, 1, 1}, which is exactly the y and z offsets a 1D image requires.
Also: IsTimerQueryResultReady now asks the query pool before the frame serial.
The pool polls with VK_QUERY_RESULT_WITH_AVAILABILITY_BIT and is the authority;
the frame serial only advances at Present and neither completion notifier will
mark the current serial done, so a timestamp written and fence-waited inside one
GL frame could never be read back within it.
Takes framebuffers_blit and textures_generate_mipmaps from failing to passing on
DirectVulkan. queries_functional still fails there on a value.
This commit is contained in:
@@ -6329,17 +6329,20 @@ void main() {
|
|||||||
|
|
||||||
if (srcX1 < srcX0 || srcY1 < srcY0 || dstX1 < dstX0 || dstY1 < dstY0) {
|
if (srcX1 < srcX0 || srcY1 < srcY0 || dstX1 < dstX0 || dstY1 < dstY0) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: depth blits with flipped rectangles are not supported yet");
|
MGLOG_E("BlitFramebuffer skipped: depth blits with flipped rectangles are not supported yet");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Int srcWidth = srcX1 - srcX0;
|
const Int srcWidth = srcX1 - srcX0;
|
||||||
const Int srcHeight = srcY1 - srcY0;
|
const Int srcHeight = srcY1 - srcY0;
|
||||||
const Int dstWidth = dstX1 - dstX0;
|
const Int dstWidth = dstX1 - dstX0;
|
||||||
const Int dstHeight = dstY1 - dstY0;
|
const Int dstHeight = dstY1 - dstY0;
|
||||||
if (srcWidth <= 0 || srcHeight <= 0 || srcWidth != dstWidth || srcHeight != dstHeight) {
|
if (srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: depth blits currently require matching source and destination extents");
|
MGLOG_E("BlitFramebuffer skipped: degenerate depth blit rectangle");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
// A scaling depth blit is legal GL and vkCmdBlitImage scales natively; only a same-size
|
||||||
|
// pair can take the cheaper vkCmdCopyImage.
|
||||||
|
const Bool depthBlitScales = srcWidth != dstWidth || srcHeight != dstHeight;
|
||||||
|
|
||||||
if (!readIsDefaultFbo) {
|
if (!readIsDefaultFbo) {
|
||||||
const auto sourceAttachmentType = ResolveFramebufferCopyAttachmentType(*readFbo, true, srcBinding.aspectMask);
|
const auto sourceAttachmentType = ResolveFramebufferCopyAttachmentType(*readFbo, true, srcBinding.aspectMask);
|
||||||
@@ -6384,7 +6387,7 @@ void main() {
|
|||||||
: *srcBinding.trackedLayout;
|
: *srcBinding.trackedLayout;
|
||||||
if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: depth source image layout is undefined");
|
MGLOG_E("BlitFramebuffer skipped: depth source image layout is undefined");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VkImageLayout dstOriginalLayout = drawIsDefaultFbo
|
const VkImageLayout dstOriginalLayout = drawIsDefaultFbo
|
||||||
@@ -6401,7 +6404,7 @@ void main() {
|
|||||||
if (srcBinding.format != dstBinding.format) {
|
if (srcBinding.format != dstBinding.format) {
|
||||||
if (readIsDefaultFbo || drawIsDefaultFbo) {
|
if (readIsDefaultFbo || drawIsDefaultFbo) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: cross-format depth/stencil blit with the default framebuffer");
|
MGLOG_E("BlitFramebuffer skipped: cross-format depth/stencil blit with the default framebuffer");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (!BlitDepthAcrossFormats(frame, srcBinding.image, srcBinding.format, srcBinding.trackedLayout,
|
if (!BlitDepthAcrossFormats(frame, srcBinding.image, srcBinding.format, srcBinding.trackedLayout,
|
||||||
srcBinding.mipLevel, srcBinding.baseArrayLayer, dstBinding.image,
|
srcBinding.mipLevel, srcBinding.baseArrayLayer, dstBinding.image,
|
||||||
@@ -6409,7 +6412,7 @@ void main() {
|
|||||||
dstBinding.baseArrayLayer, srcX0, srcY0, dstX0, dstY0, srcX1 - srcX0,
|
dstBinding.baseArrayLayer, srcX0, srcY0, dstX0, dstY0, srcX1 - srcX0,
|
||||||
srcY1 - srcY0, srcOriginalLayout, dstRestoreLayout,
|
srcY1 - srcY0, srcOriginalLayout, dstRestoreLayout,
|
||||||
depthStencilAspect == VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
depthStencilAspect == VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -6456,23 +6459,45 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(ok, "%s: failed to transition depth destination image", __func__);
|
MOBILEGL_ASSERT(ok, "%s: failed to transition depth destination image", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageCopy copyRegion{};
|
if (depthBlitScales) {
|
||||||
copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
|
// vkCmdCopyImage cannot resize; NEAREST is the only filter Vulkan allows for a
|
||||||
copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
|
// depth/stencil blit anyway, and the GL front end already rejects the others.
|
||||||
copyRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
VkImageBlit blitRegion{};
|
||||||
copyRegion.srcSubresource.layerCount = srcBinding.layerCount;
|
blitRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
|
||||||
copyRegion.srcOffset = {srcX0, srcY0, 0};
|
blitRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
|
||||||
copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
|
blitRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
||||||
copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
|
blitRegion.srcSubresource.layerCount = srcBinding.layerCount;
|
||||||
copyRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer;
|
blitRegion.srcOffsets[0] = {srcX0, srcY0, 0};
|
||||||
copyRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
blitRegion.srcOffsets[1] = {srcX1, srcY1, 1};
|
||||||
copyRegion.dstOffset = {dstX0, dstY0, 0};
|
blitRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
|
||||||
copyRegion.extent = {static_cast<Uint32>(srcWidth), static_cast<Uint32>(srcHeight), 1};
|
blitRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
|
||||||
|
blitRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer;
|
||||||
|
blitRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
||||||
|
blitRegion.dstOffsets[0] = {dstX0, dstY0, 0};
|
||||||
|
blitRegion.dstOffsets[1] = {dstX1, dstY1, 1};
|
||||||
|
vkCmdBlitImage(frame.commandBuffer,
|
||||||
|
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
1, &blitRegion, VK_FILTER_NEAREST);
|
||||||
|
} else {
|
||||||
|
VkImageCopy copyRegion{};
|
||||||
|
copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
|
||||||
|
copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
|
||||||
|
copyRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
||||||
|
copyRegion.srcSubresource.layerCount = srcBinding.layerCount;
|
||||||
|
copyRegion.srcOffset = {srcX0, srcY0, 0};
|
||||||
|
copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
|
||||||
|
copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
|
||||||
|
copyRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer;
|
||||||
|
copyRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
||||||
|
copyRegion.dstOffset = {dstX0, dstY0, 0};
|
||||||
|
copyRegion.extent = {static_cast<Uint32>(srcWidth), static_cast<Uint32>(srcHeight), 1};
|
||||||
|
|
||||||
vkCmdCopyImage(frame.commandBuffer,
|
vkCmdCopyImage(frame.commandBuffer,
|
||||||
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
1, ©Region);
|
1, ©Region);
|
||||||
|
}
|
||||||
|
|
||||||
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
VkAccessFlags srcRestoreAccessMask = 0;
|
VkAccessFlags srcRestoreAccessMask = 0;
|
||||||
@@ -7931,7 +7956,10 @@ void main() {
|
|||||||
// broken invariant. Declining leaves the mip chain unwritten; asserting took the process
|
// broken invariant. Declining leaves the mip chain unwritten; asserting took the process
|
||||||
// down with it.
|
// down with it.
|
||||||
if (textureTarget != TextureTarget::Texture2D && textureTarget != TextureTarget::Texture2DArray &&
|
if (textureTarget != TextureTarget::Texture2D && textureTarget != TextureTarget::Texture2DArray &&
|
||||||
textureTarget != TextureTarget::Texture3D && textureTarget != TextureTarget::TextureCubeMap) {
|
textureTarget != TextureTarget::Texture3D && textureTarget != TextureTarget::TextureCubeMap &&
|
||||||
|
// A 1D texture needs nothing special: its storage extent is {width, 1, 1}, so the blit
|
||||||
|
// loop below already emits the y and z offsets of 0 and 1 that a 1D image requires.
|
||||||
|
textureTarget != TextureTarget::Texture1D) {
|
||||||
MGLOG_W("GenerateMipmap: unsupported target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
|
MGLOG_W("GenerateMipmap: unsupported target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -9273,10 +9301,17 @@ void main() {
|
|||||||
if (record.harvested) {
|
if (record.harvested) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!m_timerQueryManager || !IsFrameSerialComplete(record.frameSerial)) {
|
if (!m_timerQueryManager) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return m_timerQueryManager->TryHarvest(record);
|
// Ask the pool first. It polls with VK_QUERY_RESULT_WITH_AVAILABILITY_BIT and is the
|
||||||
|
// authority on whether the timestamp has landed; the frame serial is not, because it only
|
||||||
|
// advances at Present and neither completion notifier will mark the CURRENT serial done - so
|
||||||
|
// a timestamp written and fence-waited inside one GL frame could never be read back in it.
|
||||||
|
if (m_timerQueryManager->TryHarvest(record)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return IsFrameSerialComplete(record.frameSerial) && m_timerQueryManager->TryHarvest(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::WaitForTimerQueryResult(VkTimerQueryManager::TimestampRecord& record) {
|
Bool VulkanRenderer::WaitForTimerQueryResult(VkTimerQueryManager::TimestampRecord& record) {
|
||||||
|
|||||||
Reference in New Issue
Block a user