Compare commits
	
		
			1 Commits
		
	
	
		
			desc-temp
			...
			enforce-se
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | f3bbcb31c9 | 
| @@ -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}, | ||||
|   | ||||
| @@ -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<const GLuint> 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; | ||||
|   | ||||
| @@ -130,7 +130,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     /// Creates a new program from given shader objects | ||||
|     void Create(bool separable_program, std::span<const GLuint> 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); | ||||
|   | ||||
| @@ -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<std::vector<ShaderDiskCacheRaw>> ShaderDiskCache::LoadTransferable() { | ||||
|     const bool has_title_id = GetProgramID() != 0; | ||||
| @@ -177,7 +175,7 @@ std::optional<std::vector<ShaderDiskCacheRaw>> ShaderDiskCache::LoadTransferable | ||||
| } | ||||
|  | ||||
| std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, 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<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, 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<u8> precompiled_file(file.GetSize()); | ||||
|     file.ReadBytes(precompiled_file.data(), precompiled_file.size()); | ||||
|     if (compressed) { | ||||
|         const std::vector<u8> 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<u8> 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 { | ||||
|   | ||||
| @@ -16,11 +16,8 @@ | ||||
|  | ||||
| #include <glad/glad.h> | ||||
|  | ||||
| #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<std::vector<ShaderDiskCacheRaw>> LoadTransferable(); | ||||
|  | ||||
|     /// Loads current game's precompiled cache. Invalidates on failure. | ||||
|     std::pair<ShaderDecompiledMap, ShaderDumpsMap> LoadPrecompiled(bool compressed); | ||||
|     std::pair<ShaderDecompiledMap, ShaderDumpsMap> 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<std::pair<ShaderDecompiledMap, ShaderDumpsMap>> 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; | ||||
|  | ||||
|   | ||||
| @@ -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<GLenum>& supported_formats, | ||||
|                                              bool separable) { | ||||
|                                              const std::set<GLenum>& 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<GLsizei>(dump.binary.size())); | ||||
|  | ||||
| @@ -90,91 +87,42 @@ static std::tuple<PicaVSConfig, Pica::Shader::ShaderSetup> 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<OGLShader>(shader_or_program).Create(source, type); | ||||
|         } else { | ||||
|             OGLShader shader; | ||||
|             shader.Create(source, type); | ||||
|             OGLProgram& program = std::get<OGLProgram>(shader_or_program); | ||||
|             program.Create(true, std::array{shader.handle}); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     GLuint GetHandle() const { | ||||
|         if (shader_or_program.index() == 0) { | ||||
|             return std::get<OGLShader>(shader_or_program).handle; | ||||
|         } else { | ||||
|             return std::get<OGLProgram>(shader_or_program).handle; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void Inject(OGLProgram&& program) { | ||||
|         shader_or_program = std::move(program); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::variant<OGLShader, OGLProgram> 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 <typename KeyConfigType, std::string (*CodeGenerator)(const KeyConfigType&, bool), | ||||
|           GLenum ShaderType> | ||||
| template <typename KeyConfigType, auto CodeGenerator, GLenum ShaderType> | ||||
| class ShaderCache { | ||||
| public: | ||||
|     explicit ShaderCache(bool separable) : separable(separable) {} | ||||
|     explicit ShaderCache() = default; | ||||
|  | ||||
|     std::tuple<GLuint, std::optional<std::string>> 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<std::string> 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<KeyConfigType, OGLShaderStage> shaders; | ||||
|     std::unordered_map<KeyConfigType, OGLProgram> 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 <typename KeyConfigType, | ||||
|           std::string (*CodeGenerator)(const Pica::Shader::ShaderSetup&, const KeyConfigType&, | ||||
|                                        bool), | ||||
|           GLenum ShaderType> | ||||
| template <typename KeyConfigType, auto CodeGenerator, GLenum ShaderType> | ||||
| class ShaderDoubleCache { | ||||
| public: | ||||
|     explicit ShaderDoubleCache(bool separable) : separable(separable) {} | ||||
|     explicit ShaderDoubleCache() = default; | ||||
|  | ||||
|     std::tuple<GLuint, std::optional<std::string>> Get(const KeyConfigType& key, | ||||
|                                                        const Pica::Shader::ShaderSetup& setup) { | ||||
|         std::optional<std::string> 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<KeyConfigType, OGLShaderStage*> shader_map; | ||||
|     std::unordered_map<std::string, OGLShaderStage> shader_cache; | ||||
|     std::unordered_map<KeyConfigType, OGLProgram*> shader_map; | ||||
|     std::unordered_map<std::string, OGLProgram> 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<Impl>( | ||||
|                                                                        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<std::size_t> 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<const ShaderDiskCacheRaw> 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<const ShaderDiskCacheRaw> 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()); | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
| @@ -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<const GLuint> shaders) { | ||||
|     // Link the program | ||||
| GLuint LoadProgram(std::span<const GLuint> 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<const GLuint> 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); | ||||
|  | ||||
|   | ||||
| @@ -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<const GLuint> shaders); | ||||
| GLuint LoadProgram(std::span<const GLuint> shaders); | ||||
|  | ||||
| } // namespace OpenGL | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user