mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Fix] (DirectVulkan): multisample resolve blits; per-buffer blit skip; RGBA-widened renderbuffers
Color blits from a multisampled source now use vkCmdResolveImage (both blit resolvers carry the image sample count); a buffer named in the blit mask but absent from either framebuffer skips just that buffer instead of cancelling the whole blit (GL 4.6 18.3.1); and three-channel color renderbuffers widen to their RGBA twin exactly like textures, so renderbuffer<->texture blits of the same GL format see one VkFormat. framebuffer_blit.multisampled_to_singlesampled_blit_color_config_test passes - the whole framebuffer_blit family is green.
This commit is contained in:
@@ -303,7 +303,47 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
const auto internalFormat = renderbuffer->GetInternalFormat();
|
||||
const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
|
||||
// Three-channel color formats widen to their RGBA twin exactly like textures do
|
||||
// (VkTextureManager::ResolveTextureFormatInfo): blits/resolves between a
|
||||
// renderbuffer and a texture of the same GL format then see one VkFormat.
|
||||
const VkFormat format = [&]() -> VkFormat {
|
||||
switch (internalFormat) {
|
||||
case TextureInternalFormat::RGB:
|
||||
case TextureInternalFormat::RGB8:
|
||||
case TextureInternalFormat::R3G3B2:
|
||||
case TextureInternalFormat::RGB4:
|
||||
case TextureInternalFormat::RGB5:
|
||||
return VK_FORMAT_R8G8B8A8_UNORM;
|
||||
case TextureInternalFormat::SRGB8:
|
||||
return VK_FORMAT_R8G8B8A8_SRGB;
|
||||
case TextureInternalFormat::RGB8Snorm:
|
||||
return VK_FORMAT_R8G8B8A8_SNORM;
|
||||
case TextureInternalFormat::RGB10:
|
||||
case TextureInternalFormat::RGB12:
|
||||
case TextureInternalFormat::RGB16:
|
||||
return VK_FORMAT_R16G16B16A16_UNORM;
|
||||
case TextureInternalFormat::RGB16Snorm:
|
||||
return VK_FORMAT_R16G16B16A16_SNORM;
|
||||
case TextureInternalFormat::RGB16F:
|
||||
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||
case TextureInternalFormat::RGB32F:
|
||||
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
||||
case TextureInternalFormat::RGB8I:
|
||||
return VK_FORMAT_R8G8B8A8_SINT;
|
||||
case TextureInternalFormat::RGB8UI:
|
||||
return VK_FORMAT_R8G8B8A8_UINT;
|
||||
case TextureInternalFormat::RGB16I:
|
||||
return VK_FORMAT_R16G16B16A16_SINT;
|
||||
case TextureInternalFormat::RGB16UI:
|
||||
return VK_FORMAT_R16G16B16A16_UINT;
|
||||
case TextureInternalFormat::RGB32I:
|
||||
return VK_FORMAT_R32G32B32A32_SINT;
|
||||
case TextureInternalFormat::RGB32UI:
|
||||
return VK_FORMAT_R32G32B32A32_UINT;
|
||||
default:
|
||||
return MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
|
||||
}
|
||||
}();
|
||||
const VkImageAspectFlags aspect = ResolveImageAspectMaskForFormat(format);
|
||||
// Renderbuffers are never sampled (GL has no way to bind one to a sampler), so the
|
||||
// usage set is attachment + transfer: transfer covers readback (vkCmdCopyImageToBuffer),
|
||||
|
||||
@@ -1253,6 +1253,7 @@ void main() {
|
||||
VkImageLayout* trackedLayout = nullptr;
|
||||
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_NONE;
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||
IntVec2 extent = {0, 0};
|
||||
Uint32 mipLevel = 0;
|
||||
Uint32 mipLevelCount = 1;
|
||||
@@ -1451,6 +1452,7 @@ void main() {
|
||||
outBinding.trackedLayout = &rbResource->layout;
|
||||
outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
outBinding.format = rbResource->format;
|
||||
outBinding.sampleCount = rbResource->sampleCount;
|
||||
outBinding.extent = {static_cast<Int>(rbResource->extent.width),
|
||||
static_cast<Int>(rbResource->extent.height)};
|
||||
outBinding.mipLevel = 0;
|
||||
@@ -1483,6 +1485,7 @@ void main() {
|
||||
outBinding.trackedLayout = &resource->layout;
|
||||
outBinding.aspectMask = resource->aspect;
|
||||
outBinding.format = resource->format;
|
||||
outBinding.sampleCount = resource->sampleCount;
|
||||
const auto attachmentExtent = attachment.GetSize();
|
||||
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
||||
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
||||
@@ -1536,7 +1539,11 @@ void main() {
|
||||
|
||||
const auto& attachment = fbo.GetAttachment(attachmentType);
|
||||
if (!attachment.IsComplete()) {
|
||||
MGLOG_E("BlitFramebuffer skipped: %s framebuffer attachment is incomplete", outBinding.label);
|
||||
MGLOG_E("BlitFramebuffer skipped: %s framebuffer attachment is incomplete (fbo=%u attachmentType=%d "
|
||||
"isTexture=%d isRenderbuffer=%d texId=%d)",
|
||||
outBinding.label, fbo.GetExternalIndex(), static_cast<Int>(attachmentType),
|
||||
attachment.IsTexture() ? 1 : 0, attachment.IsRenderbuffer() ? 1 : 0,
|
||||
attachment.IsTexture() && attachment.GetTexture() ? static_cast<Int>(attachment.GetTexture()->GetExternalIndex()) : -1);
|
||||
return false;
|
||||
}
|
||||
if (attachment.IsRenderbuffer()) {
|
||||
@@ -1557,6 +1564,7 @@ void main() {
|
||||
outBinding.trackedLayout = &rbResource->layout;
|
||||
outBinding.aspectMask = requiredAspectMask;
|
||||
outBinding.format = rbResource->format;
|
||||
outBinding.sampleCount = rbResource->sampleCount;
|
||||
outBinding.extent = {static_cast<Int>(rbResource->extent.width),
|
||||
static_cast<Int>(rbResource->extent.height)};
|
||||
outBinding.mipLevel = 0;
|
||||
@@ -1588,6 +1596,7 @@ void main() {
|
||||
outBinding.trackedLayout = &resource->layout;
|
||||
outBinding.aspectMask = requiredAspectMask;
|
||||
outBinding.format = resource->format;
|
||||
outBinding.sampleCount = resource->sampleCount;
|
||||
const auto attachmentExtent = attachment.GetSize();
|
||||
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
||||
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
||||
@@ -1698,6 +1707,7 @@ void main() {
|
||||
outBinding.trackedLayout = &rbResource->layout;
|
||||
outBinding.aspectMask = requiredAspectMask;
|
||||
outBinding.format = rbResource->format;
|
||||
outBinding.sampleCount = rbResource->sampleCount;
|
||||
outBinding.extent = {static_cast<Int>(rbResource->extent.width),
|
||||
static_cast<Int>(rbResource->extent.height)};
|
||||
outBinding.mipLevel = 0;
|
||||
@@ -1730,6 +1740,7 @@ void main() {
|
||||
outBinding.trackedLayout = &resource->layout;
|
||||
outBinding.aspectMask = requiredAspectMask;
|
||||
outBinding.format = resource->format;
|
||||
outBinding.sampleCount = resource->sampleCount;
|
||||
const auto attachmentExtent = attachment.GetSize();
|
||||
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
||||
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
||||
@@ -6178,7 +6189,9 @@ void main() {
|
||||
!ResolveFramebufferBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject,
|
||||
*m_textureManager, *m_renderPassManager,
|
||||
depthStencilAspect, dstBinding)) {
|
||||
return;
|
||||
// A buffer named in the mask but absent from either framebuffer copies
|
||||
// nothing for that buffer; the other requested buffers still blit.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (srcX1 < srcX0 || srcY1 < srcY0 || dstX1 < dstX0 || dstY1 < dstY0) {
|
||||
@@ -6493,10 +6506,26 @@ void main() {
|
||||
ApplyNativeBlitDefaultFramebufferTransform(m_swapchainObject.GetPreTransform(), dstBinding, blitRegion);
|
||||
}
|
||||
|
||||
vkCmdBlitImage(frame.commandBuffer,
|
||||
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &blitRegion, filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
|
||||
if (srcBinding.sampleCount != VK_SAMPLE_COUNT_1_BIT && dstBinding.sampleCount == VK_SAMPLE_COUNT_1_BIT) {
|
||||
// GL multisample resolve blits are 1:1 by spec; vkCmdBlitImage cannot read a
|
||||
// multisampled source.
|
||||
VkImageResolve resolveRegion{};
|
||||
resolveRegion.srcSubresource = blitRegion.srcSubresource;
|
||||
resolveRegion.srcOffset = {std::min(srcX0, srcX1), std::min(srcY0, srcY1), 0};
|
||||
resolveRegion.dstSubresource = blitRegion.dstSubresource;
|
||||
resolveRegion.dstOffset = {std::min(dstX0, dstX1), std::min(dstY0, dstY1), 0};
|
||||
resolveRegion.extent = {static_cast<Uint32>(std::abs(srcX1 - srcX0)),
|
||||
static_cast<Uint32>(std::abs(srcY1 - srcY0)), 1};
|
||||
vkCmdResolveImage(frame.commandBuffer,
|
||||
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &resolveRegion);
|
||||
} else {
|
||||
vkCmdBlitImage(frame.commandBuffer,
|
||||
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &blitRegion, filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
|
||||
}
|
||||
|
||||
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
VkAccessFlags srcRestoreAccessMask = 0;
|
||||
|
||||
Reference in New Issue
Block a user