From fa9ce5fc70c71905a68a536610de01c8763836f4 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Sun, 13 Oct 2024 21:17:24 +0000 Subject: [PATCH] GLES: Implement logic ops via fb fetch (#608) * GLES: Implement logic ops via fb fetch * Attempt to fix deprecated libglx-mesa0 package * Update Qt_Build.yml * GLES: Enable fb fetch instead of requiring it * GLES: Add support for GL_ARM_shader_framebuffer_fetch * Fix GL_EXT_shader_framebuffer_fetch behavior --- .github/workflows/Hydra_Build.yml | 4 +- .github/workflows/Linux_AppImage_Build.yml | 2 +- .github/workflows/Linux_Build.yml | 2 +- .github/workflows/Qt_Build.yml | 3 +- CMakeLists.txt | 2 +- include/PICA/pica_frag_config.hpp | 5 ++ include/PICA/regs.hpp | 19 +++++++ include/PICA/shader_gen.hpp | 3 +- include/renderer_gl/gl_driver.hpp | 12 +++++ include/renderer_gl/renderer_gl.hpp | 2 + src/core/PICA/shader_gen_glsl.cpp | 59 ++++++++++++++++++++-- src/core/renderer_gl/renderer_gl.cpp | 10 +++- 12 files changed, 111 insertions(+), 12 deletions(-) create mode 100644 include/renderer_gl/gl_driver.hpp diff --git a/.github/workflows/Hydra_Build.yml b/.github/workflows/Hydra_Build.yml index e2c2004b3..ce48966ca 100644 --- a/.github/workflows/Hydra_Build.yml +++ b/.github/workflows/Hydra_Build.yml @@ -107,7 +107,7 @@ jobs: - name: Install misc packages run: | - sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev + sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev - name: Install newer Clang run: | @@ -160,7 +160,7 @@ jobs: - name: Install misc packages run: | - sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev + sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@v1.2.0 diff --git a/.github/workflows/Linux_AppImage_Build.yml b/.github/workflows/Linux_AppImage_Build.yml index f32a7d38a..1e3ec0611 100644 --- a/.github/workflows/Linux_AppImage_Build.yml +++ b/.github/workflows/Linux_AppImage_Build.yml @@ -24,7 +24,7 @@ jobs: run: git submodule update --init --recursive - name: Install misc packages - run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 + run: sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 - name: Install newer Clang run: | diff --git a/.github/workflows/Linux_Build.yml b/.github/workflows/Linux_Build.yml index 9cb05303b..05f1fa543 100644 --- a/.github/workflows/Linux_Build.yml +++ b/.github/workflows/Linux_Build.yml @@ -24,7 +24,7 @@ jobs: run: git submodule update --init --recursive - name: Install misc packages - run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev + run: sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev - name: Install newer Clang run: | diff --git a/.github/workflows/Qt_Build.yml b/.github/workflows/Qt_Build.yml index d3a09866d..8a93a1560 100644 --- a/.github/workflows/Qt_Build.yml +++ b/.github/workflows/Qt_Build.yml @@ -105,8 +105,7 @@ jobs: - name: Install misc packages run: | - sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev libgl1-mesa-dev - sudo add-apt-repository -y ppa:savoury1/qt-6-2 + sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev libgl1-mesa-dev sudo apt update sudo apt install qt6-base-dev qt6-base-private-dev diff --git a/CMakeLists.txt b/CMakeLists.txt index 88a92f8aa..91dafe9ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -381,7 +381,7 @@ if(ENABLE_OPENGL) set(RENDERER_GL_INCLUDE_FILES third_party/opengl/opengl.hpp include/renderer_gl/renderer_gl.hpp include/renderer_gl/textures.hpp include/renderer_gl/surfaces.hpp include/renderer_gl/surface_cache.hpp - include/renderer_gl/gl_state.hpp + include/renderer_gl/gl_state.hpp include/renderer_gl/gl_driver.hpp ) set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp diff --git a/include/PICA/pica_frag_config.hpp b/include/PICA/pica_frag_config.hpp index 5d5f84205..7b63a7b56 100644 --- a/include/PICA/pica_frag_config.hpp +++ b/include/PICA/pica_frag_config.hpp @@ -17,6 +17,7 @@ namespace PICA { // enable == off means a CompareFunction of Always BitField<0, 3, CompareFunction> alphaTestFunction; BitField<3, 1, u32> depthMapEnable; + BitField<4, 4, LogicOpMode> logicOpMode; }; }; @@ -214,6 +215,10 @@ namespace PICA { (alphaTestConfig & 1) ? static_cast(alphaTestFunction) : PICA::CompareFunction::Always; outConfig.depthMapEnable = regs[InternalRegs::DepthmapEnable] & 1; + // Shows if blending is enabled. If it is not enabled, then logic ops are enabled instead + const bool blendingEnabled = (regs[InternalRegs::ColourOperation] & (1 << 8)) != 0; + outConfig.logicOpMode = blendingEnabled ? LogicOpMode::Copy : LogicOpMode(Helpers::getBits<0, 4>(regs[InternalRegs::LogicOp])); + texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg]; texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer]; diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp index 636e8f7ce..3185d350f 100644 --- a/include/PICA/regs.hpp +++ b/include/PICA/regs.hpp @@ -396,6 +396,25 @@ namespace PICA { GreaterOrEqual = 7, }; + enum class LogicOpMode : u32 { + Clear = 0, + And = 1, + ReverseAnd = 2, + Copy = 3, + Set = 4, + InvertedCopy = 5, + Nop = 6, + Invert = 7, + Nand = 8, + Or = 9, + Nor = 10, + Xor = 11, + Equiv = 12, + InvertedAnd = 13, + ReverseOr = 14, + InvertedOr = 15, + }; + enum class FogMode : u32 { Disabled = 0, Fog = 5, diff --git a/include/PICA/shader_gen.hpp b/include/PICA/shader_gen.hpp index 215e5adb0..f938b5589 100644 --- a/include/PICA/shader_gen.hpp +++ b/include/PICA/shader_gen.hpp @@ -25,10 +25,11 @@ namespace PICA::ShaderGen { bool isSamplerEnabled(u32 environmentID, u32 lutID); void compileFog(std::string& shader, const PICA::FragmentConfig& config); + void compileLogicOps(std::string& shader, const PICA::FragmentConfig& config); public: FragmentGenerator(API api, Language language) : api(api), language(language) {} - std::string generate(const PICA::FragmentConfig& config); + std::string generate(const PICA::FragmentConfig& config, void* driverInfo = nullptr); std::string getDefaultVertexShader(); void setTarget(API api, Language language) { diff --git a/include/renderer_gl/gl_driver.hpp b/include/renderer_gl/gl_driver.hpp new file mode 100644 index 000000000..a15c061f6 --- /dev/null +++ b/include/renderer_gl/gl_driver.hpp @@ -0,0 +1,12 @@ +#pragma once + +// Information about our OpenGL/OpenGL ES driver that we should keep track of +// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information +namespace OpenGL { + struct Driver { + bool supportsExtFbFetch = false; + bool supportsArmFbFetch = false; + + bool supportFbFetch() const { return supportsExtFbFetch || supportsArmFbFetch; } + }; +} // namespace OpenGL \ No newline at end of file diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 42b8bba1a..070695367 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -12,6 +12,7 @@ #include "PICA/pica_vertex.hpp" #include "PICA/regs.hpp" #include "PICA/shader_gen.hpp" +#include "gl_driver.hpp" #include "gl_state.hpp" #include "helpers.hpp" #include "logger.hpp" @@ -82,6 +83,7 @@ class RendererGL final : public Renderer { OpenGL::Program& getSpecializedShader(); PICA::ShaderGen::FragmentGenerator fragShaderGen; + OpenGL::Driver driverInfo; MAKE_LOG_FUNCTION(log, rendererLogger) void setupBlending(); diff --git a/src/core/PICA/shader_gen_glsl.cpp b/src/core/PICA/shader_gen_glsl.cpp index 61694fccc..e1bdf5170 100644 --- a/src/core/PICA/shader_gen_glsl.cpp +++ b/src/core/PICA/shader_gen_glsl.cpp @@ -1,6 +1,10 @@ #include "PICA/pica_frag_config.hpp" #include "PICA/regs.hpp" #include "PICA/shader_gen.hpp" + +// We can include the driver headers here since they shouldn't have any actual API-specific code +#include "renderer_gl/gl_driver.hpp" + using namespace PICA; using namespace PICA::ShaderGen; @@ -96,7 +100,7 @@ std::string FragmentGenerator::getDefaultVertexShader() { return ret; } -std::string FragmentGenerator::generate(const FragmentConfig& config) { +std::string FragmentGenerator::generate(const FragmentConfig& config, void* driverInfo) { std::string ret = ""; switch (api) { @@ -105,6 +109,27 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) { default: break; } + // For GLES we need to enable & use the framebuffer fetch extension in order to emulate logic ops + bool emitLogicOps = api == API::GLES && config.outConfig.logicOpMode != PICA::LogicOpMode::Copy && driverInfo != nullptr; + + if (emitLogicOps) { + auto driver = static_cast(driverInfo); + + // If the driver does not support framebuffer fetch at all, don't emit logic op code + if (!driver->supportFbFetch()) { + emitLogicOps = false; + } + + // Figure out which fb fetch extension we have and enable it + else { + if (driver->supportsExtFbFetch) { + ret += "\n#extension GL_EXT_shader_framebuffer_fetch : enable\n#define fb_color fragColor\n"; + } else if (driver->supportsArmFbFetch) { + ret += "\n#extension GL_ARM_shader_framebuffer_fetch : enable\n#define fb_color gl_LastFragColorARM[0]\n"; + } + } + } + bool unimplementedFlag = false; if (api == API::GLES) { ret += R"( @@ -194,10 +219,13 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) { } compileFog(ret, config); - applyAlphaTest(ret, config); - ret += "fragColor = combinerOutput;\n}"; // End of main function + if (!emitLogicOps) { + ret += "fragColor = combinerOutput;\n}"; // End of main function + } else { + compileLogicOps(ret, config); + } return ret; } @@ -673,3 +701,28 @@ void FragmentGenerator::compileFog(std::string& shader, const PICA::FragmentConf shader += "float fog_factor = clamp(value.r + value.g * delta, 0.0, 1.0);"; shader += "combinerOutput.rgb = mix(fog_color, combinerOutput.rgb, fog_factor);"; } + +void FragmentGenerator::compileLogicOps(std::string& shader, const PICA::FragmentConfig& config) { + if (api != API::GLES) [[unlikely]] { + Helpers::warn("Shadergen: Unsupported API for compileLogicOps"); + shader += "fragColor = combinerOutput;\n}"; // End of main function + + return; + } + + shader += "fragColor = "; + switch (config.outConfig.logicOpMode) { + case PICA::LogicOpMode::Copy: shader += "combinerOutput"; break; + case PICA::LogicOpMode::Nop: shader += "fb_color"; break; + case PICA::LogicOpMode::Clear: shader += "vec4(0.0)"; break; + case PICA::LogicOpMode::Set: shader += "vec4(uintBitsToFloat(0xFFFFFFFFu))"; break; + case PICA::LogicOpMode::InvertedCopy: shader += "vec4(uvec4(combinerOutput * 255.0) ^ uvec4(0xFFu)) * (1.0 / 255.0)"; break; + + default: + shader += "combinerOutput"; + Helpers::warn("Shadergen: Unimplemented logic op mode"); + break; + } + + shader += ";\n}"; // End of main function +} \ No newline at end of file diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 5146370a2..ecbee3a21 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -167,6 +167,10 @@ void RendererGL::initGraphicsContextInternal() { reset(); + // Populate our driver info structure + driverInfo.supportsExtFbFetch = GLAD_GL_EXT_shader_framebuffer_fetch != 0; + driverInfo.supportsArmFbFetch = GLAD_GL_ARM_shader_framebuffer_fetch != 0; + // Initialize the default vertex shader used with shadergen std::string defaultShadergenVSSource = fragShaderGen.getDefaultVertexShader(); defaultShadergenVs.create({defaultShadergenVSSource.c_str(), defaultShadergenVSSource.size()}, OpenGL::Vertex); @@ -839,12 +843,16 @@ OpenGL::Program& RendererGL::getSpecializedShader() { constexpr uint uboBlockBinding = 2; PICA::FragmentConfig fsConfig(regs); + // If we're not on GLES, ignore the logic op configuration and don't generate redundant shaders for it, since we use hw logic ops +#ifndef USING_GLES + fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0); +#endif CachedProgram& programEntry = shaderCache[fsConfig]; OpenGL::Program& program = programEntry.program; if (!program.exists()) { - std::string fs = fragShaderGen.generate(fsConfig); + std::string fs = fragShaderGen.generate(fsConfig, &driverInfo); OpenGL::Shader fragShader({fs.c_str(), fs.size()}, OpenGL::Fragment); program.create({defaultShadergenVs, fragShader});