Add host shader replacement and dumping support

This is useful for debugging, but shouldn't generally be used as bindings in SPIR-V etc are unstable.
This commit is contained in:
Billy Laws 2023-02-09 22:37:58 +00:00
parent 02786839a5
commit 2e64199640
3 changed files with 20 additions and 14 deletions

View File

@ -25,7 +25,7 @@ namespace skyline::gpu::interconnect::kepler_compute {
Shader::Backend::Bindings bindings{}; Shader::Backend::Bindings bindings{};
return {ctx.gpu.shader->CompileShader({}, program, bindings), program.info}; return {ctx.gpu.shader->CompileShader({}, program, bindings, packedState.shaderHash), program.info};
} }
static Pipeline::DescriptorInfo MakePipelineDescriptorInfo(const Pipeline::ShaderStage &stage) { static Pipeline::DescriptorInfo MakePipelineDescriptorInfo(const Pipeline::ShaderStage &stage) {

View File

@ -35,8 +35,10 @@ namespace skyline::gpu {
for (const auto &entry : std::filesystem::directory_iterator{replacementDirPath}) { for (const auto &entry : std::filesystem::directory_iterator{replacementDirPath}) {
if (entry.is_regular_file()) { if (entry.is_regular_file()) {
// Parse hash from filename // Parse hash from filename
u64 hash{std::stoull(entry.path().filename().string(), nullptr, 16)}; auto path{entry.path()};
auto it{shaderReplacements.insert({hash, {}})}; auto &replacementMap{path.extension().string() == ".spv" ? hostShaderReplacements : guestShaderReplacements};
u64 hash{std::stoull(path.stem().string(), nullptr, 16)};
auto it{replacementMap.insert({hash, {}})};
// Read file into map entry // Read file into map entry
std::ifstream file{entry.path(), std::ios::binary | std::ios::ate}; std::ifstream file{entry.path(), std::ios::binary | std::ios::ate};
@ -48,9 +50,10 @@ namespace skyline::gpu {
} }
} }
span<u8> ShaderManager::ProcessShaderBinary(u64 hash, span<u8> binary) { span<u8> ShaderManager::ProcessShaderBinary(bool spv, u64 hash, span<u8> binary) {
auto it{shaderReplacements.find(hash)}; auto &replacementMap{spv ? hostShaderReplacements : guestShaderReplacements};
if (it != shaderReplacements.end()) { auto it{replacementMap.find(hash)};
if (it != replacementMap.end()) {
Logger::Info("Replacing shader with hash: 0x{:X}", hash); Logger::Info("Replacing shader with hash: 0x{:X}", hash);
return it->second; return it->second;
} }
@ -58,7 +61,7 @@ namespace skyline::gpu {
if (DumpShaders) { if (DumpShaders) {
std::scoped_lock lock{dumpMutex}; std::scoped_lock lock{dumpMutex};
auto shaderPath{dumpPath / fmt::format("{:016X}", hash)}; auto shaderPath{dumpPath / fmt::format("{:016X}{}", hash, spv ? ".spv" : "")};
if (!std::filesystem::exists(shaderPath)) { if (!std::filesystem::exists(shaderPath)) {
std::ofstream file{shaderPath, std::ios::binary}; std::ofstream file{shaderPath, std::ios::binary};
file.write(reinterpret_cast<const char *>(binary.data()), static_cast<std::streamsize>(binary.size())); file.write(reinterpret_cast<const char *>(binary.data()), static_cast<std::streamsize>(binary.size()));
@ -368,7 +371,7 @@ namespace skyline::gpu {
u32 textureConstantBufferIndex, u32 textureConstantBufferIndex,
bool viewportTransformEnabled, bool viewportTransformEnabled,
const ConstantBufferRead &constantBufferRead, const GetTextureType &getTextureType) { const ConstantBufferRead &constantBufferRead, const GetTextureType &getTextureType) {
binary = ProcessShaderBinary(hash, binary); binary = ProcessShaderBinary(false, hash, binary);
std::scoped_lock lock{poolMutex}; std::scoped_lock lock{poolMutex};
@ -395,7 +398,7 @@ namespace skyline::gpu {
u32 localMemorySize, u32 sharedMemorySize, u32 localMemorySize, u32 sharedMemorySize,
std::array<u32, 3> workgroupDimensions, std::array<u32, 3> workgroupDimensions,
const ConstantBufferRead &constantBufferRead, const GetTextureType &getTextureType) { const ConstantBufferRead &constantBufferRead, const GetTextureType &getTextureType) {
binary = ProcessShaderBinary(hash, binary); binary = ProcessShaderBinary(false, hash, binary);
std::scoped_lock lock{poolMutex}; std::scoped_lock lock{poolMutex};
@ -410,11 +413,12 @@ namespace skyline::gpu {
if (program.info.loads.Legacy() || program.info.stores.Legacy()) if (program.info.loads.Legacy() || program.info.stores.Legacy())
Shader::Maxwell::ConvertLegacyToGeneric(program, runtimeInfo); Shader::Maxwell::ConvertLegacyToGeneric(program, runtimeInfo);
auto spirv{Shader::Backend::SPIRV::EmitSPIRV(profile, runtimeInfo, program, bindings)}; auto spirvEmitted{Shader::Backend::SPIRV::EmitSPIRV(profile, runtimeInfo, program, bindings)};
auto spirv{ProcessShaderBinary(true, hash, span<u32>{spirvEmitted}.cast<u8>()).cast<u32>()};
vk::ShaderModuleCreateInfo createInfo{ vk::ShaderModuleCreateInfo createInfo{
.pCode = spirv.data(), .pCode = spirv.data(),
.codeSize = spirv.size() * sizeof(u32), .codeSize = spirv.size_bytes(),
}; };
return (*gpu.vkDevice).createShaderModule(createInfo, nullptr, *gpu.vkDevice.getDispatcher()); return (*gpu.vkDevice).createShaderModule(createInfo, nullptr, *gpu.vkDevice.getDispatcher());

View File

@ -28,7 +28,9 @@ namespace skyline::gpu {
Shader::ObjectPool<Shader::Maxwell::Flow::Block> flowBlockPool; Shader::ObjectPool<Shader::Maxwell::Flow::Block> flowBlockPool;
Shader::ObjectPool<Shader::IR::Inst> instructionPool; Shader::ObjectPool<Shader::IR::Inst> instructionPool;
Shader::ObjectPool<Shader::IR::Block> blockPool; Shader::ObjectPool<Shader::IR::Block> blockPool;
std::unordered_map<u64, std::vector<u8>> shaderReplacements; //!< Map of shader hash -> replacement shader binary, populated at init time and must not be modified after std::unordered_map<u64, std::vector<u8>> guestShaderReplacements; //!< Map of guest shader hash -> replacement guest shader binary, populated at init time and must not be modified after
std::unordered_map<u64, std::vector<u8>> hostShaderReplacements; //!< ^^ same as above but for host
std::mutex poolMutex; std::mutex poolMutex;
std::filesystem::path dumpPath; std::filesystem::path dumpPath;
std::mutex dumpMutex; std::mutex dumpMutex;
@ -42,7 +44,7 @@ namespace skyline::gpu {
* @brief Returns the raw binary of shader replacement for the given hash, if no replacement is found the input binary is returned * @brief Returns the raw binary of shader replacement for the given hash, if no replacement is found the input binary is returned
* @note This will also dump the binary to disk if dumping is enabled * @note This will also dump the binary to disk if dumping is enabled
*/ */
span<u8> ProcessShaderBinary(u64 hash, span<u8> binary); span<u8> ProcessShaderBinary(bool spv, u64 hash, span<u8> binary);
public: public:
using ConstantBufferRead = std::function<u32(u32 index, u32 offset)>; //!< A function which reads a constant buffer at the specified offset and returns the value using ConstantBufferRead = std::function<u32(u32 index, u32 offset)>; //!< A function which reads a constant buffer at the specified offset and returns the value