summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl/gl_shader_cache.cpp
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2021-05-23 09:28:34 +0200
committerameerj <52414509+ameerj@users.noreply.github.com>2021-07-23 03:51:30 +0200
commitd621e96d0de212cc16897eadf71e8a1b2e1eb5dc (patch)
tree8695f2f4dddf2564b63e4574d6616ccb0e79568c /src/video_core/renderer_opengl/gl_shader_cache.cpp
parentspirv: Be aware of NAN unaware drivers (diff)
downloadyuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.tar
yuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.tar.gz
yuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.tar.bz2
yuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.tar.lz
yuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.tar.xz
yuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.tar.zst
yuzu-d621e96d0de212cc16897eadf71e8a1b2e1eb5dc.zip
Diffstat (limited to 'src/video_core/renderer_opengl/gl_shader_cache.cpp')
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp275
1 files changed, 272 insertions, 3 deletions
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index c3e490b40..c9ca1f005 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -16,6 +16,11 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
+#include "shader_recompiler/backend/spirv/emit_spirv.h"
+#include "shader_recompiler/frontend/ir/program.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+#include "shader_recompiler/frontend/maxwell/program.h"
+#include "shader_recompiler/profile.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_type.h"
@@ -25,17 +30,281 @@
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_state_tracker.h"
#include "video_core/shader_cache.h"
+#include "video_core/shader_environment.h"
#include "video_core/shader_notify.h"
namespace OpenGL {
+namespace {
+// FIXME: Move this somewhere else
+const Shader::Profile profile{
+ .supported_spirv = 0x00010000,
+
+ .unified_descriptor_binding = false,
+ .support_descriptor_aliasing = false,
+ .support_int8 = false,
+ .support_int16 = false,
+ .support_vertex_instance_id = true,
+ .support_float_controls = false,
+ .support_separate_denorm_behavior = false,
+ .support_separate_rounding_mode = false,
+ .support_fp16_denorm_preserve = false,
+ .support_fp32_denorm_preserve = false,
+ .support_fp16_denorm_flush = false,
+ .support_fp32_denorm_flush = false,
+ .support_fp16_signed_zero_nan_preserve = false,
+ .support_fp32_signed_zero_nan_preserve = false,
+ .support_fp64_signed_zero_nan_preserve = false,
+ .support_explicit_workgroup_layout = false,
+ .support_vote = true,
+ .support_viewport_index_layer_non_geometry = true,
+ .support_viewport_mask = true,
+ .support_typeless_image_loads = true,
+ .support_demote_to_helper_invocation = false,
+ .warp_size_potentially_larger_than_guest = true,
+ .support_int64_atomics = false,
+ .lower_left_origin_mode = true,
+
+ .has_broken_spirv_clamp = true,
+ .has_broken_unsigned_image_offsets = true,
+ .has_broken_signed_operations = true,
+ .ignore_nan_fp_comparisons = true,
+
+ .generic_input_types = {},
+ .convert_depth_mode = false,
+ .force_early_z = false,
+
+ .tess_primitive = {},
+ .tess_spacing = {},
+ .tess_clockwise = false,
+
+ .input_topology = Shader::InputTopology::Triangles,
+
+ .fixed_state_point_size = std::nullopt,
+
+ .alpha_test_func = Shader::CompareFunction::Always,
+ .alpha_test_reference = 0.0f,
+
+ .y_negate = false,
+
+ .xfb_varyings = {},
+};
+
+using Shader::Backend::SPIRV::EmitSPIRV;
+using Shader::Maxwell::TranslateProgram;
+using VideoCommon::ComputeEnvironment;
+using VideoCommon::GraphicsEnvironment;
+
+template <typename Container>
+auto MakeSpan(Container& container) {
+ return std::span(container.data(), container.size());
+}
+
+void AddShader(GLenum stage, GLuint program, std::span<const u32> code) {
+ OGLShader shader;
+ shader.handle = glCreateShader(stage);
+
+ glShaderBinary(1, &shader.handle, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, code.data(),
+ static_cast<GLsizei>(code.size_bytes()));
+ glSpecializeShader(shader.handle, "main", 0, nullptr, nullptr);
+ glAttachShader(program, shader.handle);
+ if (!Settings::values.renderer_debug) {
+ return;
+ }
+ GLint shader_status{};
+ glGetShaderiv(shader.handle, GL_COMPILE_STATUS, &shader_status);
+ if (shader_status == GL_FALSE) {
+ LOG_ERROR(Render_OpenGL, "Failed to build shader");
+ }
+ GLint log_length{};
+ glGetShaderiv(shader.handle, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length == 0) {
+ return;
+ }
+ std::string log(log_length, 0);
+ glGetShaderInfoLog(shader.handle, log_length, nullptr, log.data());
+ if (shader_status == GL_FALSE) {
+ LOG_ERROR(Render_OpenGL, "{}", log);
+ } else {
+ LOG_WARNING(Render_OpenGL, "{}", log);
+ }
+}
+
+void LinkProgram(GLuint program) {
+ glLinkProgram(program);
+ if (!Settings::values.renderer_debug) {
+ return;
+ }
+ GLint link_status{};
+ glGetProgramiv(program, GL_LINK_STATUS, &link_status);
+
+ GLint log_length{};
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
+ if (log_length == 0) {
+ return;
+ }
+ std::string log(log_length, 0);
+ glGetProgramInfoLog(program, log_length, nullptr, log.data());
+ if (link_status == GL_FALSE) {
+ LOG_ERROR(Render_OpenGL, "{}", log);
+ } else {
+ LOG_WARNING(Render_OpenGL, "{}", log);
+ }
+}
+
+GLenum Stage(size_t stage_index) {
+ switch (stage_index) {
+ case 0:
+ return GL_VERTEX_SHADER;
+ case 1:
+ return GL_TESS_CONTROL_SHADER;
+ case 2:
+ return GL_TESS_EVALUATION_SHADER;
+ case 3:
+ return GL_GEOMETRY_SHADER;
+ case 4:
+ return GL_FRAGMENT_SHADER;
+ }
+ UNREACHABLE_MSG("{}", stage_index);
+ return GL_NONE;
+}
+} // Anonymous namespace
ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindow& emu_window_,
- Tegra::GPU& gpu_, Tegra::Engines::Maxwell3D& maxwell3d_,
+ Tegra::Engines::Maxwell3D& maxwell3d_,
Tegra::Engines::KeplerCompute& kepler_compute_,
- Tegra::MemoryManager& gpu_memory_, const Device& device_)
+ Tegra::MemoryManager& gpu_memory_, const Device& device_,
+ TextureCache& texture_cache_, BufferCache& buffer_cache_,
+ ProgramManager& program_manager_, StateTracker& state_tracker_)
: VideoCommon::ShaderCache{rasterizer_, gpu_memory_, maxwell3d_, kepler_compute_},
- emu_window{emu_window_}, gpu{gpu_}, device{device_} {}
+ emu_window{emu_window_}, device{device_}, texture_cache{texture_cache_},
+ buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{
+ state_tracker_} {}
ShaderCache::~ShaderCache() = default;
+GraphicsProgram* ShaderCache::CurrentGraphicsProgram() {
+ if (!RefreshStages(graphics_key.unique_hashes)) {
+ return nullptr;
+ }
+ const auto& regs{maxwell3d.regs};
+ graphics_key.raw = 0;
+ graphics_key.early_z.Assign(regs.force_early_fragment_tests != 0 ? 1 : 0);
+ graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0
+ ? regs.draw.topology.Value()
+ : Maxwell::PrimitiveTopology{});
+ graphics_key.tessellation_primitive.Assign(regs.tess_mode.prim.Value());
+ graphics_key.tessellation_spacing.Assign(regs.tess_mode.spacing.Value());
+ graphics_key.tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
+
+ const auto [pair, is_new]{graphics_cache.try_emplace(graphics_key)};
+ auto& program{pair->second};
+ if (is_new) {
+ program = CreateGraphicsProgram();
+ }
+ return program.get();
+}
+
+ComputeProgram* ShaderCache::CurrentComputeProgram() {
+ const VideoCommon::ShaderInfo* const shader{ComputeShader()};
+ if (!shader) {
+ return nullptr;
+ }
+ const auto& qmd{kepler_compute.launch_description};
+ const ComputeProgramKey key{
+ .unique_hash = shader->unique_hash,
+ .shared_memory_size = qmd.shared_alloc,
+ .workgroup_size{qmd.block_dim_x, qmd.block_dim_y, qmd.block_dim_z},
+ };
+ const auto [pair, is_new]{compute_cache.try_emplace(key)};
+ auto& pipeline{pair->second};
+ if (!is_new) {
+ return pipeline.get();
+ }
+ pipeline = CreateComputeProgram(key, shader);
+ return pipeline.get();
+}
+
+std::unique_ptr<GraphicsProgram> ShaderCache::CreateGraphicsProgram() {
+ GraphicsEnvironments environments;
+ GetGraphicsEnvironments(environments, graphics_key.unique_hashes);
+
+ main_pools.ReleaseContents();
+ return CreateGraphicsProgram(main_pools, graphics_key, environments.Span(), true);
+}
+
+std::unique_ptr<GraphicsProgram> ShaderCache::CreateGraphicsProgram(
+ ShaderPools& pools, const GraphicsProgramKey& key, std::span<Shader::Environment* const> envs,
+ bool build_in_parallel) {
+ LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
+ size_t env_index{0};
+ std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
+ for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
+ if (key.unique_hashes[index] == 0) {
+ continue;
+ }
+ Shader::Environment& env{*envs[env_index]};
+ ++env_index;
+
+ const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))};
+ Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset);
+ programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg);
+ }
+ std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{};
+
+ OGLProgram gl_program;
+ gl_program.handle = glCreateProgram();
+
+ Shader::Backend::SPIRV::Bindings binding;
+ for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
+ if (key.unique_hashes[index] == 0) {
+ continue;
+ }
+ UNIMPLEMENTED_IF(index == 0);
+
+ Shader::IR::Program& program{programs[index]};
+ const size_t stage_index{index - 1};
+ infos[stage_index] = &program.info;
+
+ const std::vector<u32> code{EmitSPIRV(profile, program, binding)};
+ FILE* file = fopen("D:\\shader.spv", "wb");
+ fwrite(code.data(), 4, code.size(), file);
+ fclose(file);
+ AddShader(Stage(stage_index), gl_program.handle, code);
+ }
+ LinkProgram(gl_program.handle);
+
+ return std::make_unique<GraphicsProgram>(texture_cache, buffer_cache, gpu_memory, maxwell3d,
+ program_manager, state_tracker, std::move(gl_program),
+ infos);
+}
+
+std::unique_ptr<ComputeProgram> ShaderCache::CreateComputeProgram(
+ const ComputeProgramKey& key, const VideoCommon::ShaderInfo* shader) {
+ const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()};
+ const auto& qmd{kepler_compute.launch_description};
+ ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start};
+ env.SetCachedSize(shader->size_bytes);
+
+ main_pools.ReleaseContents();
+ return CreateComputeProgram(main_pools, key, env, true);
+}
+
+std::unique_ptr<ComputeProgram> ShaderCache::CreateComputeProgram(ShaderPools& pools,
+ const ComputeProgramKey& key,
+ Shader::Environment& env,
+ bool build_in_parallel) {
+ LOG_INFO(Render_OpenGL, "0x{:016x}", key.Hash());
+
+ Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()};
+ Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)};
+ Shader::Backend::SPIRV::Bindings binding;
+ const std::vector<u32> code{EmitSPIRV(profile, program, binding)};
+ OGLProgram gl_program;
+ gl_program.handle = glCreateProgram();
+ AddShader(GL_COMPUTE_SHADER, gl_program.handle, code);
+ LinkProgram(gl_program.handle);
+ return std::make_unique<ComputeProgram>(texture_cache, buffer_cache, gpu_memory, kepler_compute,
+ program_manager, std::move(gl_program), program.info);
+}
+
} // namespace OpenGL