Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor EFB switching #71

Merged
merged 3 commits into from
Sep 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 49 additions & 49 deletions src/efb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
}
31 changes: 28 additions & 3 deletions src/efb.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <GL/gl.h>
#include <malloc.h>
#include <ogc/gx.h>
#include <ogc/system.h>

typedef enum {
OGX_EFB_NONE = 0,
Expand All @@ -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 */
46 changes: 46 additions & 0 deletions src/gc_gl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
Expand Down
82 changes: 30 additions & 52 deletions src/stencil.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions src/stencil.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */