shader: Handle host exceptions
This commit is contained in:
		@@ -5,38 +5,62 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdexcept>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
#include <utility>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
namespace Shader {
 | 
			
		||||
 | 
			
		||||
class LogicError : public std::logic_error {
 | 
			
		||||
class Exception : public std::exception {
 | 
			
		||||
public:
 | 
			
		||||
    explicit Exception(std::string message_) noexcept : message{std::move(message_)} {}
 | 
			
		||||
 | 
			
		||||
    const char* what() const override {
 | 
			
		||||
        return message.c_str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Prepend(std::string_view prepend) {
 | 
			
		||||
        message.insert(0, prepend);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void Append(std::string_view append) {
 | 
			
		||||
        message += append;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string message;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class LogicError : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    LogicError(const char* message, Args&&... args)
 | 
			
		||||
        : std::logic_error{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
        : Exception{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class RuntimeError : public std::runtime_error {
 | 
			
		||||
class RuntimeError : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    RuntimeError(const char* message, Args&&... args)
 | 
			
		||||
        : std::runtime_error{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
        : Exception{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class NotImplementedException : public std::logic_error {
 | 
			
		||||
class NotImplementedException : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    NotImplementedException(const char* message, Args&&... args)
 | 
			
		||||
        : std::logic_error{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
        : Exception{fmt::format(message, std::forward<Args>(args)...)} {
 | 
			
		||||
        Append(" is not implemented");
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class InvalidArgument : public std::invalid_argument {
 | 
			
		||||
class InvalidArgument : public Exception {
 | 
			
		||||
public:
 | 
			
		||||
    template <typename... Args>
 | 
			
		||||
    InvalidArgument(const char* message, Args&&... args)
 | 
			
		||||
        : std::invalid_argument{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
        : Exception{fmt::format(message, std::forward<Args>(args)...)} {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Shader
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@
 | 
			
		||||
namespace Shader::Maxwell {
 | 
			
		||||
namespace {
 | 
			
		||||
constexpr std::array NAME_TABLE{
 | 
			
		||||
#define INST(name, cute, encode) #cute,
 | 
			
		||||
#define INST(name, cute, encode) cute,
 | 
			
		||||
#include "maxwell.inc"
 | 
			
		||||
#undef INST
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@
 | 
			
		||||
#include <ranges>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "shader_recompiler/exception.h"
 | 
			
		||||
#include "shader_recompiler/frontend/ir/basic_block.h"
 | 
			
		||||
#include "shader_recompiler/frontend/ir/post_order.h"
 | 
			
		||||
#include "shader_recompiler/frontend/maxwell/program.h"
 | 
			
		||||
 
 | 
			
		||||
@@ -30,16 +30,21 @@ void Translate(Environment& env, IR::Block* block, u32 location_begin, u32 locat
 | 
			
		||||
    TranslatorVisitor visitor{env, *block};
 | 
			
		||||
    for (Location pc = location_begin; pc != location_end; ++pc) {
 | 
			
		||||
        const u64 insn{env.ReadInstruction(pc.Offset())};
 | 
			
		||||
        const Opcode opcode{Decode(insn)};
 | 
			
		||||
        switch (opcode) {
 | 
			
		||||
        try {
 | 
			
		||||
            const Opcode opcode{Decode(insn)};
 | 
			
		||||
            switch (opcode) {
 | 
			
		||||
#define INST(name, cute, mask)                                                                     \
 | 
			
		||||
    case Opcode::name:                                                                             \
 | 
			
		||||
        Invoke<&TranslatorVisitor::name>(visitor, pc, insn);                                       \
 | 
			
		||||
        break;
 | 
			
		||||
#include "shader_recompiler/frontend/maxwell/maxwell.inc"
 | 
			
		||||
#undef OPCODE
 | 
			
		||||
        default:
 | 
			
		||||
            throw LogicError("Invalid opcode {}", opcode);
 | 
			
		||||
            default:
 | 
			
		||||
                throw LogicError("Invalid opcode {}", opcode);
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception& exception) {
 | 
			
		||||
            exception.Prepend(fmt::format("Translate {}: ", Decode(insn)));
 | 
			
		||||
            throw;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -221,7 +221,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
 | 
			
		||||
    SyncState();
 | 
			
		||||
 | 
			
		||||
    GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
 | 
			
		||||
 | 
			
		||||
    if (!pipeline) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
 | 
			
		||||
    pipeline->Configure(is_indexed);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,7 @@ using VideoCommon::ComputeEnvironment;
 | 
			
		||||
using VideoCommon::FileEnvironment;
 | 
			
		||||
using VideoCommon::GenericEnvironment;
 | 
			
		||||
using VideoCommon::GraphicsEnvironment;
 | 
			
		||||
using VideoCommon::SerializePipeline;
 | 
			
		||||
 | 
			
		||||
template <typename Container>
 | 
			
		||||
auto MakeSpan(Container& container) {
 | 
			
		||||
@@ -327,10 +328,11 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
 | 
			
		||||
        workers.QueueWork(
 | 
			
		||||
            [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
 | 
			
		||||
                ctx->pools.ReleaseContents();
 | 
			
		||||
                auto pipeline{CreateComputePipeline(ctx->pools, key, env, false)};
 | 
			
		||||
 | 
			
		||||
                auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
 | 
			
		||||
                std::lock_guard lock{state.mutex};
 | 
			
		||||
                compute_cache.emplace(key, std::move(pipeline));
 | 
			
		||||
                if (pipeline) {
 | 
			
		||||
                    compute_cache.emplace(key, std::move(pipeline));
 | 
			
		||||
                }
 | 
			
		||||
                ++state.built;
 | 
			
		||||
                if (state.has_loaded) {
 | 
			
		||||
                    callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
 | 
			
		||||
@@ -348,10 +350,11 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
 | 
			
		||||
                    env_ptrs.push_back(&env);
 | 
			
		||||
                }
 | 
			
		||||
                ctx->pools.ReleaseContents();
 | 
			
		||||
                auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)};
 | 
			
		||||
 | 
			
		||||
                auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs))};
 | 
			
		||||
                std::lock_guard lock{state.mutex};
 | 
			
		||||
                graphics_cache.emplace(key, std::move(pipeline));
 | 
			
		||||
                if (pipeline) {
 | 
			
		||||
                    graphics_cache.emplace(key, std::move(pipeline));
 | 
			
		||||
                }
 | 
			
		||||
                ++state.built;
 | 
			
		||||
                if (state.has_loaded) {
 | 
			
		||||
                    callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
 | 
			
		||||
@@ -419,8 +422,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
 | 
			
		||||
    GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
 | 
			
		||||
 | 
			
		||||
    main_pools.ReleaseContents();
 | 
			
		||||
    auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)};
 | 
			
		||||
    if (shader_cache_filename.empty()) {
 | 
			
		||||
    auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span())};
 | 
			
		||||
    if (!pipeline || shader_cache_filename.empty()) {
 | 
			
		||||
        return pipeline;
 | 
			
		||||
    }
 | 
			
		||||
    boost::container::static_vector<const GenericEnvironment*, Maxwell::MaxShaderProgram> env_ptrs;
 | 
			
		||||
@@ -429,13 +432,13 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
 | 
			
		||||
            env_ptrs.push_back(&environments.envs[index]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    VideoCommon::SerializePipeline(graphics_key, env_ptrs, shader_cache_filename);
 | 
			
		||||
    SerializePipeline(graphics_key, env_ptrs, shader_cache_filename);
 | 
			
		||||
    return pipeline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
 | 
			
		||||
    ShaderPools& pools, const GraphicsPipelineKey& key, std::span<Shader::Environment* const> envs,
 | 
			
		||||
    bool build_in_parallel) {
 | 
			
		||||
    ShaderPools& pools, const GraphicsPipelineKey& key,
 | 
			
		||||
    std::span<Shader::Environment* const> envs) try {
 | 
			
		||||
    LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
 | 
			
		||||
    size_t env_index{};
 | 
			
		||||
    u32 total_storage_buffers{};
 | 
			
		||||
@@ -492,6 +495,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
 | 
			
		||||
        device, texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker,
 | 
			
		||||
        std::move(source_program), std::move(assembly_programs), infos,
 | 
			
		||||
        key.xfb_enabled != 0 ? &key.xfb_state : nullptr);
 | 
			
		||||
 | 
			
		||||
} catch (Shader::Exception& exception) {
 | 
			
		||||
    LOG_ERROR(Render_OpenGL, "{}", exception.what());
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
 | 
			
		||||
@@ -502,18 +509,17 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
 | 
			
		||||
    env.SetCachedSize(shader->size_bytes);
 | 
			
		||||
 | 
			
		||||
    main_pools.ReleaseContents();
 | 
			
		||||
    auto pipeline{CreateComputePipeline(main_pools, key, env, true)};
 | 
			
		||||
    if (!shader_cache_filename.empty()) {
 | 
			
		||||
        VideoCommon::SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env},
 | 
			
		||||
                                       shader_cache_filename);
 | 
			
		||||
    auto pipeline{CreateComputePipeline(main_pools, key, env)};
 | 
			
		||||
    if (!pipeline || shader_cache_filename.empty()) {
 | 
			
		||||
        return pipeline;
 | 
			
		||||
    }
 | 
			
		||||
    SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env}, shader_cache_filename);
 | 
			
		||||
    return pipeline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(ShaderPools& pools,
 | 
			
		||||
                                                                    const ComputePipelineKey& key,
 | 
			
		||||
                                                                    Shader::Environment& env,
 | 
			
		||||
                                                                    bool build_in_parallel) {
 | 
			
		||||
                                                                    Shader::Environment& env) try {
 | 
			
		||||
    LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
 | 
			
		||||
 | 
			
		||||
    Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
 | 
			
		||||
@@ -540,6 +546,9 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(ShaderPools&
 | 
			
		||||
    return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, gpu_memory,
 | 
			
		||||
                                             kepler_compute, program_manager, program.info,
 | 
			
		||||
                                             std::move(source_program), std::move(asm_program));
 | 
			
		||||
} catch (Shader::Exception& exception) {
 | 
			
		||||
    LOG_ERROR(Render_OpenGL, "{}", exception.what());
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace OpenGL
 | 
			
		||||
 
 | 
			
		||||
@@ -65,15 +65,14 @@ private:
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
 | 
			
		||||
        ShaderPools& pools, const GraphicsPipelineKey& key,
 | 
			
		||||
        std::span<Shader::Environment* const> envs, bool build_in_parallel);
 | 
			
		||||
        std::span<Shader::Environment* const> envs);
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
 | 
			
		||||
                                                           const VideoCommon::ShaderInfo* shader);
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
 | 
			
		||||
                                                           const ComputePipelineKey& key,
 | 
			
		||||
                                                           Shader::Environment& env,
 | 
			
		||||
                                                           bool build_in_parallel);
 | 
			
		||||
                                                           Shader::Environment& env);
 | 
			
		||||
 | 
			
		||||
    Core::Frontend::EmuWindow& emu_window;
 | 
			
		||||
    const Device& device;
 | 
			
		||||
 
 | 
			
		||||
@@ -303,6 +303,9 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
 | 
			
		||||
    if (is_new) {
 | 
			
		||||
        pipeline = CreateGraphicsPipeline();
 | 
			
		||||
    }
 | 
			
		||||
    if (!pipeline) {
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
    if (current_pipeline) {
 | 
			
		||||
        current_pipeline->AddTransition(pipeline.get());
 | 
			
		||||
    }
 | 
			
		||||
@@ -362,9 +365,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
 | 
			
		||||
        workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
 | 
			
		||||
            ShaderPools pools;
 | 
			
		||||
            auto pipeline{CreateComputePipeline(pools, key, env, false)};
 | 
			
		||||
 | 
			
		||||
            std::lock_guard lock{state.mutex};
 | 
			
		||||
            compute_cache.emplace(key, std::move(pipeline));
 | 
			
		||||
            if (pipeline) {
 | 
			
		||||
                compute_cache.emplace(key, std::move(pipeline));
 | 
			
		||||
            }
 | 
			
		||||
            ++state.built;
 | 
			
		||||
            if (state.has_loaded) {
 | 
			
		||||
                callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
 | 
			
		||||
@@ -405,7 +409,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
 | 
			
		||||
    ShaderPools& pools, const GraphicsPipelineCacheKey& key,
 | 
			
		||||
    std::span<Shader::Environment* const> envs, bool build_in_parallel) {
 | 
			
		||||
    std::span<Shader::Environment* const> envs, bool build_in_parallel) try {
 | 
			
		||||
    LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
 | 
			
		||||
    size_t env_index{0};
 | 
			
		||||
    std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
 | 
			
		||||
@@ -458,6 +462,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
 | 
			
		||||
    return std::make_unique<GraphicsPipeline>(
 | 
			
		||||
        maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device, descriptor_pool,
 | 
			
		||||
        update_descriptor_queue, thread_worker, render_pass_cache, key, std::move(modules), infos);
 | 
			
		||||
 | 
			
		||||
} catch (const Shader::Exception& exception) {
 | 
			
		||||
    LOG_ERROR(Render_Vulkan, "{}", exception.what());
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
 | 
			
		||||
@@ -466,7 +474,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
 | 
			
		||||
 | 
			
		||||
    main_pools.ReleaseContents();
 | 
			
		||||
    auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)};
 | 
			
		||||
    if (pipeline_cache_filename.empty()) {
 | 
			
		||||
    if (!pipeline || pipeline_cache_filename.empty()) {
 | 
			
		||||
        return pipeline;
 | 
			
		||||
    }
 | 
			
		||||
    serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] {
 | 
			
		||||
@@ -477,7 +485,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
 | 
			
		||||
                env_ptrs.push_back(&envs[index]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        VideoCommon::SerializePipeline(key, env_ptrs, pipeline_cache_filename);
 | 
			
		||||
        SerializePipeline(key, env_ptrs, pipeline_cache_filename);
 | 
			
		||||
    });
 | 
			
		||||
    return pipeline;
 | 
			
		||||
}
 | 
			
		||||
@@ -491,18 +499,19 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
 | 
			
		||||
 | 
			
		||||
    main_pools.ReleaseContents();
 | 
			
		||||
    auto pipeline{CreateComputePipeline(main_pools, key, env, true)};
 | 
			
		||||
    if (!pipeline_cache_filename.empty()) {
 | 
			
		||||
        serialization_thread.QueueWork([this, key, env = std::move(env)] {
 | 
			
		||||
            VideoCommon::SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env},
 | 
			
		||||
                                           pipeline_cache_filename);
 | 
			
		||||
        });
 | 
			
		||||
    if (!pipeline || pipeline_cache_filename.empty()) {
 | 
			
		||||
        return pipeline;
 | 
			
		||||
    }
 | 
			
		||||
    serialization_thread.QueueWork([this, key, env = std::move(env)] {
 | 
			
		||||
        SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env},
 | 
			
		||||
                          pipeline_cache_filename);
 | 
			
		||||
    });
 | 
			
		||||
    return pipeline;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
 | 
			
		||||
    ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env,
 | 
			
		||||
    bool build_in_parallel) {
 | 
			
		||||
    bool build_in_parallel) try {
 | 
			
		||||
    LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
 | 
			
		||||
 | 
			
		||||
    Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
 | 
			
		||||
@@ -517,6 +526,10 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
 | 
			
		||||
    Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
 | 
			
		||||
    return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
 | 
			
		||||
                                             thread_worker, program.info, std::move(spv_module));
 | 
			
		||||
 | 
			
		||||
} catch (const Shader::Exception& exception) {
 | 
			
		||||
    LOG_ERROR(Render_Vulkan, "{}", exception.what());
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user