#define PVR_REGS_FOR_RENDERER // see hw/pvr/pvr_regs.h /* Created on: Nov 6, 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_pipeline.h" #include "../quad.h" #include static vk::CullModeFlags CullMode(u32 cull) { return cull == 2 ? vk::CullModeFlagBits::eBack : cull == 1 ? vk::CullModeFlagBits::eFront : vk::CullModeFlagBits::eNone; } // a key's stencil state: none, or the shadow bit set or cleared static vk::StencilOpState StencilState(u32 stencil) { if (stencil == 1) return vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eReplace, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 0, 0x80, 0x80); if (stencil == 2) return vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eReplace, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 0, 0x80, 0); return vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::StencilOp::eKeep, vk::CompareOp::eNever); } u64 OITPipelineManager::Key(u32 listType, bool autosort, const PolyParam& pp, Pass pass) const { // the shader OITShaderManager::FragmentShaderParams params = {}; params.alphaTest = listType == ListType_Punch_Through; params.bumpmap = pp.tcw.PixelFmt == PixelBumpMap; /* By the polygon alone. The clamping limits were looked at too, to leave * the clamp out of the shader while they clamp nothing - but a pipeline * is made once and kept under a key that has only the polygon's bit in * it, and the limits change: a polygon first drawn while they clamped * nothing was never clamped after. (Upstream: Red Dog.) */ params.clamping = pp.tsp.ColorClamp; params.insideClipTest = (pp.tileclip >> 28) == 3; params.fog = pp.tsp.FogCtrl; params.ignoreTexAlpha = pp.tsp.IgnoreTexA || pp.tcw.PixelFmt == Pixel565; params.offset = pp.pcw.Offset; params.shaderInstr = pp.tsp.ShadInstr; params.texture = pp.pcw.Texture; params.useAlpha = pp.tsp.UseAlpha; params.twoVolume = pp.tsp1.full != (u32)-1 || pp.tcw1.full != (u32)-1; params.palette = BaseTextureCacheData::IsGpuHandledPaletted(pp.tsp, pp.tcw); u64 key = (u32)params.key() | ((u64)pp.pcw.Gouraud << KEY_GOURAUD) | ((u64)pass << KEY_PASS); /* SSAA: shaded at each sample, but for what is drawn at a single * rate; and a translucent fragment put in the lists once a pixel, * with the samples it covers, so the lists are no longer for it */ if (renderPasses->GetSamples() > 1) key |= (u64)(pass != Pass::OIT && !SsaaSingleRate(pp) ? 1 : 2) << KEY_RATE; key |= (u64)(pass == Pass::Depth ? (listType == ListType_Translucent ? 2 : 0) : 1) << KEY_SUBPASS; // the fixed functions key |= (u64)(pp.isp.CullMode == 3 ? 2 : pp.isp.CullMode == 2 ? 1 : 0) << KEY_CULL; vk::CompareOp depthOp; if (pass == Pass::Color && !pp.isp.ZWriteDis && listType != ListType_Translucent) depthOp = vk::CompareOp::eEqual; else if (listType == ListType_Punch_Through || autosort) depthOp = vk::CompareOp::eGreaterOrEqual; else depthOp = depthOps[pp.isp.DepthMode]; bool depthWriteEnable; /* Depth is written in the depth pass. In the colour pass the depth buffer * is read-only - it is an input to the shaders there - and depth writes * are not allowed. For opaque and punch-through polygons there is nothing * to write anyway: they are drawn where their depth is the one the depth * pass left, so a write would put the same value back. * * Translucent polygons that are not sorted are the exception. They are * drawn in the colour pass only, in order, each tested against the depth * the ones before it wrote, so they still write. So does everything on * Intel, which is the driver bug workaround this condition came with. */ const bool colourPassWrites = pass == Pass::Color && (GetContext()->GetVendorID() == VENDOR_INTEL || (!autosort && listType == ListType_Translucent)); if (pass != Pass::Depth && !colourPassWrites) depthWriteEnable = false; // Z Write Disable seems to be ignored for punch-through. // Fixes Worms World Party, Bust-a-Move 4 and Re-Volt else if (listType == ListType_Punch_Through) depthWriteEnable = true; else depthWriteEnable = !pp.isp.ZWriteDis; const bool shadowed = pass == Pass::Depth && (listType == ListType_Opaque || listType == ListType_Punch_Through); key |= ((u64)depthWriteEnable << KEY_ZWRITE) | ((u64)depthOp << KEY_DEPTH_OP) | ((u64)(shadowed ? (pp.pcw.Shadow != 0 ? 1 : 2) : 0) << KEY_STENCIL); // Apparently punch-through polys support blending, or at least some combinations if (listType == ListType_Punch_Through || pass == Pass::Color) key |= (1ull << KEY_BLEND) | (1ull << KEY_COLOR_WRITE) | ((u64)pp.tsp.SrcInstr << KEY_SRC) | ((u64)pp.tsp.DstInstr << KEY_DST); return key; } OITPsoChoice OITPipelineManager::Choose(u64 key) { if (key == lastKey) return lastChoice; VkPsoWorker& worker = VkPsoWorker::Instance(); PsoSlot *slot = pipelines.Find(key); if (slot == nullptr && worker.Running() && !PsoTestGeneral) { // its own pipeline, asked for now; drawn with the general one until it is made slot = pipelines.Insert(key); if (slot != nullptr) { worker.Request(slot); GetContext()->PsoKeys().Record(who, key); } } if (PsoReady(slot)) { lastKey = key; lastChoice = { slot->pipeline, key, false }; return lastChoice; } const u64 generalKey = GeneralKey(key); PsoSlot *slotGeneral = general.Find(generalKey); if (slotGeneral == nullptr) { /* A device that cannot set this polygon's depth, stencil or blend * states with the draw, and a general pipeline for them not made * when the renderer started: made now, the draw waiting for it */ slotGeneral = general.Insert(generalKey); if (slotGeneral != nullptr) VkPsoWorker::BuildSync(slotGeneral, GetContext()->GetPipelineCache()); } if (slotGeneral == nullptr) return { VK_NULL_HANDLE, key, false }; if (PsoReady(slotGeneral)) return { slotGeneral->pipeline, key, true }; // (one handed to the worker at the start, not made yet: taken, made or waited for) const VkPipeline general = VkPsoWorker::Need(slotGeneral); return { general, key, general != VK_NULL_HANDLE }; } void OITPipelineManager::SetDynamicState(vk::CommandBuffer cmdBuffer, u64 key) const { const pso_ext::Functions& f = GetContext()->DynamicState(); const VkCommandBuffer cmd = (VkCommandBuffer)cmdBuffer; if (f.depthStencil) { f.CmdSetCullMode(cmd, (VkCullModeFlags)CullMode((key >> KEY_CULL) & 3)); f.CmdSetPrimitiveTopology(cmd, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); f.CmdSetDepthTestEnable(cmd, VK_TRUE); f.CmdSetDepthWriteEnable(cmd, (VkBool32)((key >> KEY_ZWRITE) & 1)); f.CmdSetDepthCompareOp(cmd, (VkCompareOp)((key >> KEY_DEPTH_OP) & 7)); const u32 stencil = (key >> KEY_STENCIL) & 3; const vk::StencilOpState state = StencilState(stencil); f.CmdSetStencilTestEnable(cmd, stencil != 0); f.CmdSetStencilOp(cmd, VK_STENCIL_FACE_FRONT_AND_BACK, (VkStencilOp)state.failOp, (VkStencilOp)state.passOp, (VkStencilOp)state.depthFailOp, (VkCompareOp)state.compareOp); cmdBuffer.setStencilCompareMask(vk::StencilFaceFlagBits::eVkStencilFrontAndBack, state.compareMask); cmdBuffer.setStencilWriteMask(vk::StencilFaceFlagBits::eVkStencilFrontAndBack, state.writeMask); cmdBuffer.setStencilReference(vk::StencilFaceFlagBits::eVkStencilFrontAndBack, state.reference); } if (f.blend) { const VkBool32 enable = (VkBool32)((key >> KEY_BLEND) & 1); f.CmdSetColorBlendEnable(cmd, 0, 1, &enable); pso_ext::ColorBlendEquation equation = {}; equation.colorBlendOp = VK_BLEND_OP_ADD; equation.alphaBlendOp = VK_BLEND_OP_ADD; if (enable) { equation.srcColorBlendFactor = equation.srcAlphaBlendFactor = (VkBlendFactor)getBlendFactor((key >> KEY_SRC) & 7, true); equation.dstColorBlendFactor = equation.dstAlphaBlendFactor = (VkBlendFactor)getBlendFactor((key >> KEY_DST) & 7, false); } f.CmdSetColorBlendEquation(cmd, 0, 1, &equation); const VkColorComponentFlags writeMask = ((key >> KEY_COLOR_WRITE) & 1) ? VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT : 0; f.CmdSetColorWriteMask(cmd, 0, 1, &writeMask); } } void OITPipelineManager::Reset() { // (nothing of it is to be made any more; what is being made, finished) NewGeneration(); VkPsoWorker::Instance().EndBatch(this); VkPsoWorker::Instance().WaitIdle(); if (GetContext() != nullptr && GetContext()->GetDevice()) { vk::Device device = GetContext()->GetDevice(); pipelines.Clear(device); general.Clear(device); for (PsoSlot& slot : fixedSlots) { if (slot.pipeline != VK_NULL_HANDLE) device.destroyPipeline(vk::Pipeline(slot.pipeline)); slot.pipeline = VK_NULL_HANDLE; slot.owner = nullptr; } } lastKey = ~0ull; prepared = false; } void OITPipelineManager::Prepare() { VkPsoWorker& worker = VkPsoWorker::Instance(); const u32 samples = renderPasses->GetSamples(); /* (taken here: the render passes are made when first asked for, which * is not something two threads may do at once) */ renderPass = renderPasses->GetRenderPass(true, true); std::vector now; auto addGeneral = [&](u64 key) { const u64 generalKey = GeneralKey(key); if (general.Find(generalKey) == nullptr) { PsoSlot *slot = general.Insert(generalKey); if (slot != nullptr) now.push_back(slot); } }; auto fits = [&](u64 key) { const u32 rate = (key >> KEY_RATE) & 3; return ((key >> KEY_KIND) & 15) == KIND_POLY && (samples > 1 ? rate == 1 || rate == 2 : rate == 0); }; /* The fixed ones first: every frame draws with them */ const pso_ext::Functions& dynamic = GetContext()->DynamicState(); cullSetter = dynamic.depthStencil ? dynamic.CmdSetCullMode : nullptr; cullKept = cullSetter != nullptr ? 0 : 3; for (u32 i = 0; i < FIXED_COUNT; i++) { // (with the cull mode dynamic, the modifier volumes' none ones draw them all) if (i < FIXED_FINAL && cullSetter != nullptr && i % 3 != 0) continue; if (i == FIXED_FINAL_PER_SAMPLE && samples <= 1) continue; if (i == FIXED_FINAL_SUMS && !sums) continue; PsoSlot& slot = fixedSlots[i]; slot.key = ((u64)KIND_FIXED << KEY_KIND) | i; slot.owner = this; slot.pipeline = VK_NULL_HANDLE; now.push_back(&slot); } /* The general pipelines: of each module and subpass, and of each fixed * state the device must have made with them. The depth pass draws the * opaque polygons in subpass 0, the translucent ones in 2; the colour * pass and the lists are in 1. */ for (u64 gouraud = 0; gouraud < 2; gouraud++) for (u64 pass = 0; pass < 3; pass++) for (u64 subpass = 0; subpass < 3; subpass++) { if ((pass == (u64)Pass::Depth) != (subpass != 1)) continue; for (u64 rate = samples > 1 ? 1 : 0; rate <= (samples > 1 ? 2u : 0u); rate++) { if (pass == (u64)Pass::OIT && rate == 1) continue; addGeneral((gouraud << KEY_GOURAUD) | (rate << KEY_RATE) | (pass << KEY_PASS) | (subpass << KEY_SUBPASS)); } } if (!dynamic.depthStencil || !dynamic.blend) { std::vector reach; /* A device that has a general pipeline for each fixed state: one for * every state a draw can reach, worked out by Key() itself from the * draws the drawer makes (each list in each pass it is drawn in) and * every value of the fields it reads */ static const struct { u32 list; bool autosort; Pass pass; } draws[] = { { ListType_Opaque, false, Pass::Depth }, { ListType_Punch_Through, false, Pass::Depth }, { ListType_Opaque, false, Pass::Color }, { ListType_Punch_Through, false, Pass::Color }, { ListType_Translucent, true, Pass::OIT }, { ListType_Translucent, false, Pass::Color }, { ListType_Translucent, false, Pass::Depth }, { ListType_Translucent, true, Pass::Depth }, }; const u64 kept = DEPTH_STENCIL_BITS | BLEND_BITS | (3ull << KEY_PASS) | (3ull << KEY_SUBPASS); for (const auto& d : draws) for (u32 bits = 0; bits < (1u << 13); bits++) { PolyParam pp = {}; pp.isp.CullMode = bits & 3; pp.isp.DepthMode = (bits >> 2) & 7; pp.isp.ZWriteDis = (bits >> 5) & 1; pp.pcw.Shadow = (bits >> 6) & 1; pp.tsp.SrcInstr = (bits >> 7) & 7; pp.tsp.DstInstr = (bits >> 10) & 7; pp.tsp1.full = (u32)-1; pp.tcw1.full = (u32)-1; const u64 fixed = Key(d.list, d.autosort, pp, d.pass) & kept; for (u64 gouraud = 0; gouraud < 2; gouraud++) for (u64 rate = samples > 1 ? 1 : 0; rate <= (samples > 1 ? 2u : 0u); rate++) if (d.pass != Pass::OIT || rate != 1) reach.push_back(GeneralKey(fixed | (gouraud << KEY_GOURAUD) | (rate << KEY_RATE))); } /* All made at the start, before the first frame. Where they are too * many (a device with neither extension, the per-pixel renderer's * passes: thousands, until the blend is folded as Direct3D 12's * is), those of earlier runs are, and one not seen before is made * when first drawn (VkPsoWorker::SyncBuilt() counts them) */ std::sort(reach.begin(), reach.end()); reach.erase(std::unique(reach.begin(), reach.end()), reach.end()); if (reach.size() <= PSO_GENERAL_AT_START) for (u64 key : reach) addGeneral(key); else { WARN_LOG(RENDERER, "Vulkan: %u general pipelines a draw can reach, too many to make at the start", (u32)reach.size()); GetContext()->PsoKeys().ForEach(who, [&](u64 key) { if (fits(key)) addGeneral(key); }); } } NOTICE_LOG(RENDERER, "Vulkan: %u general pipelines made at the start", general.Used()); worker.BuildSoon(now.data(), (u32)now.size(), GetContext()->GetPipelineCache()); /* The polygons' own pipelines made in earlier runs: asked for now, made * while nothing more pressing is */ if (worker.Running()) GetContext()->PsoKeys().ForEach(who, [&](u64 key) { if (fits(key) && pipelines.Find(key) == nullptr) { PsoSlot *slot = pipelines.Insert(key); if (slot != nullptr) worker.Prefetch(slot); } }); prepared = true; } VkPipeline OITPipelineManager::BuildPipeline(u64 key, vk::PipelineCache cache) { switch ((key >> KEY_KIND) & 15) { case KIND_POLY: return BuildPoly(key, false, cache); case KIND_GENERAL: return BuildPoly(key, true, cache); case KIND_FIXED: { const u32 i = (u32)(key & 0xff); // (the modifier volumes': Xor, Or, Inclusion, Exclusion, by cull mode none, front, back) if (i < FIXED_TRMODVOL) return CreateModVolPipeline((ModVolMode)(i / 3), i % 3, cache); if (i < FIXED_FINAL) return CreateTrModVolPipeline((ModVolMode)((i - FIXED_TRMODVOL) / 3), (i - FIXED_TRMODVOL) % 3, cache); if (i == FIXED_FINAL || i == FIXED_FINAL_PER_SAMPLE || i == FIXED_FINAL_SUMS) return CreateFinalPipeline(i == FIXED_FINAL_PER_SAMPLE, i == FIXED_FINAL_SUMS, cache); if (i == FIXED_CLEAR) return CreateClearPipeline(cache); return VK_NULL_HANDLE; } default: return VK_NULL_HANDLE; } } /* A polygon's pipeline, all of it from @key; @general: the one that draws * for the keys that differ from it only in what the draw sets (pso.h) */ VkPipeline OITPipelineManager::BuildPoly(u64 key, bool general, vk::PipelineCache cache) { vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(); // Input assembly state vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip); // Viewport and scissor states vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr); // Rasterization and multisample states vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo ( vk::PipelineRasterizationStateCreateFlags(), // flags false, // depthClampEnable false, // rasterizerDiscardEnable vk::PolygonMode::eFill, // polygonMode CullMode((key >> KEY_CULL) & 3), // cullMode vk::FrontFace::eCounterClockwise, // frontFace false, // depthBiasEnable 0.0f, // depthBiasConstantFactor 0.0f, // depthBiasClamp 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); const u32 rate = (key >> KEY_RATE) & 3; vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = SsaaMultisample(renderPasses->GetSamples(), rate == 1); // Depth and stencil const u32 stencil = (key >> KEY_STENCIL) & 3; const vk::StencilOpState stencilOpState = StencilState(stencil); vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo ( vk::PipelineDepthStencilStateCreateFlags(), // flags true, // depthTestEnable (key >> KEY_ZWRITE) & 1, // depthWriteEnable (vk::CompareOp)((key >> KEY_DEPTH_OP) & 7), // depthCompareOp false, // depthBoundTestEnable stencil != 0, // stencilTestEnable stencilOpState, // front stencilOpState // back ); // Color flags and blending vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState; if ((key >> KEY_BLEND) & 1) { vk::ColorComponentFlags colorComponentFlags(vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA); u32 src = (key >> KEY_SRC) & 7; u32 dst = (key >> KEY_DST) & 7; pipelineColorBlendAttachmentState = { true, // blendEnable getBlendFactor(src, true), // srcColorBlendFactor getBlendFactor(dst, false), // dstColorBlendFactor vk::BlendOp::eAdd, // colorBlendOp getBlendFactor(src, true), // srcAlphaBlendFactor getBlendFactor(dst, false), // dstAlphaBlendFactor vk::BlendOp::eAdd, // alphaBlendOp colorComponentFlags // colorWriteMask }; } else { pipelineColorBlendAttachmentState = { false, // blendEnable vk::BlendFactor::eZero, // srcColorBlendFactor vk::BlendFactor::eZero, // dstColorBlendFactor vk::BlendOp::eAdd, // colorBlendOp vk::BlendFactor::eZero, // srcAlphaBlendFactor vk::BlendFactor::eZero, // dstAlphaBlendFactor vk::BlendOp::eAdd, // alphaBlendOp vk::ColorComponentFlags() // colorWriteMask }; } vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo ( vk::PipelineColorBlendStateCreateFlags(), // flags false, // logicOpEnable vk::LogicOp::eNoOp, // logicOp 1, // attachmentCount &pipelineColorBlendAttachmentState, // pAttachments { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants ); // the general pipeline's states that are set with each draw (SetDynamicState()) vk::DynamicState dynamicStates[18] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; u32 dynamicCount = 2; if (general) { const pso_ext::Functions& dynamic = GetContext()->DynamicState(); if (dynamic.depthStencil) { const int states[] = { pso_ext::DYNAMIC_STATE_CULL_MODE, pso_ext::DYNAMIC_STATE_PRIMITIVE_TOPOLOGY, pso_ext::DYNAMIC_STATE_DEPTH_TEST_ENABLE, pso_ext::DYNAMIC_STATE_DEPTH_WRITE_ENABLE, pso_ext::DYNAMIC_STATE_DEPTH_COMPARE_OP, pso_ext::DYNAMIC_STATE_STENCIL_TEST_ENABLE, pso_ext::DYNAMIC_STATE_STENCIL_OP, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE }; for (int state : states) dynamicStates[dynamicCount++] = (vk::DynamicState)state; } if (dynamic.blend) { const int states[] = { pso_ext::DYNAMIC_STATE_COLOR_BLEND_ENABLE, pso_ext::DYNAMIC_STATE_COLOR_BLEND_EQUATION, pso_ext::DYNAMIC_STATE_COLOR_WRITE_MASK }; for (int state : states) dynamicStates[dynamicCount++] = (vk::DynamicState)state; } } vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), dynamicCount, dynamicStates); const bool gouraud = (key >> KEY_GOURAUD) & 1; vk::ShaderModule vertex_module = shaderManager->GetVertexShader(OITShaderManager::VertexShaderParams{ gouraud }); OITShaderManager::FragmentShaderParams params = {}; params.gouraud = gouraud; params.pass = (Pass)((key >> KEY_PASS) & 3); params.sampleRate = (int)rate; vk::ShaderModule fragment_module = shaderManager->GetFragmentShader(params); const PsoSpecialization specialization(general ? -1 : (s32)(key & ((1u << OIT_SC_BITS) - 1)), renderPasses->GetSamples()); vk::PipelineShaderStageCreateInfo stages[] = { { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" }, { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main", &specialization.info }, }; vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo ( vk::PipelineCreateFlags(), // flags 2, // stageCount stages, // pStages &pipelineVertexInputStateCreateInfo, // pVertexInputState &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState nullptr, // pTessellationState &pipelineViewportStateCreateInfo, // pViewportState &pipelineRasterizationStateCreateInfo, // pRasterizationState &pipelineMultisampleStateCreateInfo, // pMultisampleState &pipelineDepthStencilStateCreateInfo, // pDepthStencilState &pipelineColorBlendStateCreateInfo, // pColorBlendState &pipelineDynamicStateCreateInfo, // pDynamicState *pipelineLayout, // layout renderPass, // renderPass (u32)((key >> KEY_SUBPASS) & 3) // subpass ); return PsoCreate(GetContext()->GetDevice(), cache, graphicsPipelineCreateInfo); } VkPipeline OITPipelineManager::CreateFinalPipeline(bool perSample, bool sums, vk::PipelineCache cache) { vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo(false); // Input assembly state vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip); // Viewport and scissor states vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr); // Rasterization and multisample states vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo ( vk::PipelineRasterizationStateCreateFlags(), // flags false, // depthClampEnable false, // rasterizerDiscardEnable vk::PolygonMode::eFill, // polygonMode vk::CullModeFlagBits::eNone, // cullMode vk::FrontFace::eCounterClockwise, // frontFace false, // depthBiasEnable 0.0f, // depthBiasConstantFactor 0.0f, // depthBiasClamp 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); // (SSAA: the last subpass is of single samples; the one before, which puts each sample together, of all of them) vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = SsaaMultisample(perSample ? renderPasses->GetSamples() : 1, perSample); // Depth and stencil vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo; // Color flags and blending vk::ColorComponentFlags colorComponentFlags(vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA); vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = { false, // blendEnable vk::BlendFactor::eZero, // srcColorBlendFactor vk::BlendFactor::eZero, // dstColorBlendFactor vk::BlendOp::eAdd, // colorBlendOp vk::BlendFactor::eZero, // srcAlphaBlendFactor vk::BlendFactor::eZero, // dstAlphaBlendFactor vk::BlendOp::eAdd, // alphaBlendOp colorComponentFlags // colorWriteMask }; vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo ( vk::PipelineColorBlendStateCreateFlags(), // flags false, // logicOpEnable vk::LogicOp::eNoOp, // logicOp 1, // attachmentCount &pipelineColorBlendAttachmentState, // pAttachments { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants ); vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates); vk::ShaderModule vertex_module = shaderManager->GetFinalVertexShader(); vk::ShaderModule fragment_module = shaderManager->GetFinalShader(perSample, sums); const PsoSpecialization specialization(0, renderPasses->GetSamples()); vk::PipelineShaderStageCreateInfo stages[] = { { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" }, { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main", &specialization.info }, }; vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo ( vk::PipelineCreateFlags(), // flags 2, // stageCount stages, // pStages &pipelineVertexInputStateCreateInfo, // pVertexInputState &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState nullptr, // pTessellationState &pipelineViewportStateCreateInfo, // pViewportState &pipelineRasterizationStateCreateInfo, // pRasterizationState &pipelineMultisampleStateCreateInfo, // pMultisampleState &pipelineDepthStencilStateCreateInfo, // pDepthStencilState &pipelineColorBlendStateCreateInfo, // pColorBlendState &pipelineDynamicStateCreateInfo, // pDynamicState *pipelineLayout, // layout renderPass, // renderPass perSample ? 2 : GetFinalSubpass() // subpass ); return PsoCreate(GetContext()->GetDevice(), cache, graphicsPipelineCreateInfo); } VkPipeline OITPipelineManager::CreateClearPipeline(vk::PipelineCache cache) { vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetQuadInputStateCreateInfo(false); // Input assembly state vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip); // Viewport and scissor states vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr); // Rasterization and multisample states vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo ( vk::PipelineRasterizationStateCreateFlags(), // flags false, // depthClampEnable false, // rasterizerDiscardEnable vk::PolygonMode::eFill, // polygonMode vk::CullModeFlagBits::eNone, // cullMode vk::FrontFace::eCounterClockwise, // frontFace false, // depthBiasEnable 0.0f, // depthBiasConstantFactor 0.0f, // depthBiasClamp 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; // Depth and stencil vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo; // Color flags and blending vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState; vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo ( vk::PipelineColorBlendStateCreateFlags(), // flags false, // logicOpEnable vk::LogicOp::eNoOp, // logicOp 1, // attachmentCount &pipelineColorBlendAttachmentState, // pAttachments { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants ); vk::DynamicState dynamicStates[2] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), 2, dynamicStates); vk::ShaderModule vertex_module = shaderManager->GetFinalVertexShader(); vk::ShaderModule fragment_module = shaderManager->GetClearShader(); vk::PipelineShaderStageCreateInfo stages[] = { { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" }, { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" }, }; vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo ( vk::PipelineCreateFlags(), // flags 2, // stageCount stages, // pStages &pipelineVertexInputStateCreateInfo, // pVertexInputState &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState nullptr, // pTessellationState &pipelineViewportStateCreateInfo, // pViewportState &pipelineRasterizationStateCreateInfo, // pRasterizationState &pipelineMultisampleStateCreateInfo, // pMultisampleState &pipelineDepthStencilStateCreateInfo, // pDepthStencilState &pipelineColorBlendStateCreateInfo, // pColorBlendState &pipelineDynamicStateCreateInfo, // pDynamicState *pipelineLayout, // layout renderPass, // renderPass GetFinalSubpass() // subpass ); return PsoCreate(GetContext()->GetDevice(), cache, graphicsPipelineCreateInfo); } VkPipeline OITPipelineManager::CreateModVolPipeline(ModVolMode mode, u32 cull, vk::PipelineCache cache) { verify(mode != ModVolMode::Final); static const vk::VertexInputBindingDescription vertexBindingDescriptions[] = { { 0, sizeof(float) * 3 }, }; static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] = { vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0), // pos }; // Vertex input state vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo( vk::PipelineVertexInputStateCreateFlags(), ARRAY_SIZE(vertexBindingDescriptions), vertexBindingDescriptions, ARRAY_SIZE(vertexInputAttributeDescriptions), vertexInputAttributeDescriptions); // Input assembly state vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleList); // Viewport and scissor states vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr); // Rasterization and multisample states vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo ( vk::PipelineRasterizationStateCreateFlags(), // flags false, // depthClampEnable false, // rasterizerDiscardEnable vk::PolygonMode::eFill, // polygonMode CullMode(cull), // cullMode vk::FrontFace::eCounterClockwise, // frontFace false, // depthBiasEnable 0.0f, // depthBiasConstantFactor 0.0f, // depthBiasClamp 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = SsaaMultisample(renderPasses->GetSamples(), false); // Depth and stencil vk::StencilOpState stencilOpState; switch (mode) { case ModVolMode::Xor: stencilOpState = vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eInvert, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 0, 2, 2); break; case ModVolMode::Or: stencilOpState = vk::StencilOpState(vk::StencilOp::eKeep, vk::StencilOp::eReplace, vk::StencilOp::eKeep, vk::CompareOp::eAlways, 2, 2, 2); break; case ModVolMode::Inclusion: stencilOpState = vk::StencilOpState(vk::StencilOp::eZero, vk::StencilOp::eReplace, vk::StencilOp::eZero, vk::CompareOp::eLessOrEqual, 3, 3, 1); break; case ModVolMode::Exclusion: stencilOpState = vk::StencilOpState(vk::StencilOp::eZero, vk::StencilOp::eKeep, vk::StencilOp::eZero, vk::CompareOp::eEqual, 3, 3, 1); break; default: break; } vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo ( vk::PipelineDepthStencilStateCreateFlags(), // flags mode == ModVolMode::Xor || mode == ModVolMode::Or, // depthTestEnable false, // depthWriteEnable vk::CompareOp::eGreater, // depthCompareOp false, // depthBoundTestEnable true, // stencilTestEnable stencilOpState, // front stencilOpState // back ); // Color flags and blending vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState; vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo ( vk::PipelineColorBlendStateCreateFlags(), // flags false, // logicOpEnable vk::LogicOp::eNoOp, // logicOp 1, // attachmentCount &pipelineColorBlendAttachmentState, // pAttachments { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants ); // (the cull mode set with the draws, where it can be: PsoCull) vk::DynamicState dynamicStates[3] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, (vk::DynamicState)pso_ext::DYNAMIC_STATE_CULL_MODE }; vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), cullSetter != nullptr ? 3 : 2, dynamicStates); vk::ShaderModule vertex_module = shaderManager->GetModVolVertexShader(); vk::ShaderModule fragment_module = shaderManager->GetModVolShader(); vk::PipelineShaderStageCreateInfo stages[] = { { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" }, { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" }, }; vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo ( vk::PipelineCreateFlags(), // flags 2, // stageCount stages, // pStages &pipelineVertexInputStateCreateInfo, // pVertexInputState &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState nullptr, // pTessellationState &pipelineViewportStateCreateInfo, // pViewportState &pipelineRasterizationStateCreateInfo, // pRasterizationState &pipelineMultisampleStateCreateInfo, // pMultisampleState &pipelineDepthStencilStateCreateInfo, // pDepthStencilState &pipelineColorBlendStateCreateInfo, // pColorBlendState &pipelineDynamicStateCreateInfo, // pDynamicState *pipelineLayout, // layout renderPass, // renderPass 0 // subpass ); return PsoCreate(GetContext()->GetDevice(), cache, graphicsPipelineCreateInfo); } VkPipeline OITPipelineManager::CreateTrModVolPipeline(ModVolMode mode, u32 cull, vk::PipelineCache cache) { verify(mode != ModVolMode::Final); static const vk::VertexInputBindingDescription vertexBindingDescriptions[] = { { 0, sizeof(float) * 3 }, }; static const vk::VertexInputAttributeDescription vertexInputAttributeDescriptions[] = { vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32B32Sfloat, 0), // pos }; // Vertex input state vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo( vk::PipelineVertexInputStateCreateFlags(), ARRAY_SIZE(vertexBindingDescriptions), vertexBindingDescriptions, ARRAY_SIZE(vertexInputAttributeDescriptions), vertexInputAttributeDescriptions); // Input assembly state vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleList); // Viewport and scissor states vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr); // Rasterization and multisample states vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo ( vk::PipelineRasterizationStateCreateFlags(), // flags false, // depthClampEnable false, // rasterizerDiscardEnable vk::PolygonMode::eFill, // polygonMode CullMode(cull), // cullMode vk::FrontFace::eCounterClockwise, // frontFace false, // depthBiasEnable 0.0f, // depthBiasConstantFactor 0.0f, // depthBiasClamp 0.0f, // depthBiasSlopeFactor 1.0f // lineWidth ); vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo = SsaaMultisample(renderPasses->GetSamples(), false); // Depth and stencil vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo; // Color flags and blending vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState; vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo ( vk::PipelineColorBlendStateCreateFlags(), // flags false, // logicOpEnable vk::LogicOp::eNoOp, // logicOp 1, // attachmentCount &pipelineColorBlendAttachmentState, // pAttachments { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants ); // (the cull mode set with the draws, where it can be: PsoCull) vk::DynamicState dynamicStates[3] = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, (vk::DynamicState)pso_ext::DYNAMIC_STATE_CULL_MODE }; vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), cullSetter != nullptr ? 3 : 2, dynamicStates); vk::ShaderModule vertex_module = shaderManager->GetModVolVertexShader(); vk::ShaderModule fragment_module = shaderManager->GetTrModVolShader(mode); vk::PipelineShaderStageCreateInfo stages[] = { { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, vertex_module, "main" }, { vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, fragment_module, "main" }, }; vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo ( vk::PipelineCreateFlags(), // flags 2, // stageCount stages, // pStages &pipelineVertexInputStateCreateInfo, // pVertexInputState &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState nullptr, // pTessellationState &pipelineViewportStateCreateInfo, // pViewportState &pipelineRasterizationStateCreateInfo, // pRasterizationState &pipelineMultisampleStateCreateInfo, // pMultisampleState &pipelineDepthStencilStateCreateInfo, // pDepthStencilState &pipelineColorBlendStateCreateInfo, // pColorBlendState &pipelineDynamicStateCreateInfo, // pDynamicState *pipelineLayout, // layout renderPass, // renderPass 2 // subpass ); return PsoCreate(GetContext()->GetDevice(), cache, graphicsPipelineCreateInfo); }