Merge branch 'master' into nyom

This commit is contained in:
wheremyfoodat 2024-07-22 15:46:27 +03:00
commit a5ea268826
17 changed files with 188 additions and 46 deletions

24
.github/gles.patch vendored
View file

@ -21,7 +21,7 @@ index 990e2f80..2e7842ac 100644
void main() { void main() {
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
index 9f369e39..b4bb19d3 100644 index b9f9fe4c..f1cf286f 100644
--- a/src/host_shaders/opengl_fragment_shader.frag --- a/src/host_shaders/opengl_fragment_shader.frag
+++ b/src/host_shaders/opengl_fragment_shader.frag +++ b/src/host_shaders/opengl_fragment_shader.frag
@@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
@ -31,8 +31,8 @@ index 9f369e39..b4bb19d3 100644
in vec4 v_quaternion; in vec4 v_quaternion;
in vec4 v_colour; in vec4 v_colour;
@@ -164,11 +165,17 @@ float lutLookup(uint lut, int index) { @@ -166,11 +167,17 @@ float lutLookup(uint lut, int index) {
return texelFetch(u_tex_lighting_lut, ivec2(index, int(lut)), 0).r; return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
} }
+// some gles versions have bitfieldExtractCompat and complain if you redefine it, some don't and compile error, using this instead +// some gles versions have bitfieldExtractCompat and complain if you redefine it, some don't and compile error, using this instead
@ -50,7 +50,7 @@ index 9f369e39..b4bb19d3 100644
} }
// Convert an arbitrary-width floating point literal to an f32 // Convert an arbitrary-width floating point literal to an f32
@@ -208,16 +215,16 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light @@ -210,16 +217,16 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment
@ -70,7 +70,7 @@ index 9f369e39..b4bb19d3 100644
switch (input_id) { switch (input_id) {
case 0u: { case 0u: {
delta = dot(normal, normalize(half_vector)); delta = dot(normal, normalize(half_vector));
@@ -239,11 +246,11 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light @@ -241,11 +248,11 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
int GPUREG_LIGHTi_SPOTDIR_LOW = int(readPicaReg(0x0146u + (light_id << 4u))); int GPUREG_LIGHTi_SPOTDIR_LOW = int(readPicaReg(0x0146u + (light_id << 4u)));
int GPUREG_LIGHTi_SPOTDIR_HIGH = int(readPicaReg(0x0147u + (light_id << 4u))); int GPUREG_LIGHTi_SPOTDIR_HIGH = int(readPicaReg(0x0147u + (light_id << 4u)));
@ -86,7 +86,7 @@ index 9f369e39..b4bb19d3 100644
if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000; if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000;
if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000; if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000;
@@ -270,9 +277,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light @@ -272,9 +279,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
} }
// 0 = enabled // 0 = enabled
@ -98,7 +98,7 @@ index 9f369e39..b4bb19d3 100644
delta = max(delta, 0.0); delta = max(delta, 0.0);
} else { } else {
delta = abs(delta); delta = abs(delta);
@@ -296,7 +303,7 @@ vec3 rotateVec3ByQuaternion(vec3 v, vec4 q) { @@ -298,7 +305,7 @@ vec3 rotateVec3ByQuaternion(vec3 v, vec4 q) {
// Implements the following algorthm: https://mathb.in/26766 // Implements the following algorthm: https://mathb.in/26766
void calcLighting(out vec4 primary_color, out vec4 secondary_color) { void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
uint GPUREG_LIGHTING_ENABLE = readPicaReg(0x008Fu); uint GPUREG_LIGHTING_ENABLE = readPicaReg(0x008Fu);
@ -107,7 +107,7 @@ index 9f369e39..b4bb19d3 100644
primary_color = secondary_color = vec4(0.0); primary_color = secondary_color = vec4(0.0);
return; return;
} }
@@ -313,7 +320,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { @@ -315,7 +322,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
GPUREG_LIGHTING_LUTINPUT_ABS = readPicaReg(0x01D0u); GPUREG_LIGHTING_LUTINPUT_ABS = readPicaReg(0x01D0u);
GPUREG_LIGHTING_LUTINPUT_SELECT = readPicaReg(0x01D1u); GPUREG_LIGHTING_LUTINPUT_SELECT = readPicaReg(0x01D1u);
@ -116,7 +116,7 @@ index 9f369e39..b4bb19d3 100644
// Bump mode is ignored for now because it breaks some games ie. Toad Treasure Tracker // Bump mode is ignored for now because it breaks some games ie. Toad Treasure Tracker
switch (bump_mode) { switch (bump_mode) {
@@ -326,15 +333,15 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { @@ -328,15 +335,15 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0); vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);
vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0); vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);
@ -135,7 +135,7 @@ index 9f369e39..b4bb19d3 100644
uint GPUREG_LIGHTi_SPECULAR0 = readPicaReg(0x0140u + (light_id << 4u)); uint GPUREG_LIGHTi_SPECULAR0 = readPicaReg(0x0140u + (light_id << 4u));
uint GPUREG_LIGHTi_SPECULAR1 = readPicaReg(0x0141u + (light_id << 4u)); uint GPUREG_LIGHTi_SPECULAR1 = readPicaReg(0x0141u + (light_id << 4u));
@@ -346,12 +353,12 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { @@ -348,12 +355,12 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
float light_distance; float light_distance;
vec3 light_position = vec3( vec3 light_position = vec3(
@ -151,7 +151,7 @@ index 9f369e39..b4bb19d3 100644
light_vector = light_position + v_view; light_vector = light_position + v_view;
} }
@@ -367,23 +374,23 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { @@ -369,23 +376,23 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
float NdotL = dot(normal, light_vector); // N dot Li float NdotL = dot(normal, light_vector); // N dot Li
// Two sided diffuse // Two sided diffuse
@ -181,7 +181,7 @@ index 9f369e39..b4bb19d3 100644
float distance_attenuation_bias = decodeFP(GPUREG_LIGHTi_ATTENUATION_BIAS, 7u, 12u); float distance_attenuation_bias = decodeFP(GPUREG_LIGHTi_ATTENUATION_BIAS, 7u, 12u);
float distance_attenuation_scale = decodeFP(GPUREG_LIGHTi_ATTENUATION_SCALE, 7u, 12u); float distance_attenuation_scale = decodeFP(GPUREG_LIGHTi_ATTENUATION_SCALE, 7u, 12u);
@@ -428,8 +435,8 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) { @@ -430,8 +437,8 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
specular_sum.rgb += light_factor * clamp_factor * (specular0 + specular1); specular_sum.rgb += light_factor * clamp_factor * (specular0 + specular1);
} }

View file

@ -28,6 +28,10 @@ if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security")
endif() endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-interference-size")
endif()
option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" ON) option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" ON)
option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF) option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON) option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)

View file

@ -56,7 +56,7 @@ lut_id is one of these values
6 RR 6 RR
lut_index on the other hand represents the actual index of the LUT in the texture lut_index on the other hand represents the actual index of the LUT in the texture
u_tex_lighting_lut has 24 LUTs and they are used like so: u_tex_luts has 24 LUTs for lighting and they are used like so:
0 D0 0 D0
1 D1 1 D1
2 is missing because SP uses LUTs 8-15 2 is missing because SP uses LUTs 8-15

View file

@ -92,6 +92,9 @@ class GPU {
// Set to false by the renderer when the lighting_lut is uploaded ot the GPU // Set to false by the renderer when the lighting_lut is uploaded ot the GPU
bool lightingLUTDirty = false; bool lightingLUTDirty = false;
bool fogLUTDirty = false;
std::array<uint32_t, 128> fogLUT;
GPU(Memory& mem, EmulatorConfig& config); GPU(Memory& mem, EmulatorConfig& config);
void display() { renderer->display(); } void display() { renderer->display(); }
void screenshot(const std::string& name) { renderer->screenshot(name); } void screenshot(const std::string& name) { renderer->screenshot(name); }

View file

@ -29,6 +29,18 @@ namespace PICA {
std::array<u32, 4 * 6> tevConfigs; std::array<u32, 4 * 6> tevConfigs;
}; };
struct FogConfig {
union {
u32 raw{};
BitField<0, 3, FogMode> mode;
BitField<3, 1, u32> flipDepth;
BitField<8, 8, u32> fogColorR;
BitField<16, 8, u32> fogColorG;
BitField<24, 8, u32> fogColorB;
};
};
struct Light { struct Light {
union { union {
u16 raw; u16 raw;
@ -189,6 +201,7 @@ namespace PICA {
struct FragmentConfig { struct FragmentConfig {
OutputConfig outConfig; OutputConfig outConfig;
TextureConfig texConfig; TextureConfig texConfig;
FogConfig fogConfig;
LightingConfig lighting; LightingConfig lighting;
bool operator==(const FragmentConfig& config) const { bool operator==(const FragmentConfig& config) const {
@ -220,12 +233,21 @@ namespace PICA {
setupTevStage(4); setupTevStage(4);
setupTevStage(5); setupTevStage(5);
#undef setupTevStage #undef setupTevStage
fogConfig.mode = (FogMode)Helpers::getBits<0, 3>(regs[InternalRegs::TexEnvUpdateBuffer]);
if (fogConfig.mode == FogMode::Fog) {
fogConfig.flipDepth = Helpers::getBit<16>(regs[InternalRegs::TexEnvUpdateBuffer]);
fogConfig.fogColorR = Helpers::getBits<0, 8>(regs[InternalRegs::FogColor]);
fogConfig.fogColorG = Helpers::getBits<8, 8>(regs[InternalRegs::FogColor]);
fogConfig.fogColorB = Helpers::getBits<16, 8>(regs[InternalRegs::FogColor]);
}
} }
}; };
static_assert( static_assert(
std::has_unique_object_representations<OutputConfig>() && std::has_unique_object_representations<TextureConfig>() && std::has_unique_object_representations<OutputConfig>() && std::has_unique_object_representations<TextureConfig>() &&
std::has_unique_object_representations<Light>() std::has_unique_object_representations<FogConfig>() && std::has_unique_object_representations<Light>()
); );
} // namespace PICA } // namespace PICA

View file

@ -51,6 +51,18 @@ namespace PICA {
#undef defineTexEnv #undef defineTexEnv
// clang-format on // clang-format on
// Fog registers
FogColor = 0xE1,
FogLUTIndex = 0xE6,
FogLUTData0 = 0xE8,
FogLUTData1 = 0xE9,
FogLUTData2 = 0xEA,
FogLUTData3 = 0xEB,
FogLUTData4 = 0xEC,
FogLUTData5 = 0xED,
FogLUTData6 = 0xEE,
FogLUTData7 = 0xEF,
// Framebuffer registers // Framebuffer registers
ColourOperation = 0x100, ColourOperation = 0x100,
BlendFunc = 0x101, BlendFunc = 0x101,
@ -384,6 +396,12 @@ namespace PICA {
GreaterOrEqual = 7, GreaterOrEqual = 7,
}; };
enum class FogMode : u32 {
Disabled = 0,
Fog = 5,
Gas = 7,
};
struct TexEnvConfig { struct TexEnvConfig {
enum class Source : u8 { enum class Source : u8 {
PrimaryColor = 0x0, PrimaryColor = 0x0,

View file

@ -220,13 +220,9 @@ class PICAShader {
public: public:
static constexpr size_t maxInstructionCount = 4096; static constexpr size_t maxInstructionCount = 4096;
std::array<u32, maxInstructionCount> loadedShader; // Currently loaded & active shader std::array<u32, maxInstructionCount> loadedShader; // Currently loaded & active shader
std::array<u32, maxInstructionCount> bufferedShader; // Shader to be transferred when the SH_CODETRANSFER_END reg gets written to
PICAShader(ShaderType type) : type(type) {} PICAShader(ShaderType type) : type(type) {}
// Theese functions are in the header to be inlined more easily, though with LTO I hope I'll be able to move them
void finalize() { std::memcpy(&loadedShader[0], &bufferedShader[0], 4096 * sizeof(u32)); }
void setBufferIndex(u32 index) { bufferIndex = index & 0xfff; } void setBufferIndex(u32 index) { bufferIndex = index & 0xfff; }
void setOpDescriptorIndex(u32 index) { opDescriptorIndex = index & 0x7f; } void setOpDescriptorIndex(u32 index) { opDescriptorIndex = index & 0x7f; }
@ -235,7 +231,7 @@ class PICAShader {
Helpers::panic("o no, shader upload overflew"); Helpers::panic("o no, shader upload overflew");
} }
bufferedShader[bufferIndex++] = word; loadedShader[bufferIndex++] = word;
bufferIndex &= 0xfff; bufferIndex &= 0xfff;
codeHashDirty = true; // Signal the JIT if necessary that the program hash has potentially changed codeHashDirty = true; // Signal the JIT if necessary that the program hash has potentially changed

View file

@ -24,6 +24,8 @@ namespace PICA::ShaderGen {
void compileLUTLookup(std::string& shader, const PICA::FragmentConfig& config, u32 lightIndex, u32 lutID); void compileLUTLookup(std::string& shader, const PICA::FragmentConfig& config, u32 lightIndex, u32 lutID);
bool isSamplerEnabled(u32 environmentID, u32 lutID); bool isSamplerEnabled(u32 environmentID, u32 lutID);
void compileFog(std::string& shader, const PICA::FragmentConfig& config);
public: public:
FragmentGenerator(API api, Language language) : api(api), language(language) {} FragmentGenerator(API api, Language language) : api(api), language(language) {}
std::string generate(const PICA::FragmentConfig& config); std::string generate(const PICA::FragmentConfig& config);

View file

@ -50,6 +50,7 @@ struct NCCH {
static constexpr u64 mediaUnit = 0x200; static constexpr u64 mediaUnit = 0x200;
u64 size = 0; // Size of NCCH converted to bytes u64 size = 0; // Size of NCCH converted to bytes
u64 saveDataSize = 0;
u32 stackSize = 0; u32 stackSize = 0;
u32 bssSize = 0; u32 bssSize = 0;
u32 exheaderSize = 0; u32 exheaderSize = 0;
@ -64,8 +65,6 @@ struct NCCH {
// Contents of the .code file in the ExeFS // Contents of the .code file in the ExeFS
std::vector<u8> codeFile; std::vector<u8> codeFile;
// Contains of the cart's save data
std::vector<u8> saveData;
// The cart region. Only the CXI's region matters to us. Necessary to get past region locking // The cart region. Only the CXI's region matters to us. Necessary to get past region locking
std::optional<Regions> region = std::nullopt; std::optional<Regions> region = std::nullopt;
std::vector<u8> smdh; std::vector<u8> smdh;
@ -78,7 +77,7 @@ struct NCCH {
bool hasExeFS() { return exeFS.size != 0; } bool hasExeFS() { return exeFS.size != 0; }
bool hasRomFS() { return romFS.size != 0; } bool hasRomFS() { return romFS.size != 0; }
bool hasCode() { return codeFile.size() != 0; } bool hasCode() { return codeFile.size() != 0; }
bool hasSaveData() { return saveData.size() != 0; } bool hasSaveData() { return saveDataSize != 0; }
// Parse SMDH for region info and such. Returns false on failure, true on success // Parse SMDH for region info and such. Returns false on failure, true on success
bool parseSMDH(const std::vector<u8> &smdh); bool parseSMDH(const std::vector<u8> &smdh);

View file

@ -63,7 +63,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexBuffer dummyVBO; OpenGL::VertexBuffer dummyVBO;
OpenGL::Texture screenTexture; OpenGL::Texture screenTexture;
OpenGL::Texture lightLUTTexture; OpenGL::Texture LUTTexture;
OpenGL::Framebuffer screenFramebuffer; OpenGL::Framebuffer screenFramebuffer;
OpenGL::Texture blankTexture; OpenGL::Texture blankTexture;
// The "default" vertex shader to use when using specialized shaders but not PICA vertex shader -> GLSL recompilation // The "default" vertex shader to use when using specialized shaders but not PICA vertex shader -> GLSL recompilation
@ -90,6 +90,7 @@ class RendererGL final : public Renderer {
void setupUbershaderTexEnv(); void setupUbershaderTexEnv();
void bindTexturesToSlots(); void bindTexturesToSlots();
void updateLightingLUT(); void updateLightingLUT();
void updateFogLUT();
void initGraphicsContextInternal(); void initGraphicsContextInternal();
public: public:

View file

@ -74,6 +74,9 @@ void GPU::reset() {
lightingLUT.fill(0); lightingLUT.fill(0);
lightingLUTDirty = true; lightingLUTDirty = true;
fogLUT.fill(0);
fogLUTDirty = true;
totalAttribCount = 0; totalAttribCount = 0;
fixedAttribMask = 0; fixedAttribMask = 0;
fixedAttribIndex = 0; fixedAttribIndex = 0;

View file

@ -135,6 +135,21 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break; break;
} }
case FogLUTData0:
case FogLUTData1:
case FogLUTData2:
case FogLUTData3:
case FogLUTData4:
case FogLUTData5:
case FogLUTData6:
case FogLUTData7: {
const uint32_t index = regs[FogLUTIndex] & 0x7F;
fogLUT[index] = value;
fogLUTDirty = true;
regs[FogLUTIndex] = (index + 1) & 0x7F;
break;
}
case LightingLUTData0: case LightingLUTData0:
case LightingLUTData1: case LightingLUTData1:
case LightingLUTData2: case LightingLUTData2:
@ -314,9 +329,11 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break; break;
} }
/* TODO: Find out if this actually does anything
case VertexShaderTransferEnd: case VertexShaderTransferEnd:
if (value != 0) shaderUnit.vs.finalize(); if (value != 0) shaderUnit.vs.finalize();
break; break;
*/
case VertexShaderTransferIndex: shaderUnit.vs.setBufferIndex(value); break; case VertexShaderTransferIndex: shaderUnit.vs.setBufferIndex(value); break;

View file

@ -130,7 +130,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
uniform sampler2D u_tex0; uniform sampler2D u_tex0;
uniform sampler2D u_tex1; uniform sampler2D u_tex1;
uniform sampler2D u_tex2; uniform sampler2D u_tex2;
uniform sampler2D u_tex_lighting_lut; uniform sampler2D u_tex_luts;
)"; )";
ret += uniformDefinition; ret += uniformDefinition;
@ -144,7 +144,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
} }
float lutLookup(uint lut, int index) { float lutLookup(uint lut, int index) {
return texelFetch(u_tex_lighting_lut, ivec2(index, int(lut)), 0).r; return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
} }
vec3 regToColor(uint reg) { vec3 regToColor(uint reg) {
@ -194,6 +194,8 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
compileTEV(ret, i, config); compileTEV(ret, i, config);
} }
compileFog(ret, config);
applyAlphaTest(ret, config); applyAlphaTest(ret, config);
ret += "fragColor = combinerOutput;\n}"; // End of main function ret += "fragColor = combinerOutput;\n}"; // End of main function
@ -653,3 +655,26 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
} }
} }
} }
void FragmentGenerator::compileFog(std::string& shader, const PICA::FragmentConfig& config) {
if (config.fogConfig.mode != FogMode::Fog) {
return;
}
float r = config.fogConfig.fogColorR / 255.0f;
float g = config.fogConfig.fogColorG / 255.0f;
float b = config.fogConfig.fogColorB / 255.0f;
if (config.fogConfig.flipDepth) {
shader += "float fog_index = (1.0 - depth) * 128.0;\n";
} else {
shader += "float fog_index = depth * 128.0;\n";
}
shader += "float clamped_index = clamp(floor(fog_index), 0.0, 127.0);";
shader += "float delta = fog_index - clamped_index;";
shader += "vec3 fog_color = vec3(" + std::to_string(r) + ", " + std::to_string(g) + ", " + std::to_string(b) + ");";
shader += "vec2 value = texelFetch(u_tex_luts, ivec2(int(clamped_index), 24), 0).rg;"; // fog LUT is past the light LUTs
shader += "float fog_factor = clamp(value.r + value.g * delta, 0.0, 1.0);";
shader += "combinerOutput.rgb = mix(fog_color, combinerOutput.rgb, fog_factor);";
}

View file

@ -9,7 +9,6 @@ void ShaderUnit::reset() {
void PICAShader::reset() { void PICAShader::reset() {
loadedShader.fill(0); loadedShader.fill(0);
bufferedShader.fill(0);
operandDescriptors.fill(0); operandDescriptors.fill(0);
boolUniform = 0; boolUniform = 0;

View file

@ -25,7 +25,6 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
} }
codeFile.clear(); codeFile.clear();
saveData.clear();
smdh.clear(); smdh.clear();
partitionInfo = info; partitionInfo = info;
@ -155,8 +154,7 @@ bool NCCH::loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSIn
} }
} }
const u64 saveDataSize = *(u64*)&exheader[0x1C0 + 0x0]; // Size of save data in bytes saveDataSize = *(u64*)&exheader[0x1C0 + 0x0]; // Size of save data in bytes
saveData.resize(saveDataSize, 0xff);
compressCode = (exheader[0xD] & 1) != 0; compressCode = (exheader[0xD] & 1) != 0;
stackSize = *(u32*)&exheader[0x1C]; stackSize = *(u32*)&exheader[0x1C];

View file

@ -115,10 +115,11 @@ void RendererGL::initGraphicsContextInternal() {
const u32 screenTextureWidth = 400; // Top screen is 400 pixels wide, bottom is 320 const u32 screenTextureWidth = 400; // Top screen is 400 pixels wide, bottom is 320
const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall
lightLUTTexture.create(256, Lights::LUT_Count, GL_R32F); // 24 rows for light, 1 for fog
lightLUTTexture.bind(); LUTTexture.create(256, Lights::LUT_Count + 1, GL_RG32F);
lightLUTTexture.setMinFilter(OpenGL::Linear); LUTTexture.bind();
lightLUTTexture.setMagFilter(OpenGL::Linear); LUTTexture.setMinFilter(OpenGL::Linear);
LUTTexture.setMagFilter(OpenGL::Linear);
auto prevTexture = OpenGL::getTex2D(); auto prevTexture = OpenGL::getTex2D();
@ -353,22 +354,49 @@ void RendererGL::bindTexturesToSlots() {
} }
glActiveTexture(GL_TEXTURE0 + 3); glActiveTexture(GL_TEXTURE0 + 3);
lightLUTTexture.bind(); LUTTexture.bind();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
void RendererGL::updateLightingLUT() { void RendererGL::updateLightingLUT() {
gpu.lightingLUTDirty = false; gpu.lightingLUTDirty = false;
std::array<float, GPU::LightingLutSize> lightingLut; std::array<float, GPU::LightingLutSize * 2> lightingLut;
for (int i = 0; i < gpu.lightingLUT.size(); i++) { for (int i = 0; i < lightingLut.size(); i += 2) {
uint64_t value = gpu.lightingLUT[i] & 0xFFF; uint64_t value = gpu.lightingLUT[i >> 1] & 0xFFF;
lightingLut[i] = (float)(value << 4) / 65535.0f; lightingLut[i] = (float)(value << 4) / 65535.0f;
} }
glActiveTexture(GL_TEXTURE0 + 3); glActiveTexture(GL_TEXTURE0 + 3);
lightLUTTexture.bind(); LUTTexture.bind();
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, Lights::LUT_Count, GL_RED, GL_FLOAT, lightingLut.data()); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, Lights::LUT_Count, GL_RG, GL_FLOAT, lightingLut.data());
glActiveTexture(GL_TEXTURE0);
}
void RendererGL::updateFogLUT() {
gpu.fogLUTDirty = false;
// Fog LUT elements are of this type:
// 0-12 fixed1.1.11, Difference from next element
// 13-23 fixed0.0.11, Value
// We will store them as a 128x1 RG texture with R being the value and G being the difference
std::array<float, 128 * 2> fogLut;
for (int i = 0; i < fogLut.size(); i += 2) {
const uint32_t value = gpu.fogLUT[i >> 1];
int32_t diff = value & 0x1fff;
diff = (diff << 19) >> 19; // Sign extend the 13-bit value to 32 bits
const float fogDifference = float(diff) / 2048.0f;
const float fogValue = float((value >> 13) & 0x7ff) / 2048.0f;
fogLut[i] = fogValue;
fogLut[i + 1] = fogDifference;
}
glActiveTexture(GL_TEXTURE0 + 3);
LUTTexture.bind();
// The fog LUT exists at the end of the lighting LUT
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, Lights::LUT_Count, 128, 1, GL_RG, GL_FLOAT, fogLut.data());
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
} }
@ -453,6 +481,10 @@ void RendererGL::drawVertices(PICA::PrimType primType, std::span<const Vertex> v
bindTexturesToSlots(); bindTexturesToSlots();
if (gpu.fogLUTDirty) {
updateFogLUT();
}
if (gpu.lightingLUTDirty) { if (gpu.lightingLUTDirty) {
updateLightingLUT(); updateLightingLUT();
} }
@ -499,7 +531,6 @@ void RendererGL::display() {
gl.disableScissor(); gl.disableScissor();
gl.disableBlend(); gl.disableBlend();
gl.disableDepth(); gl.disableDepth();
gl.disableScissor();
// This will work fine whether or not logic ops are enabled. We set logic op to copy instead of disabling to avoid state changes // This will work fine whether or not logic ops are enabled. We set logic op to copy instead of disabling to avoid state changes
gl.setLogicOp(GL_COPY); gl.setLogicOp(GL_COPY);
gl.setColourMask(true, true, true, true); gl.setColourMask(true, true, true, true);
@ -811,7 +842,7 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0); glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0);
glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1); glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1);
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2); glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_lighting_lut"), 3); glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
// Allocate memory for the program UBO // Allocate memory for the program UBO
glGenBuffers(1, &programEntry.uboBinding); glGenBuffers(1, &programEntry.uboBinding);
@ -994,9 +1025,9 @@ void RendererGL::initUbershader(OpenGL::Program& program) {
ubershaderData.depthmapEnableLoc = OpenGL::uniformLocation(program, "u_depthmapEnable"); ubershaderData.depthmapEnableLoc = OpenGL::uniformLocation(program, "u_depthmapEnable");
ubershaderData.picaRegLoc = OpenGL::uniformLocation(program, "u_picaRegs"); ubershaderData.picaRegLoc = OpenGL::uniformLocation(program, "u_picaRegs");
// Init sampler objects. Texture 0 goes in texture unit 0, texture 1 in TU 1, texture 2 in TU 2, and the light maps go in TU 3 // Init sampler objects. Texture 0 goes in texture unit 0, texture 1 in TU 1, texture 2 in TU 2 and the LUTs go in TU 3
glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0); glUniform1i(OpenGL::uniformLocation(program, "u_tex0"), 0);
glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1); glUniform1i(OpenGL::uniformLocation(program, "u_tex1"), 1);
glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2); glUniform1i(OpenGL::uniformLocation(program, "u_tex2"), 2);
glUniform1i(OpenGL::uniformLocation(program, "u_tex_lighting_lut"), 3); glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
} }

View file

@ -25,7 +25,7 @@ uniform bool u_depthmapEnable;
uniform sampler2D u_tex0; uniform sampler2D u_tex0;
uniform sampler2D u_tex1; uniform sampler2D u_tex1;
uniform sampler2D u_tex2; uniform sampler2D u_tex2;
uniform sampler2D u_tex_lighting_lut; uniform sampler2D u_tex_luts;
uniform uint u_picaRegs[0x200 - 0x48]; uniform uint u_picaRegs[0x200 - 0x48];
@ -152,6 +152,8 @@ vec4 tevCalculateCombiner(int tev_id) {
#define RG_LUT 5u #define RG_LUT 5u
#define RR_LUT 6u #define RR_LUT 6u
#define FOG_INDEX 24
uint GPUREG_LIGHTi_CONFIG; uint GPUREG_LIGHTi_CONFIG;
uint GPUREG_LIGHTING_CONFIG1; uint GPUREG_LIGHTING_CONFIG1;
uint GPUREG_LIGHTING_LUTINPUT_SELECT; uint GPUREG_LIGHTING_LUTINPUT_SELECT;
@ -161,7 +163,7 @@ bool error_unimpl = false;
vec4 unimpl_color = vec4(1.0, 0.0, 1.0, 1.0); vec4 unimpl_color = vec4(1.0, 0.0, 1.0, 1.0);
float lutLookup(uint lut, int index) { float lutLookup(uint lut, int index) {
return texelFetch(u_tex_lighting_lut, ivec2(index, int(lut)), 0).r; return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
} }
vec3 regToColor(uint reg) { vec3 regToColor(uint reg) {
@ -494,7 +496,7 @@ void main() {
if (tevUnimplementedSourceFlag) { if (tevUnimplementedSourceFlag) {
// fragColour = vec4(1.0, 0.0, 1.0, 1.0); // fragColour = vec4(1.0, 0.0, 1.0, 1.0);
} }
// fragColour.rg = texture(u_tex_lighting_lut,vec2(gl_FragCoord.x/200.,float(int(gl_FragCoord.y/2)%24))).rr; // fragColour.rg = texture(u_tex_luts,vec2(gl_FragCoord.x/200.,float(int(gl_FragCoord.y/2)%24))).rr;
// Get original depth value by converting from [near, far] = [0, 1] to [-1, 1] // Get original depth value by converting from [near, far] = [0, 1] to [-1, 1]
// We do this by converting to [0, 2] first and subtracting 1 to go to [-1, 1] // We do this by converting to [0, 2] first and subtracting 1 to go to [-1, 1]
@ -507,6 +509,28 @@ void main() {
// Write final fragment depth // Write final fragment depth
gl_FragDepth = depth; gl_FragDepth = depth;
bool enable_fog = (textureEnvUpdateBuffer & 7u) == 5u;
if (enable_fog) {
bool flip_depth = (textureEnvUpdateBuffer & (1u << 16)) != 0u;
float fog_index = flip_depth ? 1.0 - depth : depth;
fog_index *= 128.0;
float clamped_index = clamp(floor(fog_index), 0.0, 127.0);
float delta = fog_index - clamped_index;
vec2 value = texelFetch(u_tex_luts, ivec2(int(clamped_index), FOG_INDEX), 0).rg;
float fog_factor = clamp(value.r + value.g * delta, 0.0, 1.0);
uint GPUREG_FOG_COLOR = readPicaReg(0x00E1u);
// Annoyingly color is not encoded in the same way as light color
float r = (GPUREG_FOG_COLOR & 0xFFu) / 255.0;
float g = ((GPUREG_FOG_COLOR >> 8) & 0xFFu) / 255.0;
float b = ((GPUREG_FOG_COLOR >> 16) & 0xFFu) / 255.0;
vec3 fog_color = vec3(r, g, b);
fragColour.rgb = mix(fog_color, fragColour.rgb, fog_factor);
}
// Perform alpha test // Perform alpha test
uint alphaControl = readPicaReg(0x104u); uint alphaControl = readPicaReg(0x104u);
if ((alphaControl & 1u) != 0u) { // Check if alpha test is on if ((alphaControl & 1u) != 0u) { // Check if alpha test is on