#define PVR_REGS_FOR_RENDERER // see hw/pvr/pvr_regs.h /* 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" vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last, bool depthWrites, bool resumed) { if (samples > 1) return MakeSsaaRenderPass(initial, last, depthWrites, resumed); vk::AttachmentDescription attachmentDescriptions[] = { // Swap chain image GetAttachment0Description(initial, last), // OP+PT color attachment vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetColorFormat(), vk::SampleCountFlagBits::e1, initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eShaderReadOnlyOptimal), // OP+PT depth attachment (kept after the last render pass for the upscaler, when there is one) vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1, initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last && !keepDepth ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eDepthStencilReadOnlyOptimal, vk::ImageLayout::eDepthStencilReadOnlyOptimal), }; if (resumed) { attachmentDescriptions[0].loadOp = vk::AttachmentLoadOp::eLoad; attachmentDescriptions[0].initialLayout = attachmentDescriptions[0].finalLayout; } 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, DepthInputLayout(depthWrites)); vk::AttachmentReference colorInput(1, vk::ImageLayout::eShaderReadOnlyOptimal); vk::SubpassDescription subpasses[] = { // Depth and modvol pass FIXME subpass 0 shouldn't reference the color attachment vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorReference, nullptr, &depthReference), // Color pass vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &depthReadOnlyRef, 1, &colorReference, nullptr, &depthReadOnlyRef), // Final pass vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &colorInput, 1, &swapChainReference, nullptr, &depthReference), // depth-only Tr pass when continuation }; std::vector dependencies = GetSubpassDependencies(); dependencies.emplace_back(VK_SUBPASS_EXTERNAL, 0, 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(VK_SUBPASS_EXTERNAL, 1, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eInputAttachmentRead, vk::AccessFlagBits::eColorAttachmentWrite, vk::DependencyFlagBits::eByRegion); /* The colour and depth attachments the polygons are drawn into, and the * per-pixel lists, are the same ones for every render pass and every * frame: the one before may still be reading or writing them when this * one clears or loads them and writes the lists again. Nothing said it * had to be done first. Most drivers work that out for themselves; the * ones that do not show one frame's leavings in the next. (From * upstream, which names MoltenVK on macOS 15.) * These two attachments are first used in subpasses 0 and 1. */ const vk::PipelineStageFlags oitStages = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests | vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput; for (u32 subpass = 0; subpass < 2; subpass++) dependencies.emplace_back(VK_SUBPASS_EXTERNAL, 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); /* The picture's attachment is first used in subpass 2. With several * render passes it was the polygons' colour attachment of the one * before, written and then read as an input. */ dependencies.emplace_back(VK_SUBPASS_EXTERNAL, 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); // carries the dependency above on to where the colour attachment changes hands for 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 depth attachment is stored at the end of subpass 2, and changes layout after that - not before dependencies.emplace_back(2, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests, oitStages, vk::AccessFlagBits::eDepthStencilAttachmentWrite, vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite | vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead); // (the colour 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(2, 2, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::DependencyFlagBits::eByRegion); return GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions, ARRAY_SIZE(subpasses), subpasses, dependencies.size(), dependencies.data())); } /* SSAA: attachment 0 is the picture, of one sample; 1 and 2 the colour * and depth of the samples, one image each for all the render passes of a * frame. The first render pass clears them; one that is not the last * stores them for the next, the last leaves them: with one render pass * they are never stored, and a tiler never backs them. The picture is all * written by the last subpass of the last render pass, so it is never * loaded, and stored by the last only. The colour samples are read and * written in subpass 2 (each put together where it is) and read in 3: * they are in the general layout there. */ vk::UniqueRenderPass RenderPasses::MakeSsaaRenderPass(bool initial, bool last, bool depthWrites, bool resumed) { const vk::SampleCountFlagBits n = (vk::SampleCountFlagBits)samples; const vk::ImageLayout depthLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal; vk::AttachmentDescription attachmentDescriptions[] = { vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetColorFormat(), vk::SampleCountFlagBits::e1, vk::AttachmentLoadOp::eDontCare, last ? vk::AttachmentStoreOp::eStore : vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal), vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetColorFormat(), n, initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare, initial ? vk::ImageLayout::eUndefined : vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral), vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), n, initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad, last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, initial ? vk::ImageLayout::eUndefined : depthLayout, depthLayout), }; if (resumed) { attachmentDescriptions[0].loadOp = vk::AttachmentLoadOp::eLoad; attachmentDescriptions[0].initialLayout = attachmentDescriptions[0].finalLayout; } vk::AttachmentReference pictureReference(0, vk::ImageLayout::eColorAttachmentOptimal); vk::AttachmentReference colorReference(1, vk::ImageLayout::eColorAttachmentOptimal); vk::AttachmentReference depthReference(2, depthLayout); vk::AttachmentReference depthReadOnlyRef(2, DepthInputLayout(depthWrites)); vk::AttachmentReference colorGeneral(1, vk::ImageLayout::eGeneral); vk::SubpassDescription subpasses[] = { // depth and stencil, modifier volumes vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr, 1, &colorReference, nullptr, &depthReference), // colour, and the translucent lists vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &depthReadOnlyRef, 1, &colorReference, nullptr, &depthReadOnlyRef), // translucent modifier volumes; each sample put together and the translucent depth, before another render pass vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &colorGeneral, 1, &colorGeneral, nullptr, &depthReference), // the picture put together and resolved vk::SubpassDescription(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 1, &colorGeneral, 1, &pictureReference, nullptr, nullptr), }; const vk::PipelineStageFlags fbStages = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests | vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput; const vk::AccessFlags writes = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite | vk::AccessFlagBits::eShaderWrite; const vk::AccessFlags all = writes | vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eInputAttachmentRead | vk::AccessFlagBits::eShaderRead; std::vector dependencies; /* The samples, the lists and the picture's image are the same ones for * every render pass and every frame: what came before is done with * them before any subpass uses them */ for (u32 subpass = 0; subpass < 4; subpass++) dependencies.emplace_back(VK_SUBPASS_EXTERNAL, subpass, fbStages, fbStages, writes | vk::AccessFlagBits::eShaderRead, all); // each subpass reads and writes what the one before wrote, where it is for (u32 subpass = 0; subpass < 3; subpass++) dependencies.emplace_back(subpass, subpass + 1, fbStages, fbStages, writes, all, vk::DependencyFlagBits::eByRegion); // (the barriers between the passes over the lists in subpasses 2 and 3) for (u32 subpass = 2; subpass < 4; subpass++) dependencies.emplace_back(subpass, subpass, vk::PipelineStageFlagBits::eFragmentShader, vk::PipelineStageFlagBits::eFragmentShader, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite, vk::DependencyFlagBits::eByRegion); // what the render pass leaves - the picture, to be shown or sampled; the samples, for the next render pass for (u32 subpass = 2; subpass < 4; subpass++) dependencies.emplace_back(subpass, VK_SUBPASS_EXTERNAL, fbStages, fbStages | vk::PipelineStageFlagBits::eTransfer, writes, all | vk::AccessFlagBits::eTransferRead); return GetContext()->GetDevice().createRenderPassUnique(vk::RenderPassCreateInfo(vk::RenderPassCreateFlags(), ARRAY_SIZE(attachmentDescriptions), attachmentDescriptions, ARRAY_SIZE(subpasses), subpasses, dependencies.size(), dependencies.data())); }