Implement Shader IR Cache

This implements the first step of a full shader cache with caching any IR by treating the shared pointer as a handle and key for an associative map alongside hashing the Maxwell shader bytecode, it supports both single shader program and dual vertex program caching.
This commit is contained in:
PixelyIon 2022-04-16 04:20:38 +05:30
parent 0baa90d641
commit 76d8172a35
2 changed files with 31 additions and 5 deletions

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <boost/functional/hash.hpp>
#include <gpu.h> #include <gpu.h>
#include <shader_compiler/common/settings.h> #include <shader_compiler/common/settings.h>
#include <shader_compiler/common/log.h> #include <shader_compiler/common/log.h>
@ -168,17 +169,32 @@ namespace skyline::gpu {
ShaderManager::DualVertexShaderProgram::DualVertexShaderProgram(Shader::IR::Program ir, std::shared_ptr<ShaderProgram> vertexA, std::shared_ptr<ShaderProgram> vertexB) : ShaderProgram{std::move(ir)}, vertexA(std::move(vertexA)), vertexB(std::move(vertexB)) {} ShaderManager::DualVertexShaderProgram::DualVertexShaderProgram(Shader::IR::Program ir, std::shared_ptr<ShaderProgram> vertexA, std::shared_ptr<ShaderProgram> vertexB) : ShaderProgram{std::move(ir)}, vertexA(std::move(vertexA)), vertexB(std::move(vertexB)) {}
std::shared_ptr<ShaderManager::ShaderProgram> ShaderManager::ParseGraphicsShader(Shader::Stage stage, span<u8> binary, u32 baseOffset, u32 bindlessTextureConstantBufferIndex) { std::shared_ptr<ShaderManager::ShaderProgram> ShaderManager::ParseGraphicsShader(Shader::Stage stage, span<u8> binary, u32 baseOffset, u32 bindlessTextureConstantBufferIndex) {
auto program{std::make_shared<SingleShaderProgram>()}; auto &program{programCache[binary]};
if (program)
return program;
program = std::make_shared<SingleShaderProgram>();
GraphicsEnvironment environment{stage, binary, baseOffset, bindlessTextureConstantBufferIndex}; GraphicsEnvironment environment{stage, binary, baseOffset, bindlessTextureConstantBufferIndex};
Shader::Maxwell::Flow::CFG cfg(environment, program->flowBlockPool, Shader::Maxwell::Location{static_cast<u32>(baseOffset + sizeof(Shader::ProgramHeader))}); Shader::Maxwell::Flow::CFG cfg(environment, program->flowBlockPool, Shader::Maxwell::Location{static_cast<u32>(baseOffset + sizeof(Shader::ProgramHeader))});
program->program = Shader::Maxwell::TranslateProgram(program->instructionPool, program->blockPool, environment, cfg, hostTranslateInfo); program->program = Shader::Maxwell::TranslateProgram(program->instructionPool, program->blockPool, environment, cfg, hostTranslateInfo);
return program; return program;
} }
constexpr size_t ShaderManager::DualVertexProgramsHash::operator()(const std::pair<std::shared_ptr<ShaderProgram>, std::shared_ptr<ShaderProgram>> &p) const {
size_t hash{};
boost::hash_combine(hash, p.first);
boost::hash_combine(hash, p.second);
return hash;
}
std::shared_ptr<ShaderManager::ShaderProgram> ShaderManager::CombineVertexShaders(const std::shared_ptr<ShaderManager::ShaderProgram> &vertexA, const std::shared_ptr<ShaderManager::ShaderProgram> &vertexB, span<u8> vertexBBinary) { std::shared_ptr<ShaderManager::ShaderProgram> ShaderManager::CombineVertexShaders(const std::shared_ptr<ShaderManager::ShaderProgram> &vertexA, const std::shared_ptr<ShaderManager::ShaderProgram> &vertexB, span<u8> vertexBBinary) {
auto &program{dualProgramCache[DualVertexPrograms{vertexA, vertexB}]};
if (program)
return program;
VertexBEnvironment vertexBEnvironment{vertexBBinary}; VertexBEnvironment vertexBEnvironment{vertexBBinary};
return std::make_shared<DualVertexShaderProgram>(Shader::Maxwell::MergeDualVertexPrograms(vertexA->program, vertexB->program, vertexBEnvironment), vertexA, vertexB); program = std::make_shared<DualVertexShaderProgram>(Shader::Maxwell::MergeDualVertexPrograms(vertexA->program, vertexB->program, vertexBEnvironment), vertexA, vertexB);
return program;
} }
vk::raii::ShaderModule ShaderManager::CompileShader(Shader::RuntimeInfo &runtimeInfo, const std::shared_ptr<ShaderProgram> &program, Shader::Backend::Bindings &bindings) { vk::raii::ShaderModule ShaderManager::CompileShader(Shader::RuntimeInfo &runtimeInfo, const std::shared_ptr<ShaderProgram> &program, Shader::Backend::Bindings &bindings) {

View File

@ -43,6 +43,8 @@ namespace skyline::gpu {
SingleShaderProgram &operator=(const SingleShaderProgram &) = delete; SingleShaderProgram &operator=(const SingleShaderProgram &) = delete;
}; };
std::unordered_map<span<u8>, std::shared_ptr<SingleShaderProgram>, SpanHash<u8>, SpanEqual<u8>> programCache; //!< A map from Maxwell bytecode to the corresponding shader program
struct DualVertexShaderProgram : ShaderProgram { struct DualVertexShaderProgram : ShaderProgram {
std::shared_ptr<ShaderProgram> vertexA; std::shared_ptr<ShaderProgram> vertexA;
std::shared_ptr<ShaderProgram> vertexB; std::shared_ptr<ShaderProgram> vertexB;
@ -54,6 +56,14 @@ namespace skyline::gpu {
DualVertexShaderProgram &operator=(const DualVertexShaderProgram &) = delete; DualVertexShaderProgram &operator=(const DualVertexShaderProgram &) = delete;
}; };
using DualVertexPrograms = std::pair<std::shared_ptr<ShaderProgram>, std::shared_ptr<ShaderProgram>>;
struct DualVertexProgramsHash {
constexpr size_t operator()(const std::pair<std::shared_ptr<ShaderProgram>, std::shared_ptr<ShaderProgram>> &p) const;
};
std::unordered_map<DualVertexPrograms, std::shared_ptr<DualVertexShaderProgram>, DualVertexProgramsHash> dualProgramCache; //!< A map from Vertex A and Vertex B shader programs to the corresponding dual vertex shader program
public: public:
ShaderManager(const DeviceState &state, GPU &gpu); ShaderManager(const DeviceState &state, GPU &gpu);
@ -63,7 +73,7 @@ namespace skyline::gpu {
* @brief Combines the VertexA and VertexB shader programs into a single program * @brief Combines the VertexA and VertexB shader programs into a single program
* @note VertexA/VertexB shader programs must be SingleShaderProgram and not DualVertexShaderProgram * @note VertexA/VertexB shader programs must be SingleShaderProgram and not DualVertexShaderProgram
*/ */
static std::shared_ptr<ShaderManager::ShaderProgram> CombineVertexShaders(const std::shared_ptr<ShaderProgram> &vertexA, const std::shared_ptr<ShaderProgram> &vertexB, span <u8> vertexBBinary); std::shared_ptr<ShaderManager::ShaderProgram> CombineVertexShaders(const std::shared_ptr<ShaderProgram> &vertexA, const std::shared_ptr<ShaderProgram> &vertexB, span<u8> vertexBBinary);
vk::raii::ShaderModule CompileShader(Shader::RuntimeInfo &runtimeInfo, const std::shared_ptr<ShaderProgram> &program, Shader::Backend::Bindings &bindings); vk::raii::ShaderModule CompileShader(Shader::RuntimeInfo &runtimeInfo, const std::shared_ptr<ShaderProgram> &program, Shader::Backend::Bindings &bindings);
}; };