[Feat] (MG_Backend/DirectVulkan): implement backend func for glCopyTex(Sub)Image2D

This commit is contained in:
2026-05-05 12:02:33 +08:00
parent bc2f2d896b
commit 99b753be9a
6 changed files with 504 additions and 51 deletions
@@ -13,10 +13,29 @@
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
UniquePtr<VulkanRenderer> pVulkanRenderer = nullptr; UniquePtr<VulkanRenderer> pVulkanRenderer = nullptr;
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {} void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {} MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferfi called with null VulkanRenderer");
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {} MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearBufferfi called with null GL context");
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {} pVulkanRenderer->ClearBufferfi(buffer, drawbuffer, depth, stencil);
}
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferfv called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearBufferfv called with null GL context");
pVulkanRenderer->ClearBufferfv(buffer, drawbuffer, value);
}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferuiv called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearBufferuiv called with null GL context");
pVulkanRenderer->ClearBufferuiv(buffer, drawbuffer, value);
}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearBufferiv called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearBufferiv called with null GL context");
pVulkanRenderer->ClearBufferiv(buffer, drawbuffer, value);
}
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {} void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {} void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {}
@@ -36,9 +55,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {} void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {}
void DrawArraysIndirect(GLenum mode, const void* indirect) {} void DrawArraysIndirect(GLenum mode, const void* indirect) {}
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border) {} GLsizei height, GLint border) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::CopyTexImage2D called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::CopyTexImage2D called with null GL context");
pVulkanRenderer->CopyTexSubImage2D(target, level, 0, 0, x, y, width, height);
}
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height) {} GLsizei height) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::CopyTexSubImage2D called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::CopyTexSubImage2D called with null GL context");
pVulkanRenderer->CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
}
void GenerateMipmap(GLenum target) {} void GenerateMipmap(GLenum target) {}
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {} void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {}
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {} void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {}
@@ -31,8 +31,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
continue; continue;
QueueClear({ QueueClear({
.color = clearPayload.color, .mask = GL_COLOR_BUFFER_BIT,
.attachmentType = drawbuf .color = clearPayload.color
}, drawFbo.GetAttachment(drawbuf).GetTexture()); }, drawFbo.GetAttachment(drawbuf).GetTexture());
MGLOG_D("%s: %s (texture %d) - color = (%.2f, %.2f, %.2f, %.2f)", __func__, MGLOG_D("%s: %s (texture %d) - color = (%.2f, %.2f, %.2f, %.2f)", __func__,
MG_Util::ConvertFramebufferAttachmentTypeToString(drawbuf).c_str(), MG_Util::ConvertFramebufferAttachmentTypeToString(drawbuf).c_str(),
@@ -44,8 +44,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (mask & GL_DEPTH_BUFFER_BIT && if (mask & GL_DEPTH_BUFFER_BIT &&
!drawFbo.GetAttachment(FramebufferAttachmentType::Depth).IsRenderbuffer()) { !drawFbo.GetAttachment(FramebufferAttachmentType::Depth).IsRenderbuffer()) {
QueueClear({ QueueClear({
.mask = GL_DEPTH_BUFFER_BIT,
.depth = clearPayload.depth, .depth = clearPayload.depth,
.attachmentType = FramebufferAttachmentType::Depth,
}, drawFbo.GetAttachment(FramebufferAttachmentType::Depth).GetTexture()); }, drawFbo.GetAttachment(FramebufferAttachmentType::Depth).GetTexture());
MGLOG_D("%s: Depth (texture %d) - depth = (%.2f)", __func__, MGLOG_D("%s: Depth (texture %d) - depth = (%.2f)", __func__,
drawFbo.GetAttachment(FramebufferAttachmentType::Depth).GetTexture()->GetExternalIndex(), clearPayload.depth); drawFbo.GetAttachment(FramebufferAttachmentType::Depth).GetTexture()->GetExternalIndex(), clearPayload.depth);
@@ -54,8 +54,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (mask & GL_STENCIL_BUFFER_BIT && if (mask & GL_STENCIL_BUFFER_BIT &&
!drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).IsRenderbuffer()) { !drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).IsRenderbuffer()) {
QueueClear({ QueueClear({
.mask = GL_STENCIL_BUFFER_BIT,
.stencil = clearPayload.stencil, .stencil = clearPayload.stencil,
.attachmentType = FramebufferAttachmentType::Stencil,
}, drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).GetTexture()); }, drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).GetTexture());
MGLOG_D("%s: Stencil (texture %d) - stencil = (%u)", __func__, MGLOG_D("%s: Stencil (texture %d) - stencil = (%u)", __func__,
drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).GetTexture()->GetExternalIndex(), clearPayload.stencil); drawFbo.GetAttachment(FramebufferAttachmentType::Stencil).GetTexture()->GetExternalIndex(), clearPayload.stencil);
@@ -64,12 +64,25 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload, void VkClearManager::QueueClear(const ClearAttachmentPayload& clearPayload,
const SharedPtr<MG_State::GLState::ITextureObject>& texture) { const SharedPtr<MG_State::GLState::ITextureObject>& texture) {
if (clearPayload.mask == 0) {
return;
}
WeakPtr<MG_State::GLState::ITextureObject> weakTexturePtr = texture; WeakPtr<MG_State::GLState::ITextureObject> weakTexturePtr = texture;
if (weakTexturePtr.expired()) if (weakTexturePtr.expired())
return; return;
auto* pTexture = weakTexturePtr.lock().get(); auto* pTexture = weakTexturePtr.lock().get();
m_aliveObjects[pTexture] = weakTexturePtr; m_aliveObjects[pTexture] = weakTexturePtr;
m_pendingClears[pTexture] = clearPayload; auto& pending = m_pendingClears[pTexture];
pending.mask |= clearPayload.mask;
if (clearPayload.mask & GL_COLOR_BUFFER_BIT) {
pending.color = clearPayload.color;
}
if (clearPayload.mask & GL_DEPTH_BUFFER_BIT) {
pending.depth = clearPayload.depth;
}
if (clearPayload.mask & GL_STENCIL_BUFFER_BIT) {
pending.stencil = clearPayload.stencil;
}
} }
Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) { Bool VkClearManager::HasPendingClear(MG_State::GLState::ITextureObject* texture) {
@@ -84,9 +97,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
outPayload = m_pendingClears[texture]; outPayload = m_pendingClears[texture];
MGLOG_D("%s: Got pending clear for texture %d (%s), clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__, MGLOG_D("%s: Got pending clear for texture %d (%s), mask=0x%x clear value: color = (%.2f, %.2f, %.2f, %.2f), depth = (%.2f), stencil = (%u)", __func__,
texture->GetExternalIndex(), texture->GetExternalIndex(),
MG_Util::ConvertTextureInternalFormatToString(texture->GetFormat()).c_str(), MG_Util::ConvertTextureInternalFormatToString(texture->GetFormat()).c_str(),
static_cast<Uint32>(outPayload.mask),
outPayload.color[0], outPayload.color[1], outPayload.color[2], outPayload.color[3], outPayload.color[0], outPayload.color[1], outPayload.color[2], outPayload.color[3],
outPayload.depth, outPayload.depth,
outPayload.stencil); outPayload.stencil);
@@ -23,12 +23,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}; };
struct ClearAttachmentPayload { struct ClearAttachmentPayload {
union { GLbitfield mask = 0;
FloatVec4 color; FloatVec4 color = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f);
Float depth{}; Float depth = 1.0f;
Uint32 stencil; Uint32 stencil = 0;
};
FramebufferAttachmentType attachmentType = FramebufferAttachmentType::Color0;
}; };
class VkClearManager { class VkClearManager {
@@ -80,8 +80,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool hasPayload = m_clearManager.GetPendingClear(texture, clearPayload); Bool hasPayload = m_clearManager.GetPendingClear(texture, clearPayload);
XXHASH_VERIFY(XXH64_update(m_hashState, &hasPayload, sizeof(hasPayload))); XXHASH_VERIFY(XXH64_update(m_hashState, &hasPayload, sizeof(hasPayload)));
if (hasPayload) { if (hasPayload) {
XXHASH_VERIFY(XXH64_update( XXHASH_VERIFY(XXH64_update(m_hashState, &clearPayload.mask, sizeof(clearPayload.mask)));
m_hashState, &clearPayload.attachmentType, sizeof(clearPayload.attachmentType)));
} }
} }
@@ -172,14 +171,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Int width = 0; Int width = 0;
Int height = 0; Int height = 0;
Vector<VkAttachmentDescription> attachmentDescriptions(validDrawBufCount); Vector<VkAttachmentDescription> attachmentDescriptions;
attachmentDescriptions.reserve(validDrawBufCount + 1);
Vector<VkAttachmentReference> colorAttachmentRefs(validDrawBufCount); Vector<VkAttachmentReference> colorAttachmentRefs(validDrawBufCount);
for (auto& attachmentRef : colorAttachmentRefs) {
attachmentRef.attachment = VK_ATTACHMENT_UNUSED;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
}
Vector<PendingClearAttachmentInfo> pendingClearAttachments; Vector<PendingClearAttachmentInfo> pendingClearAttachments;
pendingClearAttachments.reserve(validDrawBufCount + 1);
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts; Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
trackedAttachmentLayouts.reserve(validDrawBufCount + 1);
auto& textureResources = RenderPassEntry::s_textureResourcesScratch; auto& textureResources = RenderPassEntry::s_textureResourcesScratch;
textureResources.clear(); textureResources.clear();
textureResources.resize(validDrawBufCount, nullptr); textureResources.reserve(validDrawBufCount + 1);
Vector<VkImageView> attachmentViews(validDrawBufCount, VK_NULL_HANDLE); Vector<VkImageView> attachmentViews;
attachmentViews.reserve(validDrawBufCount + 1);
// This should automatically work on default & offscreen FBO // This should automatically work on default & offscreen FBO
// assuming default FBO has the right param // assuming default FBO has the right param
for (Int i = 0; i < validDrawBufCount; ++i) { for (Int i = 0; i < validDrawBufCount; ++i) {
@@ -192,9 +199,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto* texture = att.GetTexture().get(); auto* texture = att.GetTexture().get();
const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(att.GetTextureLevel(), 0)); const Uint32 attachmentMipLevel = static_cast<Uint32>(std::max(att.GetTextureLevel(), 0));
const auto textureTarget = texture->GetTarget(); const auto textureTarget = texture->GetTarget();
const Uint32 attachmentIndex = static_cast<Uint32>(attachmentDescriptions.size());
attachmentDescriptions.emplace_back();
// Color attachment description // Color attachment description
VkAttachmentDescription& desc = attachmentDescriptions[i]; VkAttachmentDescription& desc = attachmentDescriptions.back();
switch (textureTarget) { switch (textureTarget) {
case TextureTarget::Texture2D: { case TextureTarget::Texture2D: {
auto* texture2d = auto* texture2d =
@@ -219,7 +228,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
if (hasClear) { if (hasClear) {
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo { pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
.attachmentIndex = static_cast<Uint32>(i), .attachmentIndex = attachmentIndex,
.texture = texture .texture = texture
}); });
} }
@@ -238,20 +247,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
.swapchainImageIndex = swapchainImageIndex, .swapchainImageIndex = swapchainImageIndex,
.finalLayout = desc.finalLayout, .finalLayout = desc.finalLayout,
}); });
attachmentViews[i] = swapchainViews[swapchainImageIndex]; textureResources.emplace_back(nullptr);
attachmentViews.emplace_back(swapchainViews[swapchainImageIndex]);
} else { } else {
textureResources[i] = m_textureManager.SyncTextureAndGetDescriptor(*texture); auto* textureResource = m_textureManager.SyncTextureAndGetDescriptor(*texture);
MOBILEGL_ASSERT(textureResources[i], MOBILEGL_ASSERT(textureResource,
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i); "GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at color attachment %d", i);
desc.format = textureResources[i]->format; textureResources.emplace_back(textureResource);
trackedColorLayout = textureResources[i]->layout; desc.format = textureResource->format;
trackedColorLayout = textureResource->layout;
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo { trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.target = TrackedAttachmentTarget::Texture, .target = TrackedAttachmentTarget::Texture,
.texture = texture, .texture = texture,
.finalLayout = desc.finalLayout, .finalLayout = desc.finalLayout,
}); });
attachmentViews[i] = m_textureManager.GetOrCreateViewAtMipLevel(*texture, attachmentMipLevel); attachmentViews.emplace_back(m_textureManager.GetOrCreateViewAtMipLevel(*texture, attachmentMipLevel));
MOBILEGL_ASSERT(attachmentViews[i] != VK_NULL_HANDLE, MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
"GetOrCreateRenderPass: GetOrCreateAttachmentView failed at color attachment %d", i); "GetOrCreateRenderPass: GetOrCreateAttachmentView failed at color attachment %d", i);
} }
@@ -273,7 +284,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Attachment reference // Attachment reference
VkAttachmentReference& attachmentRef = colorAttachmentRefs[i]; VkAttachmentReference& attachmentRef = colorAttachmentRefs[i];
attachmentRef.attachment = i; attachmentRef.attachment = attachmentIndex;
attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
} }
@@ -290,8 +301,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Uint32 depthAttachmentIndex = static_cast<Uint32>(attachmentDescriptions.size()); const Uint32 depthAttachmentIndex = static_cast<Uint32>(attachmentDescriptions.size());
ClearAttachmentPayload clearPayload{}; ClearAttachmentPayload clearPayload{};
Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload); Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload);
Bool clearDepth = hasClear && clearPayload.attachmentType == FramebufferAttachmentType::Depth; Bool clearDepth = hasClear && (clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0;
Bool clearStencil = hasClear && clearPayload.attachmentType == FramebufferAttachmentType::Stencil; Bool clearStencil = hasClear && (clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0;
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;
@@ -453,17 +464,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!s_clearManager->GetPendingClear(pending.texture, clearPayload)) { if (!s_clearManager->GetPendingClear(pending.texture, clearPayload)) {
continue; continue;
} }
if (clearPayload.attachmentType >= FramebufferAttachmentType::Color0 && if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
clearPayload.attachmentType <= FramebufferAttachmentType::Color31) {
clearValues[pending.attachmentIndex].color = { clearValues[pending.attachmentIndex].color = {
clearPayload.color.x(), clearPayload.color.x(),
clearPayload.color.y(), clearPayload.color.y(),
clearPayload.color.z(), clearPayload.color.z(),
clearPayload.color.w() clearPayload.color.w()
}; };
} else if (clearPayload.attachmentType == FramebufferAttachmentType::Depth) { }
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
clearValues[pending.attachmentIndex].depthStencil.depth = clearPayload.depth; clearValues[pending.attachmentIndex].depthStencil.depth = clearPayload.depth;
} else if (clearPayload.attachmentType == FramebufferAttachmentType::Stencil) { }
if ((clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) {
clearValues[pending.attachmentIndex].depthStencil.stencil = clearPayload.stencil; clearValues[pending.attachmentIndex].depthStencil.stencil = clearPayload.stencil;
} }
} }
@@ -15,6 +15,7 @@
#include "MG_State/GLState/ProgramState/ShaderObject.h" #include "MG_State/GLState/ProgramState/ShaderObject.h"
#include "MG_State/GLState/SamplerState/SamplerObject.h" #include "MG_State/GLState/SamplerState/SamplerObject.h"
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h" #include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
#include "MG_Util/Converters/GLToMG/TextureEnumConverter.h"
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h" #include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
@@ -84,6 +85,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false; return false;
} }
static void RecordClearBufferError(const char* func, ErrorCode code, const char* message) {
MG_State::pGLContext->RecordError(code, MakeUnique<GenericErrorInfo>("DirectVulkan", func, message));
}
static void RecordTextureCopyError(const char* func, ErrorCode code, const char* message) {
MG_State::pGLContext->RecordError(code, MakeUnique<GenericErrorInfo>("DirectVulkan", func, message));
}
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:
@@ -154,6 +163,49 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
} }
static void GetImageTransitionDestinationState(VkImageLayout newLayout, VkPipelineStageFlags& outDstStageMask,
VkAccessFlags& outDstAccessMask) {
switch (newLayout) {
case VK_IMAGE_LAYOUT_UNDEFINED:
outDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
outDstAccessMask = 0;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
outDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
outDstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
outDstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
outDstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
outDstStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
outDstAccessMask = VK_ACCESS_SHADER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
outDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
outDstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
outDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
outDstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
outDstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
outDstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
break;
default:
outDstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
outDstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT;
break;
}
}
static Bool ResolveColorBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer, static Bool ResolveColorBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer,
Uint32 swapchainImageIndex, SwapchainObject& swapchainObject, Uint32 swapchainImageIndex, SwapchainObject& swapchainObject,
VkTextureManager& textureManager, BlitImageBinding& outBinding) { VkTextureManager& textureManager, BlitImageBinding& outBinding) {
@@ -218,6 +270,37 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true; return true;
} }
static Bool ResolveTextureCopyDestinationBinding(MG_State::GLState::ITextureObject& texture, Uint32 mipLevel,
VkTextureManager& textureManager, BlitImageBinding& outBinding) {
auto* resource = textureManager.SyncTextureAndGetDescriptor(texture);
if (resource == nullptr) {
MGLOG_E("CopyTexSubImage2D skipped: failed to sync destination textureId=%d",
texture.GetExternalIndex());
return false;
}
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) {
MGLOG_E("CopyTexSubImage2D skipped: destination textureId=%d is not a color image",
texture.GetExternalIndex());
return false;
}
if (mipLevel >= resource->mipLevels) {
MGLOG_E("CopyTexSubImage2D skipped: destination textureId=%d mip=%u out of range (mips=%u)",
texture.GetExternalIndex(), mipLevel, resource->mipLevels);
return false;
}
outBinding.image = resource->image;
outBinding.trackedLayout = &resource->layout;
outBinding.aspectMask = resource->aspect;
outBinding.extent = {
static_cast<Int>(std::max(1u, resource->extent.width >> mipLevel)),
static_cast<Int>(std::max(1u, resource->extent.height >> mipLevel))};
outBinding.mipLevel = mipLevel;
outBinding.mipLevelCount = 1;
outBinding.label = "destination texture";
return true;
}
static BlitSurfaceTransform ToBlitSurfaceTransform(VkSurfaceTransformFlagBitsKHR preTransform) { static BlitSurfaceTransform ToBlitSurfaceTransform(VkSurfaceTransformFlagBitsKHR preTransform) {
switch (preTransform) { switch (preTransform) {
case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
@@ -925,6 +1008,135 @@ void main() {
m_clearManager->QueueClear(mask, payload, *fbo); m_clearManager->QueueClear(mask, payload, *fbo);
} }
void VulkanRenderer::QueueClearBufferPayload(GLenum buffer, GLint drawbuffer,
const ClearAttachmentPayload& clearPayload) {
m_clearManager->CollectGarbage();
auto* fbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject().get();
MOBILEGL_ASSERT(fbo, "VulkanRenderer::QueueClearBufferPayload: draw framebuffer not found");
auto queueAttachmentClear = [&](FramebufferAttachmentType attachmentType) {
if (attachmentType == FramebufferAttachmentType::None) {
return;
}
const auto& attachment = fbo->GetAttachment(attachmentType);
if (!attachment.IsTexture() || attachment.IsRenderbuffer()) {
return;
}
auto texture = attachment.GetTexture();
if (!texture) {
return;
}
m_clearManager->QueueClear(clearPayload, texture);
};
switch (buffer) {
case GL_COLOR: {
if (drawbuffer < 0 ||
drawbuffer >= static_cast<GLint>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS)) {
RecordClearBufferError(__func__, ErrorCode::InvalidValue, "color drawbuffer index is out of range");
return;
}
queueAttachmentClear(fbo->GetDrawBuffers()[drawbuffer]);
return;
}
case GL_DEPTH:
if (drawbuffer != 0) {
RecordClearBufferError(__func__, ErrorCode::InvalidValue, "depth clear requires drawbuffer 0");
return;
}
queueAttachmentClear(FramebufferAttachmentType::Depth);
return;
case GL_STENCIL:
if (drawbuffer != 0) {
RecordClearBufferError(__func__, ErrorCode::InvalidValue, "stencil clear requires drawbuffer 0");
return;
}
queueAttachmentClear(FramebufferAttachmentType::Stencil);
return;
case GL_DEPTH_STENCIL:
if (drawbuffer != 0) {
RecordClearBufferError(__func__, ErrorCode::InvalidValue, "depth/stencil clear requires drawbuffer 0");
return;
}
queueAttachmentClear(FramebufferAttachmentType::Depth);
queueAttachmentClear(FramebufferAttachmentType::Stencil);
return;
default:
RecordClearBufferError(__func__, ErrorCode::InvalidEnum, "unsupported clear buffer target");
return;
}
}
void VulkanRenderer::ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {
ClearAttachmentPayload payload{};
payload.mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
payload.depth = depth;
payload.stencil = static_cast<Uint32>(stencil);
QueueClearBufferPayload(buffer, drawbuffer, payload);
}
void VulkanRenderer::ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
if (value == nullptr) {
return;
}
ClearAttachmentPayload payload{};
switch (buffer) {
case GL_COLOR:
payload.mask = GL_COLOR_BUFFER_BIT;
payload.color = FloatVec4(value[0], value[1], value[2], value[3]);
break;
case GL_DEPTH:
payload.mask = GL_DEPTH_BUFFER_BIT;
payload.depth = value[0];
break;
default:
break;
}
QueueClearBufferPayload(buffer, drawbuffer, payload);
}
void VulkanRenderer::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
if (value == nullptr) {
return;
}
ClearAttachmentPayload payload{};
switch (buffer) {
case GL_COLOR:
payload.mask = GL_COLOR_BUFFER_BIT;
payload.color = FloatVec4(static_cast<Float>(value[0]), static_cast<Float>(value[1]),
static_cast<Float>(value[2]), static_cast<Float>(value[3]));
break;
case GL_STENCIL:
payload.mask = GL_STENCIL_BUFFER_BIT;
payload.stencil = value[0];
break;
default:
break;
}
QueueClearBufferPayload(buffer, drawbuffer, payload);
}
void VulkanRenderer::ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
if (value == nullptr) {
return;
}
ClearAttachmentPayload payload{};
switch (buffer) {
case GL_COLOR:
payload.mask = GL_COLOR_BUFFER_BIT;
payload.color = FloatVec4(static_cast<Float>(value[0]), static_cast<Float>(value[1]),
static_cast<Float>(value[2]), static_cast<Float>(value[3]));
break;
case GL_STENCIL:
payload.mask = GL_STENCIL_BUFFER_BIT;
payload.stencil = static_cast<Uint32>(std::max(value[0], 0));
break;
default:
break;
}
QueueClearBufferPayload(buffer, drawbuffer, payload);
}
Bool VulkanRenderer::MaterializePendingClearForTexture(VkCommandBuffer commandBuffer, Bool VulkanRenderer::MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
MG_State::GLState::ITextureObject& texture) { MG_State::GLState::ITextureObject& texture) {
ClearAttachmentPayload clearPayload{}; ClearAttachmentPayload clearPayload{};
@@ -952,7 +1164,6 @@ void main() {
texture.GetExternalIndex()); texture.GetExternalIndex());
VkImageSubresourceRange subresourceRange{}; VkImageSubresourceRange subresourceRange{};
subresourceRange.aspectMask = resource->aspect;
subresourceRange.baseMipLevel = 0; subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1; subresourceRange.levelCount = 1;
subresourceRange.baseArrayLayer = 0; subresourceRange.baseArrayLayer = 0;
@@ -960,6 +1171,7 @@ void main() {
VkImageLayout sampledLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkImageLayout sampledLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) { if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
VkClearColorValue clearValue{}; VkClearColorValue clearValue{};
clearValue.float32[0] = clearPayload.color.x(); clearValue.float32[0] = clearPayload.color.x();
clearValue.float32[1] = clearPayload.color.y(); clearValue.float32[1] = clearPayload.color.y();
@@ -968,6 +1180,19 @@ void main() {
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
&clearValue, 1, &subresourceRange); &clearValue, 1, &subresourceRange);
} else { } else {
VkImageAspectFlags clearAspectMask = 0;
if ((resource->aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0 &&
(clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
clearAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if ((resource->aspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0 &&
(clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) {
clearAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
MOBILEGL_ASSERT(clearAspectMask != 0,
"MaterializePendingClearForTexture: textureId=%d has no matching depth/stencil clear mask",
texture.GetExternalIndex());
subresourceRange.aspectMask = clearAspectMask;
VkClearDepthStencilValue clearValue{}; VkClearDepthStencilValue clearValue{};
clearValue.depth = clearPayload.depth; clearValue.depth = clearPayload.depth;
clearValue.stencil = clearPayload.stencil; clearValue.stencil = clearPayload.stencil;
@@ -1212,7 +1437,7 @@ void main() {
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask); srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask);
MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain source image", __func__); MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain source image", __func__);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, srcLayout); m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
} else { } else {
Bool ok = VkTextureManager::TransitionImageLayout( Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
@@ -1230,7 +1455,7 @@ void main() {
dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, dstBinding.aspectMask); dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, dstBinding.aspectMask);
MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain destination image", __func__); MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain destination image", __func__);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, dstLayout); m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
} else { } else {
Bool ok = VkTextureManager::TransitionImageLayout( Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
@@ -1259,6 +1484,172 @@ void main() {
1, &blitRegion, filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST); 1, &blitRegion, filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
} }
void VulkanRenderer::CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLint x, GLint y, GLsizei width, GLsizei height) {
if (width <= 0 || height <= 0) {
return;
}
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
if (textureTarget != TextureTarget::Texture2D) {
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
"CopyTexSubImage2D currently only supports GL_TEXTURE_2D destinations.");
return;
}
if (level < 0) {
RecordTextureCopyError(__func__, ErrorCode::InvalidValue,
"CopyTexSubImage2D level must be non-negative.");
return;
}
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
auto destinationTexture = textureUnit.GetBindingSlot(textureTarget).GetBoundObject();
if (destinationTexture == nullptr) {
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
"CopyTexSubImage2D requires a bound destination texture.");
return;
}
auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
if (readFbo == nullptr) {
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
"CopyTexSubImage2D requires a framebuffer bound to GL_READ_FRAMEBUFFER.");
return;
}
auto& frame = m_frameContext.GetCurrent();
if (!frame.isCommandRecording) {
m_frameContext.BeginCommandRecording();
m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
}
const Bool readIsDefaultFbo =
(readFbo == MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo->defaultFBO);
BlitImageBinding srcBinding{};
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
srcBinding)) {
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
"CopyTexSubImage2D requires a complete color read buffer.");
return;
}
BlitImageBinding dstBinding{};
if (!ResolveTextureCopyDestinationBinding(*destinationTexture, static_cast<Uint32>(level), *m_textureManager,
dstBinding)) {
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
"CopyTexSubImage2D failed to resolve the destination texture.");
return;
}
if (!readIsDefaultFbo) {
const auto& sourceAttachment = readFbo->GetAttachment(readFbo->GetReadBuffer());
auto sourceTexture = sourceAttachment.GetTexture();
MOBILEGL_ASSERT(sourceTexture != nullptr, "CopyTexSubImage2D: source texture attachment is null");
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *sourceTexture);
MOBILEGL_ASSERT(clearReady,
"CopyTexSubImage2D: failed to materialize pending clear for source textureId=%d",
sourceTexture->GetExternalIndex());
}
const VkImageLayout srcOriginalLayout = readIsDefaultFbo
? m_swapchainObject.GetImageLayout(m_imageIndexAcquired)
: *srcBinding.trackedLayout;
if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
"CopyTexSubImage2D source image has undefined layout.");
return;
}
const VkImageLayout dstOriginalLayout = *dstBinding.trackedLayout;
const VkImageLayout dstRestoreLayout = dstOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED
? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
: dstOriginalLayout;
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccessMask = 0;
GetImageTransitionSourceState(srcOriginalLayout, srcStageMask, srcAccessMask);
if (readIsDefaultFbo) {
VkImageLayout srcTrackedLayout = srcOriginalLayout;
Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcBinding.image, srcTrackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask,
srcBinding.mipLevel, srcBinding.mipLevelCount);
MOBILEGL_ASSERT(ok, "%s: failed to transition swapchain source image", __func__);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, srcTrackedLayout);
} else {
Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, srcBinding.aspectMask,
srcBinding.mipLevel, srcBinding.mipLevelCount);
MOBILEGL_ASSERT(ok, "%s: failed to transition source image", __func__);
}
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags dstAccessMask = 0;
GetImageTransitionSourceState(dstOriginalLayout, dstStageMask, dstAccessMask);
Bool dstReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, dstBinding.aspectMask,
dstBinding.mipLevel, dstBinding.mipLevelCount);
MOBILEGL_ASSERT(dstReady, "%s: failed to transition destination image", __func__);
VkImageCopy copyRegion{};
copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
copyRegion.srcSubresource.baseArrayLayer = 0;
copyRegion.srcSubresource.layerCount = 1;
copyRegion.srcOffset = {x, y, 0};
copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
copyRegion.dstSubresource.baseArrayLayer = 0;
copyRegion.dstSubresource.layerCount = 1;
copyRegion.dstOffset = {xoffset, yoffset, 0};
copyRegion.extent = {static_cast<Uint32>(width), static_cast<Uint32>(height), 1};
vkCmdCopyImage(frame.commandBuffer,
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &copyRegion);
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcRestoreAccessMask = 0;
GetImageTransitionDestinationState(srcOriginalLayout, srcRestoreStageMask, srcRestoreAccessMask);
if (readIsDefaultFbo) {
VkImageLayout srcTrackedLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcBinding.image, srcTrackedLayout, srcOriginalLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, srcRestoreStageMask,
VK_ACCESS_TRANSFER_READ_BIT, srcRestoreAccessMask, srcBinding.aspectMask,
srcBinding.mipLevel, srcBinding.mipLevelCount);
MOBILEGL_ASSERT(ok, "%s: failed to restore swapchain source image layout", __func__);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, srcTrackedLayout);
} else {
Bool ok = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcBinding.image, *srcBinding.trackedLayout, srcOriginalLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, srcRestoreStageMask,
VK_ACCESS_TRANSFER_READ_BIT, srcRestoreAccessMask, srcBinding.aspectMask,
srcBinding.mipLevel, srcBinding.mipLevelCount);
MOBILEGL_ASSERT(ok, "%s: failed to restore source image layout", __func__);
}
VkPipelineStageFlags dstRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags dstRestoreAccessMask = 0;
GetImageTransitionDestinationState(dstRestoreLayout, dstRestoreStageMask, dstRestoreAccessMask);
Bool dstRestored = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstBinding.image, *dstBinding.trackedLayout, dstRestoreLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask,
VK_ACCESS_TRANSFER_WRITE_BIT, dstRestoreAccessMask, dstBinding.aspectMask,
dstBinding.mipLevel, dstBinding.mipLevelCount);
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__);
}
void VulkanRenderer::DrawArrays(const DrawCmd& payload) { void VulkanRenderer::DrawArrays(const DrawCmd& payload) {
auto& frame = m_frameContext.GetCurrent(); auto& frame = m_frameContext.GetCurrent();
@@ -1976,8 +2367,7 @@ void main() {
VkClearAttachment clearAttachment{}; VkClearAttachment clearAttachment{};
clearAttachment.clearValue.depthStencil = {1.0f, 0}; clearAttachment.clearValue.depthStencil = {1.0f, 0};
if (clearPayload.attachmentType >= FramebufferAttachmentType::Color0 && if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
clearPayload.attachmentType <= FramebufferAttachmentType::Color31) {
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clearAttachment.colorAttachment = pending.attachmentIndex; clearAttachment.colorAttachment = pending.attachmentIndex;
clearAttachment.clearValue.color = { clearAttachment.clearValue.color = {
@@ -1986,15 +2376,19 @@ void main() {
clearPayload.color.z(), clearPayload.color.z(),
clearPayload.color.w() clearPayload.color.w()
}; };
} else if (clearPayload.attachmentType == FramebufferAttachmentType::Depth) {
clearAttachment.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
clearAttachment.clearValue.depthStencil.depth = clearPayload.depth;
} else if (clearPayload.attachmentType == FramebufferAttachmentType::Stencil) {
clearAttachment.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
clearAttachment.clearValue.depthStencil.stencil = clearPayload.stencil;
} else { } else {
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
clearAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
clearAttachment.clearValue.depthStencil.depth = clearPayload.depth;
}
if ((clearPayload.mask & GL_STENCIL_BUFFER_BIT) != 0) {
clearAttachment.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
clearAttachment.clearValue.depthStencil.stencil = clearPayload.stencil;
}
if (clearAttachment.aspectMask == 0) {
continue; continue;
} }
}
vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect); vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect);
m_clearManager->PopPendingClear(pending.texture); m_clearManager->PopPendingClear(pending.texture);
@@ -115,9 +115,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const RenderPassEntry& compatibleRenderPassEntry); const RenderPassEntry& compatibleRenderPassEntry);
void Clear(GLbitfield mask); void Clear(GLbitfield mask);
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value);
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value);
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
GLbitfield mask, GLenum filter); GLbitfield mask, GLenum filter);
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
GLint x, GLint y, GLsizei width, GLsizei height);
void DrawArrays(const DrawCmd& payload); void DrawArrays(const DrawCmd& payload);
void DrawElements(const DrawIndexedCmd& payload); void DrawElements(const DrawIndexedCmd& payload);
void MultiDrawElements(const MultiDrawIndexedCmd& payloads); void MultiDrawElements(const MultiDrawIndexedCmd& payloads);
@@ -146,6 +152,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 samplerBinding = 0; Uint32 samplerBinding = 0;
}; };
void QueueClearBufferPayload(GLenum buffer, GLint drawbuffer, const ClearAttachmentPayload& clearPayload);
NativeWindowType m_window = 0; NativeWindowType m_window = 0;
VulkanRendererConfig m_config; VulkanRendererConfig m_config;