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..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, @@ -51,11 +52,35 @@ 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); +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); + +/* 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 867887c..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; @@ -244,23 +244,31 @@ 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; - /* 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 + * 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_content_type = OGX_EFB_STENCIL; +void _ogx_stencil_save_to_efb() +{ + GX_DrawDone(); + check_bounding_box(); + debug(OGX_LOG_STENCIL, "Saving EFB to stencil buffer, restoring color"); + _ogx_efb_buffer_save(s_stencil_buffer, OGX_EFB_COLOR); } static bool setup_tev_full(int *stages, int *tex_coords, @@ -444,27 +452,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"); - - GXColor black = { 0, 0, 0, 255 }; - GX_SetCopyClear(black, GX_MAX_Z24); - 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. */ @@ -561,17 +549,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; } @@ -609,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); @@ -637,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) { 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 */