From fb883a82a720055f9866cb2b6ca2c0f179579477 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sat, 28 Sep 2024 21:18:18 +0300 Subject: [PATCH 1/3] stencil: remove a couple of useless lines The EFB copy operation is not clearing the EFB, so setting the clear color and Z-value here does not have any effect. --- src/stencil.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stencil.c b/src/stencil.c index 867887c..96733fd 100644 --- a/src/stencil.c +++ b/src/stencil.c @@ -447,8 +447,6 @@ static bool draw_op(uint16_t op, if (_ogx_efb_content_type == OGX_EFB_SCENE) { debug(OGX_LOG_STENCIL, "Saving scene EFB, clearing, loading stencil"); - GXColor black = { 0, 0, 0, 255 }; - GX_SetCopyClear(black, GX_MAX_Z24); GX_DrawDone(); _ogx_efb_save(OGX_EFB_COLOR); From c24ad1e89a08e40b952ad5b77de3ef44188fb01f Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sun, 29 Sep 2024 09:38:23 +0300 Subject: [PATCH 2/3] EFB: add a function to switch the contents of the EFB Refactor the existing code in stencil.c, where the EFB was saved and later restored when switching between the stencil and scene drawing targets, to make it more generic and able to support more drawing targets. Now there is a centralized function, _ogx_efb_set_content_type(), which will take care of invoking the right functions to save the current EFB contents to the appropriate buffer, as well as to load the target buffer (and set it up, if it had not been used before). This will allow us to more easily support the accumulation buffer (see the glAccum function). --- src/efb.c | 98 +++++++++++++++++++++++++-------------------------- src/efb.h | 26 ++++++++++++-- src/gc_gl.c | 46 ++++++++++++++++++++++++ src/stencil.c | 57 +++++++++++++----------------- src/stencil.h | 3 ++ 5 files changed, 146 insertions(+), 84 deletions(-) diff --git a/src/efb.c b/src/efb.c index 6daf739..5f178c4 100644 --- a/src/efb.c +++ b/src/efb.c @@ -41,55 +41,6 @@ POSSIBILITY OF SUCH DAMAGE. OgxEfbContentType _ogx_efb_content_type = OGX_EFB_SCENE; -static GXTexObj s_efb_texture; -/* This is the ID of the drawing operation that was copied last (0 = none) */ -static int s_draw_count_copied = 0; - -void _ogx_efb_save(OgxEfbFlags flags) -{ - /* TODO: support saving Z-buffer (code in selection.c) */ - - if (s_draw_count_copied == glparamstate.draw_count) { - printf("Not copying EFB\n"); - /* We already copied this frame, nothing to do here */ - return; - } - - s_draw_count_copied = glparamstate.draw_count; - - u16 width = glparamstate.viewport[2]; - u16 height = glparamstate.viewport[3]; - u16 oldwidth = GX_GetTexObjWidth(&s_efb_texture); - u16 oldheight = GX_GetTexObjHeight(&s_efb_texture); - uint8_t *texels = GX_GetTexObjData(&s_efb_texture); - if (texels) { - texels = MEM_PHYSICAL_TO_K0(texels); - } - - if (width != oldwidth || height != oldheight) { - if (texels) { - free(texels); - } - u32 size = GX_GetTexBufferSize(width, height, GX_TF_RGBA8, 0, GX_FALSE); - texels = memalign(32, size); - DCInvalidateRange(texels, size); - - GX_InitTexObj(&s_efb_texture, texels, width, height, - GX_TF_RGBA8, GX_CLAMP, GX_CLAMP, GX_FALSE); - GX_InitTexObjLOD(&s_efb_texture, GX_NEAR, GX_NEAR, - 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); - } - - _ogx_efb_save_to_buffer(GX_TF_RGBA8, width, height, texels, flags); -} - -void _ogx_efb_restore(OgxEfbFlags flags) -{ - /* TODO: support restoring Z-buffer (code in selection.c) */ - - _ogx_efb_restore_texobj(&s_efb_texture); -} - void _ogx_efb_save_to_buffer(uint8_t format, uint16_t width, uint16_t height, void *texels, OgxEfbFlags flags) { @@ -153,3 +104,52 @@ void _ogx_efb_restore_texobj(GXTexObj *texobj) GX_TexCoord2u8(1, 0); GX_End(); } + +void _ogx_efb_buffer_prepare(OgxEfbBuffer **buffer, uint8_t format) +{ + if (*buffer) return; + + u16 width = glparamstate.viewport[2]; + u16 height = glparamstate.viewport[3]; + u32 size = GX_GetTexBufferSize(width, height, GX_TF_RGBA8, 0, GX_FALSE); + OgxEfbBuffer *b = memalign(32, size + sizeof(OgxEfbBuffer)); + void *texels = &(b->texels[0]); + DCInvalidateRange(texels, size); + + GX_InitTexObj(&b->texobj, texels, width, height, format, + GX_CLAMP, GX_CLAMP, GX_FALSE); + GX_InitTexObjLOD(&b->texobj, GX_NEAR, GX_NEAR, + 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); + b->draw_count = 0; + *buffer = b; +} + +void _ogx_efb_buffer_handle_resize(OgxEfbBuffer **buffer) +{ + /* If no buffer was allocated, nothing to do */ + if (!*buffer) return; + + void *texels; + u8 format, unused; + u16 oldwidth, oldheight; + GX_GetTexObjAll(&(*buffer)->texobj, &texels, &oldwidth, &oldheight, &format, + &unused, &unused, &unused); + + u16 width = glparamstate.viewport[2]; + u16 height = glparamstate.viewport[3]; + if (width != oldwidth || height != oldheight) { + free(*buffer); + *buffer = NULL; + _ogx_efb_buffer_prepare(buffer, format); + } +} + +void _ogx_efb_buffer_save(OgxEfbBuffer *buffer, OgxEfbFlags flags) +{ + void *texels; + u8 format, unused; + u16 width, height; + GX_GetTexObjAll(&buffer->texobj, &texels, &width, &height, &format, + &unused, &unused, &unused); + _ogx_efb_save_to_buffer(format, width, height, texels, flags); +} diff --git a/src/efb.h b/src/efb.h index 95a6675..064fc43 100644 --- a/src/efb.h +++ b/src/efb.h @@ -51,11 +51,31 @@ typedef enum { extern OgxEfbContentType _ogx_efb_content_type; -void _ogx_efb_save(OgxEfbFlags flags); -void _ogx_efb_restore(OgxEfbFlags flags); - void _ogx_efb_save_to_buffer(uint8_t format, uint16_t width, uint16_t height, void *texels, OgxEfbFlags flags); void _ogx_efb_restore_texobj(GXTexObj *texobj); +typedef struct { + GXTexObj texobj; + /* buffer-specific counter indicating what was the last draw operation + * saved into this buffer */ + int draw_count; + /* The texel data are stored in the same memory block at the end of this + * struct */ + _Alignas(32) uint8_t texels[0]; +} OgxEfbBuffer; + +void _ogx_efb_buffer_prepare(OgxEfbBuffer **buffer, uint8_t format); +void _ogx_efb_buffer_handle_resize(OgxEfbBuffer **buffer); +void _ogx_efb_buffer_save(OgxEfbBuffer *buffer, OgxEfbFlags flags); + +void _ogx_efb_set_content_type_real(OgxEfbContentType content_type); + +/* We inline this part since most of the times the desired content type will be + * the one already active */ +static inline void _ogx_efb_set_content_type(OgxEfbContentType content_type) { + if (content_type == _ogx_efb_content_type) return; + _ogx_efb_set_content_type_real(content_type); +} + #endif /* OPENGX_EFB_H */ diff --git a/src/gc_gl.c b/src/gc_gl.c index dfa4006..bb882d5 100644 --- a/src/gc_gl.c +++ b/src/gc_gl.c @@ -48,6 +48,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "call_lists.h" #include "clip.h" #include "debug.h" +#include "efb.h" #include "opengx.h" #include "selection.h" #include "state.h" @@ -77,6 +78,7 @@ typedef struct } DrawMode; char _ogx_log_level = 0; +static OgxEfbBuffer *s_efb_scene_buffer = NULL; static GXTexObj s_zbuffer_texture; static uint8_t s_zbuffer_texels[2 * 32] ATTRIBUTE_ALIGN(32); /* Force the inclusion of functions.c's TU in the build when GL functions are @@ -417,6 +419,49 @@ void _ogx_setup_3D_projection() update_projection_matrix(); } +static void scene_save_to_efb() +{ + _ogx_efb_buffer_prepare(&s_efb_scene_buffer, GX_TF_RGBA8); + if (s_efb_scene_buffer->draw_count == glparamstate.draw_count) return; + + GX_DrawDone(); + _ogx_efb_buffer_save(s_efb_scene_buffer, OGX_EFB_COLOR); + s_efb_scene_buffer->draw_count = glparamstate.draw_count; +} + +static void scene_load_from_efb() +{ + if (!s_efb_scene_buffer) return; + _ogx_efb_restore_texobj(&s_efb_scene_buffer->texobj); +} + +/* This function might fit best in efb.c, but since it uses symbols from other + * files, it's better to define it here to avoid cross-dependencies (which are + * mostly harmless, but not clean). */ +void _ogx_efb_set_content_type_real(OgxEfbContentType content_type) +{ + /* Save existing EFB contents, if needed */ + switch (_ogx_efb_content_type) { + case OGX_EFB_SCENE: + scene_save_to_efb(); + break; + case OGX_EFB_STENCIL: + _ogx_stencil_save_to_efb(); + break; + } + + /* Restore data from previously stored EFB for this content type */ + switch (content_type) { + case OGX_EFB_SCENE: + scene_load_from_efb(); + break; + case OGX_EFB_STENCIL: + _ogx_stencil_load_into_efb(); + break; + } + _ogx_efb_content_type = content_type; +} + void glEnable(GLenum cap) { // TODO HANDLE_CALL_LIST(ENABLE, cap); @@ -862,6 +907,7 @@ void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) GX_SetViewport(x, y, width, height, 0.0f, 1.0f); GX_SetScissor(x, y, width, height); _ogx_stencil_update(); + _ogx_efb_buffer_handle_resize(&s_efb_scene_buffer); } void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) diff --git a/src/stencil.c b/src/stencil.c index 96733fd..dd00ea5 100644 --- a/src/stencil.c +++ b/src/stencil.c @@ -244,7 +244,7 @@ static void update_stencil_texture() memset(&s_dirty_area, 0, sizeof(s_dirty_area)); } -static void load_stencil_into_efb() +void _ogx_stencil_load_into_efb() { GXTexObj texture; @@ -260,7 +260,28 @@ static void load_stencil_into_efb() GX_InvalidateTexAll(); _ogx_efb_restore_texobj(&texture); - _ogx_efb_content_type = OGX_EFB_STENCIL; + /* We clear the bounding box because at the end of the drawing + * operations on the stencil buffer we will need to update the stencil + * texture which we use for the actual drawing, and the bounding box + * allows us to do it more efficiently. */ + GX_DrawDone(); + GX_ClearBoundingBox(); + _ogx_setup_3D_projection(); + + /* When restoring the EFB we alter the cull mode, Z mode, alpha + * compare and more. All these settings need to be restored. */ + _ogx_apply_state(); +} + +void _ogx_stencil_save_to_efb() +{ + u16 width = GX_GetTexObjWidth(&s_stencil_texture); + u16 height = GX_GetTexObjHeight(&s_stencil_texture); + GX_DrawDone(); + check_bounding_box(); + debug(OGX_LOG_STENCIL, "Saving EFB to stencil buffer, restoring color"); + _ogx_efb_save_to_buffer(s_stencil_format, width, height, + s_stencil_buffer, OGX_EFB_COLOR); } static bool setup_tev_full(int *stages, int *tex_coords, @@ -444,25 +465,7 @@ static bool draw_op(uint16_t op, drawColor = refColor; } - if (_ogx_efb_content_type == OGX_EFB_SCENE) { - debug(OGX_LOG_STENCIL, "Saving scene EFB, clearing, loading stencil"); - - GX_DrawDone(); - _ogx_efb_save(OGX_EFB_COLOR); - - load_stencil_into_efb(); - /* We clear the bounding box because at the end of the drawing - * operations on the stencil buffer we will need to update the stencil - * texture which we use for the actual drawing, and the bounding box - * allows us to do it more efficiently. */ - GX_DrawDone(); - GX_ClearBoundingBox(); - _ogx_setup_3D_projection(); - - /* When restoring the EFB we alter the cull mode, Z mode, alpha - * compare and more. All these settings need to be restored. */ - _ogx_apply_state(); - } + _ogx_efb_set_content_type(OGX_EFB_STENCIL); /* Unconditionally enable color updates when drawing on the stencil buffer. */ @@ -559,17 +562,7 @@ void _ogx_stencil_draw(OgxStencilDrawCallback callback, void *cb_data) callback, cb_data); } - if (_ogx_efb_content_type == OGX_EFB_STENCIL) { - u16 width = GX_GetTexObjWidth(&s_stencil_texture); - u16 height = GX_GetTexObjHeight(&s_stencil_texture); - GX_DrawDone(); - check_bounding_box(); - debug(OGX_LOG_STENCIL, "Saving EFB to stencil buffer, restoring color"); - _ogx_efb_save_to_buffer(s_stencil_format, width, height, - s_stencil_buffer, OGX_EFB_COLOR); - _ogx_efb_restore(OGX_EFB_COLOR); - _ogx_efb_content_type = OGX_EFB_SCENE; - } + _ogx_efb_set_content_type(OGX_EFB_SCENE); glparamstate.dirty.bits.dirty_texture_gen = 1; } diff --git a/src/stencil.h b/src/stencil.h index a4d86c1..9771cc2 100644 --- a/src/stencil.h +++ b/src/stencil.h @@ -53,4 +53,7 @@ bool _ogx_stencil_setup_tev(int *stages, int *tex_coords, typedef void (*OgxStencilDrawCallback)(void *data); void _ogx_stencil_draw(OgxStencilDrawCallback callback, void *cb_data); +void _ogx_stencil_load_into_efb(); +void _ogx_stencil_save_to_efb(); + #endif /* OPENGX_STENCIL_H */ From c5a745b0fa9f97a3bd58ab91244532e05ea700d6 Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Sun, 29 Sep 2024 10:08:57 +0300 Subject: [PATCH 3/3] stencil: use new EFB functions for buffer management --- src/efb.h | 5 +++++ src/stencil.c | 31 +++++++++---------------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/efb.h b/src/efb.h index 064fc43..31f1859 100644 --- a/src/efb.h +++ b/src/efb.h @@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include typedef enum { OGX_EFB_NONE = 0, @@ -68,6 +69,10 @@ typedef struct { void _ogx_efb_buffer_prepare(OgxEfbBuffer **buffer, uint8_t format); void _ogx_efb_buffer_handle_resize(OgxEfbBuffer **buffer); void _ogx_efb_buffer_save(OgxEfbBuffer *buffer, OgxEfbFlags flags); +static inline void *_ogx_efb_buffer_get_texels(OgxEfbBuffer *buffer) { + void *texels = GX_GetTexObjData(&buffer->texobj); + return texels ? MEM_PHYSICAL_TO_K0(texels) : NULL; +} void _ogx_efb_set_content_type_real(OgxEfbContentType content_type); diff --git a/src/stencil.c b/src/stencil.c index dd00ea5..26857ea 100644 --- a/src/stencil.c +++ b/src/stencil.c @@ -46,7 +46,7 @@ static uint8_t s_stencil_format = GX_CTF_R4; OgxStencilFlags _ogx_stencil_flags = OGX_STENCIL_NONE; /* This is the authoritative stencil buffer contents: note that the order of * the pixel data follows the GX texture scrambling logic. */ -static uint8_t *s_stencil_buffer = NULL; +static OgxEfbBuffer *s_stencil_buffer = NULL; /* This is a simplified version of the stencil buffer only used for drawing: * its pixels are set to 0 for blocked areas and > 0 for paintable areas. */ static GXTexObj s_stencil_texture; @@ -183,7 +183,7 @@ static void update_stencil_texture() int block_end_x = (right + block_width - 1) / block_width; int width_blocks = block_end_x - block_start_x; - void *stencil_data = s_stencil_buffer; + void *stencil_data = _ogx_efb_buffer_get_texels(s_stencil_buffer); void *stencil_texels = MEM_PHYSICAL_TO_K0(GX_GetTexObjData(&s_stencil_texture)); uint8_t masked_ref = glparamstate.stencil.ref & glparamstate.stencil.mask; @@ -246,19 +246,9 @@ static void update_stencil_texture() void _ogx_stencil_load_into_efb() { - GXTexObj texture; - /* The stencil texture object has been created on initialization, get the - * size from it. */ - u16 width = GX_GetTexObjWidth(&s_stencil_texture); - u16 height = GX_GetTexObjHeight(&s_stencil_texture); - - GX_InitTexObj(&texture, s_stencil_buffer, width, height, - s_stencil_format, GX_CLAMP, GX_CLAMP, GX_FALSE); - GX_InitTexObjLOD(&texture, GX_NEAR, GX_NEAR, - 0.0f, 0.0f, 0.0f, 0, 0, GX_ANISO_1); GX_InvalidateTexAll(); - _ogx_efb_restore_texobj(&texture); + _ogx_efb_restore_texobj(&s_stencil_buffer->texobj); /* We clear the bounding box because at the end of the drawing * operations on the stencil buffer we will need to update the stencil @@ -275,13 +265,10 @@ void _ogx_stencil_load_into_efb() void _ogx_stencil_save_to_efb() { - u16 width = GX_GetTexObjWidth(&s_stencil_texture); - u16 height = GX_GetTexObjHeight(&s_stencil_texture); GX_DrawDone(); check_bounding_box(); debug(OGX_LOG_STENCIL, "Saving EFB to stencil buffer, restoring color"); - _ogx_efb_save_to_buffer(s_stencil_format, width, height, - s_stencil_buffer, OGX_EFB_COLOR); + _ogx_efb_buffer_save(s_stencil_buffer, OGX_EFB_COLOR); } static bool setup_tev_full(int *stages, int *tex_coords, @@ -600,10 +587,9 @@ void _ogx_stencil_update() if (!s_wants_stencil) return; u8 format = s_stencil_format; + _ogx_efb_buffer_prepare(&s_stencil_buffer, format); u32 size = GX_GetTexBufferSize(width, height, format, 0, GX_FALSE); - s_stencil_buffer = memalign(32, size); - memset(s_stencil_buffer, 0, size); - DCStoreRangeNoSync(s_stencil_buffer, size); + memset(_ogx_efb_buffer_get_texels(s_stencil_buffer), 0, size); void *stencil_texels = memalign(32, size); memset(stencil_texels, 0, size); DCStoreRange(stencil_texels, size); @@ -628,8 +614,9 @@ void _ogx_stencil_clear() value |= value << 4; } if (s_stencil_buffer) { - memset(s_stencil_buffer, value, size); - DCStoreRangeNoSync(s_stencil_buffer, size); + void *texels = _ogx_efb_buffer_get_texels(s_stencil_buffer); + memset(texels, value, size); + DCStoreRangeNoSync(texels, size); } uint8_t *texels = GX_GetTexObjData(&s_stencil_texture); if (texels) {