Introduce a pipeline manager and partial pipeline object

gpu-new will use a monolithic pipeline object for each pipeline to store state, keyed by the PackedPipelineState contents. This allows for a greater level of per-pipeline optimisations and a reduction in the overall number of lookups in a draw compared to the previous system.
This commit is contained in:
Billy Laws 2022-09-10 21:03:01 +01:00
parent 2c1b40c9a8
commit 9b05c9c0c3
4 changed files with 575 additions and 17 deletions

View File

@ -109,6 +109,9 @@ add_subdirectory("libraries/sirit")
# libadrenotools
add_subdirectory("libraries/adrenotools")
# Tessil Robin Map
add_subdirectory("libraries/robin-map")
# Build Skyline with full debugging data and -Og for debug builds
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -glldb -gdwarf-5 -fno-omit-frame-pointer")
# Build Skyline with full debugging data and some optimizations for reldebug builds, build speed is pioritised
@ -184,6 +187,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/active_state.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/packed_pipeline_state.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/constant_buffers.cpp
${source_DIR}/skyline/gpu/interconnect/maxwell_3d/maxwell_3d.cpp
${source_DIR}/skyline/gpu/interconnect/command_executor.cpp
@ -356,4 +360,4 @@ target_include_directories(skyline PRIVATE ${source_DIR}/skyline)
target_compile_options(skyline PRIVATE -Wall -Wno-unknown-attributes -Wno-c++20-extensions -Wno-c++17-extensions -Wno-c99-designator -Wno-reorder -Wno-missing-braces -Wno-unused-variable -Wno-unused-private-field -Wno-dangling-else -Wconversion -fsigned-bitfields)
target_link_libraries(skyline PRIVATE shader_recompiler)
target_link_libraries_system(skyline android perfetto fmt lz4_static tzcode oboe vkma mbedcrypto opus Boost::intrusive Boost::container range-v3 adrenotools)
target_link_libraries_system(skyline android perfetto fmt lz4_static tzcode oboe vkma mbedcrypto opus Boost::intrusive Boost::container range-v3 adrenotools tsl::robin_map)

View File

@ -0,0 +1,505 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <gpu/texture/texture.h>
#include <gpu/shader_manager.h>
#include <gpu.h>
#include <vulkan/vulkan_handles.hpp>
#include "gpu/cache/graphics_pipeline_cache.h"
#include "pipeline_manager.h"
#include "soc/gm20b/engines/maxwell/types.h"
namespace skyline::gpu::interconnect::maxwell3d {
static constexpr Shader::Stage ConvertCompilerShaderStage(engine::Pipeline::Shader::Type stage) {
switch (stage) {
case engine::Pipeline::Shader::Type::VertexCullBeforeFetch:
return Shader::Stage::VertexA;
case engine::Pipeline::Shader::Type::Vertex:
return Shader::Stage::VertexB;
case engine::Pipeline::Shader::Type::TessellationInit:
return Shader::Stage::TessellationControl;
case engine::Pipeline::Shader::Type::Tessellation:
return Shader::Stage::TessellationEval;
case engine::Pipeline::Shader::Type::Geometry:
return Shader::Stage::Geometry;
case engine::Pipeline::Shader::Type::Pixel:
return Shader::Stage::Fragment;
default:
throw exception("Invalid shader stage: {}", stage);
}
}
static vk::ShaderStageFlagBits ConvertVkShaderStage(engine::Pipeline::Shader::Type stage) {
switch (stage) {
case engine::Pipeline::Shader::Type::VertexCullBeforeFetch:
case engine::Pipeline::Shader::Type::Vertex:
return vk::ShaderStageFlagBits::eVertex;
case engine::Pipeline::Shader::Type::TessellationInit:
return vk::ShaderStageFlagBits::eTessellationControl;
case engine::Pipeline::Shader::Type::Tessellation:
return vk::ShaderStageFlagBits::eTessellationEvaluation;
case engine::Pipeline::Shader::Type::Geometry:
return vk::ShaderStageFlagBits::eGeometry;
case engine::Pipeline::Shader::Type::Pixel:
return vk::ShaderStageFlagBits::eFragment;
default:
throw exception("Invalid shader stage: {}", stage);
}
}
static Shader::TessPrimitive ConvertShaderTessPrimitive(engine::TessellationParameters::DomainType domainType) {
switch (domainType) {
case engine::TessellationParameters::DomainType::Isoline:
return Shader::TessPrimitive::Isolines;
case engine::TessellationParameters::DomainType::Triangle:
return Shader::TessPrimitive::Triangles;
case engine::TessellationParameters::DomainType::Quad:
return Shader::TessPrimitive::Quads;
}
}
static Shader::TessSpacing ConvertShaderTessSpacing(engine::TessellationParameters::Spacing spacing) {
switch (spacing) {
case engine::TessellationParameters::Spacing::Integer:
return Shader::TessSpacing::Equal;
case engine::TessellationParameters::Spacing::FractionalEven:
return Shader::TessSpacing::FractionalEven;
case engine::TessellationParameters::Spacing::FractionalOdd:
return Shader::TessSpacing::FractionalOdd;
}
}
static Shader::AttributeType ConvertShaderAttributeType(engine::VertexAttribute attribute) {
if (attribute.source == engine::VertexAttribute::Source::Inactive)
return Shader::AttributeType::Disabled;
switch (attribute.numericalType) {
case engine::VertexAttribute::NumericalType::Snorm:
case engine::VertexAttribute::NumericalType::Unorm:
case engine::VertexAttribute::NumericalType::Sscaled:
case engine::VertexAttribute::NumericalType::Uscaled:
case engine::VertexAttribute::NumericalType::Float:
return Shader::AttributeType::Float;
case engine::VertexAttribute::NumericalType::Sint:
return Shader::AttributeType::SignedInt;
case engine::VertexAttribute::NumericalType::Uint:
return Shader::AttributeType::UnsignedInt;
default:
throw exception("Invalid numerical type: {}", static_cast<u8>(attribute.numericalType));
}
}
/**
* @notes Roughly based on https://github.com/yuzu-emu/yuzu/blob/4ffbbc534884841f9a5536e57539bf3d1642af26/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp#L127
*/
static Shader::RuntimeInfo MakeRuntimeInfo(const PackedPipelineState &packedState, Shader::IR::Program &program, Shader::IR::Program *lastProgram, bool hasGeometry) {
Shader::RuntimeInfo info;
if (lastProgram) {
info.previous_stage_stores = lastProgram->info.stores;
if (lastProgram->is_geometry_passthrough)
info.previous_stage_stores.mask |= lastProgram->info.passthrough.mask;
} else {
info.previous_stage_stores.mask.set();
}
switch (program.stage) {
case Shader::Stage::VertexB:
if (!hasGeometry) {
if (packedState.topology == engine::DrawTopology::Points)
info.fixed_state_point_size = packedState.pointSize;
//if (key.state.xfb_enabled)
//info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
//}
info.convert_depth_mode = packedState.openGlNdc;
}
ranges::transform(packedState.vertexAttributes, info.generic_input_types.begin(), &ConvertShaderAttributeType);
break;
case Shader::Stage::TessellationEval:
// Double check this!
info.tess_clockwise = packedState.outputPrimitives != engine::TessellationParameters::OutputPrimitives::TrianglesCCW;
info.tess_primitive = ConvertShaderTessPrimitive(packedState.domainType);
info.tess_spacing = ConvertShaderTessSpacing(packedState.spacing);
break;
case Shader::Stage::Geometry:
if (program.output_topology == Shader::OutputTopology::PointList)
info.fixed_state_point_size = packedState.pointSize;
// if (key.state.xfb_enabled != 0) {
// info.xfb_varyings = VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
// }
info.convert_depth_mode = packedState.openGlNdc;
break;
case Shader::Stage::Fragment:
// info.alpha_test_func = MaxwellToCompareFunction(
// key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
// info.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref);
break;
default:
break;
}
switch (packedState.topology) {
case engine::DrawTopology::Points:
info.input_topology = Shader::InputTopology::Points;
break;
case engine::DrawTopology::Lines:
case engine::DrawTopology::LineLoop:
case engine::DrawTopology::LineStrip:
info.input_topology = Shader::InputTopology::Lines;
break;
case engine::DrawTopology::Triangles:
case engine::DrawTopology::TriangleStrip:
case engine::DrawTopology::TriangleFan:
case engine::DrawTopology::Quads:
case engine::DrawTopology::QuadStrip:
case engine::DrawTopology::Polygon:
case engine::DrawTopology::Patch:
info.input_topology = Shader::InputTopology::Triangles;
break;
case engine::DrawTopology::LineListAdjcy:
case engine::DrawTopology::LineStripAdjcy:
info.input_topology = Shader::InputTopology::LinesAdjacency;
break;
case engine::DrawTopology::TriangleListAdjcy:
case engine::DrawTopology::TriangleStripAdjcy:
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
break;
}
info.force_early_z = packedState.apiMandatedEarlyZ;
info.y_negate = packedState.flipYEnable;
return info;
}
static std::array<Pipeline::ShaderStage, engine::ShaderStageCount> MakePipelineShaders(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries) {
ctx.gpu.shader.ResetPools();
using PipelineStage = engine::Pipeline::Shader::Type;
auto pipelineStage{[](size_t i){ return static_cast<PipelineStage>(i); }};
auto stageIdx{[](PipelineStage stage){ return static_cast<u8>(stage); }};
std::array<Shader::IR::Program, engine::PipelineCount> programs;
bool ignoreVertexCullBeforeFetch{};
for (size_t i{}; i < engine::PipelineCount; i++) {
if (!packedState.shaderHashes[i])
continue;
auto program{ctx.gpu.shader.ParseGraphicsShader(packedState.postVtgShaderAttributeSkipMask,
ConvertCompilerShaderStage(static_cast<PipelineStage>(i)),
shaderBinaries[i].binary, shaderBinaries[i].baseOffset,
packedState.bindlessTextureConstantBufferSlotSelect,
[](int, int){ return 0; }, [](u32){return Shader::TextureType::Color2D;})};
if (i == stageIdx(PipelineStage::Vertex) && packedState.shaderHashes[stageIdx(PipelineStage::VertexCullBeforeFetch)]) {
ignoreVertexCullBeforeFetch = true;
programs[i] = ctx.gpu.shader.CombineVertexShaders(programs[stageIdx(PipelineStage::VertexCullBeforeFetch)], program, shaderBinaries[i].binary);
} else {
programs[i] = program;
}
}
bool hasGeometry{packedState.shaderHashes[stageIdx(PipelineStage::Geometry)] && programs[stageIdx(PipelineStage::Geometry)].is_geometry_passthrough};
Shader::Backend::Bindings bindings{};
Shader::IR::Program *lastProgram{};
std::array<Pipeline::ShaderStage, engine::ShaderStageCount> shaderStages{};
for (size_t i{stageIdx(ignoreVertexCullBeforeFetch ? PipelineStage::Vertex : PipelineStage::VertexCullBeforeFetch)}; i < engine::ShaderStageCount; i++) {
if (!packedState.shaderHashes[i])
continue;
auto runtimeInfo{MakeRuntimeInfo(packedState, programs[i], lastProgram, hasGeometry)};
shaderStages[i] = {ConvertVkShaderStage(pipelineStage(i)), ctx.gpu.shader.CompileShader(runtimeInfo, programs[i], bindings), programs[i].info};
lastProgram = &programs[i];
}
return shaderStages;
}
static std::vector<vk::DescriptorSetLayoutBinding> MakePipelineDescriptorSetLayoutBindings(const std::array<Pipeline::ShaderStage, engine::ShaderStageCount> &shaderStages) {
std::vector<vk::DescriptorSetLayoutBinding> layoutBindings;
u32 bindingIndex{};
for (const auto &stage : shaderStages) {
auto pushBindings{[&](vk::DescriptorType type, const auto &descs) {
for (const auto &desc : descs) {
layoutBindings.push_back(vk::DescriptorSetLayoutBinding{
.binding = bindingIndex++,
.descriptorType = type,
.descriptorCount = desc.count,
.stageFlags = stage.stage,
});
}
}};
pushBindings(vk::DescriptorType::eUniformBuffer, stage.info.constant_buffer_descriptors);
pushBindings(vk::DescriptorType::eStorageBuffer, stage.info.storage_buffers_descriptors);
pushBindings(vk::DescriptorType::eUniformTexelBuffer, stage.info.texture_buffer_descriptors);
pushBindings(vk::DescriptorType::eStorageTexelBuffer, stage.info.image_buffer_descriptors);
pushBindings(vk::DescriptorType::eCombinedImageSampler, stage.info.texture_descriptors);
pushBindings(vk::DescriptorType::eStorageImage, stage.info.image_descriptors);
}
return layoutBindings;
}
static vk::Format ConvertVertexInputAttributeFormat(engine::VertexAttribute::ComponentBitWidths componentBitWidths, engine::VertexAttribute::NumericalType numericalType) {
#define FORMAT_CASE(bitWidths, type, vkType, vkFormat, ...) \
case engine::VertexAttribute::ComponentBitWidths::bitWidths | engine::VertexAttribute::NumericalType::type: \
return vk::Format::vkFormat ## vkType ##__VA_ARGS__
#define FORMAT_INT_CASE(size, vkFormat, ...) \
FORMAT_CASE(size, Uint, Uint, vkFormat, ##__VA_ARGS__); \
FORMAT_CASE(size, Sint, Sint, vkFormat, ##__VA_ARGS__);
#define FORMAT_INT_FLOAT_CASE(size, vkFormat, ...) \
FORMAT_INT_CASE(size, vkFormat, ##__VA_ARGS__); \
FORMAT_CASE(size, Float, Sfloat, vkFormat, ##__VA_ARGS__);
#define FORMAT_NORM_INT_SCALED_CASE(size, vkFormat, ...) \
FORMAT_INT_CASE(size, vkFormat, ##__VA_ARGS__); \
FORMAT_CASE(size, Unorm, Unorm, vkFormat, ##__VA_ARGS__); \
FORMAT_CASE(size, Snorm, Unorm, vkFormat, ##__VA_ARGS__); \
FORMAT_CASE(size, Uscaled, Uscaled, vkFormat, ##__VA_ARGS__); \
FORMAT_CASE(size, Sscaled, Sscaled, vkFormat, ##__VA_ARGS__)
#define FORMAT_NORM_INT_SCALED_FLOAT_CASE(size, vkFormat) \
FORMAT_NORM_INT_SCALED_CASE(size, vkFormat); \
FORMAT_CASE(size, Float, Sfloat, vkFormat)
switch (componentBitWidths | numericalType) {
/* 8-bit components */
FORMAT_NORM_INT_SCALED_CASE(R8, eR8);
FORMAT_NORM_INT_SCALED_CASE(R8_G8, eR8G8);
FORMAT_NORM_INT_SCALED_CASE(G8R8, eR8G8);
FORMAT_NORM_INT_SCALED_CASE(R8_G8_B8, eR8G8B8);
FORMAT_NORM_INT_SCALED_CASE(R8_G8_B8_A8, eR8G8B8A8);
FORMAT_NORM_INT_SCALED_CASE(A8B8G8R8, eR8G8B8A8);
FORMAT_NORM_INT_SCALED_CASE(X8B8G8R8, eR8G8B8A8);
/* 16-bit components */
FORMAT_NORM_INT_SCALED_FLOAT_CASE(R16, eR16);
FORMAT_NORM_INT_SCALED_FLOAT_CASE(R16_G16, eR16G16);
FORMAT_NORM_INT_SCALED_FLOAT_CASE(R16_G16_B16, eR16G16B16);
FORMAT_NORM_INT_SCALED_FLOAT_CASE(R16_G16_B16_A16, eR16G16B16A16);
/* 32-bit components */
FORMAT_INT_FLOAT_CASE(R32, eR32);
FORMAT_INT_FLOAT_CASE(R32_G32, eR32G32);
FORMAT_INT_FLOAT_CASE(R32_G32_B32, eR32G32B32);
FORMAT_INT_FLOAT_CASE(R32_G32_B32_A32, eR32G32B32A32);
/* 10-bit RGB, 2-bit A */
FORMAT_NORM_INT_SCALED_CASE(A2B10G10R10, eA2B10G10R10, Pack32);
/* 11-bit G and R, 10-bit B */
FORMAT_CASE(B10G11R11, Float, Ufloat, eB10G11R11, Pack32);
default:
Logger::Warn("Unimplemented Maxwell3D Vertex Buffer Format: {} | {}", static_cast<u8>(componentBitWidths), static_cast<u8>(numericalType));
return vk::Format::eR8G8B8A8Unorm;
}
#undef FORMAT_CASE
#undef FORMAT_INT_CASE
#undef FORMAT_INT_FLOAT_CASE
#undef FORMAT_NORM_INT_SCALED_CASE
#undef FORMAT_NORM_INT_SCALED_FLOAT_CASE
}
static vk::PrimitiveTopology ConvertPrimitiveTopology(engine::DrawTopology topology) {
switch (topology) {
case engine::DrawTopology::Points:
return vk::PrimitiveTopology::ePointList;
case engine::DrawTopology::Lines:
return vk::PrimitiveTopology::eLineList;
case engine::DrawTopology::LineStrip:
return vk::PrimitiveTopology::eLineStrip;
case engine::DrawTopology::Triangles:
return vk::PrimitiveTopology::eTriangleList;
case engine::DrawTopology::TriangleStrip:
return vk::PrimitiveTopology::eTriangleStrip;
case engine::DrawTopology::TriangleFan:
return vk::PrimitiveTopology::eTriangleFan;
case engine::DrawTopology::Quads:
return vk::PrimitiveTopology::eTriangleList; // Uses quad conversion
case engine::DrawTopology::LineListAdjcy:
return vk::PrimitiveTopology::eLineListWithAdjacency;
case engine::DrawTopology::LineStripAdjcy:
return vk::PrimitiveTopology::eLineStripWithAdjacency;
case engine::DrawTopology::TriangleListAdjcy:
return vk::PrimitiveTopology::eTriangleListWithAdjacency;
case engine::DrawTopology::TriangleStripAdjcy:
return vk::PrimitiveTopology::eTriangleStripWithAdjacency;
case engine::DrawTopology::Patch:
return vk::PrimitiveTopology::ePatchList;
default:
Logger::Warn("Unimplemented input assembly topology: {}", static_cast<u8>(topology));
return vk::PrimitiveTopology::eTriangleList;
}
}
static vk::ProvokingVertexModeEXT ConvertProvokingVertex(engine::ProvokingVertex::Value provokingVertex) {
switch (provokingVertex) {
case engine::ProvokingVertex::Value::First:
return vk::ProvokingVertexModeEXT::eFirstVertex;
case engine::ProvokingVertex::Value::Last:
return vk::ProvokingVertexModeEXT::eLastVertex;
}
}
static cache::GraphicsPipelineCache::CompiledPipeline MakeCompiledPipeline(InterconnectContext &ctx,
const PackedPipelineState &packedState,
const std::array<Pipeline::ShaderStage, engine::ShaderStageCount> &shaderStages,
span<vk::DescriptorSetLayoutBinding> layoutBindings,
span<TextureView *> colorAttachments, TextureView *depthAttachment) {
boost::container::static_vector<vk::PipelineShaderStageCreateInfo, engine::ShaderStageCount> shaderStageInfos;
for (const auto &stage : shaderStages)
if (stage.module)
shaderStageInfos.push_back(vk::PipelineShaderStageCreateInfo{
.stage = stage.stage,
.module = &*stage.module,
.pName = "main"
});
boost::container::static_vector<vk::VertexInputBindingDescription, engine::VertexStreamCount> bindingDescs;
boost::container::static_vector<vk::VertexInputBindingDivisorDescriptionEXT, engine::VertexStreamCount> bindingDivisorDescs;
boost::container::static_vector<vk::VertexInputAttributeDescription, engine::VertexAttributeCount> attributeDescs;
for (u32 i{}; i < engine::VertexStreamCount; i++) {
const auto &binding{packedState.vertexBindings[i]};
bindingDescs.push_back({
.binding = i,
.stride = binding.stride,
.inputRate = binding.divisor ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex,
});
if (binding.inputRate == vk::VertexInputRate::eInstance) {
if (!ctx.gpu.traits.supportsVertexAttributeDivisor) [[unlikely]]
Logger::Warn("Vertex attribute divisor used on guest without host support");
else if (!ctx.gpu.traits.supportsVertexAttributeZeroDivisor && binding.divisor == 0) [[unlikely]]
Logger::Warn("Vertex attribute zero divisor used on guest without host support");
else
bindingDivisorDescs.push_back({
.binding = i,
.divisor = binding.divisor,
});
}
}
for (u32 i{}; i < engine::VertexAttributeCount; i++) {
const auto &attribute{packedState.vertexAttributes[i]};
if (attribute.source == engine::VertexAttribute::Source::Active && shaderStages[0].info.loads.Generic(i))
attributeDescs.push_back({
.location = i,
.binding = attribute.stream,
.format = ConvertVertexInputAttributeFormat(attribute.componentBitWidths, attribute.numericalType),
.offset = attribute.offset,
});
}
vk::StructureChain<vk::PipelineVertexInputStateCreateInfo, vk::PipelineVertexInputDivisorStateCreateInfoEXT> vertexInputState{
vk::PipelineVertexInputStateCreateInfo{
.vertexBindingDescriptionCount = static_cast<u32>(bindingDescs.size()),
.pVertexBindingDescriptions = bindingDescs.data(),
.vertexAttributeDescriptionCount = static_cast<u32>(attributeDescs.size()),
.pVertexAttributeDescriptions = attributeDescs.data(),
},
vk::PipelineVertexInputDivisorStateCreateInfoEXT{
.vertexBindingDivisorCount = static_cast<u32>(bindingDivisorDescs.size()),
.pVertexBindingDivisors = bindingDivisorDescs.data(),
},
};
if (bindingDivisorDescs.empty())
vertexInputState.unlink<vk::PipelineVertexInputDivisorStateCreateInfoEXT>();
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState{
.topology = ConvertPrimitiveTopology(packedState.topology),
.primitiveRestartEnable = packedState.primitiveRestartEnabled,
};
vk::PipelineTessellationStateCreateInfo tessellationState{
.patchControlPoints = packedState.patchSize,
};
vk::StructureChain<vk::PipelineRasterizationStateCreateInfo, vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT> rasterizationState{};
auto &rasterizationCreateInfo{rasterizationState.get<vk::PipelineRasterizationStateCreateInfo>()};
rasterizationCreateInfo.rasterizerDiscardEnable = packedState.rasterizerDiscardEnable;
rasterizationCreateInfo.polygonMode = packedState.polygonMode;
rasterizationCreateInfo.cullMode = vk::CullModeFlags{packedState.cullMode};
rasterizationCreateInfo.frontFace = packedState.frontFaceClockwise ? vk::FrontFace::eClockwise : vk::FrontFace::eCounterClockwise;
rasterizationCreateInfo.depthBiasEnable = packedState.depthBiasEnable;
rasterizationState.get<vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT>().provokingVertexMode = ConvertProvokingVertex(packedState.provokingVertex);
constexpr vk::PipelineMultisampleStateCreateInfo multisampleState{
.rasterizationSamples = vk::SampleCountFlagBits::e1,
};
vk::PipelineDepthStencilStateCreateInfo depthStencilState{
.depthTestEnable = packedState.depthTestEnable,
.depthWriteEnable = packedState.depthWriteEnable,
.depthCompareOp = packedState.depthFunc,
.depthBoundsTestEnable = packedState.depthBoundsTestEnable,
.stencilTestEnable = packedState.stencilTestEnable
};
std::tie(depthStencilState.front, depthStencilState.back) = packedState.GetStencilOpsState();
boost::container::static_vector<vk::PipelineColorBlendAttachmentState, engine::ColorTargetCount> attachmentBlendStates;
for (u32 i{}; i < colorAttachments.size(); i++)
attachmentBlendStates.push_back(packedState.GetAttachmentBlendState(i));
vk::PipelineColorBlendStateCreateInfo colorBlendState{
.logicOpEnable = packedState.logicOpEnable,
.logicOp = packedState.logicOp,
.attachmentCount = static_cast<u32>(attachmentBlendStates.size()),
.pAttachments = attachmentBlendStates.data()
};
constexpr std::array<vk::DynamicState, 9> dynamicStates{
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
vk::DynamicState::eLineWidth,
vk::DynamicState::eDepthBias,
vk::DynamicState::eBlendConstants,
vk::DynamicState::eDepthBounds,
vk::DynamicState::eStencilCompareMask,
vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eStencilReference
};
vk::PipelineDynamicStateCreateInfo dynamicState{
.dynamicStateCount = static_cast<u32>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data()
};
return ctx.gpu.graphicsPipelineCache.GetCompiledPipeline(cache::GraphicsPipelineCache::PipelineState{
.shaderStages = shaderStageInfos,
.vertexState = vertexInputState,
.inputAssemblyState = inputAssemblyState,
.tessellationState = tessellationState,
.viewportState = {},
.rasterizationState = rasterizationState,
.multisampleState = multisampleState,
.depthStencilState = depthStencilState,
.colorBlendState = colorBlendState,
.colorAttachments = colorAttachments,
.depthStencilAttachment = depthAttachment,
}, layoutBindings);
}
Pipeline::Pipeline(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries, span<TextureView *> colorAttachments, TextureView *depthAttachment)
: shaderStages{MakePipelineShaders(ctx, packedState, shaderBinaries)},
descriptorSetLayoutBindings{MakePipelineDescriptorSetLayoutBindings(shaderStages)},
compiledPipeline{MakeCompiledPipeline(ctx, packedState, shaderStages, descriptorSetLayoutBindings, colorAttachments, depthAttachment)} {
}
Pipeline *Pipeline::LookupNext(const PackedPipelineState &packedState) {
return nullptr;
}
void Pipeline::AddTransition(const PackedPipelineState &packedState, Pipeline *next) {}
}

View File

@ -0,0 +1,56 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <tsl/robin_map.h>
#include <shader_compiler/frontend/ir/program.h>
#include <gpu/cache/graphics_pipeline_cache.h>
#include "common.h"
#include "packed_pipeline_state.h"
namespace skyline::gpu {
class TextureView;
}
namespace skyline::gpu::interconnect::maxwell3d {
struct ShaderBinary {
span<u8> binary;
u32 baseOffset;
};
class Pipeline {
public:
struct ShaderStage {
vk::ShaderStageFlagBits stage;
vk::ShaderModule module;
Shader::Info info;
};
private:
std::array<ShaderStage, engine::ShaderStageCount> shaderStages;
std::vector<vk::DescriptorSetLayoutBinding> descriptorSetLayoutBindings;
cache::GraphicsPipelineCache::CompiledPipeline compiledPipeline;
public:
Pipeline(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries, span<TextureView *> colorAttachments, TextureView *depthAttachment);
Pipeline *LookupNext(const PackedPipelineState &packedState);
void AddTransition(const PackedPipelineState &packedState, Pipeline *next);
};
class PipelineManager {
private:
tsl::robin_map<PackedPipelineState, std::unique_ptr<Pipeline>, util::ObjectHash<PackedPipelineState>> map;
public:
Pipeline *FindOrCreate(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries, span<TextureView *> colorAttachments, TextureView *depthAttachment) {
auto it{map.find(packedState)};
if (it != map.end())
return it->second.get();
return map.emplace(packedState, std::make_unique<Pipeline>(ctx, packedState, shaderBinaries, colorAttachments, depthAttachment)).first->second.get();
}
};
}

View File

@ -9,7 +9,6 @@
#include <gpu/texture/format.h>
#include <gpu.h>
#include "pipeline_state.h"
#include "shader_state.h"
namespace skyline::gpu::interconnect::maxwell3d {
/* Colour Render Target */
@ -578,22 +577,16 @@ namespace skyline::gpu::interconnect::maxwell3d {
colorBlend.Update(packedState);
globalShaderConfig.Update(packedState);
constexpr std::array<vk::DynamicState, 9> dynamicStates{
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
vk::DynamicState::eLineWidth,
vk::DynamicState::eDepthBias,
vk::DynamicState::eBlendConstants,
vk::DynamicState::eDepthBounds,
vk::DynamicState::eStencilCompareMask,
vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eStencilReference
};
if (pipeline) {
if (auto newPipeline{pipeline->LookupNext(packedState)}; newPipeline) {
pipeline = newPipeline;
return;
}
}
vk::PipelineDynamicStateCreateInfo dynamicState{
.dynamicStateCount = static_cast<u32>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data()
};
auto newPipeline{pipelineManager.FindOrCreate(ctx, packedState, shaderBinaries, colorAttachments, depthAttachment)};
pipeline->AddTransition(packedState, newPipeline);
pipeline = newPipeline;
}
std::shared_ptr<TextureView> PipelineState::GetColorRenderTargetForClear(InterconnectContext &ctx, size_t index) {