/* Created on: Nov 10, 2019 Copyright 2019 flyinghead This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "oit_renderpass.h" void RenderPasses::Reset() { for (auto& renderPass : renderPasses) renderPass.reset(); } vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last, bool loadClear) { vk::AttachmentDescription attach0 = GetAttachment0Description(initial, last, loadClear); std::array attachmentDescriptions = { // Swap chain image attach0, // OP+PT color attachment vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1, loadClear && initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, loadClear && initial ? vk::ImageLayout::eUndefined : attach0.finalLayout, attach0.finalLayout), // initial layout is eUndefined for rtt, so use final layout instead // OP+PT depth attachment vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1, initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eDepthStencilAttachmentOptimal, vk::ImageLayout::eDepthStencilAttachmentOptimal), // OP+PT depth attachment for subpass 1 vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1, initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eDepthStencilAttachmentOptimal, vk::ImageLayout::eDepthStencilAttachmentOptimal), }; vk::AttachmentReference swapChainReference(0, vk::ImageLayout::eColorAttachmentOptimal); vk::AttachmentReference colorReference(1, vk::ImageLayout::eColorAttachmentOptimal); vk::AttachmentReference depthReference(2, vk::ImageLayout::eDepthStencilAttachmentOptimal); vk::AttachmentReference depthReadOnlyRef(2, vk::ImageLayout::eDepthStencilReadOnlyOptimal); vk::AttachmentReference depthReference2(3, vk::ImageLayout::eDepthStencilAttachmentOptimal); vk::AttachmentReference colorInput(1, vk::ImageLayout::eShaderReadOnlyOptimal); std::array subpasses = { // Depth and modvol pass FIXME subpass 0 shouldn't reference the color attachment vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, nullptr, colorReference, nullptr, &depthReference), // Color pass vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, depthReadOnlyRef, colorReference, nullptr, &depthReference2), // Final pass vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, colorInput, swapChainReference, nullptr, &depthReference), // depth-only Tr pass when continuation }; std::vector dependencies = GetSubpassDependencies(); dependencies.emplace_back(vk::SubpassExternal, 1, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eInputAttachmentRead, vk::AccessFlagBits::eColorAttachmentWrite, vk::DependencyFlagBits::eByRegion); // The OP+PT color and depth/stencil attachments and the OIT storage buffers are shared by consecutive // render passes and by all the frames in flight. Previous reads (input attachments) and writes (color, depth, a-buffers) // must be done before these attachments are cleared or loaded and the a-buffers are written again. // Attachments 1 and 2 are first used in subpass 0 and depth attachment 3 in subpass 1. constexpr vk::PipelineStageFlags oitStages = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests | vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput; for (u32 subpass : { 0u, 1u }) dependencies.emplace_back(vk::SubpassExternal, subpass, oitStages, oitStages, vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite | vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite | vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite); // Attachment 0 is first used in subpass 2. With multiple render passes, it was the OP+PT color attachment // of the previous render pass (written as color and read as input attachment). dependencies.emplace_back(vk::SubpassExternal, 2, vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite); dependencies.emplace_back(0, 1, vk::PipelineStageFlagBits::eLateFragmentTests, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite, vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead, vk::DependencyFlagBits::eByRegion); // Chains the external dependency above with the OP+PT color attachment transition to subpass 2 dependencies.emplace_back(0, 1, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite, vk::DependencyFlagBits::eByRegion); // The OP+PT color attachment is stored at the end of subpass 2 dependencies.emplace_back(1, 2, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite, vk::DependencyFlagBits::eByRegion); // This dependency is only needed if the render pass isn't the last: it's needed for the depth-only Tr pass // Unfortunately we want all render passes to be compatible, and that means all attachments must be identical dependencies.emplace_back(1, 2, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests, vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead, vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite, vk::DependencyFlagBits::eByRegion); dependencies.emplace_back(1, 1, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::DependencyFlagBits::eByRegion); dependencies.emplace_back(2, 2, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::DependencyFlagBits::eByRegion); if (GetContext()->GetVendorID() == VulkanContext::VENDOR_ARM) { // Avoid glitches in upper left corner with Mali GPUs. // Using eTopOfPipe as destination since eFragmentShader/eInputAttachmentRead|eShaderRead fails to fix the problem, which is odd. dependencies.emplace_back(2, vk::SubpassExternal, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eTopOfPipe, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eNone); } return GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), attachmentDescriptions, subpasses, dependencies)); } vk::AttachmentDescription RenderPasses::GetAttachment0Description(bool initial, bool last, bool loadClear) const { return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1, initial && loadClear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, config::EmulateFramebuffer && initial ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal, config::EmulateFramebuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal); } std::vector RenderPasses::GetSubpassDependencies() const { if (config::EmulateFramebuffer) return { { 2, vk::SubpassExternal, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eHost, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eHostRead, vk::DependencyFlagBits::eByRegion } }; else return { { 2, vk::SubpassExternal, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eShaderRead, vk::DependencyFlagBits::eByRegion } }; } vk::AttachmentDescription RttRenderPasses::GetAttachment0Description(bool initial, bool last, bool loadClear) const { return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, config::RenderToTextureBuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal); } std::vector RttRenderPasses::GetSubpassDependencies() const { if (config::RenderToTextureBuffer) return { { 2, vk::SubpassExternal, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eHost, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eHostRead } }; else return { { 2, vk::SubpassExternal, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eShaderRead } }; }