// SPDX-License-Identifier: MPL-2.0 // Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/) // #define PIPELINE_STATS //!< Enables recording and ranking of pipelines by the number of variants-per-shader set #pragma once #include #include #include #include #include #include #include "common.h" #include "packed_pipeline_state.h" #include "constant_buffers.h" namespace skyline::gpu { class TextureView; } namespace skyline::gpu::interconnect::maxwell3d { class Pipeline { public: /** * @brief A monolithic struct containing all the descriptor state of the pipeline */ struct DescriptorInfo { std::vector descriptorSetLayoutBindings; struct StageDescriptorInfo { vk::PipelineStageFlagBits stage; // Unwrapped counts (counting each array element as a separate descriptor) for the below desc structs u16 uniformBufferDescTotalCount; u16 storageBufferDescTotalCount; u16 uniformTexelBufferDescTotalCount; u16 storageTexelBufferDescTotalCount; u16 combinedImageSamplerDescTotalCount; u16 storageImageDescTotalCount; // Below are descriptor structs designed to be compatible with hades (hence the use of snake_case) but in a more compacted format to reduce memory usage struct UniformBufferDesc { u8 index; u8 count; UniformBufferDesc(const Shader::ConstantBufferDescriptor &desc) : index{static_cast(desc.index)}, count{static_cast(desc.count)} {} auto operator<=>(const UniformBufferDesc &) const = default; }; boost::container::static_vector uniformBufferDescs; struct StorageBufferDesc { u32 cbuf_offset; u8 cbuf_index; bool is_written; u8 count; StorageBufferDesc(const Shader::StorageBufferDescriptor &desc) : cbuf_offset{desc.cbuf_offset}, cbuf_index{static_cast(desc.cbuf_index)}, is_written{desc.is_written}, count{static_cast(desc.count)} {} auto operator<=>(const StorageBufferDesc &) const = default; }; boost::container::small_vector storageBufferDescs; struct UniformTexelBufferDesc { u32 cbuf_offset; u32 secondary_cbuf_offset; bool has_secondary; u8 cbuf_index; u8 shift_left; u8 secondary_cbuf_index; u8 secondary_shift_left; u8 count; u8 size_shift; UniformTexelBufferDesc(const Shader::TextureBufferDescriptor &desc) : cbuf_offset{desc.cbuf_offset}, secondary_cbuf_offset{desc.secondary_cbuf_offset}, has_secondary{desc.has_secondary}, cbuf_index{static_cast(desc.cbuf_index)}, shift_left{static_cast(desc.shift_left)}, secondary_cbuf_index{static_cast(desc.secondary_cbuf_index)}, secondary_shift_left{static_cast(desc.secondary_shift_left)}, count{static_cast(desc.count)}, size_shift{static_cast(desc.size_shift)} {} auto operator<=>(const UniformTexelBufferDesc &) const = default; }; std::vector uniformTexelBufferDescs; struct StorageTexelBufferDesc { Shader::ImageFormat format; u32 cbuf_offset; bool is_read; bool is_written; u8 cbuf_index; u8 count; u8 size_shift; StorageTexelBufferDesc(const Shader::ImageBufferDescriptor &desc) : format{desc.format}, cbuf_offset{desc.cbuf_offset}, is_read{desc.is_read}, is_written{desc.is_written}, cbuf_index{static_cast(desc.cbuf_index)}, count{static_cast(desc.count)}, size_shift{static_cast(desc.size_shift)} {} auto operator<=>(const StorageTexelBufferDesc &) const = default; }; std::vector storageTexelBufferDescs; struct CombinedImageSamplerDesc { Shader::TextureType type; u32 cbuf_offset; u32 secondary_cbuf_offset; bool has_secondary; u8 cbuf_index; u8 shift_left; u8 secondary_cbuf_index; u8 secondary_shift_left; u8 count; u8 size_shift; CombinedImageSamplerDesc(const Shader::TextureDescriptor &desc) : type{desc.type}, cbuf_offset{desc.cbuf_offset}, secondary_cbuf_offset{desc.secondary_cbuf_offset}, has_secondary{desc.has_secondary}, cbuf_index{static_cast(desc.cbuf_index)}, shift_left{static_cast(desc.shift_left)}, secondary_cbuf_index{static_cast(desc.secondary_cbuf_index)}, secondary_shift_left{static_cast(desc.secondary_shift_left)}, count{static_cast(desc.count)}, size_shift{static_cast(desc.size_shift)} {} auto operator<=>(const CombinedImageSamplerDesc &) const = default; }; boost::container::small_vector combinedImageSamplerDescs; struct StorageImageDesc { Shader::TextureType type; Shader::ImageFormat format; u32 cbuf_offset; bool isRead; bool is_written; u8 cbuf_index; u8 count; u8 size_shift; StorageImageDesc(const Shader::ImageDescriptor &desc) : type{desc.type}, format{desc.format}, cbuf_offset{desc.cbuf_offset}, isRead{desc.is_read}, is_written{desc.is_written}, cbuf_index{static_cast(desc.cbuf_index)}, count{static_cast(desc.count)}, size_shift{static_cast(desc.size_shift)} {} auto operator<=>(const StorageImageDesc &) const = default; }; boost::container::small_vector storageImageDescs; std::array constantBufferUsedSizes; /** * @brief Keeps track of all bindings that are dependent on a given constant buffer index to allow for quick binding */ struct ConstantBufferDescriptorUsages { struct Usage { u16 binding; //!< Vulkan binding index u16 shaderDescIdx; //!< Index of the descriptor in the appropriate shader info member u16 entirePipelineIdx; //!< Index of the image/storage buffer in the entire pipeline bool operator==(const Usage&) const = default; }; boost::container::small_vector uniformBuffers; boost::container::small_vector storageBuffers; boost::container::small_vector combinedImageSamplers; u16 totalBufferDescCount; u16 totalImageDescCount; u16 writeDescCount; bool operator==(const ConstantBufferDescriptorUsages&) const = default; }; std::array cbufUsages; bool operator==(const StageDescriptorInfo&) const = default; }; std::vector copyDescs; //!< Copy descriptors for all descs in the pipeline to allow for quick binding std::array stages; u16 totalStorageBufferCount; u16 totalCombinedImageSamplerCount; u16 totalWriteDescCount; u16 totalBufferDescCount; u16 totalTexelBufferDescCount; u16 totalImageDescCount; bool operator==(const DescriptorInfo &) const = default; }; PackedPipelineState sourcePackedState; private: std::vector storageBufferViews; ContextTag lastExecutionTag{}; //!< The last execution tag this pipeline was used at DescriptorInfo descriptorInfo; //!< Info about all descriptors used in each stage of the pipeline u8 transitionCacheNextIdx{}; //!< The next index to insert into the transition cache u8 stageMask{}; //!< Bitmask of active shader stages u16 sampledImageCount{}; std::array transitionCache{}; tsl::robin_map bindingMatchCache; //!< Cache of which pipelines have bindings that match this pipeline void SyncCachedStorageBufferViews(ContextTag executionTag); public: GraphicsPipelineAssembler::CompiledPipeline compiledPipeline; Pipeline(GPU &gpu, PipelineStateAccessor &accessor, const PackedPipelineState &packedState); /** * @brief Returns the pipeline in the transition cache (if present) that matches the given state */ Pipeline *LookupNext(const PackedPipelineState &packedState); /** * @brief Record a transition from this pipeline to the next pipeline in the transition cache */ void AddTransition(Pipeline *next); bool CheckBindingMatch(Pipeline *other); u32 GetTotalSampledImageCount() const; /** * @brief Creates a descriptor set update from the current GPU state * @param sampledImages A span of size `GetTotalSampledImageCount()` in which texture view pointers for each sampled image will be written */ DescriptorUpdateInfo *SyncDescriptors(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, Samplers &samplers, Textures &textures, span sampledImages, vk::PipelineStageFlags &srcStageMask, vk::PipelineStageFlags &dstStageMask); /** * @brief Creates a partial descriptor set update from the current GPU state for only the subset of descriptors changed by the quick bind constant buffer * @param sampledImages A span of size `GetTotalSampledImageCount()` in which texture view pointers for each sampled image will be written */ DescriptorUpdateInfo *SyncDescriptorsQuickBind(InterconnectContext &ctx, ConstantBufferSet &constantBuffers, Samplers &samplers, Textures &textures, ConstantBuffers::QuickBind quickBind, span sampledImages, vk::PipelineStageFlags &srcStageMask, vk::PipelineStageFlags &dstStageMask); }; /** * @brief Manages the caching and creation of pipelines */ class PipelineManager { private: tsl::robin_map, PackedPipelineStateHash> map; #ifdef PIPELINE_STATS std::unordered_map, std::list, util::ObjectHash>> sharedPipelines; //!< Maps a shader set to all pipelines sharing that same set std::vector*> sortedSharedPipelines; //!< Sorted list of shared pipelines #endif public: PipelineManager(GPU &gpu, JvmManager &jvm); Pipeline *FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array &shaderBinaries); }; }