shader: Handle host exceptions

This commit is contained in:
ReinUsesLisp 2021-05-27 17:51:00 -03:00 committed by ameerj
parent 83db7abae6
commit b7764c3a79
8 changed files with 98 additions and 45 deletions

View File

@ -5,38 +5,62 @@
#pragma once #pragma once
#include <stdexcept> #include <stdexcept>
#include <string>
#include <string_view>
#include <utility> #include <utility>
#include <fmt/format.h> #include <fmt/format.h>
namespace Shader { 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: public:
template <typename... Args> template <typename... Args>
LogicError(const char* message, Args&&... 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: public:
template <typename... Args> template <typename... Args>
RuntimeError(const char* message, Args&&... 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: public:
template <typename... Args> template <typename... Args>
NotImplementedException(const char* message, Args&&... 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: public:
template <typename... Args> template <typename... Args>
InvalidArgument(const char* message, Args&&... 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 } // namespace Shader

View File

@ -10,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
constexpr std::array NAME_TABLE{ constexpr std::array NAME_TABLE{
#define INST(name, cute, encode) #cute, #define INST(name, cute, encode) cute,
#include "maxwell.inc" #include "maxwell.inc"
#undef INST #undef INST
}; };

View File

@ -7,6 +7,7 @@
#include <ranges> #include <ranges>
#include <vector> #include <vector>
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/post_order.h" #include "shader_recompiler/frontend/ir/post_order.h"
#include "shader_recompiler/frontend/maxwell/program.h" #include "shader_recompiler/frontend/maxwell/program.h"

View File

@ -30,16 +30,21 @@ void Translate(Environment& env, IR::Block* block, u32 location_begin, u32 locat
TranslatorVisitor visitor{env, *block}; TranslatorVisitor visitor{env, *block};
for (Location pc = location_begin; pc != location_end; ++pc) { for (Location pc = location_begin; pc != location_end; ++pc) {
const u64 insn{env.ReadInstruction(pc.Offset())}; const u64 insn{env.ReadInstruction(pc.Offset())};
const Opcode opcode{Decode(insn)}; try {
switch (opcode) { const Opcode opcode{Decode(insn)};
switch (opcode) {
#define INST(name, cute, mask) \ #define INST(name, cute, mask) \
case Opcode::name: \ case Opcode::name: \
Invoke<&TranslatorVisitor::name>(visitor, pc, insn); \ Invoke<&TranslatorVisitor::name>(visitor, pc, insn); \
break; break;
#include "shader_recompiler/frontend/maxwell/maxwell.inc" #include "shader_recompiler/frontend/maxwell/maxwell.inc"
#undef OPCODE #undef OPCODE
default: default:
throw LogicError("Invalid opcode {}", opcode); throw LogicError("Invalid opcode {}", opcode);
}
} catch (Exception& exception) {
exception.Prepend(fmt::format("Translate {}: ", Decode(insn)));
throw;
} }
} }
} }

View File

@ -221,7 +221,9 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
SyncState(); SyncState();
GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()}; GraphicsPipeline* const pipeline{shader_cache.CurrentGraphicsPipeline()};
if (!pipeline) {
return;
}
std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
pipeline->Configure(is_indexed); pipeline->Configure(is_indexed);

View File

@ -45,6 +45,7 @@ using VideoCommon::ComputeEnvironment;
using VideoCommon::FileEnvironment; using VideoCommon::FileEnvironment;
using VideoCommon::GenericEnvironment; using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment; using VideoCommon::GraphicsEnvironment;
using VideoCommon::SerializePipeline;
template <typename Container> template <typename Container>
auto MakeSpan(Container& container) { auto MakeSpan(Container& container) {
@ -327,10 +328,11 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading,
workers.QueueWork( workers.QueueWork(
[this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable {
ctx->pools.ReleaseContents(); ctx->pools.ReleaseContents();
auto pipeline{CreateComputePipeline(ctx->pools, key, env, false)}; auto pipeline{CreateComputePipeline(ctx->pools, key, env)};
std::lock_guard lock{state.mutex}; std::lock_guard lock{state.mutex};
compute_cache.emplace(key, std::move(pipeline)); if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
}
++state.built; ++state.built;
if (state.has_loaded) { if (state.has_loaded) {
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); 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); env_ptrs.push_back(&env);
} }
ctx->pools.ReleaseContents(); 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}; std::lock_guard lock{state.mutex};
graphics_cache.emplace(key, std::move(pipeline)); if (pipeline) {
graphics_cache.emplace(key, std::move(pipeline));
}
++state.built; ++state.built;
if (state.has_loaded) { if (state.has_loaded) {
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); callback(VideoCore::LoadCallbackStage::Build, state.built, state.total);
@ -419,8 +422,8 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline() {
GetGraphicsEnvironments(environments, graphics_key.unique_hashes); GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
main_pools.ReleaseContents(); main_pools.ReleaseContents();
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)}; auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span())};
if (shader_cache_filename.empty()) { if (!pipeline || shader_cache_filename.empty()) {
return pipeline; return pipeline;
} }
boost::container::static_vector<const GenericEnvironment*, Maxwell::MaxShaderProgram> env_ptrs; 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]); 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; return pipeline;
} }
std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineKey& key, std::span<Shader::Environment* const> envs, ShaderPools& pools, const GraphicsPipelineKey& key,
bool build_in_parallel) { std::span<Shader::Environment* const> envs) try {
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
size_t env_index{}; size_t env_index{};
u32 total_storage_buffers{}; 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, device, texture_cache, buffer_cache, gpu_memory, maxwell3d, program_manager, state_tracker,
std::move(source_program), std::move(assembly_programs), infos, std::move(source_program), std::move(assembly_programs), infos,
key.xfb_enabled != 0 ? &key.xfb_state : nullptr); 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( std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
@ -502,18 +509,17 @@ std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(
env.SetCachedSize(shader->size_bytes); env.SetCachedSize(shader->size_bytes);
main_pools.ReleaseContents(); main_pools.ReleaseContents();
auto pipeline{CreateComputePipeline(main_pools, key, env, true)}; auto pipeline{CreateComputePipeline(main_pools, key, env)};
if (!shader_cache_filename.empty()) { if (!pipeline || shader_cache_filename.empty()) {
VideoCommon::SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env}, return pipeline;
shader_cache_filename);
} }
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env}, shader_cache_filename);
return pipeline; return pipeline;
} }
std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(ShaderPools& pools, std::unique_ptr<ComputePipeline> ShaderCache::CreateComputePipeline(ShaderPools& pools,
const ComputePipelineKey& key, const ComputePipelineKey& key,
Shader::Environment& env, Shader::Environment& env) try {
bool build_in_parallel) {
LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash()); LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 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, return std::make_unique<ComputePipeline>(device, texture_cache, buffer_cache, gpu_memory,
kepler_compute, program_manager, program.info, kepler_compute, program_manager, program.info,
std::move(source_program), std::move(asm_program)); std::move(source_program), std::move(asm_program));
} catch (Shader::Exception& exception) {
LOG_ERROR(Render_OpenGL, "{}", exception.what());
return nullptr;
} }
} // namespace OpenGL } // namespace OpenGL

View File

@ -65,15 +65,14 @@ private:
std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline( std::unique_ptr<GraphicsPipeline> CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineKey& key, 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, std::unique_ptr<ComputePipeline> CreateComputePipeline(const ComputePipelineKey& key,
const VideoCommon::ShaderInfo* shader); const VideoCommon::ShaderInfo* shader);
std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools, std::unique_ptr<ComputePipeline> CreateComputePipeline(ShaderPools& pools,
const ComputePipelineKey& key, const ComputePipelineKey& key,
Shader::Environment& env, Shader::Environment& env);
bool build_in_parallel);
Core::Frontend::EmuWindow& emu_window; Core::Frontend::EmuWindow& emu_window;
const Device& device; const Device& device;

View File

@ -303,6 +303,9 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
if (is_new) { if (is_new) {
pipeline = CreateGraphicsPipeline(); pipeline = CreateGraphicsPipeline();
} }
if (!pipeline) {
return nullptr;
}
if (current_pipeline) { if (current_pipeline) {
current_pipeline->AddTransition(pipeline.get()); 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 { workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable {
ShaderPools pools; ShaderPools pools;
auto pipeline{CreateComputePipeline(pools, key, env, false)}; auto pipeline{CreateComputePipeline(pools, key, env, false)};
std::lock_guard lock{state.mutex}; std::lock_guard lock{state.mutex};
compute_cache.emplace(key, std::move(pipeline)); if (pipeline) {
compute_cache.emplace(key, std::move(pipeline));
}
++state.built; ++state.built;
if (state.has_loaded) { if (state.has_loaded) {
callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); 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( std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
ShaderPools& pools, const GraphicsPipelineCacheKey& key, 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()); LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
size_t env_index{0}; size_t env_index{0};
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@ -458,6 +462,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
return std::make_unique<GraphicsPipeline>( return std::make_unique<GraphicsPipeline>(
maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device, descriptor_pool, maxwell3d, gpu_memory, scheduler, buffer_cache, texture_cache, device, descriptor_pool,
update_descriptor_queue, thread_worker, render_pass_cache, key, std::move(modules), infos); 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() { std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
@ -466,7 +474,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline() {
main_pools.ReleaseContents(); main_pools.ReleaseContents();
auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)}; auto pipeline{CreateGraphicsPipeline(main_pools, graphics_key, environments.Span(), true)};
if (pipeline_cache_filename.empty()) { if (!pipeline || pipeline_cache_filename.empty()) {
return pipeline; return pipeline;
} }
serialization_thread.QueueWork([this, key = graphics_key, envs = std::move(environments.envs)] { 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]); env_ptrs.push_back(&envs[index]);
} }
} }
VideoCommon::SerializePipeline(key, env_ptrs, pipeline_cache_filename); SerializePipeline(key, env_ptrs, pipeline_cache_filename);
}); });
return pipeline; return pipeline;
} }
@ -491,18 +499,19 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
main_pools.ReleaseContents(); main_pools.ReleaseContents();
auto pipeline{CreateComputePipeline(main_pools, key, env, true)}; auto pipeline{CreateComputePipeline(main_pools, key, env, true)};
if (!pipeline_cache_filename.empty()) { if (!pipeline || pipeline_cache_filename.empty()) {
serialization_thread.QueueWork([this, key, env = std::move(env)] { return pipeline;
VideoCommon::SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env},
pipeline_cache_filename);
});
} }
serialization_thread.QueueWork([this, key, env = std::move(env)] {
SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env},
pipeline_cache_filename);
});
return pipeline; return pipeline;
} }
std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline(
ShaderPools& pools, const ComputePipelineCacheKey& key, Shader::Environment& env, 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()); LOG_INFO(Render_Vulkan, "0x{:016x}", key.Hash());
Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; 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}; Common::ThreadWorker* const thread_worker{build_in_parallel ? &workers : nullptr};
return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue, return std::make_unique<ComputePipeline>(device, descriptor_pool, update_descriptor_queue,
thread_worker, program.info, std::move(spv_module)); thread_worker, program.info, std::move(spv_module));
} catch (const Shader::Exception& exception) {
LOG_ERROR(Render_Vulkan, "{}", exception.what());
return nullptr;
} }
} // namespace Vulkan } // namespace Vulkan