441 lines · 18.6 KB · C++
RawDownload
////  Created by Bradley Austin Davis on 2016/08/07//  Adapted for Vulkan in 2022-2025 by dr Karol Suprynowicz.//  Copyright 2013-2018 High Fidelity, Inc.//  Copyright 2023-2025 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "VKFramebuffer.h"#include "VKBackend.h"#include "VKTexture.h"#include "VKShared.h" // VKTODO: this part needs a lot of work void gpu::vk::VKFramebuffer::update() {    auto backend = _backend.lock();    VkDevice device = backend->getContext().device->logicalDevice;    // VKTODO: this is wrong, most of framebuffer code will need to be rewritten    if (vkFramebuffer != VK_NULL_HANDLE) {        // VKTODO: don't destroy immediately, recycle instead for deletion after current frame completes.        vkDestroyFramebuffer(device, vkFramebuffer, nullptr);    }    if (vkRenderPass) {        backend->_currentFrame->_renderPasses.push_back(vkRenderPass);    }    // VKTODO: free all attachments too    VKTexture* vkTexture = nullptr;    TexturePointer surface;    bool lastTextureWasNull = false;    if (_gpuObject.getColorStamps() != _colorStamps) {        if (_gpuObject.hasColor()) {            // VKTODO: Do these need to be deleted?            attachments.clear();             //int unit = 0;            for (auto& b : _gpuObject.getRenderBuffers()) {                surface = b._texture;                if (surface) {                    Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());                    vkTexture = backend->syncGPUObject(surface);                } else {                    vkTexture = nullptr;                }                 if (vkTexture) {                    if (lastTextureWasNull) {                        Q_ASSERT(false);                    }                    if (vkTexture->_target == VK_IMAGE_VIEW_TYPE_2D) {                        VKAttachmentCreateInfo attachmentCI {};                        attachmentCI.width = vkTexture->_gpuObject.getWidth();                        attachmentCI.height = vkTexture->_gpuObject.getHeight();                        attachmentCI.layerCount = 1;                        attachmentCI.format = gpu::vk::evalTexelFormatInternal(vkTexture->_gpuObject.getTexelFormat(), backend->getContext());                        attachmentCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;                        attachmentCI.imageSampleCount = VK_SAMPLE_COUNT_1_BIT;                        addAttachment(attachmentCI, vkTexture);                        //glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0);                        // VKTODO: how to do this?                        /*} else if (vkTexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {                            glNamedFramebufferTexture(_id, colorAttachments[unit], gltexture->_texture, 0);*/                    } else if (vkTexture->_target == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {                        // VKTODO: what about layers?                        VKAttachmentCreateInfo attachmentCI {};                        attachmentCI.width = vkTexture->_gpuObject.getWidth();                        attachmentCI.height = vkTexture->_gpuObject.getHeight();                        attachmentCI.layerCount = vkTexture->_gpuObject.getNumSlices() - b._subresource;                        attachmentCI.format = gpu::vk::evalTexelFormatInternal(vkTexture->_gpuObject.getTexelFormat(), backend->getContext());                        attachmentCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;                        attachmentCI.imageSampleCount = VK_SAMPLE_COUNT_1_BIT;                        addAttachment(attachmentCI, vkTexture, b._subresource);                    }else {                        // VKTODO: what is subresource?                        Q_ASSERT(false);                        //glNamedFramebufferTextureLayer(_id, colorAttachments[unit], gltexture->_texture, 0, b._subresource);                    }                    //_colorBuffers.push_back(unit);                } else {                    lastTextureWasNull = true;                    // VKTODO: what to do here?                    //glNamedFramebufferTexture(_id, colorAttachments[unit], 0, 0);                }                //unit++;            }        }        _colorStamps = _gpuObject.getColorStamps();    }     /*GLenum attachement = GL_DEPTH_STENCIL_ATTACHMENT;    if (!_gpuObject.hasStencil()) {        attachement = GL_DEPTH_ATTACHMENT;    } else if (!_gpuObject.hasDepth()) {        attachement = GL_STENCIL_ATTACHMENT;    }*/     if (_gpuObject.getDepthStamp() != _depthStamp) {        auto surface = _gpuObject.getDepthStencilBuffer();        auto subresource = _gpuObject.getDepthStencilBufferSubresource();        auto backend = _backend.lock();        if (_gpuObject.hasDepthStencil() && surface) {            Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType());            vkTexture = backend->syncGPUObject(surface);        }         if (vkTexture) {            if (vkTexture->_target == VK_IMAGE_VIEW_TYPE_2D) {                VKAttachmentCreateInfo attachmentCI {};                attachmentCI.width = vkTexture->_gpuObject.getWidth();                attachmentCI.height = vkTexture->_gpuObject.getHeight();                attachmentCI.layerCount = 1;                attachmentCI.format = gpu::vk::evalTexelFormatInternal(vkTexture->_gpuObject.getTexelFormat(), backend->getContext());                attachmentCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;                attachmentCI.imageSampleCount = VK_SAMPLE_COUNT_1_BIT;                addAttachment(attachmentCI, vkTexture);            /*} else if (vkTexture->_target == GL_TEXTURE_2D_MULTISAMPLE) {                glNamedFramebufferTexture(_id, attachement, gltexture->_texture, 0);*/            } else if (vkTexture->_target == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {                VKAttachmentCreateInfo attachmentCI {};                attachmentCI.width = vkTexture->_gpuObject.getWidth();                attachmentCI.height = vkTexture->_gpuObject.getHeight();                attachmentCI.layerCount = vkTexture->_gpuObject.getNumSlices() - subresource;                attachmentCI.format = gpu::vk::evalTexelFormatInternal(vkTexture->_gpuObject.getTexelFormat(), backend->getContext());                attachmentCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;                attachmentCI.imageSampleCount = VK_SAMPLE_COUNT_1_BIT;                addAttachment(attachmentCI, vkTexture, subresource);            } else {                Q_ASSERT(false);                // VKTODO                //glNamedFramebufferTextureLayer(_id, attachement, gltexture->_texture, 0,                //                               _gpuObject.getDepthStencilBufferSubresource());            }        } else {            //Q_ASSERT(false);            // VKTODO            //glNamedFramebufferTexture(_id, attachement, 0, 0);        }        _depthStamp = _gpuObject.getDepthStamp();    }     // Last but not least, define where we draw    if (!attachments.empty()) {        VK_CHECK_RESULT(createFramebuffer());        //glNamedFramebufferDrawBuffers(_id, (GLsizei)_colorBuffers.size(), _colorBuffers.data());    } else {        Q_ASSERT(false);        // VKTODO        //glNamedFramebufferDrawBuffer(_id, GL_NONE);    }     // Now check for completness    //_status = glCheckNamedFramebufferStatus(_id, GL_DRAW_FRAMEBUFFER);     // restore the current framebuffer    //checkStatus();} // From VKSVkResult gpu::vk::VKFramebuffer::createFramebuffer(){    std::vector<VkAttachmentDescription> attachmentDescriptions;    for (auto& attachment : attachments)    {        attachmentDescriptions.push_back(attachment.description);    };     // Collect attachment references    std::vector<VkAttachmentReference> colorReferences;    VkAttachmentReference depthReference = {};    bool hasDepth = false;    bool hasColor = false;     uint32_t attachmentIndex = 0;     for (auto& attachment : attachments)    {        if (attachment.isDepthStencil())        {            // Only one depth attachment allowed            assert(!hasDepth);            depthReference.attachment = attachmentIndex;            depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;            hasDepth = true;        }        else        {            colorReferences.push_back({ attachmentIndex, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });            hasColor = true;        }        attachmentIndex++;    };     // Default render pass setup uses only one subpass    VkSubpassDescription subpass = {};    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;    if (hasColor)    {        subpass.pColorAttachments = colorReferences.data();        subpass.colorAttachmentCount = static_cast<uint32_t>(colorReferences.size());    }    if (hasDepth)    {        subpass.pDepthStencilAttachment = &depthReference;    }     // Use subpass dependencies for attachment layout transitions    //std::array<VkSubpassDependency, 2> dependencies;     // VKTODO: what are these for?    /*dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;    dependencies[0].dstSubpass = 0;    dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;    dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;    dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;    dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;    dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;     dependencies[1].srcSubpass = 0;    dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;    dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;    dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;    dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;    dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;    dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;*/     // Create render pass    VkRenderPassCreateInfo renderPassInfo = {};    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;    renderPassInfo.pAttachments = attachmentDescriptions.data();    renderPassInfo.attachmentCount = static_cast<uint32_t>(attachmentDescriptions.size());    renderPassInfo.subpassCount = 1;    renderPassInfo.pSubpasses = &subpass;    // VKTODO    //renderPassInfo.dependencyCount = 2;    //renderPassInfo.pDependencies = dependencies.data();    renderPassInfo.dependencyCount = 0;    VK_CHECK_RESULT(vkCreateRenderPass(_backend.lock()->_context.device->logicalDevice, &renderPassInfo, nullptr, &vkRenderPass));     std::vector<VkImageView> attachmentViews;    for (auto attachment : attachments)    {        attachmentViews.push_back(attachment.view);    }     // Find. max number of layers across attachments    uint32_t maxLayers = 0;    for (auto attachment : attachments)    {        if (attachment.subresourceRange.layerCount > maxLayers)        {            maxLayers = attachment.subresourceRange.layerCount;        }    }     VkFramebufferCreateInfo framebufferInfo = {};    framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;    framebufferInfo.renderPass = vkRenderPass;    framebufferInfo.pAttachments = attachmentViews.data();    framebufferInfo.attachmentCount = static_cast<uint32_t>(attachmentViews.size());    framebufferInfo.width = _gpuObject.getWidth();    framebufferInfo.height = _gpuObject.getHeight();    framebufferInfo.layers = maxLayers;    VK_CHECK_RESULT(vkCreateFramebuffer(_backend.lock()->_context.device->logicalDevice, &framebufferInfo, nullptr, &vkFramebuffer));     return VK_SUCCESS;} //bool gpu::vk::VKFramebuffer::checkStatus(gpu::vk::VKFramebuffer::FramebufferStatus target) const {    // VKTODO    /*switch (_status) {        case GL_FRAMEBUFFER_COMPLETE:            // Success !            return true;         case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:            qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.";            break;        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:            qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT.";            break;        case GL_FRAMEBUFFER_UNSUPPORTED:            qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED.";            break;#if !defined(USE_GLES)        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:            qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER.";            break;        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:            qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER.";            break;#endif        default:            break;    }    return false;*///} gpu::vk::VKFramebuffer::~VKFramebuffer() {    auto backend = _backend.lock();    if (backend) {        auto& recycler = backend->getContext().recycler;  // VKTODO: these sometimes get destroyed after backend was destroyed?        recycler.trashVkRenderPass(vkRenderPass);        recycler.trashVkFramebuffer(vkFramebuffer);        for (auto &attachment : attachments) {            recycler.trashVkImageView(attachment.view);        }        recycler.framebufferDeleted(this);    } else {        Q_ASSERT(false);    }    //VKTODO    /*if (_id) {        auto backend = _backend.lock();        if (backend) {            backend->releaseFramebuffer(_id);        }    }*/} // From VKSuint32_t gpu::vk::VKFramebuffer::addAttachment(VKAttachmentCreateInfo createinfo, VKTexture *texture, uint32_t subresource) {    auto *attachmentTexture = dynamic_cast<VKAttachmentTexture*>(texture);    Q_ASSERT(attachmentTexture);     FramebufferAttachment attachment {};     attachment.format = createinfo.format;     VkImageAspectFlags aspectMask = VK_FLAGS_NONE;     // Select aspect mask and layout depending on usage     // Color attachment    if (createinfo.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)    {        Q_ASSERT(attachment.format != VK_FORMAT_D24_UNORM_S8_UINT);        aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;    }     // Depth (and/or stencil) attachment    if (createinfo.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)    {        if (attachment.hasDepth())        {            aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;        }        if (attachment.hasStencil())        {            aspectMask = aspectMask | VK_IMAGE_ASPECT_STENCIL_BIT;        }    }     assert(aspectMask > 0);     attachment.image = attachmentTexture->_vkImage;     attachment.subresourceRange = {};    attachment.subresourceRange.aspectMask = aspectMask;    attachment.subresourceRange.baseArrayLayer = subresource;    attachment.subresourceRange.levelCount = 1;    attachment.subresourceRange.layerCount = 1; // Even though texture can have multiple layers, we only render to one of them currently.     VkImageViewCreateInfo imageView = vks::initializers::imageViewCreateInfo();    imageView.viewType = (createinfo.layerCount == 1) ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY;    imageView.format = createinfo.format;    imageView.subresourceRange = attachment.subresourceRange;    imageView.subresourceRange.aspectMask = attachment.hasDepth() ? VkImageAspectFlags{VK_IMAGE_ASPECT_DEPTH_BIT} : aspectMask;    imageView.image = attachment.image;    VK_CHECK_RESULT(vkCreateImageView(_backend.lock()->_context.device->logicalDevice, &imageView, nullptr, &attachment.view));     // Fill attachment description    attachment.description = {};    attachment.description.samples = createinfo.imageSampleCount;    attachment.description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;    attachment.description.storeOp = (createinfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;    attachment.description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;    attachment.description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;    attachment.description.format = createinfo.format;    attachment.description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;    // Final layout    // If not, final layout depends on attachment type    if (attachment.hasDepth() || attachment.hasStencil())    {        attachment.description.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; // VKTODO: this is tricky, because it depends on what the image will be used for    }    else    {        attachment.description.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // VKTODO: this is tricky, because it depends on what the image will be used for    }     attachments.push_back(attachment);     return static_cast<uint32_t>(attachments.size() - 1);} // VKTODO: get rid of _backend.lock() #if 0 using namespace gpu;using namespace gpu::gl; VKFramebuffer::~VKFramebuffer() {     if (_id) {         auto backend = _backend.lock();        if (backend) {            backend->releaseFramebuffer(_id);        }    } } bool VKFramebuffer::checkStatus(VKenum target) const {    bool result = false;    switch (_status) {    case VK_FRAMEBUFFER_COMPLETE:        // Success !        result = true;        break;    case VK_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:        qCDebug(gpu_vk_logging) << "VKFramebuffer::syncGPUObject : Framebuffer not valid, VK_FRAMEBUFFER_INCOMPLETE_ATTACHMENT.";        break;    case VK_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:        qCDebug(gpu_vk_logging) << "VKFramebuffer::syncGPUObject : Framebuffer not valid, VK_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT.";        break;    case VK_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:        qCDebug(gpu_vk_logging) << "VKFramebuffer::syncGPUObject : Framebuffer not valid, VK_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER.";        break;    case VK_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:        qCDebug(gpu_vk_logging) << "VKFramebuffer::syncGPUObject : Framebuffer not valid, VK_FRAMEBUFFER_INCOMPLETE_READ_BUFFER.";        break;    case VK_FRAMEBUFFER_UNSUPPORTED:        qCDebug(gpu_vk_logging) << "VKFramebuffer::syncGPUObject : Framebuffer not valid, VK_FRAMEBUFFER_UNSUPPORTED.";        break;    }    return result;}#endif