6 Commits
Author SHA1 Message Date
popux 0fd290c8c3 Fix 3 2026-10-21 19:31:56 +02:00
popux b20abed21e Fix 2 2026-10-21 19:31:42 +02:00
popux c4f12ad0a5 Fix 2026-10-21 19:31:31 +02:00
popux 60989596d5 Decals fix 2026-10-21 19:31:03 +02:00
popux 256323a109 Filter 2026-10-21 19:30:45 +02:00
popux 1e46e68933 format 2026-10-21 19:30:26 +02:00
3 changed files with 175 additions and 3 deletions

No files matched your search

+14
View File
@@ -0,0 +1,14 @@
{
"clang-format.enable": false,
"C_Cpp.clang_format_style": "none",
"[c]": {
"editor.formatOnSave": false,
"editor.formatOnPaste": false,
"editor.formatOnType": false
},
"[cpp]": {
"editor.formatOnSave": false,
"editor.formatOnPaste": false,
"editor.formatOnType": false
}
}
+160 -2
View File
@@ -33,6 +33,10 @@ struct ShaderProgram {
GLint frame_count_location;
GLint noise_scale_location;
GLint three_point_filter_locations[2];
GLint decal_mode_location;
GLint decal_scale_location;
int decal_mode;
float decal_scale;
};
struct Framebuffer {
@@ -40,6 +44,8 @@ struct Framebuffer {
bool has_depth_buffer;
uint32_t msaa_level;
bool invert_y;
bool depth_copy_checked;
bool depth_copy_failed;
GLuint fbo, clrbuf, clrbuf_msaa, rbo;
};
@@ -58,6 +64,19 @@ static int current_anisotropy_level;
static FilteringMode current_filter_mode = FILTER_LINEAR;
static MipmapFilteringMode current_mipmap_filter_mode = MIPMAP_LINEAR;
static bool current_textures_linear_filter[2] = {false, false};
static struct ShaderProgram* current_shader_program;
// N64 decal depth mode: pixels only pass when close to the stored depth, using a copy of the depth buffer
#define DECAL_DEPTH_TEXTURE_UNIT 2
// Height of the N64 frame; the RDP's depth slope tolerance is measured in N64 pixels
#define DECAL_N64_SCREEN_HEIGHT 240.0f
static bool depth_copy_supported;
static bool depth_copy_dirty = true;
static size_t depth_copy_source;
static GLuint depth_copy_fbo, depth_copy_tex;
static uint32_t depth_copy_width, depth_copy_height;
static bool current_depth_test;
static bool current_depth_decal;
static int gl_glsl_version = 130;
static char gl_glsl_version_str[16] = "130";
@@ -120,6 +139,7 @@ static void gfx_opengl_unload_shader(struct ShaderProgram* old_prg) {
static void gfx_opengl_load_shader(struct ShaderProgram* new_prg) {
// if (!new_prg) return;
current_shader_program = new_prg;
glUseProgram(new_prg->opengl_program_id);
gfx_opengl_vertex_array_set_attribs(new_prg);
gfx_opengl_set_uniforms(new_prg);
@@ -382,6 +402,9 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
append_line(fs_buf, &fs_len, "uniform int frame_count;");
append_line(fs_buf, &fs_len, "uniform float noise_scale;");
append_line(fs_buf, &fs_len, "uniform int uDecalMode;");
append_line(fs_buf, &fs_len, "uniform highp float uDecalPixelScale;");
append_line(fs_buf, &fs_len, "uniform highp sampler2D uDepthTex;");
append_line(fs_buf, &fs_len, "float random(in vec3 value) {");
append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));");
@@ -442,6 +465,15 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
append_line(fs_buf, &fs_len, "void main() {");
// RDP decal Z mode: pass only if |z - stored z| <= dz (the per-pixel depth slope), and never over cleared depth.
// The slope is computed before anything can discard, since derivatives are undefined after a neighbor discards.
append_line(fs_buf, &fs_len, " highp float decalSlope = fwidth(gl_FragCoord.z);");
append_line(fs_buf, &fs_len, " if (uDecalMode != 0) {");
append_line(fs_buf, &fs_len, " highp float storedZ = texelFetch(uDepthTex, ivec2(gl_FragCoord.xy), 0).r;");
append_line(fs_buf, &fs_len, " highp float dz = max(decalSlope * uDecalPixelScale, 1.0 / 262144.0);");
append_line(fs_buf, &fs_len, " if (storedZ >= 1.0 || abs(gl_FragCoord.z - storedZ) > dz) discard;");
append_line(fs_buf, &fs_len, " }");
for (int i = 0; i < 2; i++) {
if (cc_features.used_textures[i]) {
bool s = cc_features.clamp[i][0], t = cc_features.clamp[i][1];
@@ -652,6 +684,14 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale");
prg->three_point_filter_locations[0] = glGetUniformLocation(shader_program, "three_point_filter0");
prg->three_point_filter_locations[1] = glGetUniformLocation(shader_program, "three_point_filter1");
prg->decal_mode_location = glGetUniformLocation(shader_program, "uDecalMode");
prg->decal_scale_location = glGetUniformLocation(shader_program, "uDecalPixelScale");
prg->decal_mode = 0;
prg->decal_scale = 0.0f;
if (prg->decal_mode_location >= 0) {
glUniform1i(prg->decal_mode_location, 0);
glUniform1i(glGetUniformLocation(shader_program, "uDepthTex"), DECAL_DEPTH_TEXTURE_UNIT);
}
gfx_opengl_load_shader(prg);
@@ -671,6 +711,7 @@ static void gfx_opengl_shader_get_info(struct ShaderProgram* prg, uint8_t* num_i
static void gfx_opengl_clear_shaders(void) {
glUseProgram(0);
current_shader_program = nullptr;
for (auto& pair : shader_program_pool) {
glDeleteProgram(pair.second.opengl_program_id);
}
@@ -725,8 +766,8 @@ static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint
mipmaps = mipmaps && (current_mipmap_filter_mode != MIPMAP_DISABLED);
const int mip_idx = mipmaps ? current_mipmap_filter_mode : 0;
const GLint min_filter = linear_filter ? min_filters[current_filter_mode][mip_idx] : GL_NEAREST;
const GLint max_filter = linear_filter && (current_filter_mode == FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST;
const GLint min_filter = linear_filter ? min_filters[current_filter_mode][mip_idx] : GL_LINEAR;
const GLint max_filter = linear_filter && (current_filter_mode == FILTER_LINEAR) ? GL_LINEAR : GL_LINEAR;
glActiveTexture(GL_TEXTURE0 + tile);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
@@ -741,6 +782,9 @@ static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint
}
static void gfx_opengl_set_depth_mode(bool depth_test, bool depth_update, bool depth_compare, bool depth_source_prim, uint16_t zmode) {
current_depth_test = depth_test;
current_depth_decal = depth_test && depth_compare && zmode == ZMODE_DEC;
if (depth_test) {
glEnable(GL_DEPTH_TEST);
glDepthMask(depth_update ? GL_TRUE : GL_FALSE);
@@ -810,10 +854,116 @@ static void gfx_opengl_set_use_alpha(bool use_alpha, bool modulate) {
}
}
static bool gfx_opengl_update_depth_copy(void) {
if (!depth_copy_supported) {
return false;
}
Framebuffer& fb = framebuffers[current_framebuffer];
if ((current_framebuffer != 0 && !fb.has_depth_buffer) || fb.depth_copy_failed) {
return false;
}
if (!depth_copy_dirty && depth_copy_source == current_framebuffer && depth_copy_width == fb.width &&
depth_copy_height == fb.height) {
return true;
}
if (depth_copy_tex == 0 || depth_copy_width != fb.width || depth_copy_height != fb.height) {
if (depth_copy_tex == 0) {
glGenTextures(1, &depth_copy_tex);
glGenFramebuffers(1, &depth_copy_fbo);
}
GLint active_texture;
glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture);
glActiveTexture(GL_TEXTURE0 + DECAL_DEPTH_TEXTURE_UNIT);
glBindTexture(GL_TEXTURE_2D, depth_copy_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, fb.width, fb.height, 0, GL_DEPTH_STENCIL,
GL_UNSIGNED_INT_24_8, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glActiveTexture(active_texture);
const GLenum no_draw_buffer = GL_NONE;
glBindFramebuffer(GL_FRAMEBUFFER, depth_copy_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depth_copy_tex, 0);
glDrawBuffers(1, &no_draw_buffer);
glReadBuffer(GL_NONE);
depth_copy_width = fb.width;
depth_copy_height = fb.height;
}
const bool check_errors = !fb.depth_copy_checked;
if (check_errors) {
for (int i = 0; i < 16 && glGetError() != GL_NO_ERROR; i++) {
}
}
glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, depth_copy_fbo);
glBlitFramebuffer(0, 0, fb.width, fb.height, 0, 0, fb.width, fb.height, GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
glEnable(GL_SCISSOR_TEST);
if (check_errors) {
fb.depth_copy_checked = true;
if (glGetError() != GL_NO_ERROR) {
fb.depth_copy_failed = true;
sysLogPrintf(LOG_WARNING, "GL: could not copy depth buffer of framebuffer %u, decal edges will not be clipped",
(unsigned)current_framebuffer);
return false;
}
}
depth_copy_dirty = false;
depth_copy_source = current_framebuffer;
return true;
}
static void gfx_opengl_set_decal_uniforms(struct ShaderProgram* prg, bool enabled) {
if (prg == nullptr || prg->decal_mode_location < 0) {
return;
}
const int mode = enabled ? 1 : 0;
if (prg->decal_mode != mode) {
glUniform1i(prg->decal_mode_location, mode);
prg->decal_mode = mode;
}
const float scale = framebuffers[current_framebuffer].height / DECAL_N64_SCREEN_HEIGHT;
if (enabled && prg->decal_scale != scale) {
glUniform1f(prg->decal_scale_location, scale);
prg->decal_scale = scale;
}
}
static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) {
// printf("flushing %d tris\n", buf_vbo_num_tris);
if (current_depth_decal) {
// The shader replaces the depth test like the RDP does; fall back to polygon offset if depth can't be copied
const bool decal = gfx_opengl_update_depth_copy();
glDepthFunc(decal ? GL_ALWAYS : GL_LEQUAL);
if (decal) {
glDisable(GL_POLYGON_OFFSET_FILL);
} else {
glEnable(GL_POLYGON_OFFSET_FILL);
}
gfx_opengl_set_decal_uniforms(current_shader_program, decal);
} else {
gfx_opengl_set_decal_uniforms(current_shader_program, false);
}
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buf_vbo_len, buf_vbo, GL_STREAM_DRAW);
glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris);
if (current_depth_test && current_depth_mask) {
depth_copy_dirty = true;
}
}
typedef void (APIENTRY *DEBUGPROC)(GLenum source,
@@ -990,6 +1140,11 @@ static void gfx_opengl_init(void) {
gfx_framebuffers_enabled = false;
}
depth_copy_supported = GLVersion.major >= 3 && gfx_opengl_supports_framebuffers() && glDrawBuffers && glReadBuffer;
if (!depth_copy_supported) {
sysLogPrintf(LOG_WARNING, "GL: depth buffer copies unsupported, decal edges will not be clipped");
}
if ((GLVersion.major < 4 || GLVersion.minor < 4) && !GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
// GL_MIRROR_CLAMP_TO_EDGE unsupported
gl_mirror_clamp = GL_MIRRORED_REPEAT;
@@ -1138,6 +1293,7 @@ static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width,
fb.has_depth_buffer = has_depth_buffer;
fb.msaa_level = msaa_level;
fb.invert_y = opengl_invert_y;
depth_copy_dirty = true;
}
bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
@@ -1148,6 +1304,7 @@ bool gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) {
}
glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo);
current_framebuffer = fb_id;
depth_copy_dirty = true;
return true;
} else {
return false;
@@ -1165,6 +1322,7 @@ void gfx_opengl_clear_framebuffer(bool clear_color, bool clear_depth) {
if (clear_depth) {
glDepthMask(GL_TRUE);
mask |= GL_DEPTH_BUFFER_BIT;
depth_copy_dirty = true;
}
glClear(mask);
if (clear_depth) {
+1 -1
View File
@@ -1996,7 +1996,7 @@ MenuItemHandlerResult menuhandlerMissionList(s32 operation, struct menuitem *ite
texSelect(&gdl, g_TexGeneralConfigs + 13 + stageindex, 2, 0, 2, true, NULL);
gDPSetCycleType(gdl++, G_CYC_1CYCLE);
gDPSetCombineMode(gdl++, G_CC_CUSTOM_00, G_CC_CUSTOM_00);
gDPSetTextureFilter(gdl++, G_TF_POINT);
gDPSetTextureFilter(gdl++, G_TF_AVERAGE);
gDPSetEnvColorViaWord(gdl++, 0xffffff00 | ((renderdata->colour & 0xff) * 255 / 256));
#else
texSelect(&gdl, g_TexGeneralConfigs + 13 + stageindex, 1, 0, 2, true, NULL);