diff --git a/CMakeLists.txt b/CMakeLists.txt index 832541b..280c661 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ set(TARGET opengx) include(GNUInstallDirs) add_library(${TARGET} STATIC + src/accum.c + src/accum.h src/arrays.cpp src/arrays.h src/call_lists.c diff --git a/src/accum.c b/src/accum.c new file mode 100644 index 0000000..c065c5a --- /dev/null +++ b/src/accum.c @@ -0,0 +1,213 @@ +/***************************************************************************** +Copyright (c) 2024 Alberto Mardegan (mardy@users.sourceforge.net) +All rights reserved. + +Attention! Contains pieces of code from others such as Mesa and GRRLib + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include "opengx.h" + +#include "accum.h" +#include "debug.h" +#include "efb.h" +#include "state.h" +#include "utils.h" + +#include +#include + +static OgxEfbBuffer *s_accum_buffer = NULL; + +static void draw_screen(GXTexObj *texture, float value) +{ + _ogx_setup_2D_projection(); + + u16 width = glparamstate.viewport[2]; + u16 height = glparamstate.viewport[3]; + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XY, GX_U16, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_U8, 0); + if (texture) { + GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); + GX_SetTevOp(GX_TEVSTAGE0, GX_MODULATE); + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0); + GX_SetNumTexGens(1); + GX_LoadTexObj(texture, GX_TEXMAP0); + } else { + GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORDNULL, + GX_TEXMAP_NULL, GX_COLOR0A0); + GX_SetTevOp(GX_TEVSTAGE0, GX_PASSCLR); + GX_SetNumTexGens(0); + } + GX_SetNumTevStages(1); + GX_SetNumChans(1); + GX_SetChanCtrl(GX_COLOR0A0, GX_DISABLE, GX_SRC_VTX, GX_SRC_VTX, + 0, GX_DF_NONE, GX_AF_NONE); + + GX_SetCullMode(GX_CULL_NONE); + glparamstate.dirty.bits.dirty_cull = 1; + + GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE); + glparamstate.dirty.bits.dirty_z = 1; + + GX_SetAlphaCompare(GX_ALWAYS, 0, GX_AOP_OR, GX_ALWAYS, 0); + glparamstate.dirty.bits.dirty_alphatest = 1; + + GX_SetColorUpdate(GX_TRUE); + glparamstate.dirty.bits.dirty_color_update = 1; + + u8 intensity = (u8)(value * 255.0f); + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + GX_Position2u16(0, 0); + GX_Color4u8(intensity, intensity, intensity, intensity); + GX_TexCoord2u8(0, 0); + GX_Position2u16(0, height); + GX_Color4u8(intensity, intensity, intensity, intensity); + GX_TexCoord2u8(0, 1); + GX_Position2u16(width, height); + GX_Color4u8(intensity, intensity, intensity, intensity); + GX_TexCoord2u8(1, 1); + GX_Position2u16(width, 0); + GX_Color4u8(intensity, intensity, intensity, intensity); + GX_TexCoord2u8(1, 0); + GX_End(); +} + +static OgxEfbBuffer *save_scene_into_texture() +{ + OgxEfbBuffer *scene = NULL; + + /* Since the accumulation buffer typically combines several frames, we + * don't need to capture the current scene with maximum precision; a 16-bit + * format should be enough */ + _ogx_efb_buffer_prepare(&scene, GX_TF_RGB565); + _ogx_efb_buffer_save(scene, OGX_EFB_COLOR); + return scene; +} + +void _ogx_accum_clear() +{ + if (!s_accum_buffer) return; + + void *texels; + u8 format, unused; + u16 width, height; + GX_GetTexObjAll(&s_accum_buffer->texobj, &texels, &width, &height, &format, + &unused, &unused, &unused); + texels = MEM_PHYSICAL_TO_K0(texels); + u32 size = GX_GetTexBufferSize(width, height, format, 0, GX_FALSE); + int n_blocks = size / 32; + GXColor c = glparamstate.accum_clear_color; + uint16_t pixcolor_ar = c.a << 8 | c.r; + uint16_t pixcolor_gb = c.g << 8 | c.b; + uint16_t *dst = texels; + for (int i = 0; i < n_blocks; i++) { + /* In RGBA8 format, the EFB alternates AR blocks with GB blocks */ + uint16_t pixcolor = (i % 2 == 0) ? pixcolor_ar : pixcolor_gb; + for (int j = 0; j < 16; j++) { + *dst++ = pixcolor; + } + } + DCStoreRangeNoSync(texels, size); +} + +void _ogx_accum_load_into_efb() +{ + GX_InvalidateTexAll(); + _ogx_efb_restore_texobj(&s_accum_buffer->texobj); +} + +void _ogx_accum_save_to_efb() +{ + GX_DrawDone(); + _ogx_efb_buffer_save(s_accum_buffer, OGX_EFB_COLOR); +} + +void glClearAccum(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) +{ + glparamstate.accum_clear_color.r = clampf_01(red) * 255.0f; + glparamstate.accum_clear_color.g = clampf_01(green) * 255.0f; + glparamstate.accum_clear_color.b = clampf_01(blue) * 255.0f; + glparamstate.accum_clear_color.a = clampf_01(alpha) * 255.0f; +} + +void glAccum(GLenum op, GLfloat value) +{ + OgxEfbBuffer *scene_buffer = NULL; + GXTexObj *texture = NULL; + bool must_draw = false; + + _ogx_efb_buffer_prepare(&s_accum_buffer, GX_TF_RGBA8); + if (op == GL_ACCUM || op == GL_LOAD) { + scene_buffer = save_scene_into_texture(); + texture = &scene_buffer->texobj; + } + + _ogx_efb_set_content_type(OGX_EFB_ACCUM); + if (op == GL_ACCUM || op == GL_LOAD) { + if (op == GL_ACCUM) { + GX_SetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_COPY); + } else { + GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_COPY); + } + must_draw = true; + } else if (op == GL_ADD) { + GX_SetBlendMode(GX_BM_BLEND, GX_BL_ONE, GX_BL_ONE, GX_LO_COPY); + must_draw = true; + } else if (op == GL_MULT) { + GX_SetBlendMode(GX_BM_BLEND, GX_BL_ZERO, GX_BL_SRCALPHA, GX_LO_COPY); + must_draw = true; + } + + if (must_draw) { + draw_screen(texture, value); + glparamstate.dirty.bits.dirty_blend = 1; + } + + if (op == GL_RETURN && value == 1.0f) { + /* Just leave the accumulation buffer on the scene, since it doesn't + * leave to be altered */ + _ogx_efb_content_type = OGX_EFB_SCENE; + } else { + /* We must render the contents of the accumulation buffer with an + * intensity given by "value". */ + _ogx_efb_set_content_type(OGX_EFB_SCENE); + + GX_SetBlendMode(GX_BM_NONE, GX_BL_ZERO, GX_BL_ZERO, GX_LO_COPY); + glparamstate.dirty.bits.dirty_blend = 1; + draw_screen(&s_accum_buffer->texobj, value); + } + + if (scene_buffer) { + free(scene_buffer); + } +} diff --git a/src/accum.h b/src/accum.h new file mode 100644 index 0000000..b6a535f --- /dev/null +++ b/src/accum.h @@ -0,0 +1,39 @@ +/***************************************************************************** +Copyright (c) 2024 Alberto Mardegan (mardy@users.sourceforge.net) +All rights reserved. + +Attention! Contains pieces of code from others such as Mesa and GRRLib + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#ifndef OPENGX_ACCUM_H +#define OPENGX_ACCUM_H + +void _ogx_accum_clear(void); +void _ogx_accum_save_to_efb(void); +void _ogx_accum_load_into_efb(void); + +#endif /* OPENGX_ACCUM_H */ diff --git a/src/call_lists.c b/src/call_lists.c index ecd69ed..a20e2d9 100644 --- a/src/call_lists.c +++ b/src/call_lists.c @@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "call_lists.h" #include "debug.h" +#include "efb.h" #include "stencil.h" #include "utils.h" @@ -193,6 +194,8 @@ static void run_gx_list(struct GXDisplayList *gxlist) { struct client_state cs; + _ogx_efb_set_content_type(OGX_EFB_SCENE); + cs = glparamstate.cs; glparamstate.cs = gxlist->cs; _ogx_apply_state(); diff --git a/src/efb.h b/src/efb.h index 31f1859..4bf8189 100644 --- a/src/efb.h +++ b/src/efb.h @@ -48,6 +48,7 @@ typedef enum { typedef enum { OGX_EFB_SCENE = 1, OGX_EFB_STENCIL, + OGX_EFB_ACCUM, } OgxEfbContentType; extern OgxEfbContentType _ogx_efb_content_type; diff --git a/src/functions.c b/src/functions.c index 112776e..4be4735 100644 --- a/src/functions.c +++ b/src/functions.c @@ -42,7 +42,7 @@ int _ogx_functions_c = 0; /* referenced by gc_gl.c, see the comment in there */ #define PROC(name) { #name, name } static const ProcMap s_proc_map[] = { - //PROC(glAccum), + PROC(glAccum), PROC(glAlphaFunc), //PROC(glAreTexturesResident), PROC(glArrayElement), @@ -54,7 +54,7 @@ static const ProcMap s_proc_map[] = { PROC(glCallList), PROC(glCallLists), PROC(glClear), - //PROC(glClearAccum), + PROC(glClearAccum), PROC(glClearColor), PROC(glClearDepth), //PROC(glClearIndex), @@ -109,7 +109,7 @@ static const ProcMap s_proc_map[] = { PROC(glDisable), PROC(glDisableClientState), PROC(glDrawArrays), - //PROC(glDrawBuffer), + PROC(glDrawBuffer), PROC(glDrawElements), //PROC(glDrawPixels), //PROC(glEdgeFlag), diff --git a/src/gc_gl.c b/src/gc_gl.c index bb882d5..fd5f945 100644 --- a/src/gc_gl.c +++ b/src/gc_gl.c @@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#include "accum.h" #include "call_lists.h" #include "clip.h" #include "debug.h" @@ -184,6 +185,13 @@ static void setup_cull_mode() } } +int ogx_enable_double_buffering(int double_buffering) +{ + int had_double_buffering = glparamstate.active_buffer == GL_BACK; + glparamstate.active_buffer = double_buffering ? GL_BACK : GL_FRONT; + return had_double_buffering; +} + int ogx_prepare_swap_buffers() { return glparamstate.render_mode == GL_RENDER ? 0 : -1; @@ -205,6 +213,10 @@ void ogx_initialize() glparamstate.clear_color.g = 0; glparamstate.clear_color.b = 0; glparamstate.clear_color.a = 1; + glparamstate.accum_clear_color.r = 0; + glparamstate.accum_clear_color.g = 0; + glparamstate.accum_clear_color.b = 0; + glparamstate.accum_clear_color.a = 0; glparamstate.clearz = 1.0f; glparamstate.ztest = GX_FALSE; // Depth test disabled but z write enabled @@ -368,6 +380,8 @@ void ogx_initialize() glparamstate.stencil.op_zfail = GL_KEEP; glparamstate.stencil.op_zpass = GL_KEEP; + glparamstate.active_buffer = GL_BACK; + glparamstate.error = GL_NO_ERROR; glparamstate.draw_count = 0; @@ -448,6 +462,9 @@ void _ogx_efb_set_content_type_real(OgxEfbContentType content_type) case OGX_EFB_STENCIL: _ogx_stencil_save_to_efb(); break; + case OGX_EFB_ACCUM: + _ogx_accum_save_to_efb(); + break; } /* Restore data from previously stored EFB for this content type */ @@ -458,10 +475,28 @@ void _ogx_efb_set_content_type_real(OgxEfbContentType content_type) case OGX_EFB_STENCIL: _ogx_stencil_load_into_efb(); break; + case OGX_EFB_ACCUM: + _ogx_accum_load_into_efb(); + break; } _ogx_efb_content_type = content_type; } +void glDrawBuffer(GLenum mode) +{ + if (mode != glparamstate.active_buffer) { + warning("Change the read or write buffer is not implemented"); + set_error(GL_INVALID_OPERATION); + } +} + +void glReadBuffer(GLenum mode) +{ + /* We currently don't support changing read/write buffers, so the + * implementation can be the same. */ + glDrawBuffer(mode); +} + void glEnable(GLenum cap) { // TODO HANDLE_CALL_LIST(ENABLE, cap); @@ -1137,10 +1172,16 @@ void glClear(GLbitfield mask) return; } + _ogx_efb_set_content_type(OGX_EFB_SCENE); + if (mask & GL_STENCIL_BUFFER_BIT) { _ogx_stencil_clear(); } + if (mask & GL_ACCUM_BUFFER_BIT) { + _ogx_accum_clear(); + } + if (mask & GL_DEPTH_BUFFER_BIT) { GX_SetZMode(GX_TRUE, GX_ALWAYS, GX_TRUE); GX_SetZCompLoc(GX_DISABLE); @@ -2335,6 +2376,7 @@ void glDrawArrays(GLenum mode, GLint first, GLsizei count) _ogx_stencil_draw(flat_draw_geometry, &draw_data); } + _ogx_efb_set_content_type(OGX_EFB_SCENE); should_draw = _ogx_setup_render_stages(); _ogx_apply_state(); @@ -2377,6 +2419,7 @@ void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indic _ogx_stencil_draw(flat_draw_elements, &draw_data); } + _ogx_efb_set_content_type(OGX_EFB_SCENE); should_draw = _ogx_setup_render_stages(); _ogx_apply_state(); /* When not building a display list, we can optimize the drawing by @@ -2533,7 +2576,6 @@ void glPopAttrib(void) {} void glPushClientAttrib(GLbitfield mask) {} void glPopClientAttrib(void) {} void glPolygonMode(GLenum face, GLenum mode) {} -void glReadBuffer(GLenum mode) {} void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *data) {} /* diff --git a/src/getters.c b/src/getters.c index 05a4bb4..99f8fb8 100644 --- a/src/getters.c +++ b/src/getters.c @@ -143,6 +143,9 @@ void glGetFloatv(GLenum pname, GLfloat *params) void glGetIntegerv(GLenum pname, GLint *params) { switch (pname) { + case GL_AUX_BUFFERS: + *params = 0; + break; case GL_CLIP_PLANE0: case GL_CLIP_PLANE1: case GL_CLIP_PLANE2: @@ -152,6 +155,10 @@ void glGetIntegerv(GLenum pname, GLint *params) *params = glparamstate.clip_plane_mask & (1 << (pname - GL_CLIP_PLANE0)); return; + case GL_DRAW_BUFFER: + case GL_READ_BUFFER: + *params = glparamstate.active_buffer; + break; case GL_MAX_CLIP_PLANES: *params = MAX_CLIP_PLANES; return; diff --git a/src/opengx.h b/src/opengx.h index 9412470..f9fa9ff 100644 --- a/src/opengx.h +++ b/src/opengx.h @@ -42,6 +42,14 @@ extern "C" { void ogx_initialize(void); void *ogx_get_proc_address(const char *proc); +/* Enable or disable double buffering. This is actually a choice entirely made + * by the integration library, but opengx must be informed about it in order to + * select the right value for the glReadBuffer() and glDrawBuffer() functions. + * + * If this is not called, opengx assumes double buffering is on. + */ +int ogx_enable_double_buffering(int double_buffering); + /* The display integration library (SDL, GLUT, etc.) should call this function * before copying the EFB to the XFB (and optionally, drawing a cursor). The * opengx library might need to restore the EFB (in case it was configured into diff --git a/src/state.h b/src/state.h index b2bf9bb..3e58ab1 100644 --- a/src/state.h +++ b/src/state.h @@ -104,9 +104,11 @@ typedef struct glparams_ OgxTexgenMask texture_gen_enabled; GLenum glcullmode; GLenum render_mode; + GLenum active_buffer; /* no separate buffers for reading and writing */ int glcurtex; int draw_count; GXColor clear_color; + GXColor accum_clear_color; float clearz; float polygon_offset_factor; float polygon_offset_units; diff --git a/src/stencil.c b/src/stencil.c index 26857ea..93dfe66 100644 --- a/src/stencil.c +++ b/src/stencil.c @@ -549,8 +549,6 @@ void _ogx_stencil_draw(OgxStencilDrawCallback callback, void *cb_data) callback, cb_data); } - _ogx_efb_set_content_type(OGX_EFB_SCENE); - glparamstate.dirty.bits.dirty_texture_gen = 1; }