[Feat|Fix] (MG_Backend): Implement BlitFramebuffer & DrawElementsBaseVertex.

This commit is contained in:
BZLZHH
2026-02-16 17:12:33 +08:00
parent 40fc317f69
commit 03d3fcdc66
3 changed files with 471 additions and 131 deletions
+461 -130
View File
@@ -3244,126 +3244,53 @@ namespace MobileGL::MG_Backend::DirectVulkan::TmpImpl {
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter) { GLint dstY1, GLbitfield mask, GLenum filter) {
EnsureInitialized(); EnsureInitialized();
if ((mask & GL_COLOR_BUFFER_BIT) == 0) { GLbitfield validMask = mask & (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
MGLOG_W("BlitFramebuffer: only color blit is supported"); if (!validMask) return;
return;
}
auto& readSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
auto& drawSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw); auto& drawSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
auto readFBO = readSlot.GetBoundObject(); auto& readSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
auto drawFBO = drawSlot.GetBoundObject(); auto drawFBO = drawSlot.GetBoundObject();
if (!readFBO || !drawFBO) { auto readFBO = readSlot.GetBoundObject();
MGLOG_E("BlitFramebuffer: read/draw framebuffer not bound"); if (!drawFBO || !readFBO) {
MGLOG_E("BlitFramebuffer: draw/read framebuffer is not bound");
return; return;
} }
auto getBackendFBO = [&](const SharedPtr<FramebufferObject>& stateFBO) -> SharedPtr<BackendFramebufferObject> { auto defaultFBO = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO;
auto it = g.framebuffers.find(stateFBO);
SharedPtr<BackendFramebufferObject> backend; auto& pending = g.pendingClears;
auto pendingDrawIt = pending.find(drawFBO);
if (pendingDrawIt != pending.end() && pendingDrawIt->second.mask) {
if (!EnsureRenderPassBound()) return;
}
auto ensureBackendFbo = [&](const SharedPtr<FramebufferObject>& fbo, SharedPtr<BackendFramebufferObject>& out,
Bool& isDefault) -> Bool {
if (fbo == defaultFBO) {
isDefault = true;
out = nullptr;
return true;
}
isDefault = false;
auto it = g.framebuffers.find(fbo);
if (it == g.framebuffers.end()) { if (it == g.framebuffers.end()) {
backend = MakeShared<BackendFramebufferObject>(); auto backend = MakeShared<BackendFramebufferObject>();
g.framebuffers[stateFBO] = backend; g.framebuffers[fbo] = backend;
out = backend;
} else { } else {
backend = it->second; out = it->second;
} }
if (!SyncFramebufferObject(*backend, stateFBO)) return nullptr; if (!SyncFramebufferObject(*out, fbo)) return false;
return backend; return out->IsValid();
}; };
auto readBackend = getBackendFBO(readFBO); SharedPtr<BackendFramebufferObject> readBackend = nullptr;
auto drawBackend = getBackendFBO(drawFBO); SharedPtr<BackendFramebufferObject> drawBackend = nullptr;
bool readDefault = readFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO; Bool readIsDefault = false;
bool drawDefault = drawFBO == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO; Bool drawIsDefault = false;
if (!readDefault && !readBackend) return; if (!ensureBackendFbo(readFBO, readBackend, readIsDefault)) return;
if (!drawDefault && !drawBackend) return; if (!ensureBackendFbo(drawFBO, drawBackend, drawIsDefault)) return;
auto pickSrcType = [&](const BackendFramebufferObject& backend) -> FramebufferAttachmentType {
auto type = backend.frontendReadBuffer;
if (type == FramebufferAttachmentType::None) return FramebufferAttachmentType::Color0;
return type;
};
auto pickDstType = [&](const BackendFramebufferObject& backend) -> FramebufferAttachmentType {
auto type = backend.frontendDrawBuffers[0];
if (type == FramebufferAttachmentType::None) return FramebufferAttachmentType::Color0;
return type;
};
FramebufferAttachmentType srcType = FramebufferAttachmentType::Color0;
FramebufferAttachmentType dstType = FramebufferAttachmentType::Color0;
if (!readDefault && readBackend) srcType = pickSrcType(*readBackend);
if (!drawDefault && drawBackend) dstType = pickDstType(*drawBackend);
auto resolveImage = [&](BackendFramebufferObject& backend, FramebufferAttachmentType type, VkImage& image,
VkImageLayout*& layout, VkExtent2D& extent, VkFormat& format,
bool& isRenderbuffer) -> bool {
Int32 idx = backend.attachmentIndex[static_cast<SizeT>(type)];
if (idx < 0 || static_cast<SizeT>(idx) >= backend.attachments.size()) return false;
const auto& att = backend.attachments[static_cast<SizeT>(idx)];
if (att.texture) {
auto& texRes = SyncTexture(att.texture);
image = texRes.image;
layout = &texRes.layout;
extent = {texRes.extent.width, texRes.extent.height};
format = texRes.format;
isRenderbuffer = false;
return image != VK_NULL_HANDLE;
}
if (att.renderbuffer) {
auto& rbRes = SyncRenderbuffer(att.renderbuffer);
image = rbRes.image;
layout = &rbRes.layout;
extent = rbRes.extent;
format = rbRes.format;
isRenderbuffer = true;
return image != VK_NULL_HANDLE;
}
return false;
};
VkImage srcImage = VK_NULL_HANDLE;
VkImage dstImage = VK_NULL_HANDLE;
VkImageLayout* srcLayout = nullptr;
VkImageLayout* dstLayout = nullptr;
VkExtent2D srcExtent{0, 0};
VkExtent2D dstExtent{0, 0};
VkFormat srcFormat = VK_FORMAT_UNDEFINED;
VkFormat dstFormat = VK_FORMAT_UNDEFINED;
bool srcIsRb = false;
bool dstIsRb = false;
if (readDefault) {
auto& frame = *g.frames[g.currentFrame];
const auto& images = g.swapchain->GetImages();
if (frame.CurrentImageIndex >= images.size()) return;
srcImage = images[frame.CurrentImageIndex];
srcLayout = &g.swapchainImageLayouts[frame.CurrentImageIndex];
srcExtent = g.swapchain->GetExtent();
srcFormat = g.swapchain->GetFormat();
} else {
if (!resolveImage(*readBackend, srcType, srcImage, srcLayout, srcExtent, srcFormat, srcIsRb)) {
MGLOG_E("BlitFramebuffer: failed to resolve source image");
return;
}
}
if (drawDefault) {
auto& frame = *g.frames[g.currentFrame];
const auto& images = g.swapchain->GetImages();
if (frame.CurrentImageIndex >= images.size()) return;
dstImage = images[frame.CurrentImageIndex];
dstLayout = &g.swapchainImageLayouts[frame.CurrentImageIndex];
dstExtent = g.swapchain->GetExtent();
dstFormat = g.swapchain->GetFormat();
} else {
if (!resolveImage(*drawBackend, dstType, dstImage, dstLayout, dstExtent, dstFormat, dstIsRb)) {
MGLOG_E("BlitFramebuffer: failed to resolve destination image");
return;
}
}
if (dstIsRb) {
MGLOG_W("BlitFramebuffer: renderbuffer destination is not supported yet");
return;
}
BeginCommandBuffer(); BeginCommandBuffer();
auto& frame = *g.frames[g.currentFrame]; auto& frame = *g.frames[g.currentFrame];
@@ -3374,34 +3301,438 @@ namespace MobileGL::MG_Backend::DirectVulkan::TmpImpl {
g.recordingFramebuffer = VK_NULL_HANDLE; g.recordingFramebuffer = VK_NULL_HANDLE;
} }
VkImageLayout oldSrc = srcLayout ? *srcLayout : VK_IMAGE_LAYOUT_UNDEFINED; struct BlitImageRef {
VkImageLayout oldDst = dstLayout ? *dstLayout : VK_IMAGE_LAYOUT_UNDEFINED; VkImage image = VK_NULL_HANDLE;
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageAspectFlags aspect = 0;
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageLayout restoreLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageLayout* trackedLayout = nullptr;
VkExtent2D extent{0, 0};
};
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT; auto stageForLayout = [](VkImageLayout layout) -> VkPipelineStageFlags {
CmdTransitionImageLayout(frame.CommandBuffer, srcImage, oldSrc, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, aspect); switch (layout) {
CmdTransitionImageLayout(frame.CommandBuffer, dstImage, oldDst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, aspect); case VK_IMAGE_LAYOUT_UNDEFINED:
return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_PIPELINE_STAGE_TRANSFER_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
default:
return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
}
};
VkImageBlit blit{}; auto accessForLayout = [](VkImageLayout layout) -> VkAccessFlags {
blit.srcSubresource.aspectMask = aspect; switch (layout) {
blit.srcSubresource.mipLevel = 0; case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
blit.srcSubresource.baseArrayLayer = 0; return VK_ACCESS_TRANSFER_READ_BIT;
blit.srcSubresource.layerCount = 1; case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
blit.dstSubresource = blit.srcSubresource; return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
default:
return 0;
}
};
blit.srcOffsets[0] = {srcX0, srcY0, 0}; auto transitionForBlit = [&](BlitImageRef& ref, VkImageLayout newLayout, VkImageAspectFlags aspectMask) {
blit.srcOffsets[1] = {srcX1, srcY1, 1}; if (ref.image == VK_NULL_HANDLE || ref.layout == newLayout || aspectMask == 0) return;
blit.dstOffsets[0] = {dstX0, dstY0, 0}; VkImageMemoryBarrier barrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
blit.dstOffsets[1] = {dstX1, dstY1, 1}; barrier.oldLayout = ref.layout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = ref.image;
barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.srcAccessMask = accessForLayout(ref.layout);
barrier.dstAccessMask = accessForLayout(newLayout);
vkCmdPipelineBarrier(frame.CommandBuffer, stageForLayout(ref.layout), stageForLayout(newLayout), 0, 0,
nullptr, 0, nullptr, 1, &barrier);
ref.layout = newLayout;
if (ref.trackedLayout) *ref.trackedLayout = newLayout;
};
auto resolveDefaultColor = [&](BlitImageRef& out) -> Bool {
if (g.frames.empty()) return false;
const auto& images = g.swapchain->GetImages();
Uint32 imageIndex = frame.CurrentImageIndex;
if (imageIndex >= images.size()) return false;
out.image = images[imageIndex];
out.format = g.swapchain->GetFormat();
out.aspect = VK_IMAGE_ASPECT_COLOR_BIT;
out.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
out.restoreLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
out.trackedLayout = nullptr;
out.extent = g.swapchain->GetExtent();
return out.image != VK_NULL_HANDLE;
};
auto resolveDefaultDepthStencil = [&](BlitImageRef& out) -> Bool {
if (g.depthImage == VK_NULL_HANDLE || g.depthFormat == VK_FORMAT_UNDEFINED) return false;
out.image = g.depthImage;
out.format = g.depthFormat;
out.aspect = AspectForFormat(g.depthFormat) & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
out.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
out.restoreLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
out.trackedLayout = nullptr;
out.extent = g.swapchain->GetExtent();
return out.aspect != 0;
};
auto resolveBackendAttachment = [&](const SharedPtr<BackendFramebufferObject>& backend,
FramebufferAttachmentType type, BlitImageRef& out) -> Bool {
if (!backend) return false;
Int32 idx = backend->attachmentIndex[static_cast<SizeT>(type)];
if (idx < 0 || static_cast<SizeT>(idx) >= backend->attachments.size()) return false;
const auto& info = backend->attachments[static_cast<SizeT>(idx)];
if (info.texture) {
auto& tex = SyncTexture(info.texture);
if (!tex.valid || tex.image == VK_NULL_HANDLE) return false;
out.image = tex.image;
out.format = tex.format;
out.aspect = AspectForFormat(tex.format);
out.layout = tex.layout;
out.restoreLayout = LayoutForFramebufferAttachment(type, tex.format);
out.trackedLayout = &tex.layout;
out.extent = {tex.extent.width, tex.extent.height};
return true;
}
if (info.renderbuffer) {
auto& rb = SyncRenderbuffer(info.renderbuffer);
if (!rb.valid || rb.image == VK_NULL_HANDLE) return false;
out.image = rb.image;
out.format = rb.format;
out.aspect = AspectForFormat(rb.format);
out.layout = rb.layout;
out.restoreLayout = LayoutForFramebufferAttachment(type, rb.format);
out.trackedLayout = &rb.layout;
out.extent = rb.extent;
return true;
}
return false;
};
auto clampCoord = [](GLint v, Uint32 upper) -> Int32 { return std::clamp(v, 0, static_cast<GLint>(upper)); };
auto hasBlitFeature = [&](VkFormat fmt, VkFormatFeatureFlagBits bit) -> Bool {
VkFormatProperties props{};
vkGetPhysicalDeviceFormatProperties(g.ctx->GetPhysicalDevice(), fmt, &props);
return (props.optimalTilingFeatures & bit) != 0;
};
auto blitOrCopyColor = [&](BlitImageRef src, BlitImageRef dst) {
if (src.image == VK_NULL_HANDLE || dst.image == VK_NULL_HANDLE) return;
if (src.image == dst.image) {
MGLOG_W("BlitFramebuffer: skip color blit on the same image");
return;
}
Int32 sx0 = clampCoord(srcX0, src.extent.width);
Int32 sy0 = clampCoord(srcY0, src.extent.height);
Int32 sx1 = clampCoord(srcX1, src.extent.width);
Int32 sy1 = clampCoord(srcY1, src.extent.height);
GLint dstY0Resolved = dstY0;
GLint dstY1Resolved = dstY1;
if (drawIsDefault) {
GLint h = static_cast<GLint>(dst.extent.height);
dstY0Resolved = h - dstY0;
dstY1Resolved = h - dstY1;
}
Int32 dx0 = clampCoord(dstX0, dst.extent.width);
Int32 dy0 = clampCoord(dstY0Resolved, dst.extent.height);
Int32 dx1 = clampCoord(dstX1, dst.extent.width);
Int32 dy1 = clampCoord(dstY1Resolved, dst.extent.height);
if (sx0 == sx1 || sy0 == sy1 || dx0 == dx1 || dy0 == dy1) return;
VkFilter vkFilter = (filter == GL_LINEAR) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST; VkFilter vkFilter = (filter == GL_LINEAR) ? VK_FILTER_LINEAR : VK_FILTER_NEAREST;
vkCmdBlitImage(frame.CommandBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstImage, if (filter != GL_NEAREST && filter != GL_LINEAR) {
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, vkFilter); MGLOG_W("BlitFramebuffer: unsupported filter 0x%x, fallback to GL_NEAREST", filter);
vkFilter = VK_FILTER_NEAREST;
}
if (vkFilter == VK_FILTER_LINEAR &&
!hasBlitFeature(src.format, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
vkFilter = VK_FILTER_NEAREST;
}
CmdTransitionImageLayout(frame.CommandBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, oldSrc, aspect); Bool canBlit = hasBlitFeature(src.format, VK_FORMAT_FEATURE_BLIT_SRC_BIT) &&
CmdTransitionImageLayout(frame.CommandBuffer, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, oldDst, aspect); hasBlitFeature(dst.format, VK_FORMAT_FEATURE_BLIT_DST_BIT);
Bool srcRectPositive = sx1 > sx0 && sy1 > sy0;
Bool dstRectPositive = dx1 > dx0 && dy1 > dy0;
Bool sameExtent = (sx1 - sx0) == (dx1 - dx0) && (sy1 - sy0) == (dy1 - dy0);
Bool canCopy = src.format == dst.format && srcRectPositive && dstRectPositive && sameExtent;
if (srcLayout) *srcLayout = oldSrc; transitionForBlit(src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT);
if (dstLayout) *dstLayout = oldDst; transitionForBlit(dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT);
if (canBlit) {
VkImageBlit region{};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffsets[0] = {sx0, sy0, 0};
region.srcOffsets[1] = {sx1, sy1, 1};
region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffsets[0] = {dx0, dy0, 0};
region.dstOffsets[1] = {dx1, dy1, 1};
vkCmdBlitImage(frame.CommandBuffer, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region, vkFilter);
} else if (canCopy) {
VkImageCopy region{};
region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffset = {sx0, sy0, 0};
region.dstSubresource = region.srcSubresource;
region.dstOffset = {dx0, dy0, 0};
region.extent = {static_cast<Uint32>(sx1 - sx0), static_cast<Uint32>(sy1 - sy0), 1};
vkCmdCopyImage(frame.CommandBuffer, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
} else {
MGLOG_W("BlitFramebuffer: color blit unsupported for current source/destination format or region");
}
transitionForBlit(src, src.restoreLayout, VK_IMAGE_ASPECT_COLOR_BIT);
transitionForBlit(dst, dst.restoreLayout, VK_IMAGE_ASPECT_COLOR_BIT);
};
auto blitOrCopyDepthStencil = [&](BlitImageRef src, BlitImageRef dst, VkImageAspectFlags requestedAspect) {
VkImageAspectFlags opAspect = requestedAspect & src.aspect & dst.aspect;
if (!opAspect || src.image == VK_NULL_HANDLE || dst.image == VK_NULL_HANDLE) return;
if (src.image == dst.image) {
MGLOG_W("BlitFramebuffer: skip depth/stencil blit on the same image");
return;
}
Int32 sx0 = clampCoord(srcX0, src.extent.width);
Int32 sy0 = clampCoord(srcY0, src.extent.height);
Int32 sx1 = clampCoord(srcX1, src.extent.width);
Int32 sy1 = clampCoord(srcY1, src.extent.height);
GLint dstY0Resolved = dstY0;
GLint dstY1Resolved = dstY1;
if (drawIsDefault) {
GLint h = static_cast<GLint>(dst.extent.height);
dstY0Resolved = h - dstY0;
dstY1Resolved = h - dstY1;
}
Int32 dx0 = clampCoord(dstX0, dst.extent.width);
Int32 dy0 = clampCoord(dstY0Resolved, dst.extent.height);
Int32 dx1 = clampCoord(dstX1, dst.extent.width);
Int32 dy1 = clampCoord(dstY1Resolved, dst.extent.height);
if (sx0 == sx1 || sy0 == sy1 || dx0 == dx1 || dy0 == dy1) return;
Bool canBlit = hasBlitFeature(src.format, VK_FORMAT_FEATURE_BLIT_SRC_BIT) &&
hasBlitFeature(dst.format, VK_FORMAT_FEATURE_BLIT_DST_BIT);
Bool srcRectPositive = sx1 > sx0 && sy1 > sy0;
Bool dstRectPositive = dx1 > dx0 && dy1 > dy0;
Bool sameExtent = (sx1 - sx0) == (dx1 - dx0) && (sy1 - sy0) == (dy1 - dy0);
Bool canCopy = src.format == dst.format && srcRectPositive && dstRectPositive && sameExtent;
transitionForBlit(src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, opAspect);
transitionForBlit(dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, opAspect);
if (canBlit) {
VkImageBlit region{};
region.srcSubresource.aspectMask = opAspect;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffsets[0] = {sx0, sy0, 0};
region.srcOffsets[1] = {sx1, sy1, 1};
region.dstSubresource.aspectMask = opAspect;
region.dstSubresource.mipLevel = 0;
region.dstSubresource.baseArrayLayer = 0;
region.dstSubresource.layerCount = 1;
region.dstOffsets[0] = {dx0, dy0, 0};
region.dstOffsets[1] = {dx1, dy1, 1};
vkCmdBlitImage(frame.CommandBuffer, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region, VK_FILTER_NEAREST);
} else if (canCopy) {
VkImageCopy region{};
region.srcSubresource.aspectMask = opAspect;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.srcOffset = {sx0, sy0, 0};
region.dstSubresource = region.srcSubresource;
region.dstOffset = {dx0, dy0, 0};
region.extent = {static_cast<Uint32>(sx1 - sx0), static_cast<Uint32>(sy1 - sy0), 1};
vkCmdCopyImage(frame.CommandBuffer, src.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
} else {
MGLOG_W("BlitFramebuffer: depth/stencil blit unsupported for current format or region");
}
transitionForBlit(src, src.restoreLayout, opAspect);
transitionForBlit(dst, dst.restoreLayout, opAspect);
};
if (validMask & GL_COLOR_BUFFER_BIT) {
BlitImageRef srcColor{};
Bool srcColorValid = false;
if (readIsDefault) {
srcColorValid = resolveDefaultColor(srcColor);
} else {
FramebufferAttachmentType readType = readFBO->GetReadBuffer();
if (readType >= FramebufferAttachmentType::Color0 && readType <= FramebufferAttachmentType::Color31) {
srcColorValid = resolveBackendAttachment(readBackend, readType, srcColor);
} else {
MGLOG_W("BlitFramebuffer: read buffer is not a color attachment");
}
}
if (srcColorValid) {
Vector<BlitImageRef> dstColors;
if (drawIsDefault) {
BlitImageRef dstColor{};
if (resolveDefaultColor(dstColor)) dstColors.push_back(dstColor);
} else {
const auto& drawBuffers = drawFBO->GetDrawBuffers();
for (Uint32 i = 0; i < FramebufferObject::MAX_DRAW_BUFFERS; ++i) {
FramebufferAttachmentType buf = drawBuffers[i];
if (buf < FramebufferAttachmentType::Color0 || buf > FramebufferAttachmentType::Color31) {
continue;
}
BlitImageRef dstColor{};
if (!resolveBackendAttachment(drawBackend, buf, dstColor)) continue;
bool duplicated = false;
for (const auto& existing : dstColors) {
if (existing.image == dstColor.image) {
duplicated = true;
break;
}
}
if (!duplicated) dstColors.push_back(dstColor);
}
}
for (const auto& dst : dstColors) {
blitOrCopyColor(srcColor, dst);
}
}
}
VkImageAspectFlags requestedDsAspect = 0;
if (validMask & GL_DEPTH_BUFFER_BIT) requestedDsAspect |= VK_IMAGE_ASPECT_DEPTH_BIT;
if (validMask & GL_STENCIL_BUFFER_BIT) requestedDsAspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
if (requestedDsAspect != 0) {
if (filter == GL_LINEAR) {
MGLOG_W("BlitFramebuffer: GL_LINEAR is invalid for depth/stencil blit, forcing nearest");
}
BlitImageRef srcDs{};
BlitImageRef dstDs{};
Bool srcValid = false;
Bool dstValid = false;
if (readIsDefault) {
srcValid = resolveDefaultDepthStencil(srcDs);
} else {
if ((requestedDsAspect & VK_IMAGE_ASPECT_DEPTH_BIT) &&
resolveBackendAttachment(readBackend, FramebufferAttachmentType::Depth, srcDs)) {
srcValid = true;
} else if ((requestedDsAspect & VK_IMAGE_ASPECT_STENCIL_BIT) &&
resolveBackendAttachment(readBackend, FramebufferAttachmentType::Stencil, srcDs)) {
srcValid = true;
}
}
if (drawIsDefault) {
dstValid = resolveDefaultDepthStencil(dstDs);
} else {
if ((requestedDsAspect & VK_IMAGE_ASPECT_DEPTH_BIT) &&
resolveBackendAttachment(drawBackend, FramebufferAttachmentType::Depth, dstDs)) {
dstValid = true;
} else if ((requestedDsAspect & VK_IMAGE_ASPECT_STENCIL_BIT) &&
resolveBackendAttachment(drawBackend, FramebufferAttachmentType::Stencil, dstDs)) {
dstValid = true;
}
}
if (srcValid && dstValid) {
blitOrCopyDepthStencil(srcDs, dstDs, requestedDsAspect);
}
}
}
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {
VkPipeline pipeline = VK_NULL_HANDLE;
VkDescriptorSet drawSet = VK_NULL_HANDLE;
VertexArrayObject* vao = nullptr;
const RenderStateParameters* rs = nullptr;
ProgramResource* prog = nullptr;
DV::FrameContext* frame = nullptr;
if (!PrepareForDraw(mode, pipeline, drawSet, vao, rs, prog, frame)) return;
if (vao) {
for (Uint32 i = 0; i < VertexArrayObject::MAX_VERTEX_ATTRIBS; ++i) {
const auto& attr = vao->GetAttribute(i);
if (!attr.Enabled || !attr.Buffer) continue;
auto& vbo = SyncBuffer(attr.Buffer.get());
VkDeviceSize offset = 0;
VkBuffer buf = vbo.buffer;
vkCmdBindVertexBuffers(frame->CommandBuffer, i, 1, &buf, &offset);
}
}
VkIndexType indexType = ToVkIndexType(type);
Uint32 indexSize = IndexTypeSize(type);
VkDeviceSize indexOffset = 0;
BufferResource* iboRes = nullptr;
if (vao) {
auto ibo = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (ibo) {
iboRes = &SyncBuffer(ibo.get());
indexOffset = reinterpret_cast<uintptr_t>(indices);
}
}
BufferResource tempIndex;
if (!iboRes && indices) {
VkDeviceSize sz = static_cast<VkDeviceSize>(count) * indexSize;
CreateBuffer(sz, VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, tempIndex);
Memcpy(tempIndex.mapped, indices, sz);
iboRes = &tempIndex;
indexOffset = 0;
}
if (iboRes && iboRes->buffer != VK_NULL_HANDLE) {
vkCmdBindIndexBuffer(frame->CommandBuffer, iboRes->buffer, 0, indexType);
Uint32 firstIndex = indexSize ? static_cast<Uint32>(indexOffset / indexSize) : 0;
vkCmdDrawIndexed(frame->CommandBuffer, count, 1, firstIndex, basevertex, 0);
}
if (tempIndex.buffer != VK_NULL_HANDLE || tempIndex.memory != VK_NULL_HANDLE) {
frame->TrashBuffers.push_back({tempIndex.buffer, tempIndex.memory, tempIndex.mapped != nullptr});
}
} }
void DrawArrays(GLenum mode, GLint first, GLsizei count) { void DrawArrays(GLenum mode, GLint first, GLsizei count) {
@@ -13,6 +13,7 @@
#include <MG_Backend/DirectGLES/DirectGLES.h> #include <MG_Backend/DirectGLES/DirectGLES.h>
#endif #endif
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN #if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN
#include <MG_Backend/DirectVulkan/TmpImpl.h>
#include <MG_Backend/DirectVulkan/DirectVulkan.h> #include <MG_Backend/DirectVulkan/DirectVulkan.h>
#endif #endif
#include <MG_Util/BackendLoaders/OpenGL/Loader.h> #include <MG_Util/BackendLoaders/OpenGL/Loader.h>
@@ -78,6 +79,8 @@ namespace MobileGL {
#endif #endif
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES #if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::DrawElementsBaseVertex(mode, count, type, indices, basevertex); MG_Backend::DirectGLES::DrawElementsBaseVertex(mode, count, type, indices, basevertex);
#elif MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN
MG_Backend::DirectVulkan::TmpImpl::DrawElementsBaseVertex(mode, count, type, indices, basevertex);
#endif #endif
} }
@@ -19,6 +19,9 @@
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES #if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
#include <MG_Backend/DirectGLES/DirectGLES.h> #include <MG_Backend/DirectGLES/DirectGLES.h>
#endif #endif
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN
#include <MG_Backend/DirectVulkan/TmpImpl.h>
#endif
namespace MobileGL { namespace MobileGL {
namespace MG_Impl::GLImpl { namespace MG_Impl::GLImpl {
@@ -27,6 +30,9 @@ namespace MobileGL {
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES #if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
MG_Backend::DirectGLES::BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, MG_Backend::DirectGLES::BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask,
filter); filter);
#elif MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_VULKAN
MG_Backend::DirectVulkan::TmpImpl::BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
mask, filter);
#endif #endif
} }