mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (DirectVulkan): implement color renderbuffer attachments - render pass/pipeline/blit/copy/readback/clear paths treated color renderbuffers as absent (writes masked to VK_ATTACHMENT_UNUSED, glClear dropped, readback zeros)
This commit is contained in:
@@ -251,11 +251,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
const auto internalFormat = renderbuffer->GetInternalFormat();
|
const auto internalFormat = renderbuffer->GetInternalFormat();
|
||||||
const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
|
const VkFormat format = MG_Util::ConvertTextureInternalFormatToVkEnum(internalFormat);
|
||||||
const VkImageAspectFlags aspect = ResolveImageAspectMaskForFormat(format);
|
const VkImageAspectFlags aspect = ResolveImageAspectMaskForFormat(format);
|
||||||
if ((aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
// Renderbuffers are never sampled (GL has no way to bind one to a sampler), so the
|
||||||
MGLOG_E("GetOrCreateRenderbufferResource: color renderbuffer %u is not supported by DirectVulkan render passes yet",
|
// usage set is attachment + transfer: transfer covers readback (vkCmdCopyImageToBuffer),
|
||||||
renderbuffer->GetExternalIndex());
|
// BlitFramebuffer, CopyTexImage sources, and out-of-render-pass clear materialization.
|
||||||
return nullptr;
|
const VkImageUsageFlags imageUsage =
|
||||||
}
|
((aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0 ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
|
||||||
|
: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) |
|
||||||
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
|
||||||
auto& resource = m_renderbufferResources[renderbuffer.get()];
|
auto& resource = m_renderbufferResources[renderbuffer.get()];
|
||||||
const Bool needsCreate =
|
const Bool needsCreate =
|
||||||
@@ -285,7 +287,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
imageInfo.format = format;
|
imageInfo.format = format;
|
||||||
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||||
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
imageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
imageInfo.usage = imageUsage;
|
||||||
imageInfo.samples = sampleCount;
|
imageInfo.samples = sampleCount;
|
||||||
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
|
||||||
@@ -386,6 +388,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void VkRenderPassManager::QueueRenderbufferClear(
|
void VkRenderPassManager::QueueRenderbufferClear(
|
||||||
GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
GLbitfield mask, const ClearFramebufferPayload& clearPayload,
|
||||||
const MG_State::GLState::FramebufferObject& drawFbo) {
|
const MG_State::GLState::FramebufferObject& drawFbo) {
|
||||||
|
if ((mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||||
|
// Color renderbuffer draw buffers take the framebuffer-level clear too; texture
|
||||||
|
// attachments are skipped by the per-attachment overload's IsRenderbuffer guard.
|
||||||
|
for (const auto attachmentType : drawFbo.GetDrawBuffers()) {
|
||||||
|
if (attachmentType == FramebufferAttachmentType::None) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QueueRenderbufferClear(
|
||||||
|
ClearAttachmentPayload{.mask = GL_COLOR_BUFFER_BIT, .color = clearPayload.color},
|
||||||
|
drawFbo.GetAttachment(attachmentType));
|
||||||
|
}
|
||||||
|
}
|
||||||
if ((mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
if ((mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||||
QueueRenderbufferClear(
|
QueueRenderbufferClear(
|
||||||
ClearAttachmentPayload{.mask = GL_DEPTH_BUFFER_BIT, .depth = clearPayload.depth},
|
ClearAttachmentPayload{.mask = GL_DEPTH_BUFFER_BIT, .depth = clearPayload.depth},
|
||||||
@@ -682,6 +696,83 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// assuming default FBO has the right param
|
// assuming default FBO has the right param
|
||||||
for (Uint32 i = 0; i < colorAttachmentSlotCount; ++i) {
|
for (Uint32 i = 0; i < colorAttachmentSlotCount; ++i) {
|
||||||
auto drawbuf = drawbufs[i];
|
auto drawbuf = drawbufs[i];
|
||||||
|
|
||||||
|
// Renderbuffer color attachments mirror the texture path below, with the
|
||||||
|
// resource (image/view/format/layout) coming from the render-pass manager's
|
||||||
|
// renderbuffer store instead of the texture manager.
|
||||||
|
if (drawbuf != FramebufferAttachmentType::None && !isDefaultFbo) {
|
||||||
|
const auto& rbAtt = fbo.GetAttachment(drawbuf);
|
||||||
|
if (rbAtt.IsRenderbuffer() && rbAtt.IsComplete()) {
|
||||||
|
const auto& renderbuffer = rbAtt.GetRenderbuffer();
|
||||||
|
auto* rbResource = GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
if (rbResource == nullptr || (rbResource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) {
|
||||||
|
MGLOG_E("GetOrCreateRenderPass: draw buffer slot %u on FBO %u has an unsupported color "
|
||||||
|
"renderbuffer %u; using VK_ATTACHMENT_UNUSED",
|
||||||
|
i, fbo.GetExternalIndex(), renderbuffer->GetExternalIndex());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uint32 rbAttachmentIndex = static_cast<Uint32>(attachmentDescriptions.size());
|
||||||
|
attachmentDescriptions.emplace_back();
|
||||||
|
VkAttachmentDescription& rbDesc = attachmentDescriptions.back();
|
||||||
|
|
||||||
|
ClearAttachmentPayload rbClearPayload{};
|
||||||
|
Bool rbHasClear = GetPendingRenderbufferClear(renderbuffer.get(), rbClearPayload) &&
|
||||||
|
(rbClearPayload.mask & GL_COLOR_BUFFER_BIT) != 0;
|
||||||
|
if (rbHasClear &&
|
||||||
|
MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3) {
|
||||||
|
// RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1.
|
||||||
|
rbClearPayload.color =
|
||||||
|
FloatVec4(rbClearPayload.color.x(), rbClearPayload.color.y(),
|
||||||
|
rbClearPayload.color.z(), 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkImageLayout trackedRbLayout = rbResource->layout;
|
||||||
|
rbDesc.flags = 0;
|
||||||
|
rbDesc.format = rbResource->format;
|
||||||
|
rbDesc.samples = rbResource->sampleCount;
|
||||||
|
rbDesc.loadOp = rbHasClear ? VK_ATTACHMENT_LOAD_OP_CLEAR :
|
||||||
|
(trackedRbLayout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE
|
||||||
|
: VK_ATTACHMENT_LOAD_OP_LOAD);
|
||||||
|
rbDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||||
|
rbDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||||
|
rbDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||||
|
rbDesc.initialLayout = (rbHasClear || trackedRbLayout == VK_IMAGE_LAYOUT_UNDEFINED) ?
|
||||||
|
VK_IMAGE_LAYOUT_UNDEFINED : trackedRbLayout;
|
||||||
|
rbDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
adoptRenderPassSampleCount(rbResource->sampleCount, "color",
|
||||||
|
static_cast<Int>(renderbuffer->GetExternalIndex()));
|
||||||
|
|
||||||
|
if (rbHasClear) {
|
||||||
|
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
|
||||||
|
.attachmentIndex = rbAttachmentIndex,
|
||||||
|
.colorAttachmentSlot = i,
|
||||||
|
.renderbuffer = renderbuffer.get(),
|
||||||
|
.hasInlinePayload = true,
|
||||||
|
.inlinePayload = rbClearPayload,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == 0)
|
||||||
|
width = static_cast<Int>(rbResource->extent.width);
|
||||||
|
if (height == 0)
|
||||||
|
height = static_cast<Int>(rbResource->extent.height);
|
||||||
|
|
||||||
|
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
|
||||||
|
.target = TrackedAttachmentTarget::Renderbuffer,
|
||||||
|
.renderbuffer = renderbuffer,
|
||||||
|
.finalLayout = rbDesc.finalLayout,
|
||||||
|
});
|
||||||
|
textureResources.emplace_back(nullptr);
|
||||||
|
attachmentViews.emplace_back(rbResource->view);
|
||||||
|
MOBILEGL_ASSERT(attachmentViews.back() != VK_NULL_HANDLE,
|
||||||
|
"GetOrCreateRenderPass: renderbuffer view missing at color attachment %d", i);
|
||||||
|
|
||||||
|
colorAttachmentRefs[i].attachment = rbAttachmentIndex;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto* texture = ResolveCompleteColorAttachmentTexture(fbo, drawbuf, i);
|
auto* texture = ResolveCompleteColorAttachmentTexture(fbo, drawbuf, i);
|
||||||
if (texture == nullptr)
|
if (texture == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Uint64 m_rpFastRbEpoch = 0;
|
Uint64 m_rpFastRbEpoch = 0;
|
||||||
Uint64 m_rpFastRenderPassHash = 0;
|
Uint64 m_rpFastRenderPassHash = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
struct RenderbufferResource {
|
struct RenderbufferResource {
|
||||||
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
@@ -227,6 +228,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void Destroy(VkDevice device, VmaAllocator allocator);
|
void Destroy(VkDevice device, VmaAllocator allocator);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Public so the renderer's blit/copy/readback bindings can source renderbuffer
|
||||||
|
// attachments the same way texture attachments go through the texture manager.
|
||||||
|
RenderbufferResource* GetOrCreateRenderbufferResource(
|
||||||
|
const SharedPtr<MG_State::GLState::RenderbufferObject>& renderbuffer);
|
||||||
|
Bool GetPendingRenderbufferClear(MG_State::GLState::RenderbufferObject* renderbuffer,
|
||||||
|
ClearAttachmentPayload& outPayload) const;
|
||||||
|
|
||||||
|
private:
|
||||||
struct PendingRenderbufferClear {
|
struct PendingRenderbufferClear {
|
||||||
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
||||||
ClearAttachmentPayload payload{};
|
ClearAttachmentPayload payload{};
|
||||||
@@ -235,10 +244,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
UnorderedMap<MG_State::GLState::RenderbufferObject*, RenderbufferResource> m_renderbufferResources;
|
UnorderedMap<MG_State::GLState::RenderbufferObject*, RenderbufferResource> m_renderbufferResources;
|
||||||
UnorderedMap<MG_State::GLState::RenderbufferObject*, PendingRenderbufferClear> m_pendingRenderbufferClears;
|
UnorderedMap<MG_State::GLState::RenderbufferObject*, PendingRenderbufferClear> m_pendingRenderbufferClears;
|
||||||
|
|
||||||
RenderbufferResource* GetOrCreateRenderbufferResource(
|
|
||||||
const SharedPtr<MG_State::GLState::RenderbufferObject>& renderbuffer);
|
|
||||||
Bool GetPendingRenderbufferClear(MG_State::GLState::RenderbufferObject* renderbuffer,
|
|
||||||
ClearAttachmentPayload& outPayload) const;
|
|
||||||
Bool HasPendingRenderbufferClear(
|
Bool HasPendingRenderbufferClear(
|
||||||
const MG_State::GLState::FramebufferAttachmentObject& attachment) const;
|
const MG_State::GLState::FramebufferAttachmentObject& attachment) const;
|
||||||
void CollectRenderbufferGarbage();
|
void CollectRenderbufferGarbage();
|
||||||
|
|||||||
@@ -1274,7 +1274,8 @@ void main() {
|
|||||||
|
|
||||||
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,
|
||||||
|
VkRenderPassManager& renderPassManager, BlitImageBinding& outBinding) {
|
||||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||||
const FramebufferAttachmentType attachmentType =
|
const FramebufferAttachmentType attachmentType =
|
||||||
isReadFramebuffer ? fbo.GetReadBuffer() : fbo.GetDrawBuffers()[0];
|
isReadFramebuffer ? fbo.GetReadBuffer() : fbo.GetDrawBuffers()[0];
|
||||||
@@ -1316,8 +1317,24 @@ void main() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (attachment.IsRenderbuffer()) {
|
if (attachment.IsRenderbuffer()) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: renderbuffer attachments are not supported yet");
|
const auto& renderbuffer = attachment.GetRenderbuffer();
|
||||||
return false;
|
auto* rbResource = renderPassManager.GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
if (rbResource == nullptr || (rbResource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) == 0) {
|
||||||
|
MGLOG_E("BlitFramebuffer skipped: %s framebuffer color renderbuffer %u is unsupported",
|
||||||
|
outBinding.label, renderbuffer->GetExternalIndex());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outBinding.image = rbResource->image;
|
||||||
|
outBinding.trackedLayout = &rbResource->layout;
|
||||||
|
outBinding.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
outBinding.format = rbResource->format;
|
||||||
|
outBinding.extent = {static_cast<Int>(rbResource->extent.width),
|
||||||
|
static_cast<Int>(rbResource->extent.height)};
|
||||||
|
outBinding.mipLevel = 0;
|
||||||
|
outBinding.mipLevelCount = 1;
|
||||||
|
outBinding.baseArrayLayer = 0;
|
||||||
|
outBinding.layerCount = 1;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (!attachment.IsTexture()) {
|
if (!attachment.IsTexture()) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: unsupported framebuffer attachment type");
|
MGLOG_E("BlitFramebuffer skipped: unsupported framebuffer attachment type");
|
||||||
@@ -1355,6 +1372,7 @@ void main() {
|
|||||||
static Bool ResolveFramebufferBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer,
|
static Bool ResolveFramebufferBlitBinding(MG_State::GLState::FramebufferObject& fbo, Bool isReadFramebuffer,
|
||||||
Uint32 swapchainImageIndex, SwapchainObject& swapchainObject,
|
Uint32 swapchainImageIndex, SwapchainObject& swapchainObject,
|
||||||
VkTextureManager& textureManager,
|
VkTextureManager& textureManager,
|
||||||
|
VkRenderPassManager& renderPassManager,
|
||||||
VkImageAspectFlags requiredAspectMask,
|
VkImageAspectFlags requiredAspectMask,
|
||||||
BlitImageBinding& outBinding) {
|
BlitImageBinding& outBinding) {
|
||||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||||
@@ -1398,8 +1416,29 @@ void main() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (attachment.IsRenderbuffer()) {
|
if (attachment.IsRenderbuffer()) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: renderbuffer attachments are not supported yet");
|
const auto& renderbuffer = attachment.GetRenderbuffer();
|
||||||
return false;
|
auto* rbResource = renderPassManager.GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
if (rbResource == nullptr) {
|
||||||
|
MGLOG_E("BlitFramebuffer skipped: %s framebuffer renderbuffer %u is unsupported",
|
||||||
|
outBinding.label, renderbuffer->GetExternalIndex());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((rbResource->aspect & requiredAspectMask) != requiredAspectMask) {
|
||||||
|
MGLOG_E("BlitFramebuffer skipped: %s framebuffer renderbuffer %u is missing aspect mask=0x%x",
|
||||||
|
outBinding.label, renderbuffer->GetExternalIndex(),
|
||||||
|
static_cast<Uint32>(requiredAspectMask));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outBinding.image = rbResource->image;
|
||||||
|
outBinding.trackedLayout = &rbResource->layout;
|
||||||
|
outBinding.aspectMask = requiredAspectMask;
|
||||||
|
outBinding.extent = {static_cast<Int>(rbResource->extent.width),
|
||||||
|
static_cast<Int>(rbResource->extent.height)};
|
||||||
|
outBinding.mipLevel = 0;
|
||||||
|
outBinding.mipLevelCount = 1;
|
||||||
|
outBinding.baseArrayLayer = 0;
|
||||||
|
outBinding.layerCount = 1;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (!attachment.IsTexture()) {
|
if (!attachment.IsTexture()) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: unsupported framebuffer attachment type");
|
MGLOG_E("BlitFramebuffer skipped: unsupported framebuffer attachment type");
|
||||||
@@ -1470,6 +1509,7 @@ void main() {
|
|||||||
static Bool ResolveTextureCopySourceBinding(MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex,
|
static Bool ResolveTextureCopySourceBinding(MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex,
|
||||||
SwapchainObject& swapchainObject,
|
SwapchainObject& swapchainObject,
|
||||||
VkTextureManager& textureManager,
|
VkTextureManager& textureManager,
|
||||||
|
VkRenderPassManager& renderPassManager,
|
||||||
VkImageAspectFlags requiredAspectMask,
|
VkImageAspectFlags requiredAspectMask,
|
||||||
BlitImageBinding& outBinding) {
|
BlitImageBinding& outBinding) {
|
||||||
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
const Bool isDefaultFbo = fbo.IsDefaultFramebuffer();
|
||||||
@@ -1514,8 +1554,30 @@ void main() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (attachment.IsRenderbuffer()) {
|
if (attachment.IsRenderbuffer()) {
|
||||||
MGLOG_E("CopyTexSubImage2D skipped: renderbuffer read attachments are not supported yet");
|
const auto& renderbuffer = attachment.GetRenderbuffer();
|
||||||
return false;
|
auto* rbResource = renderPassManager.GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
if (rbResource == nullptr) {
|
||||||
|
MGLOG_E("CopyTexSubImage2D skipped: read framebuffer renderbuffer %u is unsupported",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((rbResource->aspect & requiredAspectMask) != requiredAspectMask) {
|
||||||
|
MGLOG_E("CopyTexSubImage2D skipped: read framebuffer renderbuffer %u aspect mask=0x%x "
|
||||||
|
"does not satisfy requested mask=0x%x",
|
||||||
|
renderbuffer->GetExternalIndex(), static_cast<Uint32>(rbResource->aspect),
|
||||||
|
static_cast<Uint32>(requiredAspectMask));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outBinding.image = rbResource->image;
|
||||||
|
outBinding.trackedLayout = &rbResource->layout;
|
||||||
|
outBinding.aspectMask = requiredAspectMask;
|
||||||
|
outBinding.extent = {static_cast<Int>(rbResource->extent.width),
|
||||||
|
static_cast<Int>(rbResource->extent.height)};
|
||||||
|
outBinding.mipLevel = 0;
|
||||||
|
outBinding.mipLevelCount = 1;
|
||||||
|
outBinding.baseArrayLayer = 0;
|
||||||
|
outBinding.layerCount = 1;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if (!attachment.IsTexture()) {
|
if (!attachment.IsTexture()) {
|
||||||
MGLOG_E("CopyTexSubImage2D skipped: unsupported read framebuffer attachment type");
|
MGLOG_E("CopyTexSubImage2D skipped: unsupported read framebuffer attachment type");
|
||||||
@@ -3560,6 +3622,7 @@ void main() {
|
|||||||
(bufferMask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u));
|
(bufferMask.a() ? VK_COLOR_COMPONENT_A_BIT : 0u));
|
||||||
Bool effectiveBlendEnabled = blendEnabled;
|
Bool effectiveBlendEnabled = blendEnabled;
|
||||||
MG_State::GLState::ITextureObject* colorAttachmentTexture = nullptr;
|
MG_State::GLState::ITextureObject* colorAttachmentTexture = nullptr;
|
||||||
|
MG_State::GLState::RenderbufferObject* colorAttachmentRenderbuffer = nullptr;
|
||||||
if (isDefaultDrawFbo && i < drawBuffers.size() &&
|
if (isDefaultDrawFbo && i < drawBuffers.size() &&
|
||||||
drawBuffers[i] == FramebufferAttachmentType::None) {
|
drawBuffers[i] == FramebufferAttachmentType::None) {
|
||||||
// The default framebuffer spans the same MAX_DRAW_BUFFERS slots as an FBO
|
// The default framebuffer spans the same MAX_DRAW_BUFFERS slots as an FBO
|
||||||
@@ -3574,12 +3637,24 @@ void main() {
|
|||||||
if (!isDefaultDrawFbo && i < drawBuffers.size()) {
|
if (!isDefaultDrawFbo && i < drawBuffers.size()) {
|
||||||
const auto drawBuffer = drawBuffers[i];
|
const auto drawBuffer = drawBuffers[i];
|
||||||
colorAttachmentTexture = resolveCompleteColorAttachmentTexture(i);
|
colorAttachmentTexture = resolveCompleteColorAttachmentTexture(i);
|
||||||
if (drawBuffer == FramebufferAttachmentType::None || colorAttachmentTexture == nullptr) {
|
if (colorAttachmentTexture == nullptr && drawBuffer != FramebufferAttachmentType::None) {
|
||||||
|
const auto& attachment = drawFboBinding->GetAttachment(drawBuffer);
|
||||||
|
if (attachment.IsRenderbuffer() && attachment.IsComplete()) {
|
||||||
|
colorAttachmentRenderbuffer = attachment.GetRenderbuffer().get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (drawBuffer == FramebufferAttachmentType::None ||
|
||||||
|
(colorAttachmentTexture == nullptr && colorAttachmentRenderbuffer == nullptr)) {
|
||||||
// GL ignores writes and per-target blend state for GL_NONE draw buffer slots.
|
// GL ignores writes and per-target blend state for GL_NONE draw buffer slots.
|
||||||
// Depth-only or otherwise unattached draw buffers should also discard color writes.
|
// Depth-only or otherwise unattached draw buffers should also discard color writes.
|
||||||
attachmentColorWriteMask = 0;
|
attachmentColorWriteMask = 0;
|
||||||
effectiveBlendEnabled = false;
|
effectiveBlendEnabled = false;
|
||||||
}
|
}
|
||||||
|
if (colorAttachmentRenderbuffer != nullptr) {
|
||||||
|
const SizeT componentCount = MG_Util::GetBaseInternalFormatComponentCount(
|
||||||
|
colorAttachmentRenderbuffer->GetInternalFormat());
|
||||||
|
attachmentColorWriteMask &= GetSupportedColorWriteMaskForComponentCount(componentCount);
|
||||||
|
}
|
||||||
if (colorAttachmentTexture != nullptr) {
|
if (colorAttachmentTexture != nullptr) {
|
||||||
auto* texture = colorAttachmentTexture;
|
auto* texture = colorAttachmentTexture;
|
||||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
||||||
@@ -3656,10 +3731,14 @@ void main() {
|
|||||||
Int textureExternalIndex = -1;
|
Int textureExternalIndex = -1;
|
||||||
if (isDefaultDrawFbo) {
|
if (isDefaultDrawFbo) {
|
||||||
colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format;
|
colorAttachmentFormat = m_swapchainObject.GetSurfaceFormat().format;
|
||||||
|
} else if (colorAttachmentRenderbuffer != nullptr) {
|
||||||
|
textureExternalIndex = static_cast<Int>(colorAttachmentRenderbuffer->GetExternalIndex());
|
||||||
|
colorAttachmentFormat = MG_Util::ConvertTextureInternalFormatToVkEnum(
|
||||||
|
colorAttachmentRenderbuffer->GetInternalFormat());
|
||||||
} else {
|
} else {
|
||||||
auto* texture = colorAttachmentTexture;
|
auto* texture = colorAttachmentTexture;
|
||||||
MOBILEGL_ASSERT(texture != nullptr,
|
MOBILEGL_ASSERT(texture != nullptr,
|
||||||
"GetOrCreatePipeline: blend is enabled on draw buffer %u but no complete texture attachment is bound",
|
"GetOrCreatePipeline: blend is enabled on draw buffer %u but no complete color attachment is bound",
|
||||||
i);
|
i);
|
||||||
textureExternalIndex = texture->GetExternalIndex();
|
textureExternalIndex = texture->GetExternalIndex();
|
||||||
auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
|
auto* textureResource = m_textureManager->SyncTextureAndGetDescriptor(*texture);
|
||||||
@@ -4810,6 +4889,98 @@ void main() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool VulkanRenderer::MaterializePendingClearForRenderbuffer(
|
||||||
|
VkCommandBuffer commandBuffer, const SharedPtr<MG_State::GLState::RenderbufferObject>& renderbuffer) {
|
||||||
|
if (renderbuffer == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
ClearAttachmentPayload clearPayload{};
|
||||||
|
if (!m_renderPassManager->GetPendingRenderbufferClear(renderbuffer.get(), clearPayload)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
MOBILEGL_ASSERT(VkRenderPassManager::GetActiveRenderPass() == nullptr,
|
||||||
|
"MaterializePendingClearForRenderbuffer requires no active render pass");
|
||||||
|
|
||||||
|
auto* resource = m_renderPassManager->GetOrCreateRenderbufferResource(renderbuffer);
|
||||||
|
if (resource == nullptr) {
|
||||||
|
MGLOG_E("MaterializePendingClearForRenderbuffer: no resource for renderbuffer %u",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
|
VkAccessFlags srcAccessMask = 0;
|
||||||
|
GetImageTransitionSourceState(resource->layout, srcStageMask, srcAccessMask);
|
||||||
|
|
||||||
|
Bool ok = VkTextureManager::TransitionImageLayout(
|
||||||
|
commandBuffer, resource->image, resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT, srcAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
resource->aspect, 0, 1, 1);
|
||||||
|
MOBILEGL_ASSERT(ok,
|
||||||
|
"MaterializePendingClearForRenderbuffer: failed to transition renderbuffer %u to TRANSFER_DST",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
|
||||||
|
VkImageSubresourceRange subresourceRange{};
|
||||||
|
subresourceRange.baseMipLevel = 0;
|
||||||
|
subresourceRange.levelCount = 1;
|
||||||
|
subresourceRange.baseArrayLayer = 0;
|
||||||
|
subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
VkImageLayout steadyLayout;
|
||||||
|
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
||||||
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
VkClearColorValue clearValue{};
|
||||||
|
clearValue.float32[0] = clearPayload.color.x();
|
||||||
|
clearValue.float32[1] = clearPayload.color.y();
|
||||||
|
clearValue.float32[2] = clearPayload.color.z();
|
||||||
|
// RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1.
|
||||||
|
clearValue.float32[3] =
|
||||||
|
MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3 ?
|
||||||
|
1.0f : clearPayload.color.w();
|
||||||
|
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
&clearValue, 1, &subresourceRange);
|
||||||
|
steadyLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
if (clearAspectMask != 0) {
|
||||||
|
subresourceRange.aspectMask = clearAspectMask;
|
||||||
|
VkClearDepthStencilValue clearValue{};
|
||||||
|
clearValue.depth = clearPayload.depth;
|
||||||
|
clearValue.stencil = clearPayload.stencil;
|
||||||
|
vkCmdClearDepthStencilImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
&clearValue, 1, &subresourceRange);
|
||||||
|
}
|
||||||
|
steadyLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageLayout clearLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
ok = VkTextureManager::TransitionImageLayout(
|
||||||
|
commandBuffer, resource->image, clearLayout, steadyLayout,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
||||||
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT |
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT,
|
||||||
|
resource->aspect, 0, 1, 1);
|
||||||
|
MOBILEGL_ASSERT(ok,
|
||||||
|
"MaterializePendingClearForRenderbuffer: failed to transition renderbuffer %u to steady layout",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
resource->layout = steadyLayout;
|
||||||
|
|
||||||
|
m_renderPassManager->PopPendingRenderbufferClear(renderbuffer.get());
|
||||||
|
MGLOG_D("MaterializePendingClearForRenderbuffer: renderbuffer %u pending clear materialized",
|
||||||
|
renderbuffer->GetExternalIndex());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame,
|
Bool VulkanRenderer::TryBlitToDefaultFramebufferWithShader(FrameContext::FrameData& frame,
|
||||||
MG_State::GLState::FramebufferObject& readFbo,
|
MG_State::GLState::FramebufferObject& readFbo,
|
||||||
MG_State::GLState::FramebufferObject& drawFbo,
|
MG_State::GLState::FramebufferObject& drawFbo,
|
||||||
@@ -4823,8 +4994,10 @@ void main() {
|
|||||||
|
|
||||||
BlitImageBinding srcBinding{};
|
BlitImageBinding srcBinding{};
|
||||||
BlitImageBinding dstBinding{};
|
BlitImageBinding dstBinding{};
|
||||||
if (!ResolveColorBlitBinding(readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, srcBinding) ||
|
if (!ResolveColorBlitBinding(readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||||
!ResolveColorBlitBinding(drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, dstBinding)) {
|
*m_renderPassManager, srcBinding) ||
|
||||||
|
!ResolveColorBlitBinding(drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||||
|
*m_renderPassManager, dstBinding)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (srcBinding.trackedLayout == nullptr) {
|
if (srcBinding.trackedLayout == nullptr) {
|
||||||
@@ -5010,9 +5183,11 @@ void main() {
|
|||||||
BlitImageBinding srcBinding{};
|
BlitImageBinding srcBinding{};
|
||||||
BlitImageBinding dstBinding{};
|
BlitImageBinding dstBinding{};
|
||||||
if (!ResolveFramebufferBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject,
|
if (!ResolveFramebufferBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject,
|
||||||
*m_textureManager, VK_IMAGE_ASPECT_DEPTH_BIT, srcBinding) ||
|
*m_textureManager, *m_renderPassManager,
|
||||||
|
VK_IMAGE_ASPECT_DEPTH_BIT, srcBinding) ||
|
||||||
!ResolveFramebufferBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject,
|
!ResolveFramebufferBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject,
|
||||||
*m_textureManager, VK_IMAGE_ASPECT_DEPTH_BIT, dstBinding)) {
|
*m_textureManager, *m_renderPassManager,
|
||||||
|
VK_IMAGE_ASPECT_DEPTH_BIT, dstBinding)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5177,8 +5352,10 @@ void main() {
|
|||||||
|
|
||||||
BlitImageBinding srcBinding{};
|
BlitImageBinding srcBinding{};
|
||||||
BlitImageBinding dstBinding{};
|
BlitImageBinding dstBinding{};
|
||||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, srcBinding) ||
|
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||||
!ResolveColorBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager, dstBinding)) {
|
*m_renderPassManager, srcBinding) ||
|
||||||
|
!ResolveColorBlitBinding(*drawFbo, false, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||||
|
*m_renderPassManager, dstBinding)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5382,7 +5559,7 @@ void main() {
|
|||||||
|
|
||||||
BlitImageBinding srcBinding{};
|
BlitImageBinding srcBinding{};
|
||||||
if (!ResolveTextureCopySourceBinding(*readFbo, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
if (!ResolveTextureCopySourceBinding(*readFbo, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||||
dstBinding.aspectMask, srcBinding)) {
|
*m_renderPassManager, dstBinding.aspectMask, srcBinding)) {
|
||||||
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
|
RecordTextureCopyError(__func__, ErrorCode::InvalidOperation,
|
||||||
"CopyTexSubImage2D requires a complete read attachment compatible with the destination texture.");
|
"CopyTexSubImage2D requires a complete read attachment compatible with the destination texture.");
|
||||||
return;
|
return;
|
||||||
@@ -5723,7 +5900,7 @@ void main() {
|
|||||||
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
const Bool readIsDefaultFbo = readFbo->IsDefaultFramebuffer();
|
||||||
BlitImageBinding srcBinding{};
|
BlitImageBinding srcBinding{};
|
||||||
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
if (!ResolveColorBlitBinding(*readFbo, true, m_imageIndexAcquired, m_swapchainObject, *m_textureManager,
|
||||||
srcBinding)) {
|
*m_renderPassManager, srcBinding)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!readIsDefaultFbo) {
|
if (!readIsDefaultFbo) {
|
||||||
@@ -5734,6 +5911,12 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(clearReady,
|
MOBILEGL_ASSERT(clearReady,
|
||||||
"ReadPixels: failed to materialize pending clear for source textureId=%d",
|
"ReadPixels: failed to materialize pending clear for source textureId=%d",
|
||||||
sourceTexture->GetExternalIndex());
|
sourceTexture->GetExternalIndex());
|
||||||
|
} else if (sourceAttachment.IsRenderbuffer()) {
|
||||||
|
const Bool clearReady =
|
||||||
|
MaterializePendingClearForRenderbuffer(frame.commandBuffer, sourceAttachment.GetRenderbuffer());
|
||||||
|
MOBILEGL_ASSERT(clearReady,
|
||||||
|
"ReadPixels: failed to materialize pending clear for source renderbuffer %u",
|
||||||
|
sourceAttachment.GetRenderbuffer()->GetExternalIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -561,6 +561,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
GLenum filter);
|
GLenum filter);
|
||||||
Bool MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
|
Bool MaterializePendingClearForTexture(VkCommandBuffer commandBuffer,
|
||||||
MG_State::GLState::ITextureObject& texture);
|
MG_State::GLState::ITextureObject& texture);
|
||||||
|
Bool MaterializePendingClearForRenderbuffer(
|
||||||
|
VkCommandBuffer commandBuffer,
|
||||||
|
const SharedPtr<MG_State::GLState::RenderbufferObject>& renderbuffer);
|
||||||
VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry);
|
VkPipeline GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry);
|
||||||
Bool GenerateDepthMipmapWithShader(FrameContext::FrameData& frame,
|
Bool GenerateDepthMipmapWithShader(FrameContext::FrameData& frame,
|
||||||
MG_State::GLState::ITextureObject& texture,
|
MG_State::GLState::ITextureObject& texture,
|
||||||
|
|||||||
Reference in New Issue
Block a user