GLSL shader gen: Add alpha test (...half of it I guess)

This commit is contained in:
wheremyfoodat 2024-05-13 00:51:40 +03:00
parent 798c651a17
commit 842943fa4c
7 changed files with 108 additions and 38 deletions

View file

@ -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(

View file

@ -0,0 +1,53 @@
#pragma once
#include <array>
#include <cstring>
#include <type_traits>
#include <unordered_map>
#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<u32, 5 * 6> 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<OutputConfig>() && std::has_unique_object_representations<TextureConfig>() &&
std::has_unique_object_representations<FragmentConfig>()
);
} // namespace PICA
// Override std::hash for our fragment config class
template <>
struct std::hash<PICA::FragmentConfig> {
std::size_t operator()(const PICA::FragmentConfig& config) const noexcept { return PICAHash::computeHash((const char*)&config, sizeof(config)); }
};

View file

@ -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,

View file

@ -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:

View file

@ -7,6 +7,7 @@
#include <unordered_map>
#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<u32, 5 * 6> 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<PICA::FragmentConfig> {
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 = {};

View file

@ -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<CompareFunction>(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";
}

View file

@ -796,22 +796,27 @@ std::optional<ColourBuffer> 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<PICA::CompareFunction>(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], &regs[InternalRegs::TexEnv0Source], 5 * sizeof(u32));
std::memcpy(&fsConfig.tevConfigs[1 * 5], &regs[InternalRegs::TexEnv1Source], 5 * sizeof(u32));
std::memcpy(&fsConfig.tevConfigs[2 * 5], &regs[InternalRegs::TexEnv2Source], 5 * sizeof(u32));
std::memcpy(&fsConfig.tevConfigs[3 * 5], &regs[InternalRegs::TexEnv3Source], 5 * sizeof(u32));
std::memcpy(&fsConfig.tevConfigs[4 * 5], &regs[InternalRegs::TexEnv4Source], 5 * sizeof(u32));
std::memcpy(&fsConfig.tevConfigs[5 * 5], &regs[InternalRegs::TexEnv5Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[0 * 5], &regs[InternalRegs::TexEnv0Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[1 * 5], &regs[InternalRegs::TexEnv1Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[2 * 5], &regs[InternalRegs::TexEnv2Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[3 * 5], &regs[InternalRegs::TexEnv3Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[4 * 5], &regs[InternalRegs::TexEnv4Source], 5 * sizeof(u32));
std::memcpy(&texConfig.tevConfigs[5 * 5], &regs[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";