diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bdb8abb..c6b12188 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,7 +242,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp include/PICA/shader_gen.hpp include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp - include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp + include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp ) cmrc_add_resource_library( diff --git a/include/PICA/pica_frag_config.hpp b/include/PICA/pica_frag_config.hpp new file mode 100644 index 00000000..c4d46b11 --- /dev/null +++ b/include/PICA/pica_frag_config.hpp @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include + +#include "PICA/pica_hash.hpp" +#include "PICA/regs.hpp" +#include "bitfield.hpp" +#include "helpers.hpp" + +namespace PICA { + struct OutputConfig { + union { + u32 raw; + // Merge the enable + compare function into 1 field to avoid duplicate shaders + // enable == off means a CompareFunction of Always + BitField<0, 3, CompareFunction> alphaTestFunction; + }; + }; + + struct TextureConfig { + u32 texUnitConfig; + u32 texEnvUpdateBuffer; + + // TODO: This should probably be a uniform + u32 texEnvBufferColor; + + // There's 6 TEV stages, and each one is configured via 5 word-sized registers + std::array tevConfigs; + }; + + struct FragmentConfig { + OutputConfig outConfig; + TextureConfig texConfig; + + bool operator==(const FragmentConfig& config) const { + // Hash function and equality operator required by std::unordered_map + return std::memcmp(this, &config, sizeof(FragmentConfig)) == 0; + } + }; + + static_assert( + std::has_unique_object_representations() && std::has_unique_object_representations() && + std::has_unique_object_representations() + ); +} // namespace PICA + +// Override std::hash for our fragment config class +template <> +struct std::hash { + std::size_t operator()(const PICA::FragmentConfig& config) const noexcept { return PICAHash::computeHash((const char*)&config, sizeof(config)); } +}; \ No newline at end of file diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp index 5b9e1830..74f8c7d5 100644 --- a/include/PICA/regs.hpp +++ b/include/PICA/regs.hpp @@ -345,6 +345,17 @@ namespace PICA { GeometryPrimitive = 3, }; + enum class CompareFunction : u32 { + Never = 0, + Always = 1, + Equal = 2, + NotEqual = 3, + Less = 4, + LessOrEqual = 5, + Greater = 6, + GreaterOrEqual = 7, + }; + struct TexEnvConfig { enum class Source : u8 { PrimaryColor = 0x0, diff --git a/include/PICA/shader_gen.hpp b/include/PICA/shader_gen.hpp index 23a87120..e8e8ca20 100644 --- a/include/PICA/shader_gen.hpp +++ b/include/PICA/shader_gen.hpp @@ -24,6 +24,8 @@ namespace PICA::ShaderGen { void getColorOperation(std::string& shader, PICA::TexEnvConfig::Operation op); void getAlphaOperation(std::string& shader, PICA::TexEnvConfig::Operation op); + void applyAlphaTest(std::string& shader, const PICARegs& regs); + u32 textureConfig = 0; public: diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index e8eaeacb..53ca9975 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -7,6 +7,7 @@ #include #include "PICA/float_types.hpp" +#include "PICA/pica_frag_config.hpp" #include "PICA/pica_hash.hpp" #include "PICA/pica_vertex.hpp" #include "PICA/regs.hpp" @@ -21,32 +22,6 @@ // More circular dependencies! class GPU; -namespace PICA { - struct FragmentConfig { - u32 texUnitConfig; - u32 texEnvUpdateBuffer; - - // TODO: This should probably be a uniform - u32 texEnvBufferColor; - - // There's 6 TEV stages, and each one is configured via 5 word-sized registers - std::array tevConfigs; - - // Hash function and equality operator required by std::unordered_map - bool operator==(const FragmentConfig& config) const { - return std::memcmp(this, &config, sizeof(FragmentConfig)) == 0; - } - }; -} // namespace PICA - -// Override std::hash for our fragment config class -template <> -struct std::hash { - std::size_t operator()(const PICA::FragmentConfig& config) const noexcept { - return PICAHash::computeHash((const char*)&config, sizeof(config)); - } -}; - class RendererGL final : public Renderer { GLStateManager gl = {}; diff --git a/src/core/PICA/shader_gen_glsl.cpp b/src/core/PICA/shader_gen_glsl.cpp index c3056815..50be94f0 100644 --- a/src/core/PICA/shader_gen_glsl.cpp +++ b/src/core/PICA/shader_gen_glsl.cpp @@ -144,6 +144,8 @@ std::string FragmentGenerator::generate(const PICARegs& regs) { compileTEV(ret, i, regs); } + applyAlphaTest(ret, regs); + ret += "fragColor = combinerOutput;\n"; ret += "}"; // End of main function ret += "\n\n\n\n\n\n\n\n\n\n\n\n\n"; @@ -353,3 +355,25 @@ void FragmentGenerator::getAlphaOperation(std::string& shader, TexEnvConfig::Ope break; } } + +void FragmentGenerator::applyAlphaTest(std::string& shader, const PICARegs& regs) { + const u32 alphaConfig = regs[InternalRegs::AlphaTestConfig]; + // Alpha test disabled + if (Helpers::getBit<0>(alphaConfig) == 0) { + return; + } + + const auto function = static_cast(Helpers::getBits<4, 3>(alphaConfig)); + + shader += "if ("; + switch (function) { + case CompareFunction::Never: shader += "true"; break; + case CompareFunction::Always: shader += "false"; break; + default: + Helpers::warn("Unimplemented alpha test function"); + shader += "false"; + break; + } + + shader += ") { discard; }\n"; +} diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 5d3ed1b1..cfd197f8 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -796,22 +796,27 @@ std::optional RendererGL::getColourBuffer(u32 addr, PICA::ColorFmt OpenGL::Program& RendererGL::getSpecializedShader() { PICA::FragmentConfig fsConfig; - fsConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg]; - fsConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer]; - fsConfig.texEnvBufferColor = regs[InternalRegs::TexEnvBufferColor]; + auto& outConfig = fsConfig.outConfig; + auto& texConfig = fsConfig.texConfig; + + auto alphaTestConfig = regs[InternalRegs::AlphaTestConfig]; + auto alphaTestFunction = Helpers::getBits<4, 3>(alphaTestConfig); + outConfig.alphaTestFunction = (alphaTestConfig & 1) ? static_cast(alphaTestFunction) : PICA::CompareFunction::Always; + + texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg]; + texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer]; + texConfig.texEnvBufferColor = 0; // Set up TEV stages - std::memcpy(&fsConfig.tevConfigs[0 * 5], ®s[InternalRegs::TexEnv0Source], 5 * sizeof(u32)); - std::memcpy(&fsConfig.tevConfigs[1 * 5], ®s[InternalRegs::TexEnv1Source], 5 * sizeof(u32)); - std::memcpy(&fsConfig.tevConfigs[2 * 5], ®s[InternalRegs::TexEnv2Source], 5 * sizeof(u32)); - std::memcpy(&fsConfig.tevConfigs[3 * 5], ®s[InternalRegs::TexEnv3Source], 5 * sizeof(u32)); - std::memcpy(&fsConfig.tevConfigs[4 * 5], ®s[InternalRegs::TexEnv4Source], 5 * sizeof(u32)); - std::memcpy(&fsConfig.tevConfigs[5 * 5], ®s[InternalRegs::TexEnv5Source], 5 * sizeof(u32)); + std::memcpy(&texConfig.tevConfigs[0 * 5], ®s[InternalRegs::TexEnv0Source], 5 * sizeof(u32)); + std::memcpy(&texConfig.tevConfigs[1 * 5], ®s[InternalRegs::TexEnv1Source], 5 * sizeof(u32)); + std::memcpy(&texConfig.tevConfigs[2 * 5], ®s[InternalRegs::TexEnv2Source], 5 * sizeof(u32)); + std::memcpy(&texConfig.tevConfigs[3 * 5], ®s[InternalRegs::TexEnv3Source], 5 * sizeof(u32)); + std::memcpy(&texConfig.tevConfigs[4 * 5], ®s[InternalRegs::TexEnv4Source], 5 * sizeof(u32)); + std::memcpy(&texConfig.tevConfigs[5 * 5], ®s[InternalRegs::TexEnv5Source], 5 * sizeof(u32)); OpenGL::Program& program = shaderCache[fsConfig]; if (!program.exists()) { - printf("Creating specialized shader\n"); - std::string vs = fragShaderGen.getVertexShader(regs); std::string fs = fragShaderGen.generate(regs); std::cout << vs << "\n\n" << fs << "\n";