From 9b05c9c0c36940ccdeeba5ebaaa3d0ac0d848891 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Sat, 10 Sep 2022 21:03:01 +0100 Subject: [PATCH] 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. --- app/CMakeLists.txt | 6 +- .../maxwell_3d/pipeline_manager.cpp | 505 ++++++++++++++++++ .../maxwell_3d/pipeline_manager.h | 56 ++ .../maxwell_3d/pipeline_state.cpp | 25 +- 4 files changed, 575 insertions(+), 17 deletions(-) create mode 100644 app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp create mode 100644 app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4e93a6a2..303d004a 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -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) diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp new file mode 100644 index 00000000..818f644a --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.cpp @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include +#include +#include +#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(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(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 MakePipelineShaders(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array &shaderBinaries) { + ctx.gpu.shader.ResetPools(); + + using PipelineStage = engine::Pipeline::Shader::Type; + auto pipelineStage{[](size_t i){ return static_cast(i); }}; + auto stageIdx{[](PipelineStage stage){ return static_cast(stage); }}; + + std::array 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(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 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 MakePipelineDescriptorSetLayoutBindings(const std::array &shaderStages) { + std::vector 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(componentBitWidths), static_cast(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(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 &shaderStages, + span layoutBindings, + span colorAttachments, TextureView *depthAttachment) { + boost::container::static_vector 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 bindingDescs; + boost::container::static_vector bindingDivisorDescs; + boost::container::static_vector 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 vertexInputState{ + vk::PipelineVertexInputStateCreateInfo{ + .vertexBindingDescriptionCount = static_cast(bindingDescs.size()), + .pVertexBindingDescriptions = bindingDescs.data(), + .vertexAttributeDescriptionCount = static_cast(attributeDescs.size()), + .pVertexAttributeDescriptions = attributeDescs.data(), + }, + vk::PipelineVertexInputDivisorStateCreateInfoEXT{ + .vertexBindingDivisorCount = static_cast(bindingDivisorDescs.size()), + .pVertexBindingDivisors = bindingDivisorDescs.data(), + }, + }; + + if (bindingDivisorDescs.empty()) + vertexInputState.unlink(); + + vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState{ + .topology = ConvertPrimitiveTopology(packedState.topology), + .primitiveRestartEnable = packedState.primitiveRestartEnabled, + }; + + vk::PipelineTessellationStateCreateInfo tessellationState{ + .patchControlPoints = packedState.patchSize, + }; + + vk::StructureChain rasterizationState{}; + + auto &rasterizationCreateInfo{rasterizationState.get()}; + 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().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 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(attachmentBlendStates.size()), + .pAttachments = attachmentBlendStates.data() + }; + + constexpr std::array 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(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 &shaderBinaries, span 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) {} +} + diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h new file mode 100644 index 00000000..81447727 --- /dev/null +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_manager.h @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include +#include "common.h" +#include "packed_pipeline_state.h" + +namespace skyline::gpu { + class TextureView; +} + +namespace skyline::gpu::interconnect::maxwell3d { + struct ShaderBinary { + span binary; + u32 baseOffset; + }; + + class Pipeline { + public: + struct ShaderStage { + vk::ShaderStageFlagBits stage; + vk::ShaderModule module; + Shader::Info info; + }; + + private: + std::array shaderStages; + std::vector descriptorSetLayoutBindings; + cache::GraphicsPipelineCache::CompiledPipeline compiledPipeline; + + public: + Pipeline(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array &shaderBinaries, span colorAttachments, TextureView *depthAttachment); + + Pipeline *LookupNext(const PackedPipelineState &packedState); + + void AddTransition(const PackedPipelineState &packedState, Pipeline *next); + }; + + class PipelineManager { + private: + tsl::robin_map, util::ObjectHash> map; + + public: + Pipeline *FindOrCreate(InterconnectContext &ctx, const PackedPipelineState &packedState, const std::array &shaderBinaries, span colorAttachments, TextureView *depthAttachment) { + auto it{map.find(packedState)}; + if (it != map.end()) + return it->second.get(); + + return map.emplace(packedState, std::make_unique(ctx, packedState, shaderBinaries, colorAttachments, depthAttachment)).first->second.get(); + } + }; +} diff --git a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp index 3805fcf5..e14b34ba 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp +++ b/app/src/main/cpp/skyline/gpu/interconnect/maxwell_3d/pipeline_state.cpp @@ -9,7 +9,6 @@ #include #include #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 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(dynamicStates.size()), - .pDynamicStates = dynamicStates.data() - }; + auto newPipeline{pipelineManager.FindOrCreate(ctx, packedState, shaderBinaries, colorAttachments, depthAttachment)}; + pipeline->AddTransition(packedState, newPipeline); + pipeline = newPipeline; } std::shared_ptr PipelineState::GetColorRenderTargetForClear(InterconnectContext &ctx, size_t index) {