#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);
}