mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 21:54:16 +01:00
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:
parent
2c1b40c9a8
commit
9b05c9c0c3
@ -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)
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user