From f3bbcb31c9ba29dfc170bdbe9c3c1f2e96e07203 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Sat, 4 Nov 2023 01:59:25 +0200 Subject: [PATCH] renderer_opengl: Remove non-separable path --- .../renderer_opengl/gl_rasterizer.cpp | 4 +- .../renderer_opengl/gl_resource_manager.cpp | 72 ++-- .../renderer_opengl/gl_resource_manager.h | 2 +- .../renderer_opengl/gl_shader_disk_cache.cpp | 49 +-- .../renderer_opengl/gl_shader_disk_cache.h | 15 +- .../renderer_opengl/gl_shader_manager.cpp | 379 ++++++------------ .../renderer_opengl/gl_shader_manager.h | 9 +- .../renderer_opengl/gl_shader_util.cpp | 22 +- .../renderer_opengl/gl_shader_util.h | 7 +- src/video_core/renderer_opengl/gl_state.cpp | 1 + 10 files changed, 207 insertions(+), 353 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index a5bed59ef..8f66decdf 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -78,8 +78,8 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, VideoCore::CustomTexManager& custom_tex_manager, VideoCore::RendererBase& renderer, Driver& driver_) : VideoCore::RasterizerAccelerated{memory}, driver{driver_}, - shader_manager{renderer.GetRenderWindow(), driver, !driver.IsOpenGLES()}, - runtime{driver, renderer}, res_cache{memory, custom_tex_manager, runtime, regs, renderer}, + shader_manager{renderer.GetRenderWindow(), driver}, runtime{driver, renderer}, + res_cache{memory, custom_tex_manager, runtime, regs, renderer}, texture_buffer_size{TextureBufferSize()}, vertex_buffer{driver, GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE}, uniform_buffer{driver, GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE}, diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp index 87a53ccdf..cd4c0e76c 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.cpp +++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp @@ -2,14 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "common/microprofile.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/gl_state.h" -MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192)); -MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192)); - namespace OpenGL { void OGLRenderbuffer::Create() { @@ -17,7 +13,6 @@ void OGLRenderbuffer::Create() { return; } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenRenderbuffers(1, &handle); } @@ -26,7 +21,6 @@ void OGLRenderbuffer::Release() { return; } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteRenderbuffers(1, &handle); OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply(); handle = 0; @@ -37,7 +31,6 @@ void OGLTexture::Create() { return; } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenTextures(1, &handle); } @@ -46,7 +39,6 @@ void OGLTexture::Release() { return; } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteTextures(1, &handle); OpenGLState::GetCurState().ResetTexture(handle).Apply(); handle = 0; @@ -88,7 +80,6 @@ void OGLSampler::Create() { return; } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenSamplers(1, &handle); } @@ -97,37 +88,41 @@ void OGLSampler::Release() { return; } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteSamplers(1, &handle); OpenGLState::GetCurState().ResetSampler(handle).Apply(); handle = 0; } void OGLShader::Create(std::string_view source, GLenum type) { - if (handle != 0) + if (handle != 0) { return; - if (source.empty()) + } + if (source.empty()) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); handle = LoadShader(source, type); } void OGLShader::Release() { - if (handle == 0) + if (handle == 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteShader(handle); handle = 0; } -void OGLProgram::Create(bool separable_program, std::span shaders) { - if (handle != 0) +void OGLProgram::Create(std::string_view source, GLenum type) { + if (handle != 0) { return; + } + if (source.empty()) { + return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); - handle = LoadProgram(separable_program, shaders); + const std::array sources{GetPreamble().data(), source.data()}; + handle = glCreateShaderProgramv(type, 2, sources.data()); } void OGLProgram::Create(std::string_view vert_shader, std::string_view frag_shader) { @@ -135,88 +130,87 @@ void OGLProgram::Create(std::string_view vert_shader, std::string_view frag_shad vert.Create(vert_shader, GL_VERTEX_SHADER); frag.Create(frag_shader, GL_FRAGMENT_SHADER); - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); const std::array shaders{vert.handle, frag.handle}; - Create(false, shaders); + handle = LoadProgram(shaders); } void OGLProgram::Release() { - if (handle == 0) + if (handle == 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteProgram(handle); OpenGLState::GetCurState().ResetProgram(handle).Apply(); handle = 0; } void OGLPipeline::Create() { - if (handle != 0) + if (handle != 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenProgramPipelines(1, &handle); } void OGLPipeline::Release() { - if (handle == 0) + if (handle == 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteProgramPipelines(1, &handle); OpenGLState::GetCurState().ResetPipeline(handle).Apply(); handle = 0; } void OGLBuffer::Create() { - if (handle != 0) + if (handle != 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenBuffers(1, &handle); } void OGLBuffer::Release() { - if (handle == 0) + if (handle == 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteBuffers(1, &handle); OpenGLState::GetCurState().ResetBuffer(handle).Apply(); handle = 0; } void OGLVertexArray::Create() { - if (handle != 0) + if (handle != 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenVertexArrays(1, &handle); } void OGLVertexArray::Release() { - if (handle == 0) + if (handle == 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteVertexArrays(1, &handle); OpenGLState::GetCurState().ResetVertexArray(handle).Apply(); handle = 0; } void OGLFramebuffer::Create() { - if (handle != 0) + if (handle != 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceCreation); glGenFramebuffers(1, &handle); } void OGLFramebuffer::Release() { - if (handle == 0) + if (handle == 0) { return; + } - MICROPROFILE_SCOPE(OpenGL_ResourceDeletion); glDeleteFramebuffers(1, &handle); OpenGLState::GetCurState().ResetFramebuffer(handle).Apply(); handle = 0; diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 2d168a85a..553ef1f03 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -130,7 +130,7 @@ public: } /// Creates a new program from given shader objects - void Create(bool separable_program, std::span shaders); + void Create(std::string_view source, GLenum type); /// Creates a new program from given shader soruce code void Create(std::string_view vert_shader, std::string_view frag_shader); diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index 9f2ed2bab..26cc02db5 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -103,10 +103,8 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const { return true; } -ShaderDiskCache::ShaderDiskCache(bool separable) - : separable{separable}, transferable_file(AppendTransferableFile()), - // seperable shaders use the virtual precompile file, that already has a header. - precompiled_file(AppendPrecompiledFile(!separable)) {} +ShaderDiskCache::ShaderDiskCache() + : transferable_file(AppendTransferableFile()), precompiled_file(AppendPrecompiledFile()) {} std::optional> ShaderDiskCache::LoadTransferable() { const bool has_title_id = GetProgramID() != 0; @@ -177,7 +175,7 @@ std::optional> ShaderDiskCache::LoadTransferable } std::pair, ShaderDumpsMap> -ShaderDiskCache::LoadPrecompiled(bool compressed) { +ShaderDiskCache::LoadPrecompiled() { if (!IsUsable()) return {}; @@ -187,7 +185,7 @@ ShaderDiskCache::LoadPrecompiled(bool compressed) { return {}; } - const auto result = LoadPrecompiledFile(precompiled_file, compressed); + const auto result = LoadPrecompiledFile(precompiled_file); if (!result) { LOG_INFO(Render_OpenGL, "Failed to load precompiled cache for game with title id={} - removing", @@ -199,22 +197,16 @@ ShaderDiskCache::LoadPrecompiled(bool compressed) { } std::optional, ShaderDumpsMap>> -ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file, bool compressed) { +ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file) { // Read compressed file from disk and decompress to virtual precompiled cache file std::vector precompiled_file(file.GetSize()); file.ReadBytes(precompiled_file.data(), precompiled_file.size()); - if (compressed) { - const std::vector decompressed = - Common::Compression::DecompressDataZSTD(precompiled_file); - if (decompressed.empty()) { - LOG_ERROR(Render_OpenGL, "Could not decompress precompiled shader cache."); - return std::nullopt; - } - SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); - } else { - SaveArrayToPrecompiled(precompiled_file.data(), precompiled_file.size()); + const std::vector decompressed = Common::Compression::DecompressDataZSTD(precompiled_file); + if (decompressed.empty()) { + LOG_ERROR(Render_OpenGL, "Could not decompress precompiled shader cache."); + return std::nullopt; } - + SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); decompressed_precompiled_cache_offset = 0; ShaderCacheVersionHash file_hash{}; @@ -353,7 +345,7 @@ void ShaderDiskCache::InvalidatePrecompiled() { if (!FileUtil::Delete(GetPrecompiledPath())) { LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); } - precompiled_file = AppendPrecompiledFile(!separable); + precompiled_file = AppendPrecompiledFile(); } void ShaderDiskCache::SaveRaw(const ShaderDiskCacheRaw& entry) { @@ -471,12 +463,11 @@ FileUtil::IOFile ShaderDiskCache::AppendTransferableFile() { return file; } -FileUtil::IOFile ShaderDiskCache::AppendPrecompiledFile(bool write_header) { +FileUtil::IOFile ShaderDiskCache::AppendPrecompiledFile() { if (!EnsureDirectories()) return {}; const auto precompiled_path{GetPrecompiledPath()}; - const bool existed = FileUtil::Exists(precompiled_path); FileUtil::IOFile file(precompiled_path, "ab+"); if (!file.IsOpen()) { @@ -484,15 +475,6 @@ FileUtil::IOFile ShaderDiskCache::AppendPrecompiledFile(bool write_header) { return {}; } - // If the file didn't exist, write its version - if (write_header && (!existed || file.GetSize() == 0)) { - const auto hash{GetShaderCacheVersionHash()}; - if (file.WriteArray(hash.data(), hash.size()) != hash.size()) { - LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}", - precompiled_path); - return {}; - } - } return file; } @@ -516,7 +498,7 @@ void ShaderDiskCache::SaveVirtualPrecompiledFile() { if (!FileUtil::Delete(GetPrecompiledPath())) { LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); } - precompiled_file = AppendPrecompiledFile(!separable); + precompiled_file = AppendPrecompiledFile(); if (precompiled_file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) { LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}", @@ -558,10 +540,7 @@ std::string ShaderDiskCache::GetPrecompiledDir() const { } std::string ShaderDiskCache::GetPrecompiledShaderDir() const { - if (separable) { - return GetPrecompiledDir() + DIR_SEP "separable"; - } - return GetPrecompiledDir() + DIR_SEP "conventional"; + return GetPrecompiledDir() + DIR_SEP "separable"; } std::string ShaderDiskCache::GetBaseDir() const { diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index 8c285b032..03450af58 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h @@ -16,11 +16,8 @@ #include -#include "common/assert.h" -#include "common/common_types.h" #include "common/file_util.h" -#include "video_core/regs.h" -#include "video_core/shader/generator/glsl_shader_gen.h" +#include "video_core/shader/generator/shader_gen.h" namespace Core { class System; @@ -90,14 +87,14 @@ struct ShaderDiskCacheDump { class ShaderDiskCache { public: - explicit ShaderDiskCache(bool separable); + explicit ShaderDiskCache(); ~ShaderDiskCache() = default; /// Loads transferable cache. If file has a old version or on failure, it deletes the file. std::optional> LoadTransferable(); /// Loads current game's precompiled cache. Invalidates on failure. - std::pair LoadPrecompiled(bool compressed); + std::pair LoadPrecompiled(); /// Removes the transferable (and precompiled) cache file. void InvalidateAll(); @@ -123,7 +120,7 @@ public: private: /// Loads the transferable cache. Returns empty on failure. std::optional> LoadPrecompiledFile( - FileUtil::IOFile& file, bool compressed); + FileUtil::IOFile& file); /// Loads a decompiled cache entry from m_precompiled_cache_virtual_file. Returns empty on /// failure. @@ -143,7 +140,7 @@ private: FileUtil::IOFile AppendTransferableFile(); /// Opens current game's precompiled file and write it's header if it doesn't exist - FileUtil::IOFile AppendPrecompiledFile(bool write_header); + FileUtil::IOFile AppendPrecompiledFile(); /// Save precompiled header to precompiled_cache_in_memory void SavePrecompiledHeaderToVirtualPrecompiledCache(); @@ -219,8 +216,6 @@ private: // The cache has been loaded at boot bool tried_to_load{}; - bool separable{}; - u64 program_id{}; std::string title_id; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index e171820bf..6ffbefd60 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp @@ -14,7 +14,7 @@ #include "video_core/renderer_opengl/gl_shader_disk_cache.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_state.h" -#include "video_core/shader/generator/shader_uniforms.h" +#include "video_core/shader/generator/glsl_shader_gen.h" #include "video_core/video_core.h" using namespace Pica::Shader::Generator; @@ -35,19 +35,16 @@ static u64 GetUniqueIdentifier(const Pica::Regs& regs, const ProgramCode& code) } static OGLProgram GeneratePrecompiledProgram(const ShaderDiskCacheDump& dump, - const std::set& supported_formats, - bool separable) { + const std::set& supported_formats) { if (supported_formats.find(dump.binary_format) == supported_formats.end()) { LOG_INFO(Render_OpenGL, "Precompiled cache entry with unsupported format - removing"); return {}; } - auto shader = OGLProgram(); + OGLProgram shader{}; shader.handle = glCreateProgram(); - if (separable) { - glProgramParameteri(shader.handle, GL_PROGRAM_SEPARABLE, GL_TRUE); - } + glProgramParameteri(shader.handle, GL_PROGRAM_SEPARABLE, GL_TRUE); glProgramBinary(shader.handle, dump.binary_format, dump.binary.data(), static_cast(dump.binary.size())); @@ -90,91 +87,42 @@ static std::tuple BuildVSConfigFromRaw( setup}; } -/** - * An object representing a shader program staging. It can be either a shader object or a program - * object, depending on whether separable program is used. - */ -class OGLShaderStage { -public: - explicit OGLShaderStage(bool separable) { - if (separable) { - shader_or_program = OGLProgram(); - } else { - shader_or_program = OGLShader(); - } - } - - void Create(const char* source, GLenum type) { - if (shader_or_program.index() == 0) { - std::get(shader_or_program).Create(source, type); - } else { - OGLShader shader; - shader.Create(source, type); - OGLProgram& program = std::get(shader_or_program); - program.Create(true, std::array{shader.handle}); - } - } - - GLuint GetHandle() const { - if (shader_or_program.index() == 0) { - return std::get(shader_or_program).handle; - } else { - return std::get(shader_or_program).handle; - } - } - - void Inject(OGLProgram&& program) { - shader_or_program = std::move(program); - } - -private: - std::variant shader_or_program; -}; - class TrivialVertexShader { public: - explicit TrivialVertexShader(const Driver& driver, bool separable) : program(separable) { - const auto code = - GLSL::GenerateTrivialVertexShader(driver.HasClipCullDistance(), separable); - program.Create(code.c_str(), GL_VERTEX_SHADER); + explicit TrivialVertexShader(const Driver& driver) { + const auto code = GLSL::GenerateTrivialVertexShader(driver.HasClipCullDistance(), true); + program.Create(code, GL_VERTEX_SHADER); } GLuint Get() const { - return program.GetHandle(); + return program.handle; } private: - OGLShaderStage program; + OGLProgram program; }; -template +template class ShaderCache { public: - explicit ShaderCache(bool separable) : separable(separable) {} + explicit ShaderCache() = default; + std::tuple> Get(const KeyConfigType& config) { - auto [iter, new_shader] = shaders.emplace(config, OGLShaderStage{separable}); - OGLShaderStage& cached_shader = iter->second; + auto [iter, new_shader] = shaders.try_emplace(config); + OGLProgram& cached_shader = iter->second; std::optional result{}; if (new_shader) { - result = CodeGenerator(config, separable); - cached_shader.Create(result->c_str(), ShaderType); + result = CodeGenerator(config, true); + cached_shader.Create(result.value(), ShaderType); } - return {cached_shader.GetHandle(), std::move(result)}; + return {cached_shader.handle, std::move(result)}; } - void Inject(const KeyConfigType& key, OGLProgram&& program) { - OGLShaderStage stage{separable}; - stage.Inject(std::move(program)); - shaders.emplace(key, std::move(stage)); - } - - void Inject(const KeyConfigType& key, OGLShaderStage&& stage) { + void Inject(const KeyConfigType& key, OGLProgram&& stage) { shaders.emplace(key, std::move(stage)); } private: - bool separable; - std::unordered_map shaders; + std::unordered_map shaders; }; // This is a cache designed for shaders translated from PICA shaders. The first cache matches the @@ -182,59 +130,48 @@ private: // GLSL code. The configuration is like this because there might be leftover code in the PICA shader // program buffer from the previous shader, which is hashed into the config, resulting several // different config values from the same shader program. -template +template class ShaderDoubleCache { public: - explicit ShaderDoubleCache(bool separable) : separable(separable) {} + explicit ShaderDoubleCache() = default; + std::tuple> Get(const KeyConfigType& key, const Pica::Shader::ShaderSetup& setup) { std::optional result{}; auto map_it = shader_map.find(key); if (map_it == shader_map.end()) { - auto program = CodeGenerator(setup, key, separable); + const auto program = CodeGenerator(setup, key, separable); if (program.empty()) { shader_map[key] = nullptr; return {0, std::nullopt}; } - auto [iter, new_shader] = shader_cache.emplace(program, OGLShaderStage{separable}); - OGLShaderStage& cached_shader = iter->second; + const auto [iter, new_shader] = shader_cache.try_emplace(program); + OGLProgram& cached_shader = iter->second; if (new_shader) { result = program; - cached_shader.Create(program.c_str(), ShaderType); + cached_shader.Create(program, ShaderType); } shader_map[key] = &cached_shader; - return {cached_shader.GetHandle(), std::move(result)}; + return {cached_shader.handle, std::move(result)}; } if (map_it->second == nullptr) { return {0, std::nullopt}; } - return {map_it->second->GetHandle(), std::nullopt}; + return {map_it->second->handle, std::nullopt}; } void Inject(const KeyConfigType& key, std::string decomp, OGLProgram&& program) { - OGLShaderStage stage{separable}; - stage.Inject(std::move(program)); - const auto iter = shader_cache.emplace(std::move(decomp), std::move(stage)).first; - OGLShaderStage& cached_shader = iter->second; - shader_map.insert_or_assign(key, &cached_shader); - } - - void Inject(const KeyConfigType& key, std::string decomp, OGLShaderStage&& stage) { - const auto iter = shader_cache.emplace(std::move(decomp), std::move(stage)).first; - OGLShaderStage& cached_shader = iter->second; - shader_map.insert_or_assign(key, &cached_shader); + const auto iter = shader_cache.emplace(std::move(decomp), std::move(program)).first; + shader_map.insert_or_assign(key, &iter->second); } private: bool separable; - std::unordered_map shader_map; - std::unordered_map shader_cache; + std::unordered_map shader_map; + std::unordered_map shader_cache; }; using ProgrammableVertexShaders = @@ -248,12 +185,8 @@ using FragmentShaders = class ShaderProgramManager::Impl { public: - explicit Impl(const Driver& driver, bool separable) - : separable(separable), programmable_vertex_shaders(separable), - trivial_vertex_shader(driver, separable), fixed_geometry_shaders(separable), - fragment_shaders(separable), disk_cache(separable) { - if (separable) - pipeline.Create(); + explicit Impl(const Driver& driver) : trivial_vertex_shader(driver) { + pipeline.Create(); } struct ShaderTuple { @@ -282,8 +215,6 @@ public: static_assert(offsetof(ShaderTuple, fs_hash) == sizeof(std::size_t) * 2, "ShaderTuple layout changed!"); - bool separable; - ShaderTuple current; ProgrammableVertexShaders programmable_vertex_shaders; @@ -297,11 +228,10 @@ public: ShaderDiskCache disk_cache; }; -ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, const Driver& driver_, - bool separable) +ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, const Driver& driver_) : emu_window{emu_window_}, driver{driver_}, strict_context_required{emu_window.StrictContextRequired()}, impl{std::make_unique( - driver_, separable)} {} + driver_)} {} ShaderProgramManager::~ShaderProgramManager() = default; @@ -363,30 +293,18 @@ void ShaderProgramManager::UseFragmentShader(const Pica::Regs& regs, bool use_no } void ShaderProgramManager::ApplyTo(OpenGLState& state) { - if (impl->separable) { - if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) { - glUseProgramStages( - impl->pipeline.handle, - GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 0); - } - - glUseProgramStages(impl->pipeline.handle, GL_VERTEX_SHADER_BIT, impl->current.vs); - glUseProgramStages(impl->pipeline.handle, GL_GEOMETRY_SHADER_BIT, impl->current.gs); - glUseProgramStages(impl->pipeline.handle, GL_FRAGMENT_SHADER_BIT, impl->current.fs); - state.draw.shader_program = 0; - state.draw.program_pipeline = impl->pipeline.handle; - } else { - const u64 unique_identifier = impl->current.GetConfigHash(); - OGLProgram& cached_program = impl->program_cache[unique_identifier]; - if (cached_program.handle == 0) { - cached_program.Create(false, - std::array{impl->current.vs, impl->current.gs, impl->current.fs}); - auto& disk_cache = impl->disk_cache; - disk_cache.SaveDumpToFile(unique_identifier, cached_program.handle, - VideoCore::g_hw_shader_accurate_mul); - } - state.draw.shader_program = cached_program.handle; + if (driver.HasBug(DriverBug::ShaderStageChangeFreeze)) { + glUseProgramStages(impl->pipeline.handle, + GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, + 0); } + + glUseProgramStages(impl->pipeline.handle, GL_VERTEX_SHADER_BIT, impl->current.vs); + glUseProgramStages(impl->pipeline.handle, GL_GEOMETRY_SHADER_BIT, impl->current.gs); + glUseProgramStages(impl->pipeline.handle, GL_FRAGMENT_SHADER_BIT, impl->current.fs); + + state.draw.shader_program = 0; + state.draw.program_pipeline = impl->pipeline.handle; } void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, @@ -400,7 +318,7 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, // Load uncompressed precompiled file for non-separable shaders. // Precompiled file for separable shaders is compressed. - auto [decompiled, dumps] = disk_cache.LoadPrecompiled(impl->separable); + auto [decompiled, dumps] = disk_cache.LoadPrecompiled(); if (stop_loading) { return; @@ -418,117 +336,81 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, callback(VideoCore::LoadCallbackStage::Decompile, 0, raws.size()); } std::vector load_raws_index; + // Loads both decompiled and precompiled shaders from the cache. If either one is missing for - const auto LoadPrecompiledShader = [&](std::size_t begin, std::size_t end, - std::span raw_cache, - const ShaderDecompiledMap& decompiled_map, - const ShaderDumpsMap& dump_map) { - for (std::size_t i = begin; i < end; ++i) { - if (stop_loading || compilation_failed) { - return; - } - const auto& raw{raw_cache[i]}; - const u64 unique_identifier{raw.GetUniqueIdentifier()}; - - const u64 calculated_hash = - GetUniqueIdentifier(raw.GetRawShaderConfig(), raw.GetProgramCode()); - if (unique_identifier != calculated_hash) { - LOG_ERROR(Render_OpenGL, - "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing " - "shader cache", - raw.GetUniqueIdentifier(), calculated_hash); - disk_cache.InvalidateAll(); - return; - } - - const auto dump{dump_map.find(unique_identifier)}; - const auto decomp{decompiled_map.find(unique_identifier)}; - - OGLProgram shader; - - if (dump != dump_map.end() && decomp != decompiled_map.end()) { - // Only load the vertex shader if its sanitize_mul setting matches - if (raw.GetProgramType() == ProgramType::VS && - decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { - continue; - } - - // If the shader is dumped, attempt to load it - shader = - GeneratePrecompiledProgram(dump->second, supported_formats, impl->separable); - if (shader.handle == 0) { - // If any shader failed, stop trying to compile, delete the cache, and start - // loading from raws - compilation_failed = true; + const auto LoadPrecompiledShader = + [&](std::size_t begin, std::size_t end, std::span raw_cache, + const ShaderDecompiledMap& decompiled_map, const ShaderDumpsMap& dump_map) { + for (std::size_t i = begin; i < end; ++i) { + if (stop_loading || compilation_failed) { return; } - // we have both the binary shader and the decompiled, so inject it into the - // cache - if (raw.GetProgramType() == ProgramType::VS) { - auto [conf, setup] = BuildVSConfigFromRaw(raw, driver); - std::scoped_lock lock(mutex); - impl->programmable_vertex_shaders.Inject(conf, decomp->second.code, - std::move(shader)); - } else if (raw.GetProgramType() == ProgramType::FS) { - PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), false, - driver.HasBlendMinMaxFactor()); - std::scoped_lock lock(mutex); - impl->fragment_shaders.Inject(conf, std::move(shader)); + const auto& raw{raw_cache[i]}; + const u64 unique_identifier{raw.GetUniqueIdentifier()}; + + const u64 calculated_hash = + GetUniqueIdentifier(raw.GetRawShaderConfig(), raw.GetProgramCode()); + if (unique_identifier != calculated_hash) { + LOG_ERROR(Render_OpenGL, + "Invalid hash in entry={:016x} (obtained hash={:016x}) - removing " + "shader cache", + raw.GetUniqueIdentifier(), calculated_hash); + disk_cache.InvalidateAll(); + return; + } + + const auto dump{dump_map.find(unique_identifier)}; + const auto decomp{decompiled_map.find(unique_identifier)}; + + OGLProgram shader; + + if (dump != dump_map.end() && decomp != decompiled_map.end()) { + // Only load the vertex shader if its sanitize_mul setting matches + if (raw.GetProgramType() == ProgramType::VS && + decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { + continue; + } + + // If the shader is dumped, attempt to load it + shader = GeneratePrecompiledProgram(dump->second, supported_formats); + if (shader.handle == 0) { + // If any shader failed, stop trying to compile, delete the cache, and start + // loading from raws. + compilation_failed = true; + return; + } + // We have both the binary shader and the decompiled, so inject it into the + // cache. + if (raw.GetProgramType() == ProgramType::VS) { + auto [conf, setup] = BuildVSConfigFromRaw(raw, driver); + std::scoped_lock lock(mutex); + impl->programmable_vertex_shaders.Inject(conf, decomp->second.code, + std::move(shader)); + } else if (raw.GetProgramType() == ProgramType::FS) { + PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), + false, driver.HasBlendMinMaxFactor()); + std::scoped_lock lock(mutex); + impl->fragment_shaders.Inject(conf, std::move(shader)); + } else { + // Unsupported shader type got stored somehow so nuke the cache + + LOG_CRITICAL(Frontend, "failed to load raw ProgramType {}", + raw.GetProgramType()); + compilation_failed = true; + return; + } } else { - // Unsupported shader type got stored somehow so nuke the cache - - LOG_CRITICAL(Frontend, "failed to load raw ProgramType {}", - raw.GetProgramType()); - compilation_failed = true; - return; + // Since precompiled didn't have the dump, we'll load them in the next phase + std::scoped_lock lock(mutex); + load_raws_index.push_back(i); + } + if (callback) { + callback(VideoCore::LoadCallbackStage::Decompile, i, raw_cache.size()); } - } else { - // Since precompiled didn't have the dump, we'll load them in the next phase - std::scoped_lock lock(mutex); - load_raws_index.push_back(i); } - if (callback) { - callback(VideoCore::LoadCallbackStage::Decompile, i, raw_cache.size()); - } - } - }; + }; - const auto LoadPrecompiledProgram = [&](const ShaderDecompiledMap& decompiled_map, - const ShaderDumpsMap& dump_map) { - std::size_t i{0}; - for (const auto& dump : dump_map) { - if (stop_loading) { - break; - } - const u64 unique_identifier{dump.first}; - const auto decomp{decompiled_map.find(unique_identifier)}; - - // Only load the program if its sanitize_mul setting matches - if (decomp->second.sanitize_mul != VideoCore::g_hw_shader_accurate_mul) { - continue; - } - - // If the shader program is dumped, attempt to load it - OGLProgram shader = - GeneratePrecompiledProgram(dump.second, supported_formats, impl->separable); - if (shader.handle != 0) { - impl->program_cache.emplace(unique_identifier, std::move(shader)); - } else { - LOG_ERROR(Frontend, "Failed to link Precompiled program!"); - compilation_failed = true; - break; - } - if (callback) { - callback(VideoCore::LoadCallbackStage::Decompile, ++i, dump_map.size()); - } - } - }; - - if (impl->separable) { - LoadPrecompiledShader(0, raws.size(), raws, decompiled, dumps); - } else { - LoadPrecompiledProgram(decompiled, dumps); - } + LoadPrecompiledShader(0, raws.size(), raws, decompiled, dumps); bool load_all_raws = false; if (compilation_failed) { @@ -539,11 +421,6 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, precompiled_cache_altered = true; load_all_raws = true; } - // TODO(SachinV): Skip loading raws until we implement a proper way to link non-seperable - // shaders. - if (!impl->separable) { - return; - } const std::size_t load_raws_size = load_all_raws ? raws.size() : load_raws_index.size(); @@ -570,25 +447,25 @@ void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading, GLuint handle{0}; std::string code; // Otherwise decompile and build the shader at boot and save the result to the - // precompiled file + // precompiled file. if (raw.GetProgramType() == ProgramType::VS) { - auto [conf, setup] = BuildVSConfigFromRaw(raw, driver); - code = GLSL::GenerateVertexShader(setup, conf, impl->separable); - OGLShaderStage stage{impl->separable}; - stage.Create(code.c_str(), GL_VERTEX_SHADER); - handle = stage.GetHandle(); + const auto [conf, setup] = BuildVSConfigFromRaw(raw, driver); + code = GLSL::GenerateVertexShader(setup, conf, true); + OGLProgram program{}; + program.Create(code, GL_VERTEX_SHADER); + handle = program.handle; sanitize_mul = conf.state.sanitize_mul; std::scoped_lock lock(mutex); - impl->programmable_vertex_shaders.Inject(conf, code, std::move(stage)); + impl->programmable_vertex_shaders.Inject(conf, code, std::move(program)); } else if (raw.GetProgramType() == ProgramType::FS) { PicaFSConfig conf(raw.GetRawShaderConfig(), false, driver.IsOpenGLES(), false, driver.HasBlendMinMaxFactor()); - code = GLSL::GenerateFragmentShader(conf, impl->separable); - OGLShaderStage stage{impl->separable}; - stage.Create(code.c_str(), GL_FRAGMENT_SHADER); - handle = stage.GetHandle(); + code = GLSL::GenerateFragmentShader(conf, true); + OGLProgram program{}; + program.Create(code, GL_FRAGMENT_SHADER); + handle = program.handle; std::scoped_lock lock(mutex); - impl->fragment_shaders.Inject(conf, std::move(stage)); + impl->fragment_shaders.Inject(conf, std::move(program)); } else { // Unsupported shader type got stored somehow so nuke the cache LOG_ERROR(Frontend, "failed to load raw ProgramType {}", raw.GetProgramType()); diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 16c1b2142..242cb0cb8 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -33,22 +33,29 @@ enum UniformBindings { /// A class that manage different shader stages and configures them with given config data. class ShaderProgramManager { public: - ShaderProgramManager(Frontend::EmuWindow& emu_window, const Driver& driver, bool separable); + explicit ShaderProgramManager(Frontend::EmuWindow& emu_window, const Driver& driver); ~ShaderProgramManager(); + /// Loads the pipeline cache stored to disk. void LoadDiskCache(const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback); + /// Binds a PICA decompiled vertex shader. bool UseProgrammableVertexShader(const Pica::Regs& config, Pica::Shader::ShaderSetup& setup); + /// Binds a passthrough vertex shader. void UseTrivialVertexShader(); + /// Binds a passthrough geometry shader. void UseFixedGeometryShader(const Pica::Regs& regs); + /// Binds no geometry shader. void UseTrivialGeometryShader(); + /// Binds a fragment shader generated from PICA state. void UseFragmentShader(const Pica::Regs& config, bool use_normal); + /// Binds current shader state to provided OpenGLState. void ApplyTo(OpenGLState& state); private: diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp index 50eafb436..f48430ad7 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.cpp +++ b/src/video_core/renderer_opengl/gl_shader_util.cpp @@ -13,10 +13,9 @@ namespace OpenGL { -GLuint LoadShader(std::string_view source, GLenum type) { - std::string preamble; +std::string_view GetPreamble() { if (GLES) { - preamble = R"(#version 320 es + return R"(#version 320 es #if defined(GL_ANDROID_extension_pack_es31a) #extension GL_ANDROID_extension_pack_es31a : enable @@ -26,9 +25,12 @@ GLuint LoadShader(std::string_view source, GLenum type) { #extension GL_EXT_clip_cull_distance : enable #endif // defined(GL_EXT_clip_cull_distance) )"; - } else { - preamble = "#version 430 core\n"; } + return "#version 430 core\n"; +} + +GLuint LoadShader(std::string_view source, GLenum type) { + const auto preamble = GetPreamble(); std::string_view debug_type; switch (type) { @@ -72,11 +74,9 @@ GLuint LoadShader(std::string_view source, GLenum type) { return shader_id; } -GLuint LoadProgram(bool separable_program, std::span shaders) { - // Link the program +GLuint LoadProgram(std::span shaders) { LOG_DEBUG(Render_OpenGL, "Linking program..."); - - GLuint program_id = glCreateProgram(); + const GLuint program_id = glCreateProgram(); for (GLuint shader : shaders) { if (shader != 0) { @@ -84,10 +84,6 @@ GLuint LoadProgram(bool separable_program, std::span shaders) { } } - if (separable_program) { - glProgramParameteri(program_id, GL_PROGRAM_SEPARABLE, GL_TRUE); - } - glProgramParameteri(program_id, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); glLinkProgram(program_id); diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h index 3b52253b8..44ef586ca 100644 --- a/src/video_core/renderer_opengl/gl_shader_util.h +++ b/src/video_core/renderer_opengl/gl_shader_util.h @@ -9,6 +9,11 @@ namespace OpenGL { +/** + * Utility function to retrieve the preamble to compile an OpenGL GLSL shader + */ +std::string_view GetPreamble(); + /** * Utility function to create and compile an OpenGL GLSL shader * @param source String of the GLSL shader program @@ -22,6 +27,6 @@ GLuint LoadShader(std::string_view source, GLenum type); * @param shaders ID of shaders to attach to the program * @returns Handle of the newly created OpenGL program object */ -GLuint LoadProgram(bool separable_program, std::span shaders); +GLuint LoadProgram(std::span shaders); } // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index c0c99349f..225207ae3 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -59,6 +59,7 @@ OpenGLState::OpenGLState() { texture_buffer_lut_lf.texture_buffer = 0; texture_buffer_lut_rg.texture_buffer = 0; texture_buffer_lut_rgba.texture_buffer = 0; + color_buffer.texture_2d = 0; image_shadow_buffer = 0; image_shadow_texture_px = 0;