From d132ddefef3c8f5f3cf462c9b60dfc30caaa4951 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Mon, 21 Nov 2022 01:19:02 +1000 Subject: [PATCH] GL: Add support for compute shaders --- common/GL/Program.cpp | 11 +++ common/GL/Program.h | 2 + common/GL/ShaderCache.cpp | 185 +++++++++++++++++++++++++++++++------- common/GL/ShaderCache.h | 8 ++ 4 files changed, 175 insertions(+), 31 deletions(-) diff --git a/common/GL/Program.cpp b/common/GL/Program.cpp index 5163844a45..ce0169c80a 100644 --- a/common/GL/Program.cpp +++ b/common/GL/Program.cpp @@ -137,6 +137,17 @@ namespace GL return true; } + bool Program::CompileCompute(const std::string_view glsl) + { + GLuint id = CompileShader(GL_COMPUTE_SHADER, glsl); + if (id == 0) + return false; + + m_program_id = glCreateProgram(); + glAttachShader(m_program_id, id); + return true; + } + bool Program::CreateFromBinary(const void* data, u32 data_length, u32 data_format) { GLuint prog = glCreateProgram(); diff --git a/common/GL/Program.h b/common/GL/Program.h index 77563995be..0d1e9afe49 100644 --- a/common/GL/Program.h +++ b/common/GL/Program.h @@ -37,6 +37,8 @@ namespace GL bool Compile(const std::string_view vertex_shader, const std::string_view geometry_shader, const std::string_view fragment_shader); + bool CompileCompute(const std::string_view glsl); + bool CreateFromBinary(const void* data, u32 data_length, u32 data_format); bool GetBinary(std::vector* out_data, u32* out_data_format); diff --git a/common/GL/ShaderCache.cpp b/common/GL/ShaderCache.cpp index 84cb264ce2..c2fca6af58 100644 --- a/common/GL/ShaderCache.cpp +++ b/common/GL/ShaderCache.cpp @@ -352,6 +352,42 @@ namespace GL return true; } + bool ShaderCache::WriteToBlobFile(const CacheIndexKey& key, const std::vector& prog_data, u32 prog_format) + { + if (!m_blob_file || std::fseek(m_blob_file, 0, SEEK_END) != 0) + return false; + + CacheIndexData data; + data.file_offset = static_cast(std::ftell(m_blob_file)); + data.blob_size = static_cast(prog_data.size()); + data.blob_format = prog_format; + + CacheIndexEntry entry = {}; + entry.vertex_source_hash_low = key.vertex_source_hash_low; + entry.vertex_source_hash_high = key.vertex_source_hash_high; + entry.vertex_source_length = key.vertex_source_length; + entry.geometry_source_hash_low = key.geometry_source_hash_low; + entry.geometry_source_hash_high = key.geometry_source_hash_high; + entry.geometry_source_length = key.geometry_source_length; + entry.fragment_source_hash_low = key.fragment_source_hash_low; + entry.fragment_source_hash_high = key.fragment_source_hash_high; + entry.fragment_source_length = key.fragment_source_length; + entry.file_offset = data.file_offset; + entry.blob_size = data.blob_size; + entry.blob_format = data.blob_format; + + if (std::fwrite(prog_data.data(), 1, entry.blob_size, m_blob_file) != entry.blob_size || + std::fflush(m_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 || + std::fflush(m_index_file) != 0) + { + Console.Error("Failed to write shader blob to file"); + return false; + } + + m_index.emplace(key, data); + return true; + } + std::optional ShaderCache::CompileProgram(const std::string_view& vertex_shader, const std::string_view& geometry_shader, const std::string_view& fragment_shader, @@ -373,6 +409,25 @@ namespace GL return std::optional(std::move(prog)); } + std::optional ShaderCache::CompileComputeProgram(const std::string_view& glsl, + const PreLinkCallback& callback, bool set_retrievable) + { + Program prog; + if (!prog.CompileCompute(glsl)) + return std::nullopt; + + if (callback) + callback(prog); + + if (set_retrievable) + prog.SetBinaryRetrievableHint(); + + if (!prog.Link()) + return std::nullopt; + + return std::optional(std::move(prog)); + } + std::optional ShaderCache::CompileAndAddProgram(const CacheIndexKey& key, const std::string_view& vertex_shader, const std::string_view& geometry_shader, @@ -401,43 +456,111 @@ namespace GL const float binary_time = timer.GetTimeMilliseconds(); timer.Reset(); #endif - - if (!m_blob_file || std::fseek(m_blob_file, 0, SEEK_END) != 0) - return prog; - - CacheIndexData data; - data.file_offset = static_cast(std::ftell(m_blob_file)); - data.blob_size = static_cast(prog_data.size()); - data.blob_format = prog_format; - - CacheIndexEntry entry = {}; - entry.vertex_source_hash_low = key.vertex_source_hash_low; - entry.vertex_source_hash_high = key.vertex_source_hash_high; - entry.vertex_source_length = key.vertex_source_length; - entry.geometry_source_hash_low = key.geometry_source_hash_low; - entry.geometry_source_hash_high = key.geometry_source_hash_high; - entry.geometry_source_length = key.geometry_source_length; - entry.fragment_source_hash_low = key.fragment_source_hash_low; - entry.fragment_source_hash_high = key.fragment_source_hash_high; - entry.fragment_source_length = key.fragment_source_length; - entry.file_offset = data.file_offset; - entry.blob_size = data.blob_size; - entry.blob_format = data.blob_format; - - if (std::fwrite(prog_data.data(), 1, entry.blob_size, m_blob_file) != entry.blob_size || - std::fflush(m_blob_file) != 0 || std::fwrite(&entry, sizeof(entry), 1, m_index_file) != 1 || - std::fflush(m_index_file) != 0) - { - Console.Error("Failed to write shader blob to file"); - return prog; - } + + WriteToBlobFile(key, prog_data, prog_format); #ifdef PCSX2_DEVBUILD const float write_time = timer.GetTimeMilliseconds(); Console.WriteLn("Compiled and cached shader: Compile: %.2fms, Binary: %.2fms, Write: %.2fms", compile_time, binary_time, write_time); #endif - m_index.emplace(key, data); + return prog; + } + + std::optional ShaderCache::GetComputeProgram(const std::string_view glsl, const PreLinkCallback& callback) + { + if (!m_program_binary_supported || !m_blob_file) + { +#ifdef PCSX2_DEVBUILD + Common::Timer timer; +#endif + + std::optional res = CompileComputeProgram(glsl, callback, false); + +#ifdef PCSX2_DEVBUILD + Console.WriteLn("Time to compile shader without caching: %.2fms", timer.GetTimeMilliseconds()); +#endif + return res; + } + + const auto key = GetCacheKey(glsl, std::string_view(), std::string_view()); + auto iter = m_index.find(key); + if (iter == m_index.end()) + return CompileAndAddComputeProgram(key, glsl, callback); + + std::vector data(iter->second.blob_size); + if (std::fseek(m_blob_file, iter->second.file_offset, SEEK_SET) != 0 || + std::fread(data.data(), 1, iter->second.blob_size, m_blob_file) != iter->second.blob_size) + { + Console.Error("Read blob from file failed"); + return {}; + } + +#ifdef PCSX2_DEVBUILD + Common::Timer timer; +#endif + + Program prog; + if (prog.CreateFromBinary(data.data(), static_cast(data.size()), iter->second.blob_format)) + { +#ifdef PCSX2_DEVBUILD + Console.WriteLn("Time to create program from binary: %.2fms", timer.GetTimeMilliseconds()); +#endif + + return std::optional(std::move(prog)); + } + + Console.Warning( + "Failed to create program from binary, this may be due to a driver or GPU Change. Recreating cache."); + if (!Recreate()) + return CompileComputeProgram(glsl, callback, false); + else + return CompileAndAddComputeProgram(key, glsl, callback); + } + + bool ShaderCache::GetComputeProgram(Program* out_program, const std::string_view glsl, const PreLinkCallback& callback) + { + auto prog = GetComputeProgram(glsl, callback); + if (!prog) + return false; + + *out_program = std::move(*prog); + return true; + } + + std::optional ShaderCache::CompileAndAddComputeProgram( + const CacheIndexKey& key, const std::string_view& glsl, const PreLinkCallback& callback) + { +#ifdef PCSX2_DEVBUILD + Common::Timer timer; +#endif + + std::optional prog = CompileComputeProgram(glsl, callback, true); + if (!prog) + return std::nullopt; + +#ifdef PCSX2_DEVBUILD + const float compile_time = timer.GetTimeMilliseconds(); + timer.Reset(); +#endif + + std::vector prog_data; + u32 prog_format = 0; + if (!prog->GetBinary(&prog_data, &prog_format)) + return std::nullopt; + +#ifdef PCSX2_DEVBUILD + const float binary_time = timer.GetTimeMilliseconds(); + timer.Reset(); +#endif + + WriteToBlobFile(key, prog_data, prog_format); + +#ifdef PCSX2_DEVBUILD + const float write_time = timer.GetTimeMilliseconds(); + Console.WriteLn("Compiled and cached compute shader: Compile: %.2fms, Binary: %.2fms, Write: %.2fms", compile_time, binary_time, write_time); +#endif + return prog; } } // namespace GL diff --git a/common/GL/ShaderCache.h b/common/GL/ShaderCache.h index 20a04841c5..31dba488bf 100644 --- a/common/GL/ShaderCache.h +++ b/common/GL/ShaderCache.h @@ -42,6 +42,9 @@ namespace GL bool GetProgram(Program* out_program, const std::string_view vertex_shader, const std::string_view geometry_shader, const std::string_view fragment_shader, const PreLinkCallback& callback = {}); + std::optional GetComputeProgram(const std::string_view glsl, const PreLinkCallback& callback = {}); + bool GetComputeProgram(Program* out_program, const std::string_view glsl, const PreLinkCallback& callback = {}); + private: static constexpr u32 FILE_VERSION = 1; @@ -94,6 +97,8 @@ namespace GL void Close(); bool Recreate(); + bool WriteToBlobFile(const CacheIndexKey& key, const std::vector& prog_data, u32 prog_format); + std::optional CompileProgram(const std::string_view& vertex_shader, const std::string_view& geometry_shader, const std::string_view& fragment_shader, const PreLinkCallback& callback, bool set_retrievable); @@ -101,6 +106,9 @@ namespace GL const std::string_view& geometry_shader, const std::string_view& fragment_shader, const PreLinkCallback& callback); + std::optional CompileComputeProgram(const std::string_view& glsl, const PreLinkCallback& callback, bool set_retrievable); + std::optional CompileAndAddComputeProgram(const CacheIndexKey& key, const std::string_view& glsl, const PreLinkCallback& callback); + std::string m_base_path; std::FILE* m_index_file = nullptr; std::FILE* m_blob_file = nullptr;