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{};
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) {

View File

@ -35,8 +35,10 @@ namespace skyline::gpu {
for (const auto &entry : std::filesystem::directory_iterator{replacementDirPath}) {
if (entry.is_regular_file()) {
// Parse hash from filename
u64 hash{std::stoull(entry.path().filename().string(), nullptr, 16)};
auto it{shaderReplacements.insert({hash, {}})};
auto path{entry.path()};
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
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) {
auto it{shaderReplacements.find(hash)};
if (it != shaderReplacements.end()) {
span<u8> ShaderManager::ProcessShaderBinary(bool spv, u64 hash, span<u8> binary) {
auto &replacementMap{spv ? hostShaderReplacements : guestShaderReplacements};
auto it{replacementMap.find(hash)};
if (it != replacementMap.end()) {
Logger::Info("Replacing shader with hash: 0x{:X}", hash);
return it->second;
}
@ -58,7 +61,7 @@ namespace skyline::gpu {
if (DumpShaders) {
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)) {
std::ofstream file{shaderPath, std::ios::binary};
file.write(reinterpret_cast<const char *>(binary.data()), static_cast<std::streamsize>(binary.size()));
@ -368,7 +371,7 @@ namespace skyline::gpu {
u32 textureConstantBufferIndex,
bool viewportTransformEnabled,
const ConstantBufferRead &constantBufferRead, const GetTextureType &getTextureType) {
binary = ProcessShaderBinary(hash, binary);
binary = ProcessShaderBinary(false, hash, binary);
std::scoped_lock lock{poolMutex};
@ -395,7 +398,7 @@ namespace skyline::gpu {
u32 localMemorySize, u32 sharedMemorySize,
std::array<u32, 3> workgroupDimensions,
const ConstantBufferRead &constantBufferRead, const GetTextureType &getTextureType) {
binary = ProcessShaderBinary(hash, binary);
binary = ProcessShaderBinary(false, hash, binary);
std::scoped_lock lock{poolMutex};
@ -410,11 +413,12 @@ namespace skyline::gpu {
if (program.info.loads.Legacy() || program.info.stores.Legacy())
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{
.pCode = spirv.data(),
.codeSize = spirv.size() * sizeof(u32),
.codeSize = spirv.size_bytes(),
};
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::IR::Inst> instructionPool;
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::filesystem::path dumpPath;
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
* @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:
using ConstantBufferRead = std::function<u32(u32 index, u32 offset)>; //!< A function which reads a constant buffer at the specified offset and returns the value