From deacd769d54d90c155b224ecee5d650ee391349d Mon Sep 17 00:00:00 2001 From: AloXado320 Date: Fri, 18 Aug 2023 20:29:59 -0500 Subject: [PATCH 1/6] Initial F3D Engine update - OpenGL only for now --- Makefile | 2 +- defines.mk | 6 +- src/menu/intro_geo.c | 8 +- src/pc/configfile.c | 8 +- src/pc/controller/controller_touchscreen.c | 2 +- src/pc/crash/crash_handler.c | 2 +- src/pc/gfx/gbi_pc.h | 50 + src/pc/gfx/gfx_cc.c | 41 - src/pc/gfx/gfx_cc.cpp | 58 + src/pc/gfx/gfx_cc.h | 55 +- src/pc/gfx/gfx_direct3d11.cpp | 2 +- src/pc/gfx/gfx_dxgi.cpp | 2 +- src/pc/gfx/gfx_opengl.c | 1087 ------- src/pc/gfx/gfx_opengl.cpp | 1088 +++++++ src/pc/gfx/gfx_pc.c | 2048 ------------- src/pc/gfx/gfx_pc.cpp | 3061 ++++++++++++++++++++ src/pc/gfx/gfx_pc.h | 107 +- src/pc/gfx/gfx_rendering_api.h | 62 +- src/pc/gfx/gfx_screen_config.h | 3 - src/pc/gfx/gfx_sdl2.c | 487 ---- src/pc/gfx/gfx_sdl2.cpp | 519 ++++ src/pc/gfx/gfx_window_manager_api.h | 25 +- src/pc/{pc_main.c => pc_main.cpp} | 63 +- 23 files changed, 5019 insertions(+), 3767 deletions(-) create mode 100644 src/pc/gfx/gbi_pc.h delete mode 100644 src/pc/gfx/gfx_cc.c create mode 100644 src/pc/gfx/gfx_cc.cpp delete mode 100755 src/pc/gfx/gfx_opengl.c create mode 100644 src/pc/gfx/gfx_opengl.cpp delete mode 100644 src/pc/gfx/gfx_pc.c create mode 100644 src/pc/gfx/gfx_pc.cpp delete mode 100644 src/pc/gfx/gfx_sdl2.c create mode 100644 src/pc/gfx/gfx_sdl2.cpp rename src/pc/{pc_main.c => pc_main.cpp} (93%) diff --git a/Makefile b/Makefile index 366b3f003..9c242b74f 100755 --- a/Makefile +++ b/Makefile @@ -1647,7 +1647,7 @@ $(BUILD_DIR)/%.o: $(BUILD_DIR)/%.c # Compile C++ code $(BUILD_DIR)/%.o: %.cpp $(call print,Compiling:,$<,$@) - $(V)$(CXX) -c $(CFLAGS) -D_LANGUAGE_C_PLUS_PLUS=1 $(OPT_FLAGS) -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< + $(V)$(CXX) -c $(CFLAGS) -std=c++20 -D_LANGUAGE_C_PLUS_PLUS=1 $(OPT_FLAGS) -MMD -MF $(BUILD_DIR)/$*.d -o $@ $< # Assemble assembly code $(BUILD_DIR)/%.o: %.s diff --git a/defines.mk b/defines.mk index 8a1b3ac53..1c6d7f7f7 100644 --- a/defines.mk +++ b/defines.mk @@ -102,9 +102,9 @@ endif ifeq ($(TARGET_PORT_CONSOLE),0) # Check for Mouse Option - ifeq ($(MOUSE_ACTIONS),1) - CUSTOM_C_DEFINES += -DMOUSE_ACTIONS - endif + #ifeq ($(MOUSE_ACTIONS),1) + # CUSTOM_C_DEFINES += -DMOUSE_ACTIONS + #endif # Check for Discord Rich Presence option ifeq ($(DISCORDRPC),1) diff --git a/src/menu/intro_geo.c b/src/menu/intro_geo.c index 6268151e1..a33cdcb24 100644 --- a/src/menu/intro_geo.c +++ b/src/menu/intro_geo.c @@ -524,11 +524,11 @@ u16 *intro_sample_framebuffer(s32 imageW, s32 imageH, s32 sampleW, s32 sampleH) s32 xOffset = 120; s32 yOffset = 80; -#ifdef TARGET_N64 +//#ifdef TARGET_N64 fb = sFramebuffers[sRenderingFramebuffer]; -#else - fb = get_framebuffer(); -#endif +//#else +// fb = get_framebuffer(); +//#endif image = alloc_display_list(imageW * imageH * sizeof(u16)); diff --git a/src/pc/configfile.c b/src/pc/configfile.c index 7a49b9249..940bdc84a 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -49,10 +49,10 @@ ConfigWindow configWindow = { .reset = false, .settings_changed = false, #else - .x = WAPI_WIN_CENTERPOS, - .y = WAPI_WIN_CENTERPOS, - .w = DESIRED_SCREEN_WIDTH, - .h = DESIRED_SCREEN_HEIGHT, + .x = 640, // TODO + .y = 360, + .w = 640, + .h = 360, .vsync = false, .reset = false, .fullscreen = false, diff --git a/src/pc/controller/controller_touchscreen.c b/src/pc/controller/controller_touchscreen.c index 42d416656..68414faae 100644 --- a/src/pc/controller/controller_touchscreen.c +++ b/src/pc/controller/controller_touchscreen.c @@ -1,6 +1,6 @@ //Feel free to use it in your port too, but please keep authorship! //Touch Controls made by: VDavid003 -#ifdef TOUCH_CONTROLS +#if 0 #include #include #include diff --git a/src/pc/crash/crash_handler.c b/src/pc/crash/crash_handler.c index 8177f7fae..ccb366704 100644 --- a/src/pc/crash/crash_handler.c +++ b/src/pc/crash/crash_handler.c @@ -11,7 +11,7 @@ #define ENABLE_CRASH_HANDLER #endif -#if (defined(_WIN32) || defined(__linux__)) && !defined(WAPI_DUMMY) && defined(ENABLE_CRASH_HANDLER) +#if 0 #ifdef HAVE_SDL2 #include #endif diff --git a/src/pc/gfx/gbi_pc.h b/src/pc/gfx/gbi_pc.h new file mode 100644 index 000000000..790da65cc --- /dev/null +++ b/src/pc/gfx/gbi_pc.h @@ -0,0 +1,50 @@ +#pragma once + +#include "PR/gbi.h" + +// CUSTOM OTR COMMANDS +#define G_SETTIMG_OTR 0x20 +#define G_SETFB 0x21 +#define G_RESETFB 0x22 +#define G_SETTIMG_FB 0x23 +#define G_DL_OTR 0x31 +#define G_VTX_OTR 0x32 +#define G_MARKER 0x33 +#define G_INVALTEXCACHE 0x34 +#define G_BRANCH_Z_OTR 0x35 +#define G_MTX_OTR 0x36 +#define G_SETOVERRIDECOLOR 0x41 +//#define G_TEXRECT_WIDE 0x37 +//#define G_FILLWIDERECT 0x38 + +#define G_TEXRECT_WIDE G_TEXRECT +#define G_FILLWIDERECT G_FILLRECT + +/* GFX Effects */ + +// RDP Cmd +#define G_SETGRAYSCALE 0x39 +#define G_SETINTENSITY 0x40 + +/* Private macro to wrap other macros in do {...} while (0) */ +#define _DW(macro) do {macro} while (0) + +#define gsSPGrayscale(pkt, state) \ + { \ + Gfx* _g = (Gfx*)(pkt); \ + \ + _g->words.w0 = _SHIFTL(G_SETGRAYSCALE, 24, 8); \ + _g->words.w1 = state; \ + } + +#define gsDPSetGrayscaleColor(pkt, r, g, b, lerp) DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp) + +#define gDPSetOverrideColor(pkt, m, l, r, g, b, a) \ +_DW({ \ + Gfx *_g = (Gfx *)(pkt); \ + \ + _g->words.w0 = (_SHIFTL(G_SETOVERRIDECOLOR, 24, 8) | \ + _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8)); \ + _g->words.w1 = (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | \ + _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)); \ +}) diff --git a/src/pc/gfx/gfx_cc.c b/src/pc/gfx/gfx_cc.c deleted file mode 100644 index d43f54bc8..000000000 --- a/src/pc/gfx/gfx_cc.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "gfx_cc.h" - -void gfx_cc_get_features(uint32_t shader_id, struct CCFeatures *cc_features) { - for (int i = 0; i < 4; i++) { - cc_features->c[0][i] = (shader_id >> (i * 3)) & 7; - cc_features->c[1][i] = (shader_id >> (12 + i * 3)) & 7; - } - - cc_features->opt_alpha = (shader_id & SHADER_OPT_ALPHA) != 0; - cc_features->opt_fog = (shader_id & SHADER_OPT_FOG) != 0; - cc_features->opt_texture_edge = (shader_id & SHADER_OPT_TEXTURE_EDGE) != 0; - cc_features->opt_noise = (shader_id & SHADER_OPT_NOISE) != 0; - - cc_features->used_textures[0] = false; - cc_features->used_textures[1] = false; - cc_features->num_inputs = 0; - - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - if (cc_features->c[i][j] >= SHADER_INPUT_1 && cc_features->c[i][j] <= SHADER_INPUT_4) { - if (cc_features->c[i][j] > cc_features->num_inputs) { - cc_features->num_inputs = cc_features->c[i][j]; - } - } - if (cc_features->c[i][j] == SHADER_TEXEL0 || cc_features->c[i][j] == SHADER_TEXEL0A) { - cc_features->used_textures[0] = true; - } - if (cc_features->c[i][j] == SHADER_TEXEL1) { - cc_features->used_textures[1] = true; - } - } - } - - cc_features->do_single[0] = cc_features->c[0][2] == 0; - cc_features->do_single[1] = cc_features->c[1][2] == 0; - cc_features->do_multiply[0] = cc_features->c[0][1] == 0 && cc_features->c[0][3] == 0; - cc_features->do_multiply[1] = cc_features->c[1][1] == 0 && cc_features->c[1][3] == 0; - cc_features->do_mix[0] = cc_features->c[0][1] == cc_features->c[0][3]; - cc_features->do_mix[1] = cc_features->c[1][1] == cc_features->c[1][3]; - cc_features->color_alpha_same = (shader_id & 0xfff) == ((shader_id >> 12) & 0xfff); -} diff --git a/src/pc/gfx/gfx_cc.cpp b/src/pc/gfx/gfx_cc.cpp new file mode 100644 index 000000000..b49e6967b --- /dev/null +++ b/src/pc/gfx/gfx_cc.cpp @@ -0,0 +1,58 @@ +#include "gfx_cc.h" + +void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeatures* cc_features) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 4; k++) { + cc_features->c[i][j][k] = (shader_id0 >> (i * 32 + j * 16 + k * 4)) & 0xf; + } + } + } + + cc_features->opt_alpha = (shader_id1 & SHADER_OPT_ALPHA) != 0; + cc_features->opt_fog = (shader_id1 & SHADER_OPT_FOG) != 0; + cc_features->opt_texture_edge = (shader_id1 & SHADER_OPT_TEXTURE_EDGE) != 0; + cc_features->opt_noise = (shader_id1 & SHADER_OPT_NOISE) != 0; + cc_features->opt_2cyc = (shader_id1 & SHADER_OPT_2CYC) != 0; + cc_features->opt_alpha_threshold = (shader_id1 & SHADER_OPT_ALPHA_THRESHOLD) != 0; + cc_features->opt_invisible = (shader_id1 & SHADER_OPT_INVISIBLE) != 0; + cc_features->opt_grayscale = (shader_id1 & SHADER_OPT_GRAYSCALE) != 0; + + cc_features->clamp[0][0] = (shader_id1 & SHADER_OPT_TEXEL0_CLAMP_S); + cc_features->clamp[0][1] = (shader_id1 & SHADER_OPT_TEXEL0_CLAMP_T); + cc_features->clamp[1][0] = (shader_id1 & SHADER_OPT_TEXEL1_CLAMP_S); + cc_features->clamp[1][1] = (shader_id1 & SHADER_OPT_TEXEL1_CLAMP_T); + + cc_features->used_textures[0] = false; + cc_features->used_textures[1] = false; + cc_features->num_inputs = 0; + + for (int c = 0; c < 2; c++) { + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 4; j++) { + if (cc_features->c[c][i][j] >= SHADER_INPUT_1 && cc_features->c[c][i][j] <= SHADER_INPUT_7) { + if (cc_features->c[c][i][j] > cc_features->num_inputs) { + cc_features->num_inputs = cc_features->c[c][i][j]; + } + } + if (cc_features->c[c][i][j] == SHADER_TEXEL0 || cc_features->c[c][i][j] == SHADER_TEXEL0A) { + cc_features->used_textures[0] = true; + } + if (cc_features->c[c][i][j] == SHADER_TEXEL1 || cc_features->c[c][i][j] == SHADER_TEXEL1A) { + cc_features->used_textures[1] = true; + } + } + } + } + + for (int c = 0; c < 2; c++) { + cc_features->do_single[c][0] = cc_features->c[c][0][2] == SHADER_0; + cc_features->do_single[c][1] = cc_features->c[c][1][2] == SHADER_0; + cc_features->do_multiply[c][0] = cc_features->c[c][0][1] == SHADER_0 && cc_features->c[c][0][3] == SHADER_0; + cc_features->do_multiply[c][1] = cc_features->c[c][1][1] == SHADER_0 && cc_features->c[c][1][3] == SHADER_0; + cc_features->do_mix[c][0] = cc_features->c[c][0][1] == cc_features->c[c][0][3]; + cc_features->do_mix[c][1] = cc_features->c[c][1][1] == cc_features->c[c][1][3]; + cc_features->color_alpha_same[c] = + ((shader_id0 >> c * 32) & 0xffff) == ((shader_id0 >> (c * 32 + 16)) & 0xffff); + } +} diff --git a/src/pc/gfx/gfx_cc.h b/src/pc/gfx/gfx_cc.h index 2840b83ee..5492bf12a 100644 --- a/src/pc/gfx/gfx_cc.h +++ b/src/pc/gfx/gfx_cc.h @@ -4,7 +4,7 @@ #include #include -enum { +/*enum { CC_0, CC_TEXEL0, CC_TEXEL1, @@ -13,7 +13,7 @@ enum { CC_ENV, CC_TEXEL0A, CC_LOD -}; +};*/ enum { SHADER_0, @@ -21,38 +21,51 @@ enum { SHADER_INPUT_2, SHADER_INPUT_3, SHADER_INPUT_4, + SHADER_INPUT_5, + SHADER_INPUT_6, + SHADER_INPUT_7, SHADER_TEXEL0, SHADER_TEXEL0A, - SHADER_TEXEL1 + SHADER_TEXEL1, + SHADER_TEXEL1A, + SHADER_1, + SHADER_COMBINED, + SHADER_NOISE }; -#define SHADER_OPT_ALPHA (1 << 24) -#define SHADER_OPT_FOG (1 << 25) -#define SHADER_OPT_TEXTURE_EDGE (1 << 26) -#define SHADER_OPT_NOISE (1 << 27) +#define SHADER_OPT_ALPHA (1 << 0) +#define SHADER_OPT_FOG (1 << 1) +#define SHADER_OPT_TEXTURE_EDGE (1 << 2) +#define SHADER_OPT_NOISE (1 << 3) +#define SHADER_OPT_2CYC (1 << 4) +#define SHADER_OPT_ALPHA_THRESHOLD (1 << 5) +#define SHADER_OPT_INVISIBLE (1 << 6) +#define SHADER_OPT_GRAYSCALE (1 << 7) +#define SHADER_OPT_TEXEL0_CLAMP_S (1 << 8) +#define SHADER_OPT_TEXEL0_CLAMP_T (1 << 9) +#define SHADER_OPT_TEXEL1_CLAMP_S (1 << 10) +#define SHADER_OPT_TEXEL1_CLAMP_T (1 << 11) +#define CC_SHADER_OPT_POS 56 struct CCFeatures { - uint8_t c[2][4]; + uint8_t c[2][2][4]; bool opt_alpha; bool opt_fog; bool opt_texture_edge; bool opt_noise; + bool opt_2cyc; + bool opt_alpha_threshold; + bool opt_invisible; + bool opt_grayscale; bool used_textures[2]; + bool clamp[2][2]; int num_inputs; - bool do_single[2]; - bool do_multiply[2]; - bool do_mix[2]; - bool color_alpha_same; + bool do_single[2][2]; + bool do_multiply[2][2]; + bool do_mix[2][2]; + bool color_alpha_same[2]; }; -#ifdef __cplusplus -extern "C" { -#endif - -void gfx_cc_get_features(uint32_t shader_id, struct CCFeatures *cc_features); - -#ifdef __cplusplus -} -#endif +void gfx_cc_get_features(uint64_t shader_id0, uint32_t shader_id1, struct CCFeatures* cc_features); #endif diff --git a/src/pc/gfx/gfx_direct3d11.cpp b/src/pc/gfx/gfx_direct3d11.cpp index 76669cf4f..44c102534 100644 --- a/src/pc/gfx/gfx_direct3d11.cpp +++ b/src/pc/gfx/gfx_direct3d11.cpp @@ -1,4 +1,4 @@ -#ifdef RAPI_D3D11 +#if 0 #include #include diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp index 7d51ba802..180091fd2 100644 --- a/src/pc/gfx/gfx_dxgi.cpp +++ b/src/pc/gfx/gfx_dxgi.cpp @@ -1,4 +1,4 @@ -#ifdef WAPI_DXGI +#if 0 #include #include diff --git a/src/pc/gfx/gfx_opengl.c b/src/pc/gfx/gfx_opengl.c deleted file mode 100755 index 31d3efb48..000000000 --- a/src/pc/gfx/gfx_opengl.c +++ /dev/null @@ -1,1087 +0,0 @@ -#ifdef RAPI_GL - -#include -#include - -#ifndef _LANGUAGE_C -# define _LANGUAGE_C -#endif -#include - -#ifdef __MINGW32__ -# define USES_WINDOWS -#endif - -#if defined(USES_WINDOWS) || defined(OSX_BUILD) -# define GLEW_STATIC -# include -#endif - -#define GL_GLEXT_PROTOTYPES 1 - -#ifdef WAPI_SDL2 -# include -# ifdef USE_GLES -# include -# else -# include -# endif -#elif defined(WAPI_SDL1) -# include -# ifndef GLEW_STATIC -# include -# endif -#endif - -#ifdef WAPI_SDL2 -# if defined(USE_GLES) -# define USE_FRAMEBUFFER 1 -# else -# if defined(USES_WINDOWS) -# define USE_FRAMEBUFFER GLEW_ARB_framebuffer_object -# else -# define USE_FRAMEBUFFER 0 -# endif -# endif -#else -# define USE_FRAMEBUFFER 0 -#endif - -#include "../platform.h" -#include "../configfile.h" -#include "gfx_cc.h" -#include "gfx_rendering_api.h" -#include "gfx_screen_config.h" -#include "gfx_pc.h" - -struct ShaderProgram { - uint32_t shader_id; - GLuint opengl_program_id; - uint8_t num_inputs; - bool used_textures[2]; - uint8_t num_floats; - GLint attrib_locations[7]; - uint8_t attrib_sizes[7]; - uint8_t num_attribs; - bool used_noise; - GLint frame_count_location; - GLint window_height_location; - GLint noise_frame_location; - GLint noise_scale_location; - GLint texture_width_location; - GLint texture_height_location; - GLint texture_linear_filtering_location; -}; - -struct RenderTarget { - GLuint framebuffer_id; - GLuint color_texture_id; - GLuint depth_renderbuffer_id; - - uint32_t width; - uint32_t height; -}; - -static struct { - int32_t viewport_x, viewport_y, viewport_width, viewport_height; - int32_t scissor_x, scissor_y, scissor_width, scissor_height; - int8_t depth_test, depth_mask; - int8_t zmode_decal; - - uint8_t active_texture; - GLuint bound_framebuffer; -} gl_state = { 0 }; - -static struct RenderTarget main_rt; -static struct RenderTarget framebuffer_rt; - -static struct ShaderProgram shader_program_pool[64]; -static struct ShaderProgram *current_shader_program; -static uint8_t shader_program_pool_size; - -static struct ShaderProgram rt_shader_program; - -static GLuint opengl_vbo; - -// Current values - -static uint32_t current_width; -static uint32_t current_height; - -static int8_t current_depth_test, current_depth_mask; -static int8_t current_zmode_decal; - -static int32_t current_viewport_x, current_viewport_y, current_viewport_width, current_viewport_height; -static int32_t current_scissor_x, current_scissor_y, current_scissor_width, current_scissor_height; - -static uint32_t noise_frame; -static float noise_scale[2]; -#ifdef THREE_POINT_FILTERING -struct TextureInfo { - uint16_t width; - uint16_t height; - bool linear_filtering; - -} textures[1024]; -static GLuint current_texture_ids[2]; -static uint8_t current_tile; -#endif - -static const char *rt_vertex_shader = -#ifdef USE_GLES - "#version 100\n" -#else - "#version 120\n" -#endif - "attribute vec2 a_position;\n" - "attribute vec2 a_uv;\n" - "varying vec2 v_uv;\n" - "void main() {\n" - " gl_Position = vec4(a_position.x, a_position.y, 0.0, 1.0);\n" - " v_uv = a_uv;\n" - "}\n"; - -static const char *rt_fragment_shader = -#ifdef USE_GLES - "#version 100\n" - "precision mediump float;\n" -#else - "#version 120\n" -#endif - "varying vec2 v_uv;\n" - "uniform sampler2D u_texture;" - "void main() {\n" - " gl_FragColor = vec4(texture2D(u_texture, v_uv).rgb, 1);\n" - "}\n"; - -static void set_viewport(int32_t x, int32_t y, int32_t width, int32_t height) { - if (gl_state.viewport_x == x && gl_state.viewport_y == y && gl_state.viewport_width == width && gl_state.viewport_height == height) { - return; - } - - gl_state.viewport_x = x; - gl_state.viewport_y = y; - gl_state.viewport_width = width; - gl_state.viewport_height = height; - - glViewport(x, y, width, height); -} - -static void set_scissor(int32_t x, int32_t y, int32_t width, int32_t height) { - if (gl_state.scissor_x == x && gl_state.scissor_y == y && gl_state.scissor_width == width && gl_state.scissor_height == height) { - return; - } - - gl_state.scissor_x = x; - gl_state.scissor_y = y; - gl_state.scissor_width = width; - gl_state.scissor_height = height; - - glScissor(x, y, width, height); -} - -static void set_depth_test(bool depth_test) { - if (gl_state.depth_test == depth_test) { - return; - } - - gl_state.depth_test = depth_test; - - if (depth_test) { - glEnable(GL_DEPTH_TEST); - } else { - glDisable(GL_DEPTH_TEST); - } -} - -static void set_depth_mask(bool depth_mask) { - if (gl_state.depth_mask == depth_mask) { - return; - } - - gl_state.depth_mask = depth_mask; - - glDepthMask(depth_mask ? GL_TRUE : GL_FALSE); -} - -static void set_zmode_decal(bool zmode_decal) { - if (gl_state.zmode_decal == zmode_decal) { - return; - } - - gl_state.zmode_decal = zmode_decal; - - if (zmode_decal) { - glPolygonOffset(-2, -2); - glEnable(GL_POLYGON_OFFSET_FILL); - } else { - glPolygonOffset(0, 0); - glDisable(GL_POLYGON_OFFSET_FILL); - } -} - -static void set_active_texture(uint8_t active_texture) { - if (gl_state.active_texture == active_texture) { - return; - } - - gl_state.active_texture = active_texture; - - glActiveTexture(GL_TEXTURE0 + active_texture); -} - -static void set_vertex_buffer(float buffer[], size_t buffer_length) { - glBufferData(GL_ARRAY_BUFFER, buffer_length, buffer, GL_STREAM_DRAW); -} - -static GLuint compile_shader(const char *vertex_shader, const char *fragment_shader) { - GLint success; - - GLuint vs = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vs, 1, &vertex_shader, NULL); - glCompileShader(vs); - glGetShaderiv(vs, GL_COMPILE_STATUS, &success); - if (!success) { - GLint max_length = 0; - glGetShaderiv(vs, GL_INFO_LOG_LENGTH, &max_length); - char error_log[1024]; - fprintf(stderr, "Vertex shader compilation failed\n"); - glGetShaderInfoLog(vs, max_length, &max_length, &error_log[0]); - fprintf(stderr, "%s\n", &error_log[0]); - sys_fatal("vertex shader compilation failed (see terminal)"); - } - - GLuint fs = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fs, 1, &fragment_shader, NULL); - glCompileShader(fs); - glGetShaderiv(fs, GL_COMPILE_STATUS, &success); - if (!success) { - GLint max_length = 0; - glGetShaderiv(fs, GL_INFO_LOG_LENGTH, &max_length); - char error_log[1024]; - fprintf(stderr, "Fragment shader compilation failed\n"); - glGetShaderInfoLog(fs, max_length, &max_length, &error_log[0]); - fprintf(stderr, "%s\n", &error_log[0]); - sys_fatal("fragment shader compilation failed (see terminal)"); - } - - GLuint shader_program_id = glCreateProgram(); - glAttachShader(shader_program_id, vs); - glAttachShader(shader_program_id, fs); - glLinkProgram(shader_program_id); - - glDetachShader(shader_program_id, vs); - glDetachShader(shader_program_id, fs); - glDeleteShader(vs); - glDeleteShader(fs); - - return shader_program_id; -} - -static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram *prg) { - size_t num_floats = prg->num_floats; - size_t pos = 0; - - for (int i = 0; i < prg->num_attribs; i++) { - glEnableVertexAttribArray(prg->attrib_locations[i]); - glVertexAttribPointer(prg->attrib_locations[i], prg->attrib_sizes[i], GL_FLOAT, GL_FALSE, num_floats * sizeof(float), (void *) (pos * sizeof(float))); - pos += prg->attrib_sizes[i]; - } -} - -static void gfx_opengl_set_per_program_uniforms() { - if (current_shader_program->used_noise) { - glUniform1i(current_shader_program->noise_frame_location, noise_frame); - glUniform2f(current_shader_program->noise_scale_location, noise_scale[0], noise_scale[1]); - } -} - -static void gfx_opengl_set_per_draw_uniforms() { -#ifdef THREE_POINT_FILTERING - if (current_shader_program->used_textures[0] || current_shader_program->used_textures[1]) { - GLint filtering[2] = { textures[current_texture_ids[0]].linear_filtering, textures[current_texture_ids[1]].linear_filtering }; - glUniform1iv(current_shader_program->texture_linear_filtering_location, 2, filtering); - - GLint width[2] = { textures[current_texture_ids[0]].width, textures[current_texture_ids[1]].width }; - glUniform1iv(current_shader_program->texture_width_location, 2, width); - - GLint height[2] = { textures[current_texture_ids[0]].height, textures[current_texture_ids[1]].height }; - glUniform1iv(current_shader_program->texture_height_location, 2, height); - } -#endif -} - -static void gfx_opengl_load_shader(struct ShaderProgram *new_prg) { - current_shader_program = new_prg; - glUseProgram(new_prg->opengl_program_id); - gfx_opengl_vertex_array_set_attribs(new_prg); - gfx_opengl_set_per_program_uniforms(); -} - -static void gfx_opengl_unload_shader(struct ShaderProgram *old_prg) { - if (old_prg != NULL) { - for (int i = 0; i < old_prg->num_attribs; i++) { - glDisableVertexAttribArray(old_prg->attrib_locations[i]); - } - } -} - -static void bind_render_target(const struct RenderTarget *render_target) { - GLuint id = render_target == NULL ? 0 : render_target->framebuffer_id; - - if (gl_state.bound_framebuffer != id) { - gl_state.bound_framebuffer = id; - glBindFramebuffer(GL_FRAMEBUFFER, id); - } -} - -static void create_render_target(uint32_t width, uint32_t height, bool is_resizing, bool has_depth_buffer, struct RenderTarget *render_target) { - // Create color texture and buffers - - if (!is_resizing) { - glGenTextures(1, &render_target->color_texture_id); - if (has_depth_buffer) { - glGenRenderbuffers(1, &render_target->depth_renderbuffer_id); - } - glGenFramebuffers(1, &render_target->framebuffer_id); - } - - // Configure color texture - - glBindTexture(GL_TEXTURE_2D, render_target->color_texture_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Configure the depth buffer - - if (has_depth_buffer) { - glBindRenderbuffer(GL_RENDERBUFFER, render_target->depth_renderbuffer_id); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); - } - - // Bind color and depth to the framebuffer - - if (!is_resizing) { - bind_render_target(render_target); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_target->color_texture_id, 0); - if (has_depth_buffer) { - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_target->depth_renderbuffer_id); - } - } - - render_target->width = width; - render_target->height = height; -} - -// function only used when USE_FRAMEBUFFER is defined -static void draw_render_target(const struct RenderTarget *dst_render_target, const struct RenderTarget *src_render_target, bool clear_before_drawing) { - // Set render target - - uint32_t dst_width, dst_height; - - bind_render_target(dst_render_target); - - if (dst_render_target == NULL) { - dst_width = current_width; - dst_height = current_height; - } else { - dst_width = dst_render_target->width; - dst_height = dst_render_target->height; - } - - // Set some states and clear after that - - set_depth_test(false); - set_depth_mask(false); - set_zmode_decal(false); - set_viewport(0, 0, dst_width, dst_height); - set_scissor(0, 0, dst_width, dst_height); - - if (clear_before_drawing) { - glClear(GL_COLOR_BUFFER_BIT); - } - - // Set color texture - - set_active_texture(0); - glBindTexture(GL_TEXTURE_2D, src_render_target->color_texture_id); - - // Set vertex buffer data - - float dst_aspect = (float) dst_width / (float) dst_height; - float src_aspect = (float) src_render_target->width / (float) src_render_target->height; - float w = src_aspect / dst_aspect; - - float buf_vbo[] = { - -w, +1.0, 0.0, 1.0, - -w, -1.0, 0.0, 0.0, - +w, +1.0, 1.0, 1.0, - +w, -1.0, 1.0, 0.0 - }; - - uint32_t stride = 2 * 2 * sizeof(float); - set_vertex_buffer(buf_vbo, 4 * stride); - - // Draw the quad - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -static void create_render_target_views(bool is_resize) { - if (!is_resize) { - // Initialize the framebuffer only the first time - create_render_target(FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, false, false, &framebuffer_rt); - } - - // Create the main render target where contents will be rendered - - create_render_target(current_width, current_height, is_resize, true, &main_rt); -} - -static bool gfx_opengl_z_is_from_0_to_1(void) { - return false; -} - -static void append_str(char *buf, size_t *len, const char *str) { - while (*str != '\0') buf[(*len)++] = *str++; -} - -static void append_line(char *buf, size_t *len, const char *str) { - while (*str != '\0') buf[(*len)++] = *str++; - buf[(*len)++] = '\n'; -} - -static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_alpha, bool inputs_have_alpha, bool hint_single_element) { - if (!only_alpha) { - switch (item) { - case SHADER_0: - return with_alpha ? "vec4(0.0, 0.0, 0.0, 0.0)" : "vec3(0.0, 0.0, 0.0)"; - case SHADER_INPUT_1: - return with_alpha || !inputs_have_alpha ? "vInput1" : "vInput1.rgb"; - case SHADER_INPUT_2: - return with_alpha || !inputs_have_alpha ? "vInput2" : "vInput2.rgb"; - case SHADER_INPUT_3: - return with_alpha || !inputs_have_alpha ? "vInput3" : "vInput3.rgb"; - case SHADER_INPUT_4: - return with_alpha || !inputs_have_alpha ? "vInput4" : "vInput4.rgb"; - case SHADER_TEXEL0: - return with_alpha ? "texVal0" : "texVal0.rgb"; - case SHADER_TEXEL0A: - return hint_single_element ? "texVal0.a" : - (with_alpha ? "vec4(texVal0.a, texVal0.a, texVal0.a, texVal0.a)" : "vec3(texVal0.a, texVal0.a, texVal0.a)"); - case SHADER_TEXEL1: - return with_alpha ? "texVal1" : "texVal1.rgb"; - } - } else { - switch (item) { - case SHADER_0: - return "0.0"; - case SHADER_INPUT_1: - return "vInput1.a"; - case SHADER_INPUT_2: - return "vInput2.a"; - case SHADER_INPUT_3: - return "vInput3.a"; - case SHADER_INPUT_4: - return "vInput4.a"; - case SHADER_TEXEL0: - return "texVal0.a"; - case SHADER_TEXEL0A: - return "texVal0.a"; - case SHADER_TEXEL1: - return "texVal1.a"; - } - } - return "0"; -} - -static void append_formula(char *buf, size_t *len, uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, bool with_alpha, bool only_alpha, bool opt_alpha) { - if (do_single) { - append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false)); - } else if (do_multiply) { - append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false)); - append_str(buf, len, " * "); - append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true)); - } else if (do_mix) { - append_str(buf, len, "mix("); - append_str(buf, len, shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false)); - append_str(buf, len, ", "); - append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false)); - append_str(buf, len, ", "); - append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true)); - append_str(buf, len, ")"); - } else { - append_str(buf, len, "("); - append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false)); - append_str(buf, len, " - "); - append_str(buf, len, shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false)); - append_str(buf, len, ") * "); - append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true)); - append_str(buf, len, " + "); - append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false)); - } -} - -static struct ShaderProgram *gfx_opengl_create_and_load_new_shader(uint32_t shader_id) { - struct CCFeatures cc_features; - gfx_cc_get_features(shader_id, &cc_features); - - char vs_buf[1024]; - char fs_buf[2048]; - size_t vs_len = 0; - size_t fs_len = 0; - size_t num_floats = 4; - - // Vertex shader -#ifdef USE_GLES - append_line(vs_buf, &vs_len, "#version 100"); -#else - append_line(vs_buf, &vs_len, "#version 120"); -#endif - append_line(vs_buf, &vs_len, "attribute vec4 aVtxPos;"); - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_line(vs_buf, &vs_len, "attribute vec2 aTexCoord;"); - append_line(vs_buf, &vs_len, "varying vec2 vTexCoord;"); - num_floats += 2; - } - if (cc_features.opt_fog) { - append_line(vs_buf, &vs_len, "attribute vec4 aFog;"); - append_line(vs_buf, &vs_len, "varying vec4 vFog;"); - num_floats += 4; - } - for (int i = 0; i < cc_features.num_inputs; i++) { - vs_len += sprintf(vs_buf + vs_len, "attribute vec%d aInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); - vs_len += sprintf(vs_buf + vs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); - num_floats += cc_features.opt_alpha ? 4 : 3; - } - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(vs_buf, &vs_len, "varying vec4 screenPos;"); - } - append_line(vs_buf, &vs_len, "void main() {"); - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_line(vs_buf, &vs_len, "vTexCoord = aTexCoord;"); - } - if (cc_features.opt_fog) { - append_line(vs_buf, &vs_len, "vFog = aFog;"); - } - for (int i = 0; i < cc_features.num_inputs; i++) { - vs_len += sprintf(vs_buf + vs_len, "vInput%d = aInput%d;\n", i + 1, i + 1); - } - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(vs_buf, &vs_len, "screenPos = aVtxPos;"); - } - append_line(vs_buf, &vs_len, "gl_Position = aVtxPos;"); - append_line(vs_buf, &vs_len, "}"); - - // Fragment shader -#ifdef USE_GLES - append_line(fs_buf, &fs_len, "#version 100"); - append_line(fs_buf, &fs_len, "precision mediump float;"); -#else - append_line(fs_buf, &fs_len, "#version 120"); -#endif - - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_line(fs_buf, &fs_len, "varying vec2 vTexCoord;"); - } - if (cc_features.opt_fog) { - append_line(fs_buf, &fs_len, "varying vec4 vFog;"); - } - for (int i = 0; i < cc_features.num_inputs; i++) { - fs_len += sprintf(fs_buf + fs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); - } - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(fs_buf, &fs_len, "varying vec4 screenPos;"); - } - if (cc_features.used_textures[0]) { - append_line(fs_buf, &fs_len, "uniform sampler2D uTex0;"); - } - if (cc_features.used_textures[1]) { - append_line(fs_buf, &fs_len, "uniform sampler2D uTex1;"); - } - - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(fs_buf, &fs_len, "uniform int noise_frame;"); - append_line(fs_buf, &fs_len, "uniform vec2 noise_scale;"); - - append_line(fs_buf, &fs_len, "float random(in vec3 value) {"); - append_line(fs_buf, &fs_len, " float random = dot(value, vec3(12.9898, 78.233, 37.719));"); -#ifdef USE_GLES - append_line(fs_buf, &fs_len, " return fract(sin(random) * 143.7585453);"); -#else - append_line(fs_buf, &fs_len, " return fract(sin(random) * 143758.5453);"); -#endif - append_line(fs_buf, &fs_len, "}"); - } - -#ifdef THREE_POINT_FILTERING - // 3 point texture filtering - // Original author: ArthurCarvalho - // Based on GLSL implementation by twinaphex, mupen64plus-libretro project. - - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_line(fs_buf, &fs_len, "uniform int texture_width[2];"); - append_line(fs_buf, &fs_len, "uniform int texture_height[2];"); - append_line(fs_buf, &fs_len, "uniform bool texture_linear_filtering[2];"); - append_line(fs_buf, &fs_len, "#define TEX_OFFSET(tex, texCoord, off, texSize) texture2D(tex, texCoord - off / texSize)"); - append_line(fs_buf, &fs_len, "vec4 tex2D3PointFilter(in sampler2D tex, in vec2 texCoord, in vec2 texSize) {"); - append_line(fs_buf, &fs_len, " vec2 offset = fract(texCoord * texSize - vec2(0.5, 0.5));"); - append_line(fs_buf, &fs_len, " offset -= step(1.0, offset.x + offset.y);"); - append_line(fs_buf, &fs_len, " vec4 c0 = TEX_OFFSET(tex, texCoord, offset, texSize);"); - append_line(fs_buf, &fs_len, " vec4 c1 = TEX_OFFSET(tex, texCoord, vec2(offset.x - sign(offset.x), offset.y), texSize);"); - append_line(fs_buf, &fs_len, " vec4 c2 = TEX_OFFSET(tex, texCoord, vec2(offset.x, offset.y - sign(offset.y)), texSize);"); - append_line(fs_buf, &fs_len, " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0);"); - append_line(fs_buf, &fs_len, "}"); - } -#endif - - append_line(fs_buf, &fs_len, "void main() {"); - - if (cc_features.used_textures[0]) { -#ifdef THREE_POINT_FILTERING - append_line(fs_buf, &fs_len, " vec4 texVal0;"); - append_line(fs_buf, &fs_len, " if (texture_linear_filtering[0])"); - append_line(fs_buf, &fs_len, " texVal0 = tex2D3PointFilter(uTex0, vTexCoord, vec2(texture_width[0], texture_height[0]));"); - append_line(fs_buf, &fs_len, " else"); - append_line(fs_buf, &fs_len, " texVal0 = texture2D(uTex0, vTexCoord);"); -#else - append_line(fs_buf, &fs_len, "vec4 texVal0 = texture2D(uTex0, vTexCoord);"); -#endif - } - if (cc_features.used_textures[1]) { -#ifdef THREE_POINT_FILTERING - append_line(fs_buf, &fs_len, " vec4 texVal1;"); - append_line(fs_buf, &fs_len, " if (texture_linear_filtering[1])"); - append_line(fs_buf, &fs_len, " texVal1 = tex2D3PointFilter(uTex1, vTexCoord, vec2(texture_width[1], texture_height[1]));"); - append_line(fs_buf, &fs_len, " else"); - append_line(fs_buf, &fs_len, " texVal1 = texture2D(uTex1, vTexCoord);"); -#else - append_line(fs_buf, &fs_len, "vec4 texVal1 = texture2D(uTex1, vTexCoord);"); -#endif - } - - append_str(fs_buf, &fs_len, cc_features.opt_alpha ? "vec4 texel = " : "vec3 texel = "); - if (!cc_features.color_alpha_same && cc_features.opt_alpha) { - append_str(fs_buf, &fs_len, "vec4("); - append_formula(fs_buf, &fs_len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], false, false, true); - append_str(fs_buf, &fs_len, ", "); - append_formula(fs_buf, &fs_len, cc_features.c, cc_features.do_single[1], cc_features.do_multiply[1], cc_features.do_mix[1], true, true, true); - append_str(fs_buf, &fs_len, ")"); - } else { - append_formula(fs_buf, &fs_len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], cc_features.opt_alpha, false, cc_features.opt_alpha); - } - append_line(fs_buf, &fs_len, ";"); - - if (cc_features.opt_texture_edge && cc_features.opt_alpha) { - append_line(fs_buf, &fs_len, "if (texel.a > 0.3) texel.a = 1.0; else discard;"); - } - // TODO discard if alpha is 0? - if (cc_features.opt_fog) { - if (cc_features.opt_alpha) { - append_line(fs_buf, &fs_len, "texel = vec4(mix(texel.rgb, vFog.rgb, vFog.a), texel.a);"); - } else { - append_line(fs_buf, &fs_len, "texel = mix(texel, vFog.rgb, vFog.a);"); - } - } - - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(fs_buf, &fs_len, "vec2 coords = (screenPos.xy / screenPos.w) * noise_scale;"); - append_line(fs_buf, &fs_len, "texel.a *= floor(clamp(random(vec3(floor(coords), float(noise_frame))) + texel.a, 0.0, 1.0));"); - } - - if (cc_features.opt_alpha) { - append_line(fs_buf, &fs_len, "gl_FragColor = texel;"); - } else { - append_line(fs_buf, &fs_len, "gl_FragColor = vec4(texel, 1.0);"); - } - append_line(fs_buf, &fs_len, "}"); - - vs_buf[vs_len] = '\0'; - fs_buf[fs_len] = '\0'; - - /*puts("Vertex shader:"); - puts(vs_buf); - puts("Fragment shader:"); - puts(fs_buf); - puts("End");*/ - - GLuint shader_program = compile_shader(vs_buf, fs_buf); - - struct ShaderProgram *prg = &shader_program_pool[shader_program_pool_size++]; - prg->opengl_program_id = shader_program; - - size_t cnt = 0; - - prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aVtxPos"); - prg->attrib_sizes[cnt] = 4; - ++cnt; - - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aTexCoord"); - prg->attrib_sizes[cnt] = 2; - ++cnt; - } - - if (cc_features.opt_fog) { - prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aFog"); - prg->attrib_sizes[cnt] = 4; - ++cnt; - } - - for (int i = 0; i < cc_features.num_inputs; i++) { - char name[16]; - sprintf(name, "aInput%d", i + 1); - prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, name); - prg->attrib_sizes[cnt] = cc_features.opt_alpha ? 4 : 3; - ++cnt; - } - - prg->shader_id = shader_id; - prg->opengl_program_id = shader_program; - prg->num_inputs = cc_features.num_inputs; - prg->used_textures[0] = cc_features.used_textures[0]; - prg->used_textures[1] = cc_features.used_textures[1]; - prg->num_floats = num_floats; - prg->num_attribs = cnt; - - gfx_opengl_load_shader(prg); - - if (cc_features.used_textures[0]) { - GLint sampler_location = glGetUniformLocation(shader_program, "uTex0"); - glUniform1i(sampler_location, 0); - } - if (cc_features.used_textures[1]) { - GLint sampler_location = glGetUniformLocation(shader_program, "uTex1"); - glUniform1i(sampler_location, 1); - } - - if (cc_features.opt_alpha && cc_features.opt_noise) { - prg->noise_frame_location = glGetUniformLocation(shader_program, "noise_frame"); - prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale"); - prg->used_noise = true; - } else { - prg->used_noise = false; - } - -#ifdef THREE_POINT_FILTERING - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - prg->texture_width_location = glGetUniformLocation(shader_program, "texture_width"); - prg->texture_height_location = glGetUniformLocation(shader_program, "texture_height"); - prg->texture_linear_filtering_location = glGetUniformLocation(shader_program, "texture_linear_filtering"); - } -#endif - - return prg; -} - -static struct ShaderProgram *gfx_opengl_lookup_shader(uint32_t shader_id) { - for (size_t i = 0; i < shader_program_pool_size; i++) { - if (shader_program_pool[i].shader_id == shader_id) { - return &shader_program_pool[i]; - } - } - return NULL; -} - -static void gfx_opengl_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { - *num_inputs = prg->num_inputs; - used_textures[0] = prg->used_textures[0]; - used_textures[1] = prg->used_textures[1]; -} - -static GLuint gfx_opengl_new_texture(void) { - GLuint ret; - glGenTextures(1, &ret); - return ret; -} - -static void gfx_opengl_select_texture(int tile, GLuint texture_id) { - set_active_texture(tile); - glBindTexture(GL_TEXTURE_2D, texture_id); -#ifdef THREE_POINT_FILTERING - current_texture_ids[tile] = texture_id; - current_tile = tile; -#endif -} - -static void gfx_opengl_upload_texture(const uint8_t *rgba32_buf, int width, int height) { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf); -#ifdef THREE_POINT_FILTERING - textures[current_texture_ids[current_tile]].width = width; - textures[current_texture_ids[current_tile]].height = height; -#endif -} - -static uint32_t gfx_cm_to_opengl(uint32_t val) { - if (val & G_TX_CLAMP) { - return GL_CLAMP_TO_EDGE; - } - return (val & G_TX_MIRROR) ? GL_MIRRORED_REPEAT : GL_REPEAT; -} - -static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { - set_active_texture(tile); - -#ifdef THREE_POINT_FILTERING - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - textures[current_texture_ids[tile]].linear_filtering = linear_filter; -#else - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear_filter ? GL_LINEAR : GL_NEAREST); -#endif - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt)); -} - -static void gfx_opengl_set_depth_test(bool depth_test) { - current_depth_test = depth_test; -} - -static void gfx_opengl_set_depth_mask(bool z_upd) { - current_depth_mask = z_upd; -} - -static void gfx_opengl_set_zmode_decal(bool zmode_decal) { - current_zmode_decal = zmode_decal; -} - -static void gfx_opengl_set_viewport(int x, int y, int width, int height) { - current_viewport_x = x; - current_viewport_y = y; - current_viewport_width = width; - current_viewport_height = height; - - float aspect_ratio = (float) width / (float) height; - noise_scale[0] = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 - noise_scale[1] = 120; -} - -static void gfx_opengl_set_scissor(int x, int y, int width, int height) { - current_scissor_x = x; - current_scissor_y = y; - current_scissor_width = width; - current_scissor_height = height; -} - -static void gfx_opengl_set_use_alpha(bool use_alpha) { - if (use_alpha) { - glEnable(GL_BLEND); - } else { - glDisable(GL_BLEND); - } -} - -static void gfx_opengl_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { - // Depth - - set_depth_test(current_depth_test); - set_depth_mask(current_depth_mask); - set_zmode_decal(current_zmode_decal); - - // Viewport and Scissor - - set_viewport(current_viewport_x, current_viewport_y, current_viewport_width, current_viewport_height); - set_scissor(current_scissor_x, current_scissor_y, current_scissor_width, current_scissor_height); - - // Set Uniforms - - gfx_opengl_set_per_draw_uniforms(); - - // Draw vertex buffer - - set_vertex_buffer(buf_vbo, buf_vbo_len * sizeof(float)); - - glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris); -} - -static inline bool gl_get_version(int *major, int *minor, bool *is_es) { - const char *vstr = (const char *)glGetString(GL_VERSION); - if (!vstr || !vstr[0]) return false; - - if (!strncmp(vstr, "OpenGL ES ", 10)) { - vstr += 10; - *is_es = true; - } else if (!strncmp(vstr, "OpenGL ES-CM ", 13)) { - vstr += 13; - *is_es = true; - } - - return (sscanf(vstr, "%d.%d", major, minor) == 2); -} - -static void gfx_opengl_get_framebuffer(uint16_t *buffer) { - if (USE_FRAMEBUFFER) { - bind_render_target(&framebuffer_rt); - - uint8_t pixels[FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4]; - glReadPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - uint32_t bi = 0; - for (int32_t y = FRAMEBUFFER_HEIGHT - 1; y >= 0; y--) { - for (int32_t x = 0; x < FRAMEBUFFER_WIDTH; x++) { - uint32_t fb_pixel = (y * FRAMEBUFFER_WIDTH + x) * 4; - - uint8_t r = pixels[fb_pixel + 0] >> 3; - uint8_t g = pixels[fb_pixel + 1] >> 3; - uint8_t b = pixels[fb_pixel + 2] >> 3; - uint8_t a = 1; //pixels[fb_pixel + 3] / 255; - - buffer[bi] = (r << 11) | (g << 6) | (b << 1) | a; - bi++; - } - } - } -} - -// static void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam) { -// fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""), type, severity, message); -// } - -static void gfx_opengl_init(void) { -#if defined(USES_WINDOWS) || defined(OSX_BUILD) - GLenum err; - if ((err = glewInit()) != GLEW_OK) - sys_fatal("could not init GLEW:\n%s", glewGetErrorString(err)); -#endif - - // check GL version - int vmajor, vminor; - bool is_es = false; - gl_get_version(&vmajor, &vminor, &is_es); - if (vmajor < 2 && vminor < 1 && !is_es) - sys_fatal("OpenGL 2.1+ is required.\nReported version: %s%d.%d", is_es ? "ES" : "", vmajor, vminor); - - // Initialize resolution before drawing first frame - - if (current_width != gfx_current_dimensions.width || current_height != gfx_current_dimensions.height) { - current_width = gfx_current_dimensions.width; - current_height = gfx_current_dimensions.height; - } - - // Initialize render targets - - if (USE_FRAMEBUFFER) { - create_render_target_views(false); - - // Create the render target shader, used to draw into fullscreen quads - - rt_shader_program.opengl_program_id = compile_shader(rt_vertex_shader, rt_fragment_shader); - rt_shader_program.attrib_locations[0] = glGetAttribLocation(rt_shader_program.opengl_program_id, "a_position"); - rt_shader_program.attrib_sizes[0] = 2; - rt_shader_program.attrib_locations[1] = glGetAttribLocation(rt_shader_program.opengl_program_id, "a_uv"); - rt_shader_program.attrib_sizes[1] = 2; - rt_shader_program.num_attribs = 2; - rt_shader_program.num_floats = 4; - rt_shader_program.used_textures[0] = true; - rt_shader_program.used_textures[1] = false; - rt_shader_program.num_inputs = 0; // Unused in this case - rt_shader_program.shader_id = 0; // Unused in this case - rt_shader_program.used_noise = false; // Unused in this case - - glUseProgram(rt_shader_program.opengl_program_id); - GLint sampler_location = glGetUniformLocation(rt_shader_program.opengl_program_id, "u_texture"); - glUniform1i(sampler_location, 0); - } - - // Initialize vertex buffer - - glGenBuffers(1, &opengl_vbo); - glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo); - - // Initialize misc states - - glDepthFunc(GL_LEQUAL); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - // if (GLEW_KHR_debug) { - // glEnable(GL_DEBUG_OUTPUT); - // glDebugMessageCallback(MessageCallback, 0); - // } -} - -static void gfx_opengl_on_resize(void) { -} - -static void gfx_opengl_start_frame(void) { - noise_frame++; - if (noise_frame > 150) { - // No high values, as noise starts to look ugly - noise_frame = 0; - } - - if (USE_FRAMEBUFFER) { - if (current_width != gfx_current_dimensions.width || current_height != gfx_current_dimensions.height) { - current_width = gfx_current_dimensions.width; - current_height = gfx_current_dimensions.height; - create_render_target_views(true); - } - } - - bind_render_target(&main_rt); - - glDisable(GL_SCISSOR_TEST); - set_depth_mask(true); // Must be set to clear Z-buffer - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glEnable(GL_SCISSOR_TEST); -} - -static void gfx_opengl_end_frame(void) { - if (USE_FRAMEBUFFER) { - // Set the shader and vertex attribs for quad rendering - - glUseProgram(rt_shader_program.opengl_program_id); - gfx_opengl_vertex_array_set_attribs(&rt_shader_program); - - // Draw quad with main render target into the other render targets - - draw_render_target(NULL, &main_rt, false); - draw_render_target(&framebuffer_rt, &main_rt, true); - - // Set again the last shader used before drawing render targets. - // Not doing so can lead to rendering issues on the first drawcalls - // of the next frame, if they use the same shader as the ones before. - - gfx_opengl_load_shader(current_shader_program); - } -} - -static void gfx_opengl_finish_render(void) { -} - -static void gfx_opengl_shutdown(void) { -} - -struct GfxRenderingAPI gfx_opengl_api = { - gfx_opengl_z_is_from_0_to_1, - gfx_opengl_unload_shader, - gfx_opengl_load_shader, - gfx_opengl_create_and_load_new_shader, - gfx_opengl_lookup_shader, - gfx_opengl_shader_get_info, - gfx_opengl_new_texture, - gfx_opengl_select_texture, - gfx_opengl_upload_texture, - gfx_opengl_set_sampler_parameters, - gfx_opengl_set_depth_test, - gfx_opengl_set_depth_mask, - gfx_opengl_set_zmode_decal, - gfx_opengl_set_viewport, - gfx_opengl_set_scissor, - gfx_opengl_set_use_alpha, - gfx_opengl_draw_triangles, - gfx_opengl_get_framebuffer, - gfx_opengl_init, - gfx_opengl_on_resize, - gfx_opengl_start_frame, - gfx_opengl_end_frame, - gfx_opengl_finish_render, - gfx_opengl_shutdown -}; - -#endif // RAPI_GL diff --git a/src/pc/gfx/gfx_opengl.cpp b/src/pc/gfx/gfx_opengl.cpp new file mode 100644 index 000000000..c6ae73140 --- /dev/null +++ b/src/pc/gfx/gfx_opengl.cpp @@ -0,0 +1,1088 @@ +//#include "core/Window.h" +#if 1 + +#include +#include +#include + +#include +#include +#include + +#ifdef __MINGW32__ +#define FOR_WINDOWS 1 +# define GLEW_STATIC +#else +#define FOR_WINDOWS 0 +#endif + +#ifdef _MSC_VER +#include +// #define GL_GLEXT_PROTOTYPES 1 +#include +#elif FOR_WINDOWS +#include +#define GL_GLEXT_PROTOTYPES 1 +#include +#include "SDL2/SDL_opengl.h" +#elif __APPLE__ +#include +#include +#elif __SWITCH__ +#include +#include +#elif NO_GLEW +#define GL_GLEXT_PROTOTYPES 1 +#include +#include +#include +#else +#include +#include +#define GL_GLEXT_PROTOTYPES 1 +// #include +#endif + +#include "gfx_cc.h" +#include "gfx_rendering_api.h" +//#include "menu/ImGuiImpl.h" +//#include "core/Window.h" +#include "gfx_pc.h" + +#ifndef _LANGUAGE_C +#define _LANGUAGE_C +#endif +#include + +#if defined(__APPLE__) || defined(USE_OPENGLES) +#define USE_NEWER_GLSL 1 +#endif + +using namespace std; + +struct ShaderProgram { + GLuint opengl_program_id; + uint8_t num_inputs; + bool used_textures[2]; + uint8_t num_floats; + GLint attrib_locations[16]; + uint8_t attrib_sizes[16]; + uint8_t num_attribs; + GLint frame_count_location; + GLint noise_scale_location; +}; + +struct Framebuffer { + uint32_t width, height; + bool has_depth_buffer; + uint32_t msaa_level; + bool invert_y; + + GLuint fbo, clrbuf, clrbuf_msaa, rbo; +}; + +static map, struct ShaderProgram> shader_program_pool; +static GLuint opengl_vbo; +#ifdef __APPLE__ +static GLuint opengl_vao; +#endif +static bool current_depth_mask; + +static uint32_t frame_count; + +static vector framebuffers; +static size_t current_framebuffer; +static float current_noise_scale; +static FilteringMode current_filter_mode = FILTER_LINEAR; + +GLuint pixel_depth_rb, pixel_depth_fb; +size_t pixel_depth_rb_size; + +static struct GfxClipParameters gfx_opengl_get_clip_parameters(void) { +#if ENABLE_FRAMEBUFFER + return { false, framebuffers[current_framebuffer].invert_y }; +#else + return { false, false }; +#endif +} + +static void gfx_opengl_vertex_array_set_attribs(struct ShaderProgram* prg) { + size_t num_floats = prg->num_floats; + size_t pos = 0; + + for (int i = 0; i < prg->num_attribs; i++) { + glEnableVertexAttribArray(prg->attrib_locations[i]); + glVertexAttribPointer(prg->attrib_locations[i], prg->attrib_sizes[i], GL_FLOAT, GL_FALSE, + num_floats * sizeof(float), (void*)(pos * sizeof(float))); + pos += prg->attrib_sizes[i]; + } +} + +static void gfx_opengl_set_uniforms(struct ShaderProgram* prg) { + glUniform1i(prg->frame_count_location, frame_count); + glUniform1f(prg->noise_scale_location, current_noise_scale); +} + +static void gfx_opengl_unload_shader(struct ShaderProgram* old_prg) { + if (old_prg != NULL) { + for (int i = 0; i < old_prg->num_attribs; i++) { + glDisableVertexAttribArray(old_prg->attrib_locations[i]); + } + } +} + +static void gfx_opengl_load_shader(struct ShaderProgram* new_prg) { + // if (!new_prg) return; + glUseProgram(new_prg->opengl_program_id); + gfx_opengl_vertex_array_set_attribs(new_prg); + gfx_opengl_set_uniforms(new_prg); +} + +static void append_str(char* buf, size_t* len, const char* str) { + while (*str != '\0') { + buf[(*len)++] = *str++; + } +} + +static void append_line(char* buf, size_t* len, const char* str) { + while (*str != '\0') { + buf[(*len)++] = *str++; + } + buf[(*len)++] = '\n'; +} + +#define RAND_NOISE "((random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))) + 1.0) / 2.0)" + +static const char* shader_item_to_str(uint32_t item, bool with_alpha, bool only_alpha, bool inputs_have_alpha, + bool hint_single_element) { + if (!only_alpha) { + switch (item) { + case SHADER_0: + return with_alpha ? "vec4(0.0, 0.0, 0.0, 0.0)" : "vec3(0.0, 0.0, 0.0)"; + case SHADER_1: + return with_alpha ? "vec4(1.0, 1.0, 1.0, 1.0)" : "vec3(1.0, 1.0, 1.0)"; + case SHADER_INPUT_1: + return with_alpha || !inputs_have_alpha ? "vInput1" : "vInput1.rgb"; + case SHADER_INPUT_2: + return with_alpha || !inputs_have_alpha ? "vInput2" : "vInput2.rgb"; + case SHADER_INPUT_3: + return with_alpha || !inputs_have_alpha ? "vInput3" : "vInput3.rgb"; + case SHADER_INPUT_4: + return with_alpha || !inputs_have_alpha ? "vInput4" : "vInput4.rgb"; + case SHADER_TEXEL0: + return with_alpha ? "texVal0" : "texVal0.rgb"; + case SHADER_TEXEL0A: + return hint_single_element ? "texVal0.a" + : (with_alpha ? "vec4(texVal0.a, texVal0.a, texVal0.a, texVal0.a)" + : "vec3(texVal0.a, texVal0.a, texVal0.a)"); + case SHADER_TEXEL1A: + return hint_single_element ? "texVal1.a" + : (with_alpha ? "vec4(texVal1.a, texVal1.a, texVal1.a, texVal1.a)" + : "vec3(texVal1.a, texVal1.a, texVal1.a)"); + case SHADER_TEXEL1: + return with_alpha ? "texVal1" : "texVal1.rgb"; + case SHADER_COMBINED: + return with_alpha ? "texel" : "texel.rgb"; + case SHADER_NOISE: + return with_alpha ? "vec4(" RAND_NOISE ", " RAND_NOISE ", " RAND_NOISE ", " RAND_NOISE ")" + : "vec3(" RAND_NOISE ", " RAND_NOISE ", " RAND_NOISE ")"; + } + } else { + switch (item) { + case SHADER_0: + return "0.0"; + case SHADER_1: + return "1.0"; + case SHADER_INPUT_1: + return "vInput1.a"; + case SHADER_INPUT_2: + return "vInput2.a"; + case SHADER_INPUT_3: + return "vInput3.a"; + case SHADER_INPUT_4: + return "vInput4.a"; + case SHADER_TEXEL0: + return "texVal0.a"; + case SHADER_TEXEL0A: + return "texVal0.a"; + case SHADER_TEXEL1A: + return "texVal1.a"; + case SHADER_TEXEL1: + return "texVal1.a"; + case SHADER_COMBINED: + return "texel.a"; + case SHADER_NOISE: + return RAND_NOISE; + } + } + return ""; +} + +#undef RAND_NOISE + +static void append_formula(char* buf, size_t* len, uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, + bool with_alpha, bool only_alpha, bool opt_alpha) { + if (do_single) { + append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false)); + } else if (do_multiply) { + append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false)); + append_str(buf, len, " * "); + append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true)); + } else if (do_mix) { + append_str(buf, len, "mix("); + append_str(buf, len, shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false)); + append_str(buf, len, ", "); + append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false)); + append_str(buf, len, ", "); + append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true)); + append_str(buf, len, ")"); + } else { + append_str(buf, len, "("); + append_str(buf, len, shader_item_to_str(c[only_alpha][0], with_alpha, only_alpha, opt_alpha, false)); + append_str(buf, len, " - "); + append_str(buf, len, shader_item_to_str(c[only_alpha][1], with_alpha, only_alpha, opt_alpha, false)); + append_str(buf, len, ") * "); + append_str(buf, len, shader_item_to_str(c[only_alpha][2], with_alpha, only_alpha, opt_alpha, true)); + append_str(buf, len, " + "); + append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false)); + } +} + +static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shader_id0, uint32_t shader_id1) { + struct CCFeatures cc_features; + gfx_cc_get_features(shader_id0, shader_id1, &cc_features); + + char vs_buf[1024]; + char fs_buf[3000]; + size_t vs_len = 0; + size_t fs_len = 0; + size_t num_floats = 4; + + // Vertex shader +#ifdef __APPLE__ + append_line(vs_buf, &vs_len, "#version 410 core"); + append_line(vs_buf, &vs_len, "in vec4 aVtxPos;"); +#elif USE_OPENGLES + append_line(vs_buf, &vs_len, "#version 300 es"); + append_line(vs_buf, &vs_len, "in vec4 aVtxPos;"); +#else + append_line(vs_buf, &vs_len, "#version 110"); + append_line(vs_buf, &vs_len, "attribute vec4 aVtxPos;"); +#endif + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { +#ifdef USE_NEWER_GLSL + vs_len += sprintf(vs_buf + vs_len, "in vec2 aTexCoord%d;\n", i); + vs_len += sprintf(vs_buf + vs_len, "out vec2 vTexCoord%d;\n", i); +#else + vs_len += sprintf(vs_buf + vs_len, "attribute vec2 aTexCoord%d;\n", i); + vs_len += sprintf(vs_buf + vs_len, "varying vec2 vTexCoord%d;\n", i); +#endif + num_floats += 2; + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { +#ifdef USE_NEWER_GLSL + vs_len += sprintf(vs_buf + vs_len, "in float aTexClamp%s%d;\n", j == 0 ? "S" : "T", i); + vs_len += sprintf(vs_buf + vs_len, "out float vTexClamp%s%d;\n", j == 0 ? "S" : "T", i); +#else + vs_len += sprintf(vs_buf + vs_len, "attribute float aTexClamp%s%d;\n", j == 0 ? "S" : "T", i); + vs_len += sprintf(vs_buf + vs_len, "varying float vTexClamp%s%d;\n", j == 0 ? "S" : "T", i); +#endif + num_floats += 1; + } + } + } + } + if (cc_features.opt_fog) { +#ifdef USE_NEWER_GLSL + append_line(vs_buf, &vs_len, "in vec4 aFog;"); + append_line(vs_buf, &vs_len, "out vec4 vFog;"); +#else + append_line(vs_buf, &vs_len, "attribute vec4 aFog;"); + append_line(vs_buf, &vs_len, "varying vec4 vFog;"); +#endif + num_floats += 4; + } + + if (cc_features.opt_grayscale) { +#ifdef USE_NEWER_GLSL + append_line(vs_buf, &vs_len, "in vec4 aGrayscaleColor;"); + append_line(vs_buf, &vs_len, "out vec4 vGrayscaleColor;"); +#else + append_line(vs_buf, &vs_len, "attribute vec4 aGrayscaleColor;"); + append_line(vs_buf, &vs_len, "varying vec4 vGrayscaleColor;"); +#endif + num_floats += 4; + } + + for (int i = 0; i < cc_features.num_inputs; i++) { +#ifdef USE_NEWER_GLSL + vs_len += sprintf(vs_buf + vs_len, "in vec%d aInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); + vs_len += sprintf(vs_buf + vs_len, "out vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); +#else + vs_len += sprintf(vs_buf + vs_len, "attribute vec%d aInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); + vs_len += sprintf(vs_buf + vs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); +#endif + num_floats += cc_features.opt_alpha ? 4 : 3; + } + append_line(vs_buf, &vs_len, "void main() {"); + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + vs_len += sprintf(vs_buf + vs_len, "vTexCoord%d = aTexCoord%d;\n", i, i); + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { + vs_len += sprintf(vs_buf + vs_len, "vTexClamp%s%d = aTexClamp%s%d;\n", j == 0 ? "S" : "T", i, + j == 0 ? "S" : "T", i); + } + } + } + } + if (cc_features.opt_fog) { + append_line(vs_buf, &vs_len, "vFog = aFog;"); + } + if (cc_features.opt_grayscale) { + append_line(vs_buf, &vs_len, "vGrayscaleColor = aGrayscaleColor;"); + } + for (int i = 0; i < cc_features.num_inputs; i++) { + vs_len += sprintf(vs_buf + vs_len, "vInput%d = aInput%d;\n", i + 1, i + 1); + } + append_line(vs_buf, &vs_len, "gl_Position = aVtxPos;"); + append_line(vs_buf, &vs_len, "}"); + + // Fragment shader +#ifdef __APPLE__ + append_line(fs_buf, &fs_len, "#version 410 core"); +#elif USE_OPENGLES + append_line(fs_buf, &fs_len, "#version 300 es"); + append_line(fs_buf, &fs_len, "precision mediump float;"); +#else + append_line(fs_buf, &fs_len, "#version 130"); +#endif + // append_line(fs_buf, &fs_len, "precision mediump float;"); + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { +#ifdef USE_NEWER_GLSL + fs_len += sprintf(fs_buf + fs_len, "in vec2 vTexCoord%d;\n", i); +#else + fs_len += sprintf(fs_buf + fs_len, "varying vec2 vTexCoord%d;\n", i); +#endif + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { +#ifdef USE_NEWER_GLSL + fs_len += sprintf(fs_buf + fs_len, "in float vTexClamp%s%d;\n", j == 0 ? "S" : "T", i); +#else + fs_len += sprintf(fs_buf + fs_len, "varying float vTexClamp%s%d;\n", j == 0 ? "S" : "T", i); +#endif + } + } + } + } + if (cc_features.opt_fog) { +#ifdef USE_NEWER_GLSL + append_line(fs_buf, &fs_len, "in vec4 vFog;"); +#else + append_line(fs_buf, &fs_len, "varying vec4 vFog;"); +#endif + } + if (cc_features.opt_grayscale) { +#ifdef USE_NEWER_GLSL + append_line(fs_buf, &fs_len, "in vec4 vGrayscaleColor;"); +#else + append_line(fs_buf, &fs_len, "varying vec4 vGrayscaleColor;"); +#endif + } + for (int i = 0; i < cc_features.num_inputs; i++) { +#ifdef USE_NEWER_GLSL + fs_len += sprintf(fs_buf + fs_len, "in vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); +#else + fs_len += sprintf(fs_buf + fs_len, "varying vec%d vInput%d;\n", cc_features.opt_alpha ? 4 : 3, i + 1); +#endif + } + if (cc_features.used_textures[0]) { + append_line(fs_buf, &fs_len, "uniform sampler2D uTex0;"); + } + if (cc_features.used_textures[1]) { + append_line(fs_buf, &fs_len, "uniform sampler2D uTex1;"); + } + + 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, "float random(in vec3 value) {"); + append_line(fs_buf, &fs_len, " float random = dot(sin(value), vec3(12.9898, 78.233, 37.719));"); + append_line(fs_buf, &fs_len, " return fract(sin(random) * 143758.5453);"); + append_line(fs_buf, &fs_len, "}"); + + if (current_filter_mode == FILTER_THREE_POINT) { +#if USE_NEWER_GLSL + append_line(fs_buf, &fs_len, "#define TEX_OFFSET(off) texture(tex, texCoord - (off)/texSize)"); +#else + append_line(fs_buf, &fs_len, "#define TEX_OFFSET(off) texture2D(tex, texCoord - (off)/texSize)"); +#endif + append_line(fs_buf, &fs_len, "vec4 filter3point(in sampler2D tex, in vec2 texCoord, in vec2 texSize) {"); + append_line(fs_buf, &fs_len, " vec2 offset = fract(texCoord*texSize - vec2(0.5));"); + append_line(fs_buf, &fs_len, " offset -= step(1.0, offset.x + offset.y);"); + append_line(fs_buf, &fs_len, " vec4 c0 = TEX_OFFSET(offset);"); + append_line(fs_buf, &fs_len, " vec4 c1 = TEX_OFFSET(vec2(offset.x - sign(offset.x), offset.y));"); + append_line(fs_buf, &fs_len, " vec4 c2 = TEX_OFFSET(vec2(offset.x, offset.y - sign(offset.y)));"); + append_line(fs_buf, &fs_len, " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0);"); + append_line(fs_buf, &fs_len, "}"); + append_line(fs_buf, &fs_len, "vec4 hookTexture2D(in sampler2D tex, in vec2 uv, in vec2 texSize) {"); + append_line(fs_buf, &fs_len, " return filter3point(tex, uv, texSize);"); + append_line(fs_buf, &fs_len, "}"); + } else { + append_line(fs_buf, &fs_len, "vec4 hookTexture2D(in sampler2D tex, in vec2 uv, in vec2 texSize) {"); +#if USE_NEWER_GLSL + append_line(fs_buf, &fs_len, " return texture(tex, uv);"); +#else + append_line(fs_buf, &fs_len, " return texture2D(tex, uv);"); +#endif + append_line(fs_buf, &fs_len, "}"); + } + +#if USE_NEWER_GLSL + append_line(fs_buf, &fs_len, "out vec4 outColor;"); +#endif + + append_line(fs_buf, &fs_len, "void main() {"); + + // Reference approach to color wrapping as per GLideN64 + // Return wrapped value of x in interval [low, high) + append_line(fs_buf, &fs_len, "#define WRAP(x, low, high) mod((x)-(low), (high)-(low)) + (low)"); + + 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]; + + #ifdef USE_OPENGLES + // ES glsl can't implicitly convert ivec2 -> vec2 + fs_len += sprintf(fs_buf + fs_len, "ivec2 itexSize%d = textureSize(uTex%d, 0);\n", i, i); + fs_len += sprintf(fs_buf + fs_len, "vec2 texSize%d = vec2(itexSize%d.x, itexSize%d.y);\n", i, i, i); + #else + fs_len += sprintf(fs_buf + fs_len, "vec2 texSize%d = textureSize(uTex%d, 0);\n", i, i); + #endif + + if (!s && !t) { + fs_len += sprintf(fs_buf + fs_len, "vec4 texVal%d = hookTexture2D(uTex%d, vTexCoord%d, texSize%d);\n", + i, i, i, i); + } else { + if (s && t) { + fs_len += sprintf(fs_buf + fs_len, + "vec4 texVal%d = hookTexture2D(uTex%d, clamp(vTexCoord%d, 0.5 / texSize%d, " + "vec2(vTexClampS%d, vTexClampT%d)), texSize%d);\n", + i, i, i, i, i, i, i); + } else if (s) { + fs_len += sprintf(fs_buf + fs_len, + "vec4 texVal%d = hookTexture2D(uTex%d, vec2(clamp(vTexCoord%d.s, 0.5 / " + "texSize%d.s, vTexClampS%d), vTexCoord%d.t), texSize%d);\n", + i, i, i, i, i, i, i); + } else { + fs_len += sprintf(fs_buf + fs_len, + "vec4 texVal%d = hookTexture2D(uTex%d, vec2(vTexCoord%d.s, clamp(vTexCoord%d.t, " + "0.5 / texSize%d.t, vTexClampT%d)), texSize%d);\n", + i, i, i, i, i, i, i); + } + } + } + } + + append_line(fs_buf, &fs_len, cc_features.opt_alpha ? "vec4 texel;" : "vec3 texel;"); + for (int c = 0; c < (cc_features.opt_2cyc ? 2 : 1); c++) { + append_str(fs_buf, &fs_len, "texel = "); + if (!cc_features.color_alpha_same[c] && cc_features.opt_alpha) { + append_str(fs_buf, &fs_len, "vec4("); + append_formula(fs_buf, &fs_len, cc_features.c[c], cc_features.do_single[c][0], + cc_features.do_multiply[c][0], cc_features.do_mix[c][0], false, false, true); + append_str(fs_buf, &fs_len, ", "); + append_formula(fs_buf, &fs_len, cc_features.c[c], cc_features.do_single[c][1], + cc_features.do_multiply[c][1], cc_features.do_mix[c][1], true, true, true); + append_str(fs_buf, &fs_len, ")"); + } else { + append_formula(fs_buf, &fs_len, cc_features.c[c], cc_features.do_single[c][0], + cc_features.do_multiply[c][0], cc_features.do_mix[c][0], cc_features.opt_alpha, false, + cc_features.opt_alpha); + } + append_line(fs_buf, &fs_len, ";"); + + if (c == 0) { + append_str(fs_buf, &fs_len, "texel = WRAP(texel, -1.01, 1.01);"); + } + } + + append_str(fs_buf, &fs_len, "texel = WRAP(texel, -0.51, 1.51);"); + append_str(fs_buf, &fs_len, "texel = clamp(texel, 0.0, 1.0);"); + // TODO discard if alpha is 0? + if (cc_features.opt_fog) { + if (cc_features.opt_alpha) { + append_line(fs_buf, &fs_len, "texel = vec4(mix(texel.rgb, vFog.rgb, vFog.a), texel.a);"); + } else { + append_line(fs_buf, &fs_len, "texel = mix(texel, vFog.rgb, vFog.a);"); + } + } + + if (cc_features.opt_texture_edge && cc_features.opt_alpha) { + append_line(fs_buf, &fs_len, "if (texel.a > 0.19) texel.a = 1.0; else discard;"); + } + + if (cc_features.opt_alpha && cc_features.opt_noise) { + append_line(fs_buf, &fs_len, + "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * noise_scale), float(frame_count))) + " + "texel.a, 0.0, 1.0));"); + } + + if (cc_features.opt_grayscale) { + append_line(fs_buf, &fs_len, "float intensity = (texel.r + texel.g + texel.b) / 3.0;"); + append_line(fs_buf, &fs_len, "vec3 new_texel = vGrayscaleColor.rgb * intensity;"); + append_line(fs_buf, &fs_len, "texel.rgb = mix(texel.rgb, new_texel, vGrayscaleColor.a);"); + } + + if (cc_features.opt_alpha) { + if (cc_features.opt_alpha_threshold) { + append_line(fs_buf, &fs_len, "if (texel.a < 8.0 / 256.0) discard;"); + } + if (cc_features.opt_invisible) { + append_line(fs_buf, &fs_len, "texel.a = 0.0;"); + } +#if USE_NEWER_GLSL + append_line(fs_buf, &fs_len, "outColor = texel;"); +#else + append_line(fs_buf, &fs_len, "gl_FragColor = texel;"); +#endif + } else { +#if USE_NEWER_GLSL + append_line(fs_buf, &fs_len, "outColor = vec4(texel, 1.0);"); +#else + append_line(fs_buf, &fs_len, "gl_FragColor = vec4(texel, 1.0);"); +#endif + } + append_line(fs_buf, &fs_len, "}"); + + vs_buf[vs_len] = '\0'; + fs_buf[fs_len] = '\0'; + + /*puts("Vertex shader:"); + puts(vs_buf); + puts("Fragment shader:"); + puts(fs_buf); + puts("End");*/ + + const GLchar* sources[2] = { vs_buf, fs_buf }; + const GLint lengths[2] = { (GLint)vs_len, (GLint)fs_len }; + GLint success; + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &sources[0], &lengths[0]); + glCompileShader(vertex_shader); + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLint max_length = 0; + glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &max_length); + char error_log[1024]; + // fprintf(stderr, "Vertex shader compilation failed\n"); + glGetShaderInfoLog(vertex_shader, max_length, &max_length, &error_log[0]); + // fprintf(stderr, "%s\n", &error_log[0]); + abort(); + } + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &sources[1], &lengths[1]); + glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) { + GLint max_length = 0; + glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &max_length); + char error_log[1024]; + fprintf(stderr, "Fragment shader compilation failed\n"); + glGetShaderInfoLog(fragment_shader, max_length, &max_length, &error_log[0]); + fprintf(stderr, "%s\n", &error_log[0]); + abort(); + } + + GLuint shader_program = glCreateProgram(); + glAttachShader(shader_program, vertex_shader); + glAttachShader(shader_program, fragment_shader); + glLinkProgram(shader_program); + + size_t cnt = 0; + + struct ShaderProgram* prg = &shader_program_pool[make_pair(shader_id0, shader_id1)]; + prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aVtxPos"); + prg->attrib_sizes[cnt] = 4; + ++cnt; + + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + char name[32]; + sprintf(name, "aTexCoord%d", i); + prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, name); + prg->attrib_sizes[cnt] = 2; + ++cnt; + + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { + sprintf(name, "aTexClamp%s%d", j == 0 ? "S" : "T", i); + prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, name); + prg->attrib_sizes[cnt] = 1; + ++cnt; + } + } + } + } + + if (cc_features.opt_fog) { + prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aFog"); + prg->attrib_sizes[cnt] = 4; + ++cnt; + } + + if (cc_features.opt_grayscale) { + prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, "aGrayscaleColor"); + prg->attrib_sizes[cnt] = 4; + ++cnt; + } + + for (int i = 0; i < cc_features.num_inputs; i++) { + char name[16]; + sprintf(name, "aInput%d", i + 1); + prg->attrib_locations[cnt] = glGetAttribLocation(shader_program, name); + prg->attrib_sizes[cnt] = cc_features.opt_alpha ? 4 : 3; + ++cnt; + } + + prg->opengl_program_id = shader_program; + prg->num_inputs = cc_features.num_inputs; + prg->used_textures[0] = cc_features.used_textures[0]; + prg->used_textures[1] = cc_features.used_textures[1]; + prg->num_floats = num_floats; + prg->num_attribs = cnt; + + gfx_opengl_load_shader(prg); + + if (cc_features.used_textures[0]) { + GLint sampler_location = glGetUniformLocation(shader_program, "uTex0"); + glUniform1i(sampler_location, 0); + } + if (cc_features.used_textures[1]) { + GLint sampler_location = glGetUniformLocation(shader_program, "uTex1"); + glUniform1i(sampler_location, 1); + } + + prg->frame_count_location = glGetUniformLocation(shader_program, "frame_count"); + prg->noise_scale_location = glGetUniformLocation(shader_program, "noise_scale"); + + return prg; +} + +static struct ShaderProgram* gfx_opengl_lookup_shader(uint64_t shader_id0, uint32_t shader_id1) { + auto it = shader_program_pool.find(make_pair(shader_id0, shader_id1)); + return it == shader_program_pool.end() ? nullptr : &it->second; +} + +static void gfx_opengl_shader_get_info(struct ShaderProgram* prg, uint8_t* num_inputs, bool used_textures[2]) { + *num_inputs = prg->num_inputs; + used_textures[0] = prg->used_textures[0]; + used_textures[1] = prg->used_textures[1]; +} + +static GLuint gfx_opengl_new_texture(void) { + GLuint ret; + glGenTextures(1, &ret); + return ret; +} + +static void gfx_opengl_delete_texture(uint32_t texID) { + glDeleteTextures(1, &texID); +} + +static void gfx_opengl_select_texture(int tile, GLuint texture_id) { + glActiveTexture(GL_TEXTURE0 + tile); + glBindTexture(GL_TEXTURE_2D, texture_id); +} + +static void gfx_opengl_upload_texture(const uint8_t* rgba32_buf, uint32_t width, uint32_t height) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgba32_buf); +} + +#ifdef __SWITCH__ +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +#endif + +static uint32_t gfx_cm_to_opengl(uint32_t val) { + switch (val) { + case G_TX_NOMIRROR | G_TX_CLAMP: + return GL_CLAMP_TO_EDGE; + case G_TX_MIRROR | G_TX_WRAP: + return GL_MIRRORED_REPEAT; + case G_TX_MIRROR | G_TX_CLAMP: + return GL_MIRROR_CLAMP_TO_EDGE; + case G_TX_NOMIRROR | G_TX_WRAP: + return GL_REPEAT; + } + return 0; +} + +static void gfx_opengl_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { + const GLint filter = linear_filter && current_filter_mode == FILTER_LINEAR ? GL_LINEAR : GL_NEAREST; + glActiveTexture(GL_TEXTURE0 + tile); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gfx_cm_to_opengl(cms)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gfx_cm_to_opengl(cmt)); +} + +static void gfx_opengl_set_depth_test_and_mask(bool depth_test, bool z_upd) { + if (depth_test || z_upd) { + glEnable(GL_DEPTH_TEST); + glDepthMask(z_upd ? GL_TRUE : GL_FALSE); + glDepthFunc(depth_test ? GL_LEQUAL : GL_ALWAYS); + current_depth_mask = z_upd; + } else { + glDisable(GL_DEPTH_TEST); + } +} + +static void gfx_opengl_set_zmode_decal(bool zmode_decal) { + if (zmode_decal) { + glPolygonOffset(-2, -2); + glEnable(GL_POLYGON_OFFSET_FILL); + } else { + glPolygonOffset(0, 0); + glDisable(GL_POLYGON_OFFSET_FILL); + } +} + +static void gfx_opengl_set_viewport(int x, int y, int width, int height) { + glViewport(x, y, width, height); +} + +static void gfx_opengl_set_scissor(int x, int y, int width, int height) { + glScissor(x, y, width, height); +} + +static void gfx_opengl_set_use_alpha(bool use_alpha) { + if (use_alpha) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } +} + +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); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * buf_vbo_len, buf_vbo, GL_STREAM_DRAW); + glDrawArrays(GL_TRIANGLES, 0, 3 * buf_vbo_num_tris); +} + +static void gfx_opengl_init(void) { +#if !(defined(__SWITCH__) || defined(NO_GLEW)) + glewInit(); +#endif + + glGenBuffers(1, &opengl_vbo); + glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo); + +#ifdef __APPLE__ + glGenVertexArrays(1, &opengl_vao); + glBindVertexArray(opengl_vao); +#endif + + glEnable(GL_DEPTH_CLAMP); + glDepthFunc(GL_LEQUAL); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + +#if ENABLE_FRAMEBUFFER + framebuffers.resize(1); // for the default screen buffer + + glGenRenderbuffers(1, &pixel_depth_rb); + glBindRenderbuffer(GL_RENDERBUFFER, pixel_depth_rb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &pixel_depth_fb); + glBindFramebuffer(GL_FRAMEBUFFER, pixel_depth_fb); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, pixel_depth_rb); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +#endif + + pixel_depth_rb_size = 1; +} + +static void gfx_opengl_on_resize(void) { +} + +static void gfx_opengl_start_frame(void) { + frame_count++; + +#if !ENABLE_FRAMEBUFFER + glDisable(GL_SCISSOR_TEST); + glDepthMask(GL_TRUE); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDepthMask(current_depth_mask ? GL_TRUE : GL_FALSE); + glEnable(GL_SCISSOR_TEST); +#endif +} + +static void gfx_opengl_end_frame(void) { + glFlush(); +} + +static void gfx_opengl_finish_render(void) { +} + +static int gfx_opengl_create_framebuffer() { +#if ENABLE_FRAMEBUFFER + GLuint clrbuf; + glGenTextures(1, &clrbuf); + glBindTexture(GL_TEXTURE_2D, clrbuf); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + GLuint clrbuf_msaa; + glGenRenderbuffers(1, &clrbuf_msaa); + + GLuint rbo; + glGenRenderbuffers(1, &rbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 1, 1); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + GLuint fbo; + glGenFramebuffers(1, &fbo); + + size_t i = framebuffers.size(); + framebuffers.resize(i + 1); + + framebuffers[i].fbo = fbo; + framebuffers[i].clrbuf = clrbuf; + framebuffers[i].clrbuf_msaa = clrbuf_msaa; + framebuffers[i].rbo = rbo; + + return i; +#else + return 0; +#endif +} + +static void gfx_opengl_update_framebuffer_parameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invert_y, bool render_target, bool has_depth_buffer, + bool can_extract_depth) { +#if ENABLE_FRAMEBUFFER + Framebuffer& fb = framebuffers[fb_id]; + + width = max(width, 1U); + height = max(height, 1U); + + glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); + + if (fb_id != 0) { + if (fb.width != width || fb.height != height || fb.msaa_level != msaa_level) { + if (msaa_level <= 1) { + glBindTexture(GL_TEXTURE_2D, fb.clrbuf); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.clrbuf, 0); + } else { + glBindRenderbuffer(GL_RENDERBUFFER, fb.clrbuf_msaa); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level, GL_RGB8, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, fb.clrbuf_msaa); + } + } + + if (has_depth_buffer && + (fb.width != width || fb.height != height || fb.msaa_level != msaa_level || !fb.has_depth_buffer)) { + glBindRenderbuffer(GL_RENDERBUFFER, fb.rbo); + if (msaa_level <= 1) { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); + } else { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_level, GL_DEPTH24_STENCIL8, width, height); + } + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + + if (!fb.has_depth_buffer && has_depth_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.rbo); + } else if (fb.has_depth_buffer && !has_depth_buffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); + } + } + + fb.width = width; + fb.height = height; + fb.has_depth_buffer = has_depth_buffer; + fb.msaa_level = msaa_level; + fb.invert_y = opengl_invert_y; +#endif +} + +void gfx_opengl_start_draw_to_framebuffer(int fb_id, float noise_scale) { + if (noise_scale != 0.0f) { + current_noise_scale = 1.0f / noise_scale; + } +#if ENABLE_FRAMEBUFFER + Framebuffer& fb = framebuffers[fb_id]; + + glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); + current_framebuffer = fb_id; +#endif +} + +void gfx_opengl_clear_framebuffer() { +#if ENABLE_FRAMEBUFFER + glDisable(GL_SCISSOR_TEST); + glDepthMask(GL_TRUE); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glDepthMask(current_depth_mask ? GL_TRUE : GL_FALSE); + glEnable(GL_SCISSOR_TEST); +#endif +} + +void gfx_opengl_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) { +#if ENABLE_FRAMEBUFFER + Framebuffer& fb_dst = framebuffers[fb_id_target]; + Framebuffer& fb_src = framebuffers[fb_id_source]; + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_dst.fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_src.fbo); + glBlitFramebuffer(0, 0, fb_src.width, fb_src.height, 0, 0, fb_dst.width, fb_dst.height, GL_COLOR_BUFFER_BIT, + GL_NEAREST); + glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer); +#endif +} + +void* gfx_opengl_get_framebuffer_texture_id(int fb_id) { +#if ENABLE_FRAMEBUFFER + return (void*)(uintptr_t)framebuffers[fb_id].clrbuf; +#else + return NULL; +#endif +} + +void gfx_opengl_select_texture_fb(int fb_id) { + // glDisable(GL_DEPTH_TEST); +#if ENABLE_FRAMEBUFFER + glActiveTexture(GL_TEXTURE0 + 0); + glBindTexture(GL_TEXTURE_2D, framebuffers[fb_id].clrbuf); +#endif +} + +static std::unordered_map, uint16_t, hash_pair_ff> +gfx_opengl_get_pixel_depth(int fb_id, const std::set>& coordinates) { + + std::unordered_map, uint16_t, hash_pair_ff> res; +#if ENABLE_FRAMEBUFFER + Framebuffer& fb = framebuffers[fb_id]; + + if (coordinates.size() == 1) { + uint32_t depth_stencil_value; + glBindFramebuffer(GL_FRAMEBUFFER, fb.fbo); + int x = coordinates.begin()->first; + int y = coordinates.begin()->second; + glReadPixels(x, fb.invert_y ? fb.height - y : y, 1, 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, + &depth_stencil_value); + res.emplace(*coordinates.begin(), (depth_stencil_value >> 18) << 2); + } else { + if (pixel_depth_rb_size < coordinates.size()) { + // Resizing a renderbuffer seems broken with Intel's driver, so recreate one instead. + glBindFramebuffer(GL_FRAMEBUFFER, pixel_depth_fb); + glDeleteRenderbuffers(1, &pixel_depth_rb); + glGenRenderbuffers(1, &pixel_depth_rb); + glBindRenderbuffer(GL_RENDERBUFFER, pixel_depth_rb); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, coordinates.size(), 1); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, pixel_depth_rb); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + pixel_depth_rb_size = coordinates.size(); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb.fbo); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, pixel_depth_fb); + + glDisable(GL_SCISSOR_TEST); // needed for the blit operation + + { + size_t i = 0; + for (const auto& coord : coordinates) { + int x = coord.first; + int y = coord.second; + if (fb.invert_y) { + y = fb.height - y; + } + glBlitFramebuffer(x, y, x + 1, y + 1, i, 0, i + 1, 1, GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, + GL_NEAREST); + ++i; + } + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, pixel_depth_fb); + vector depth_stencil_values(coordinates.size()); + glReadPixels(0, 0, coordinates.size(), 1, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, depth_stencil_values.data()); + + { + size_t i = 0; + for (const auto& coord : coordinates) { + res.emplace(coord, (depth_stencil_values[i++] >> 18) << 2); + } + } + } + + glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer); +#endif + return res; +} + +void gfx_opengl_set_texture_filter(FilteringMode mode) { + current_filter_mode = mode; + gfx_texture_cache_clear(); +} + +FilteringMode gfx_opengl_get_texture_filter(void) { + return current_filter_mode; +} + +void gfx_opengl_read_pixels(int fb, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t type, void* data) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[fb].fbo); + glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer); +} + +struct GfxRenderingAPI gfx_opengl_api = { + gfx_opengl_get_clip_parameters, + gfx_opengl_unload_shader, + gfx_opengl_load_shader, + gfx_opengl_create_and_load_new_shader, + gfx_opengl_lookup_shader, + gfx_opengl_shader_get_info, + gfx_opengl_new_texture, + gfx_opengl_select_texture, + gfx_opengl_upload_texture, + gfx_opengl_set_sampler_parameters, + gfx_opengl_set_depth_test_and_mask, + gfx_opengl_read_pixels, + gfx_opengl_set_zmode_decal, + gfx_opengl_set_viewport, + gfx_opengl_set_scissor, + gfx_opengl_set_use_alpha, + gfx_opengl_draw_triangles, + gfx_opengl_init, + gfx_opengl_on_resize, + gfx_opengl_start_frame, + gfx_opengl_end_frame, + gfx_opengl_finish_render, + gfx_opengl_create_framebuffer, + gfx_opengl_update_framebuffer_parameters, + gfx_opengl_start_draw_to_framebuffer, + gfx_opengl_clear_framebuffer, + gfx_opengl_resolve_msaa_color_buffer, + gfx_opengl_get_pixel_depth, + gfx_opengl_get_framebuffer_texture_id, + gfx_opengl_select_texture_fb, + gfx_opengl_delete_texture, + gfx_opengl_set_texture_filter, + gfx_opengl_get_texture_filter +}; + +#endif diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c deleted file mode 100644 index 8bef45d65..000000000 --- a/src/pc/gfx/gfx_pc.c +++ /dev/null @@ -1,2048 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#ifdef EXTERNAL_DATA -#define STB_IMAGE_IMPLEMENTATION -#include -#endif - -#ifndef _LANGUAGE_C -#define _LANGUAGE_C -#endif -#include - -#include "config.h" - -#include "gfx_pc.h" -#include "gfx_cc.h" -#include "gfx_window_manager_api.h" -#include "gfx_rendering_api.h" -#include "gfx_screen_config.h" - -#ifdef TARGET_N3DS -#include "gfx_3ds.h" -#endif - -#include "../platform.h" -#include "../configfile.h" -#include "../fs/fs.h" - -#include "macros.h" - -#define SUPPORT_CHECK(x) assert(x) - -// SCALE_M_N: upscale/downscale M-bit integer to N-bit -#define SCALE_5_8(VAL_) (((VAL_) * 0xFF) / 0x1F) -#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF) -#define SCALE_4_8(VAL_) ((VAL_) * 0x11) -#define SCALE_8_4(VAL_) ((VAL_) / 0x11) -#define SCALE_3_8(VAL_) ((VAL_) * 0x24) -#define SCALE_8_3(VAL_) ((VAL_) / 0x24) - -#define SCREEN_WIDTH 320 -#define SCREEN_HEIGHT 240 -#define HALF_SCREEN_WIDTH (SCREEN_WIDTH / 2) -#define HALF_SCREEN_HEIGHT (SCREEN_HEIGHT / 2) - -#define RATIO_X (gfx_current_dimensions.width / (2.0f * HALF_SCREEN_WIDTH)) -#define RATIO_Y (gfx_current_dimensions.height / (2.0f * HALF_SCREEN_HEIGHT)) - -#define MAX_LIGHTS 2 -#define MAX_VERTICES 64 - -#ifdef EXTERNAL_DATA -# define MAX_CACHED_TEXTURES 4096 // for preloading purposes -# define HASH_SHIFT 0 -#else -# define MAX_CACHED_TEXTURES 512 -# define HASH_SHIFT 5 -#endif - -#define HASHMAP_LEN (MAX_CACHED_TEXTURES * 2) -#define HASH_MASK (HASHMAP_LEN - 1) - -struct RGBA { - uint8_t r, g, b, a; -}; - -struct XYWidthHeight { - uint16_t x, y, width, height; -}; - -struct LoadedVertex { - float x, y, z, w; - float u, v; - struct RGBA color; - uint8_t clip_rej; -}; - -struct TextureHashmapNode { - struct TextureHashmapNode *next; - - const uint8_t *texture_addr; - uint8_t fmt, siz; - - const uint8_t *palette; - - uint32_t texture_id; - uint8_t cms, cmt; - bool linear_filter; - - uint32_t checksum; -}; -static struct { - struct TextureHashmapNode *hashmap[HASHMAP_LEN]; - struct TextureHashmapNode pool[MAX_CACHED_TEXTURES]; - uint32_t pool_pos; -} gfx_texture_cache; - -struct ColorCombiner { - uint32_t cc_id; - struct ShaderProgram *prg; - uint8_t shader_input_mapping[2][4]; -}; - -static struct ColorCombiner color_combiner_pool[64]; -static uint8_t color_combiner_pool_size; - -static struct RSP { - float modelview_matrix_stack[11][4][4]; - uint8_t modelview_matrix_stack_size; - - float MP_matrix[4][4]; - float P_matrix[4][4]; - - Light_t current_lights[MAX_LIGHTS + 1]; - float current_lights_coeffs[MAX_LIGHTS][3]; - float current_lookat_coeffs[2][3]; // lookat_x, lookat_y - uint8_t current_num_lights; // includes ambient light - bool lights_changed; - - uint32_t geometry_mode; - int16_t fog_mul, fog_offset; - - struct { - // U0.16 - uint16_t s, t; - } texture_scaling_factor; - - struct LoadedVertex loaded_vertices[MAX_VERTICES + 4]; - - uint8_t saved_opcode; - uint8_t saved_tile; - uint16_t saved_uls, saved_ult; - int32_t saved_lrx, saved_lry, saved_ulx, saved_uly; -} rsp; - -static struct RDP { - const uint8_t *palette; - struct { - const uint8_t *addr; - uint8_t siz; - uint8_t tile_number; - } texture_to_load; - struct { - const uint8_t *addr; - uint32_t size_bytes; - } loaded_texture[2]; - struct { - uint8_t fmt; - uint8_t siz; - uint8_t cms, cmt; - uint16_t uls, ult, lrs, lrt; // U10.2 - uint32_t line_size_bytes; - } texture_tile; - bool textures_changed[2]; - - uint32_t other_mode_l, other_mode_h; - uint32_t combine_mode; - - struct RGBA env_color, prim_color, fog_color, fill_color; - struct XYWidthHeight viewport, scissor; - bool viewport_or_scissor_changed; - void *z_buf_address; - void *color_image_address; -} rdp; - -static struct RenderingState { - bool depth_test; - bool depth_mask; - bool decal_mode; - bool alpha_blend; - struct XYWidthHeight viewport, scissor; - struct ShaderProgram *shader_program; - struct TextureHashmapNode *textures[2]; -} rendering_state; - -struct GfxDimensions gfx_current_dimensions; - -static bool dropped_frame; - -static float buf_vbo[VERTEX_BUFFER_SIZE]; -static size_t buf_vbo_len; -static size_t buf_vbo_num_tris; - -static struct GfxWindowManagerAPI *gfx_wapi; -static struct GfxRenderingAPI *gfx_rapi; - -static uint16_t *framebuffer_data; -static bool requested_framebuffer; - -#ifdef EXTERNAL_DATA -// 4x4 pink-black checkerboard texture to indicate missing textures -#define MISSING_W 4 -#define MISSING_H 4 -static const uint8_t missing_texture[MISSING_W * MISSING_H * 4] = { - 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, - 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, -}; - -static inline size_t string_hash(const uint8_t *str) { - size_t h = 0; - for (const uint8_t *p = str; *p; p++) - h = 31 * h + *p; - return h; -} -#endif - -#ifdef TARGET_N3DS -static void gfx_set_2d(int mode_2d) -{ - gfx_rapi->set_2d(mode_2d); -} - -static void gfx_set_iod(unsigned int iod) -{ - float z, w; - switch(iod) { - case iodNormal : - z = 8.0f; - w = 16.0f; - break; - case iodGoddard : - z = 0.5f; - w = 0.5f; - break; - case iodFileSelect : - z = 96.0f; - w = 128.0f; - break; - case iodStarSelect : - z = 128.0f; - w = 76.0f; - break; - case iodCannon : - z = 0.0f; - w = -128.0f; - break; - } - gfx_rapi->set_iod(z, w); -} -#endif - -static void gfx_flush(void) { - if (buf_vbo_len > 0) { - gfx_rapi->draw_triangles(buf_vbo, buf_vbo_len, buf_vbo_num_tris); - buf_vbo_len = 0; - buf_vbo_num_tris = 0; - } -} - -static struct ShaderProgram *gfx_lookup_or_create_shader_program(uint32_t shader_id) { - struct ShaderProgram *prg = gfx_rapi->lookup_shader(shader_id); - if (prg == NULL) { - gfx_rapi->unload_shader(rendering_state.shader_program); - prg = gfx_rapi->create_and_load_new_shader(shader_id); - rendering_state.shader_program = prg; - } - return prg; -} - -static void gfx_generate_cc(struct ColorCombiner *comb, uint32_t cc_id) { - uint8_t c[2][4]; - uint32_t shader_id = (cc_id >> 24) << 24; - uint8_t shader_input_mapping[2][4] = {{0}}; - for (int i = 0; i < 4; i++) { - c[0][i] = (cc_id >> (i * 3)) & 7; - c[1][i] = (cc_id >> (12 + i * 3)) & 7; - } - for (int i = 0; i < 2; i++) { - if (c[i][0] == c[i][1] || c[i][2] == CC_0) { - c[i][0] = c[i][1] = c[i][2] = 0; - } - uint8_t input_number[8] = {0}; - int next_input_number = SHADER_INPUT_1; - for (int j = 0; j < 4; j++) { - int val = 0; - switch (c[i][j]) { - case CC_0: - break; - case CC_TEXEL0: - val = SHADER_TEXEL0; - break; - case CC_TEXEL1: - val = SHADER_TEXEL1; - break; - case CC_TEXEL0A: - val = SHADER_TEXEL0A; - break; - case CC_PRIM: - case CC_SHADE: - case CC_ENV: - case CC_LOD: - if (input_number[c[i][j]] == 0) { - shader_input_mapping[i][next_input_number - 1] = c[i][j]; - input_number[c[i][j]] = next_input_number++; - } - val = input_number[c[i][j]]; - break; - } - shader_id |= val << (i * 12 + j * 3); - } - } - comb->cc_id = cc_id; - comb->prg = gfx_lookup_or_create_shader_program(shader_id); - memcpy(comb->shader_input_mapping, shader_input_mapping, sizeof(shader_input_mapping)); -} - -static struct ColorCombiner *gfx_lookup_or_create_color_combiner(uint32_t cc_id) { - static struct ColorCombiner *prev_combiner; - if (prev_combiner != NULL && prev_combiner->cc_id == cc_id) { - return prev_combiner; - } - - for (size_t i = 0; i < color_combiner_pool_size; i++) { - if (color_combiner_pool[i].cc_id == cc_id) { - return prev_combiner = &color_combiner_pool[i]; - } - } - gfx_flush(); - struct ColorCombiner *comb = &color_combiner_pool[color_combiner_pool_size++]; - gfx_generate_cc(comb, cc_id); - return prev_combiner = comb; -} - -static bool gfx_texture_cache_lookup(int tile, struct TextureHashmapNode **n, const uint8_t *orig_addr, uint32_t fmt, uint32_t siz, const uint8_t *palette, uint32_t checksum) { - - #ifdef EXTERNAL_DATA // hash and compare the data (i.e. the texture name) itself - size_t hash = string_hash(orig_addr); - #define CMPADDR(x, y) (x && !sys_strcasecmp((const char *)x, (const char *)y)) - #else // hash and compare the address - size_t hash = (uintptr_t)orig_addr; - #define CMPADDR(x, y) x == y - #endif - - hash = (hash >> HASH_SHIFT) & HASH_MASK; - - struct TextureHashmapNode **node = &gfx_texture_cache.hashmap[hash]; - while (*node != NULL && *node - gfx_texture_cache.pool < gfx_texture_cache.pool_pos) { - if (CMPADDR((*node)->texture_addr, orig_addr) && (*node)->fmt == fmt && (*node)->siz == siz && (*node)->palette == palette && (*node)->checksum == checksum) { - gfx_rapi->select_texture(tile, (*node)->texture_id); - *n = *node; - return true; - } - node = &(*node)->next; - } - if (gfx_texture_cache.pool_pos == sizeof(gfx_texture_cache.pool) / sizeof(struct TextureHashmapNode)) { - // Pool is full. We just invalidate everything and start over. - gfx_texture_cache.pool_pos = 0; - node = &gfx_texture_cache.hashmap[hash]; - // puts("Clearing texture cache"); - } - *node = &gfx_texture_cache.pool[gfx_texture_cache.pool_pos++]; - if ((*node)->texture_addr == NULL) { - (*node)->texture_id = gfx_rapi->new_texture(); - } - gfx_rapi->select_texture(tile, (*node)->texture_id); - gfx_rapi->set_sampler_parameters(tile, false, 0, 0); - (*node)->cms = 0; - (*node)->cmt = 0; - (*node)->linear_filter = false; - (*node)->next = NULL; - (*node)->texture_addr = orig_addr; - (*node)->fmt = fmt; - (*node)->siz = siz; - (*node)->palette = palette; - (*node)->checksum = checksum; - *n = *node; - return false; - #undef CMPADDR -} - -#ifndef EXTERNAL_DATA - -static uint8_t rgba32_buf[32768] __attribute__((aligned(32))); - -static void import_texture_rgba16(int tile) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes / 2; i++) { - uint16_t col16 = (rdp.loaded_texture[tile].addr[2 * i] << 8) | rdp.loaded_texture[tile].addr[2 * i + 1]; - uint8_t a = col16 & 1; - uint8_t r = col16 >> 11; - uint8_t g = (col16 >> 6) & 0x1f; - uint8_t b = (col16 >> 1) & 0x1f; - rgba32_buf[4*i + 0] = SCALE_5_8(r); - rgba32_buf[4*i + 1] = SCALE_5_8(g); - rgba32_buf[4*i + 2] = SCALE_5_8(b); - rgba32_buf[4*i + 3] = a ? 255 : 0; - } - - uint32_t width = rdp.texture_tile.line_size_bytes / 2; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_rgba32(int tile) { - uint32_t width = rdp.texture_tile.line_size_bytes / 2; - uint32_t height = (rdp.loaded_texture[tile].size_bytes / 2) / rdp.texture_tile.line_size_bytes; - gfx_rapi->upload_texture(rdp.loaded_texture[tile].addr, width, height); -} - -static void import_texture_ia4(int tile) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) { - uint8_t byte = rdp.loaded_texture[tile].addr[i / 2]; - uint8_t part = (byte >> (4 - (i % 2) * 4)) & 0xf; - uint8_t intensity = part >> 1; - uint8_t alpha = part & 1; - uint8_t r = intensity; - uint8_t g = intensity; - uint8_t b = intensity; - rgba32_buf[4*i + 0] = SCALE_3_8(r); - rgba32_buf[4*i + 1] = SCALE_3_8(g); - rgba32_buf[4*i + 2] = SCALE_3_8(b); - rgba32_buf[4*i + 3] = alpha ? 255 : 0; - } - - uint32_t width = rdp.texture_tile.line_size_bytes * 2; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_ia8(int tile) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) { - uint8_t intensity = rdp.loaded_texture[tile].addr[i] >> 4; - uint8_t alpha = rdp.loaded_texture[tile].addr[i] & 0xf; - uint8_t r = intensity; - uint8_t g = intensity; - uint8_t b = intensity; - rgba32_buf[4*i + 0] = SCALE_4_8(r); - rgba32_buf[4*i + 1] = SCALE_4_8(g); - rgba32_buf[4*i + 2] = SCALE_4_8(b); - rgba32_buf[4*i + 3] = SCALE_4_8(alpha); - } - - uint32_t width = rdp.texture_tile.line_size_bytes; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_ia16(int tile) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes / 2; i++) { - uint8_t intensity = rdp.loaded_texture[tile].addr[2 * i]; - uint8_t alpha = rdp.loaded_texture[tile].addr[2 * i + 1]; - uint8_t r = intensity; - uint8_t g = intensity; - uint8_t b = intensity; - rgba32_buf[4*i + 0] = r; - rgba32_buf[4*i + 1] = g; - rgba32_buf[4*i + 2] = b; - rgba32_buf[4*i + 3] = alpha; - } - - uint32_t width = rdp.texture_tile.line_size_bytes / 2; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_i4(int tile) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) { - uint8_t byte = rdp.loaded_texture[tile].addr[i / 2]; - uint8_t intensity = (byte >> (4 - (i % 2) * 4)) & 0xf; - rgba32_buf[4*i + 0] = SCALE_4_8(intensity); - rgba32_buf[4*i + 1] = SCALE_4_8(intensity); - rgba32_buf[4*i + 2] = SCALE_4_8(intensity); - rgba32_buf[4*i + 3] = 255; - } - - uint32_t width = rdp.texture_tile.line_size_bytes * 2; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_i8(int tile) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) { - uint8_t intensity = rdp.loaded_texture[tile].addr[i]; - rgba32_buf[4*i + 0] = intensity; - rgba32_buf[4*i + 1] = intensity; - rgba32_buf[4*i + 2] = intensity; - rgba32_buf[4*i + 3] = 255; - } - - uint32_t width = rdp.texture_tile.line_size_bytes; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_ci4(int tile) { - uint32_t mode = (rdp.other_mode_h & (3U << G_MDSFT_TEXTLUT)); - - if (!mode) return; // Accuracy: Don't render texture if lut flag is not defined - - if (mode == G_TT_IA16) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) { - uint8_t byte = rdp.loaded_texture[tile].addr[i / 2]; - uint8_t idx = (byte >> (4 - (i % 2) * 4)) & 0xf; - - uint8_t intensity = rdp.palette[idx * 2]; - uint8_t alpha = rdp.palette[idx * 2 + 1]; - uint8_t r = intensity; - uint8_t g = intensity; - uint8_t b = intensity; - rgba32_buf[4*i + 0] = r; - rgba32_buf[4*i + 1] = g; - rgba32_buf[4*i + 2] = b; - rgba32_buf[4*i + 3] = alpha; - } - } else { // G_TT_RGBA16 - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes * 2; i++) { - uint8_t byte = rdp.loaded_texture[tile].addr[i / 2]; - uint8_t idx = (byte >> (4 - (i % 2) * 4)) & 0xf; - - uint16_t col16 = (rdp.palette[idx * 2] << 8) | rdp.palette[idx * 2 + 1]; // Big endian load - uint8_t a = col16 & 1; - uint8_t r = col16 >> 11; - uint8_t g = (col16 >> 6) & 0x1f; - uint8_t b = (col16 >> 1) & 0x1f; - rgba32_buf[4*i + 0] = SCALE_5_8(r); - rgba32_buf[4*i + 1] = SCALE_5_8(g); - rgba32_buf[4*i + 2] = SCALE_5_8(b); - rgba32_buf[4*i + 3] = a ? 255 : 0; - } - } - - uint32_t width = rdp.texture_tile.line_size_bytes * 2; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -static void import_texture_ci8(int tile) { - uint32_t mode = (rdp.other_mode_h & (3U << G_MDSFT_TEXTLUT)); - - if (!mode) return; // Accuracy: Don't render texture if lut flag is not defined - - if (mode == G_TT_IA16) { - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) { - uint8_t idx = rdp.loaded_texture[tile].addr[i]; - - uint8_t intensity = rdp.palette[idx * 2]; - uint8_t alpha = rdp.palette[idx * 2 + 1]; - uint8_t r = intensity; - uint8_t g = intensity; - uint8_t b = intensity; - rgba32_buf[4*i + 0] = r; - rgba32_buf[4*i + 1] = g; - rgba32_buf[4*i + 2] = b; - rgba32_buf[4*i + 3] = alpha; - } - } else { // G_TT_RGBA16 - for (uint32_t i = 0; i < rdp.loaded_texture[tile].size_bytes; i++) { - uint8_t idx = rdp.loaded_texture[tile].addr[i]; - - uint16_t col16 = (rdp.palette[idx * 2] << 8) | rdp.palette[idx * 2 + 1]; // Big endian load - uint8_t a = col16 & 1; - uint8_t r = col16 >> 11; - uint8_t g = (col16 >> 6) & 0x1f; - uint8_t b = (col16 >> 1) & 0x1f; - rgba32_buf[4*i + 0] = SCALE_5_8(r); - rgba32_buf[4*i + 1] = SCALE_5_8(g); - rgba32_buf[4*i + 2] = SCALE_5_8(b); - rgba32_buf[4*i + 3] = a ? 255 : 0; - } - } - - uint32_t width = rdp.texture_tile.line_size_bytes; - uint32_t height = rdp.loaded_texture[tile].size_bytes / rdp.texture_tile.line_size_bytes; - - gfx_rapi->upload_texture(rgba32_buf, width, height); -} - -#else // EXTERNAL_DATA - -static inline void load_texture(const char *fullpath) { - int w, h; - u64 imgsize = 0; - - u8 *imgdata = fs_load_file(fullpath, &imgsize); - if (imgdata) { - // TODO: implement stbi_callbacks or some shit instead of loading the whole texture - u8 *data = stbi_load_from_memory(imgdata, imgsize, &w, &h, NULL, 4); - free(imgdata); - if (data) { - gfx_rapi->upload_texture(data, w, h); - stbi_image_free(data); // don't need this anymore - return; - } - } - - fprintf(stderr, "could not load texture: `%s`\n", fullpath); - // replace with missing texture - gfx_rapi->upload_texture(missing_texture, MISSING_W, MISSING_H); -} - - -// this is taken straight from n64graphics -static bool texname_to_texformat(const char *name, u8 *fmt, u8 *siz) { - static const struct { - const char *name; - const u8 format; - const u8 size; - } fmt_table[] = { - { "rgba16", G_IM_FMT_RGBA, G_IM_SIZ_16b }, - { "rgba32", G_IM_FMT_RGBA, G_IM_SIZ_32b }, - { "ia1", G_IM_FMT_IA, G_IM_SIZ_8b }, // uhh - { "ia4", G_IM_FMT_IA, G_IM_SIZ_4b }, - { "ia8", G_IM_FMT_IA, G_IM_SIZ_8b }, - { "ia16", G_IM_FMT_IA, G_IM_SIZ_16b }, - { "i4", G_IM_FMT_I, G_IM_SIZ_4b }, - { "i8", G_IM_FMT_I, G_IM_SIZ_8b }, - { "ci8", G_IM_FMT_I, G_IM_SIZ_8b }, - { "ci16", G_IM_FMT_I, G_IM_SIZ_16b }, - }; - - char *fstr = strrchr(name, '.'); - if (!fstr) return false; // no format string? - fstr++; - - for (unsigned i = 0; i < sizeof(fmt_table) / sizeof(fmt_table[0]); ++i) { - if (!sys_strcasecmp(fstr, fmt_table[i].name)) { - *fmt = fmt_table[i].format; - *siz = fmt_table[i].size; - return true; - } - } - - return false; -} - -// calls import_texture() on every texture in the res folder -// we can get the format and size from the texture files -// and then cache them using gfx_texture_cache_lookup -static bool preload_texture(void *user, const char *path) { - // strip off the extension - char texname[SYS_MAX_PATH]; - strncpy(texname, path, sizeof(texname)); - texname[sizeof(texname)-1] = 0; - char *dot = strrchr(texname, '.'); - if (dot) *dot = 0; - - // get the format and size from filename - u8 fmt, siz; - if (!texname_to_texformat(texname, &fmt, &siz)) { - fprintf(stderr, "unknown texture format: `%s`, skipping\n", texname); - return true; // just skip it, might be a stray skybox or something - } - - char *actualname = texname; - // strip off the prefix // TODO: make a fs_ function for this shit - if (!strncmp(FS_TEXTUREDIR "/", actualname, 4)) actualname += 4; - // this will be stored in the hashtable, so make a copy - actualname = sys_strdup(actualname); - assert(actualname); - - struct TextureHashmapNode *n; - if (!gfx_texture_cache_lookup(0, &n, actualname, fmt, siz, 0, 0)) - load_texture(path); // new texture, load it - - return true; -} - -#endif // EXTERNAL_DATA - -#define ADLER_BASE 65521U -#define ADLER_NMAX 5552 - -#define ADLER_DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} -#define ADLER_DO2(buf,i) ADLER_DO1(buf,i); ADLER_DO1(buf,i+1); -#define ADLER_DO4(buf,i) ADLER_DO2(buf,i); ADLER_DO2(buf,i+2); -#define ADLER_DO8(buf,i) ADLER_DO4(buf,i); ADLER_DO4(buf,i+4); -#define ADLER_DO16(buf) ADLER_DO8(buf,0); ADLER_DO8(buf,8); - -uint32_t calculate_checksum(const uint8_t *buf, size_t len) { - uint32_t adler = 1; - uint32_t sum2; - unsigned n; - - /* split Adler-32 into component sums */ - sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* do length ADLER_NMAX blocks -- requires just one modulo operation */ - while (len >= ADLER_NMAX) { - len -= ADLER_NMAX; - n = ADLER_NMAX / 16; /* ADLER_NMAX is divisible by 16 */ - do { - ADLER_DO16(buf); /* 16 sums unrolled */ - buf += 16; - } while (--n); - adler %= ADLER_BASE; - sum2 %= ADLER_BASE; - } - - /* do remaining bytes (less than ADLER_NMAX, still just one modulo) */ - if (len) { /* avoid modulos if none remaining */ - while (len >= 16) { - len -= 16; - ADLER_DO16(buf); - buf += 16; - } - while (len--) { - adler += *buf++; - sum2 += adler; - } - adler %= ADLER_BASE; - sum2 %= ADLER_BASE; - } - - /* return recombined sums */ - return adler | (sum2 << 16); -} - -static void import_texture(int tile) { - uint32_t checksum; - uint8_t fmt = rdp.texture_tile.fmt; - uint8_t siz = rdp.texture_tile.siz; - - if (!rdp.loaded_texture[tile].addr) { - fprintf(stderr, "NULL texture: tile %d, format %d/%d, size %d\n", - tile, (int)fmt, (int)siz, (int)rdp.loaded_texture[tile].size_bytes); - return; - } - - checksum = calculate_checksum(rdp.loaded_texture[tile].addr, rdp.loaded_texture[tile].size_bytes); - - if (gfx_texture_cache_lookup(tile, &rendering_state.textures[tile], rdp.loaded_texture[tile].addr, fmt, siz, rdp.palette, checksum)) { - return; - } - -#ifdef EXTERNAL_DATA - // the "texture data" is actually a C string with the path to our texture in it - // load it from an external image in our data path - char texname[SYS_MAX_PATH]; - snprintf(texname, sizeof(texname), FS_TEXTUREDIR "/%s.png", (const char*)rdp.loaded_texture[tile].addr); - load_texture(texname); -#else - // the texture data is actual texture data - if (fmt == G_IM_FMT_RGBA) { - if (siz == G_IM_SIZ_32b) { - import_texture_rgba32(tile); - } - else if (siz == G_IM_SIZ_16b) { - import_texture_rgba16(tile); - } else { - sys_fatal("unsupported RGBA texture size: %u", siz); - } - } else if (fmt == G_IM_FMT_IA) { - if (siz == G_IM_SIZ_4b) { - import_texture_ia4(tile); - } else if (siz == G_IM_SIZ_8b) { - import_texture_ia8(tile); - } else if (siz == G_IM_SIZ_16b) { - import_texture_ia16(tile); - } else { - sys_fatal("unsupported IA texture size: %u", siz); - } - } else if (fmt == G_IM_FMT_CI) { - if (siz == G_IM_SIZ_4b) { - import_texture_ci4(tile); - } else if (siz == G_IM_SIZ_8b) { - import_texture_ci8(tile); - } else { - sys_fatal("unsupported CI texture size: %u", siz); - } - } else if (fmt == G_IM_FMT_I) { - if (siz == G_IM_SIZ_4b) { - import_texture_i4(tile); - } else if (siz == G_IM_SIZ_8b) { - import_texture_i8(tile); - } else { - sys_fatal("unsupported I texture size: %u", siz); - } - } else { - sys_fatal("unsupported texture format: %u", fmt); - } -#endif -} - -static void gfx_normalize_vector(float v[3]) { - float s = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - v[0] /= s; - v[1] /= s; - v[2] /= s; -} - -static void gfx_transposed_matrix_mul(float res[3], const float a[3], const float b[4][4]) { - res[0] = a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2]; - res[1] = a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2]; - res[2] = a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2]; -} - -static void calculate_normal_dir(const Light_t *light, float coeffs[3]) { - float light_dir[3] = { - light->dir[0] / 127.0f, - light->dir[1] / 127.0f, - light->dir[2] / 127.0f - }; - gfx_transposed_matrix_mul(coeffs, light_dir, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1]); - gfx_normalize_vector(coeffs); -} - -static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4][4]) { - float tmp[4][4]; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - tmp[i][j] = a[i][0] * b[0][j] + - a[i][1] * b[1][j] + - a[i][2] * b[2][j] + - a[i][3] * b[3][j]; - } - } - memcpy(res, tmp, sizeof(tmp)); -} - -static void gfx_sp_matrix(uint8_t parameters, const int32_t *addr) { - float matrix[4][4]; -#if 0 - // Original code when fixed point matrices were used - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j += 2) { - int32_t int_part = addr[i * 2 + j / 2]; - uint32_t frac_part = addr[8 + i * 2 + j / 2]; - matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f; - matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f; - } - } -#else - memcpy(matrix, addr, sizeof(matrix)); -#endif - - if (parameters & G_MTX_PROJECTION) { - if (parameters & G_MTX_LOAD) { - memcpy(rsp.P_matrix, matrix, sizeof(matrix)); - } else { - gfx_matrix_mul(rsp.P_matrix, matrix, rsp.P_matrix); - } - } else { // G_MTX_MODELVIEW - if ((parameters & G_MTX_PUSH) && rsp.modelview_matrix_stack_size < 11) { - ++rsp.modelview_matrix_stack_size; - memcpy(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 2], sizeof(matrix)); - } - if (parameters & G_MTX_LOAD) { - memcpy(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], matrix, sizeof(matrix)); - } else { - gfx_matrix_mul(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1]); - } - rsp.lights_changed = 1; - } - gfx_matrix_mul(rsp.MP_matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.P_matrix); -} - -static void gfx_sp_pop_matrix(uint32_t count) { - while (count--) { - if (rsp.modelview_matrix_stack_size > 0) { - --rsp.modelview_matrix_stack_size; - if (rsp.modelview_matrix_stack_size > 0) { - gfx_matrix_mul(rsp.MP_matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.P_matrix); - } - } - } -} - -static float gfx_adjust_x_for_aspect_ratio(float x) { - return x * (4.0f / 3.0f) / ((float)gfx_current_dimensions.width / (float)gfx_current_dimensions.height); -} - -static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx *vertices) { - for (size_t i = 0; i < n_vertices; i++, dest_index++) { - const Vtx_t *v = &vertices[i].v; - const Vtx_tn *vn = &vertices[i].n; - struct LoadedVertex *d = &rsp.loaded_vertices[dest_index]; - - float x = v->ob[0] * rsp.MP_matrix[0][0] + v->ob[1] * rsp.MP_matrix[1][0] + v->ob[2] * rsp.MP_matrix[2][0] + rsp.MP_matrix[3][0]; - float y = v->ob[0] * rsp.MP_matrix[0][1] + v->ob[1] * rsp.MP_matrix[1][1] + v->ob[2] * rsp.MP_matrix[2][1] + rsp.MP_matrix[3][1]; - float z = v->ob[0] * rsp.MP_matrix[0][2] + v->ob[1] * rsp.MP_matrix[1][2] + v->ob[2] * rsp.MP_matrix[2][2] + rsp.MP_matrix[3][2]; - float w = v->ob[0] * rsp.MP_matrix[0][3] + v->ob[1] * rsp.MP_matrix[1][3] + v->ob[2] * rsp.MP_matrix[2][3] + rsp.MP_matrix[3][3]; - - x = gfx_adjust_x_for_aspect_ratio(x); - - short U = v->tc[0] * rsp.texture_scaling_factor.s >> 16; - short V = v->tc[1] * rsp.texture_scaling_factor.t >> 16; - - if (rsp.geometry_mode & G_LIGHTING) { - if (rsp.lights_changed) { - for (int i = 0; i < rsp.current_num_lights - 1; i++) { - calculate_normal_dir(&rsp.current_lights[i], rsp.current_lights_coeffs[i]); - } - static const Light_t lookat_x = {{0, 0, 0}, 0, {0, 0, 0}, 0, {127, 0, 0}, 0}; - static const Light_t lookat_y = {{0, 0, 0}, 0, {0, 0, 0}, 0, {0, 127, 0}, 0}; - calculate_normal_dir(&lookat_x, rsp.current_lookat_coeffs[0]); - calculate_normal_dir(&lookat_y, rsp.current_lookat_coeffs[1]); - rsp.lights_changed = false; - } - - const bool useFirstColor = (dest_index & 1) == 0; - const unsigned char* col = useFirstColor ? rsp.current_lights[rsp.current_num_lights - 1].col : rsp.current_lights[rsp.current_num_lights - 1].colc; - int r = col[0]; - int g = col[1]; - int b = col[2]; - - for (int i = 0; i < rsp.current_num_lights - 1; i++) { - float intensity = 0; - intensity += vn->n[0] * rsp.current_lights_coeffs[i][0]; - intensity += vn->n[1] * rsp.current_lights_coeffs[i][1]; - intensity += vn->n[2] * rsp.current_lights_coeffs[i][2]; - intensity /= 127.0f; - if (intensity > 0.0f) { - col = useFirstColor ? rsp.current_lights[i].col : rsp.current_lights[i].colc; - r += intensity * col[0]; - g += intensity * col[1]; - b += intensity * col[2]; - } - } - - d->color.r = r > 255 ? 255 : r; - d->color.g = g > 255 ? 255 : g; - d->color.b = b > 255 ? 255 : b; - - if (rsp.geometry_mode & G_TEXTURE_GEN) { - float dotx = 0, doty = 0; - dotx += vn->n[0] * rsp.current_lookat_coeffs[0][0]; - dotx += vn->n[1] * rsp.current_lookat_coeffs[0][1]; - dotx += vn->n[2] * rsp.current_lookat_coeffs[0][2]; - doty += vn->n[0] * rsp.current_lookat_coeffs[1][0]; - doty += vn->n[1] * rsp.current_lookat_coeffs[1][1]; - doty += vn->n[2] * rsp.current_lookat_coeffs[1][2]; - - U = (int32_t)((dotx / 127.0f + 1.0f) / 4.0f * rsp.texture_scaling_factor.s); - V = (int32_t)((doty / 127.0f + 1.0f) / 4.0f * rsp.texture_scaling_factor.t); - } - } else { - d->color.r = v->cn[0]; - d->color.g = v->cn[1]; - d->color.b = v->cn[2]; - } - - d->u = U; - d->v = V; - - // trivial clip rejection - d->clip_rej = 0; -#ifdef TARGET_N3DS - if ((gGfx3DSMode == GFX_3DS_MODE_NORMAL || gGfx3DSMode == GFX_3DS_MODE_AA_22) && gSliderLevel > 0.0f) { - float wMod = w * 1.2f; // expanded w-range for testing clip rejection - if (x < -wMod) d->clip_rej |= 1; - if (x > wMod) d->clip_rej |= 2; - if (y < -wMod) d->clip_rej |= 4; - if (y > wMod) d->clip_rej |= 8; - } - else { - if (x < -w) d->clip_rej |= 1; - if (x > w) d->clip_rej |= 2; - if (y < -w) d->clip_rej |= 4; - if (y > w) d->clip_rej |= 8; - } -#else - if (x < -w) d->clip_rej |= 1; - if (x > w) d->clip_rej |= 2; - if (y < -w) d->clip_rej |= 4; - if (y > w) d->clip_rej |= 8; -#endif - if (z < -w) d->clip_rej |= 16; - if (z > w) d->clip_rej |= 32; - - d->x = x; - d->y = y; - d->z = z; - d->w = w; - - if (rsp.geometry_mode & G_FOG) { - if (fabsf(w) < 0.001f) { - // To avoid division by zero - w = 0.001f; - } - - float winv = 1.0f / w; - if (winv < 0.0f) { - winv = 32767.0f; - } - - float fog_z = z * winv * rsp.fog_mul + rsp.fog_offset; - if (fog_z < 0) fog_z = 0; - if (fog_z > 255) fog_z = 255; - d->color.a = fog_z; // Use alpha variable to store fog factor - } else { - d->color.a = v->cn[3]; - } - } -} - -static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx) { - struct LoadedVertex *v1 = &rsp.loaded_vertices[vtx1_idx]; - struct LoadedVertex *v2 = &rsp.loaded_vertices[vtx2_idx]; - struct LoadedVertex *v3 = &rsp.loaded_vertices[vtx3_idx]; - struct LoadedVertex *v_arr[3] = {v1, v2, v3}; - - //if (rand()%2) return; - - if (v1->clip_rej & v2->clip_rej & v3->clip_rej) { - // The whole triangle lies outside the visible area - return; - } - - if ((rsp.geometry_mode & G_CULL_BOTH) != 0) { - float dx1 = v1->x / (v1->w) - v2->x / (v2->w); - float dy1 = v1->y / (v1->w) - v2->y / (v2->w); - float dx2 = v3->x / (v3->w) - v2->x / (v2->w); - float dy2 = v3->y / (v3->w) - v2->y / (v2->w); - float cross = dx1 * dy2 - dy1 * dx2; - - if ((v1->w < 0) ^ (v2->w < 0) ^ (v3->w < 0)) { - // If one vertex lies behind the eye, negating cross will give the correct result. - // If all vertices lie behind the eye, the triangle will be rejected anyway. - cross = -cross; - } - - switch (rsp.geometry_mode & G_CULL_BOTH) { - case G_CULL_FRONT: - if (cross <= 0) return; - break; - case G_CULL_BACK: - if (cross >= 0) return; - break; - case G_CULL_BOTH: - // Why is this even an option? - return; - } - } - - bool depth_test = (rsp.geometry_mode & G_ZBUFFER) == G_ZBUFFER; - if (depth_test != rendering_state.depth_test) { - gfx_flush(); - gfx_rapi->set_depth_test(depth_test); - rendering_state.depth_test = depth_test; - } - - bool z_upd = (rdp.other_mode_l & Z_UPD) == Z_UPD; - if (z_upd != rendering_state.depth_mask) { - gfx_flush(); - gfx_rapi->set_depth_mask(z_upd); - rendering_state.depth_mask = z_upd; - } - - bool zmode_decal = (rdp.other_mode_l & ZMODE_DEC) == ZMODE_DEC; - if (zmode_decal != rendering_state.decal_mode) { - gfx_flush(); - gfx_rapi->set_zmode_decal(zmode_decal); - rendering_state.decal_mode = zmode_decal; - } - - if (rdp.viewport_or_scissor_changed) { - if (memcmp(&rdp.viewport, &rendering_state.viewport, sizeof(rdp.viewport)) != 0) { - gfx_flush(); - gfx_rapi->set_viewport(rdp.viewport.x, rdp.viewport.y, rdp.viewport.width, rdp.viewport.height); - rendering_state.viewport = rdp.viewport; - } - if (memcmp(&rdp.scissor, &rendering_state.scissor, sizeof(rdp.scissor)) != 0) { - gfx_flush(); - gfx_rapi->set_scissor(rdp.scissor.x, rdp.scissor.y, rdp.scissor.width, rdp.scissor.height); - rendering_state.scissor = rdp.scissor; - } - rdp.viewport_or_scissor_changed = false; - } - - uint32_t cc_id = rdp.combine_mode; - - bool use_alpha = (rdp.other_mode_l & (G_BL_A_MEM << 18)) == 0; - bool use_fog = (rdp.other_mode_l >> 30) == G_BL_CLR_FOG; - bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; - bool use_noise = (rdp.other_mode_l & G_AC_DITHER) == G_AC_DITHER; - - if (texture_edge) { - use_alpha = true; - } - - if (use_alpha) cc_id |= SHADER_OPT_ALPHA; - if (use_fog) cc_id |= SHADER_OPT_FOG; - if (texture_edge) cc_id |= SHADER_OPT_TEXTURE_EDGE; - if (use_noise) cc_id |= SHADER_OPT_NOISE; - - if (!use_alpha) { - cc_id &= ~0xfff000; - } - - struct ColorCombiner *comb = gfx_lookup_or_create_color_combiner(cc_id); - struct ShaderProgram *prg = comb->prg; - if (prg != rendering_state.shader_program) { - gfx_flush(); - gfx_rapi->unload_shader(rendering_state.shader_program); - gfx_rapi->load_shader(prg); - rendering_state.shader_program = prg; - } - if (use_alpha != rendering_state.alpha_blend) { - gfx_flush(); - gfx_rapi->set_use_alpha(use_alpha); - rendering_state.alpha_blend = use_alpha; - } - uint8_t num_inputs; - bool used_textures[2]; - gfx_rapi->shader_get_info(prg, &num_inputs, used_textures); - - for (int i = 0; i < 2; i++) { - if (used_textures[i]) { - if (rdp.textures_changed[i]) { - gfx_flush(); - import_texture(i); - rdp.textures_changed[i] = false; - } - bool linear_filter = configFiltering && ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT); - if (linear_filter != rendering_state.textures[i]->linear_filter || rdp.texture_tile.cms != rendering_state.textures[i]->cms || rdp.texture_tile.cmt != rendering_state.textures[i]->cmt) { - gfx_flush(); - gfx_rapi->set_sampler_parameters(i, linear_filter, rdp.texture_tile.cms, rdp.texture_tile.cmt); - rendering_state.textures[i]->linear_filter = linear_filter; - rendering_state.textures[i]->cms = rdp.texture_tile.cms; - rendering_state.textures[i]->cmt = rdp.texture_tile.cmt; - } - } - } - - bool use_texture = used_textures[0] || used_textures[1]; - uint32_t tex_width = (rdp.texture_tile.lrs - rdp.texture_tile.uls + 4) / 4; - uint32_t tex_height = (rdp.texture_tile.lrt - rdp.texture_tile.ult + 4) / 4; - - bool z_is_from_0_to_1 = gfx_rapi->z_is_from_0_to_1(); - - for (int i = 0; i < 3; i++) { - float z = v_arr[i]->z, w = v_arr[i]->w; - if (z_is_from_0_to_1) { - z = (z + w) / 2.0f; - } - - buf_vbo[buf_vbo_len++] = v_arr[i]->x; - buf_vbo[buf_vbo_len++] = v_arr[i]->y; -#ifdef TARGET_N3DS - buf_vbo[buf_vbo_len++] = -z; -#else - buf_vbo[buf_vbo_len++] = z; -#endif - buf_vbo[buf_vbo_len++] = w; - - if (use_texture) { - float u = (v_arr[i]->u - rdp.texture_tile.uls * 8) / 32.0f; - float v = (v_arr[i]->v - rdp.texture_tile.ult * 8) / 32.0f; - if ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT) { - // Linear filter adds 0.5f to the coordinates - u += 0.5f; - v += 0.5f; - } - buf_vbo[buf_vbo_len++] = u / tex_width; - buf_vbo[buf_vbo_len++] = v / tex_height; -#ifdef TARGET_WII_U - // Padding for faster GPU reading - buf_vbo[buf_vbo_len++] = 0.0f; - buf_vbo[buf_vbo_len++] = 0.0f; -#endif - } -#ifndef TARGET_N3DS - if (use_fog) { - buf_vbo[buf_vbo_len++] = rdp.fog_color.r / 255.0f; - buf_vbo[buf_vbo_len++] = rdp.fog_color.g / 255.0f; - buf_vbo[buf_vbo_len++] = rdp.fog_color.b / 255.0f; - buf_vbo[buf_vbo_len++] = v_arr[i]->color.a / 255.0f; // fog factor (not alpha) - } -#endif - for (int j = 0; j < num_inputs; j++) { - struct RGBA *color; - struct RGBA tmp; - for (int k = 0; k < 1 + (use_alpha ? 1 : 0); k++) { - switch (comb->shader_input_mapping[k][j]) { - case CC_PRIM: - color = &rdp.prim_color; - break; - case CC_SHADE: - color = &v_arr[i]->color; - break; - case CC_ENV: - color = &rdp.env_color; - break; - case CC_LOD: - { - float distance_frac = (v1->w - 3000.0f) / 3000.0f; - if (distance_frac < 0.0f) distance_frac = 0.0f; - if (distance_frac > 1.0f) distance_frac = 1.0f; - tmp.r = tmp.g = tmp.b = tmp.a = distance_frac * 255.0f; - color = &tmp; - break; - } - default: - memset(&tmp, 0, sizeof(tmp)); - color = &tmp; - break; - } - if (k == 0) { - buf_vbo[buf_vbo_len++] = color->r / 255.0f; - buf_vbo[buf_vbo_len++] = color->g / 255.0f; - buf_vbo[buf_vbo_len++] = color->b / 255.0f; -#ifdef TARGET_WII_U - if (!use_alpha) { - buf_vbo[buf_vbo_len++] = 1.0f; - } -#endif - } else { - if (use_fog && color == &v_arr[i]->color) { - // Shade alpha is 100% for fog - buf_vbo[buf_vbo_len++] = 1.0f; - } else { - buf_vbo[buf_vbo_len++] = color->a / 255.0f; - } - } - } - } - /*struct RGBA *color = &v_arr[i]->color; - buf_vbo[buf_vbo_len++] = color->r / 255.0f; - buf_vbo[buf_vbo_len++] = color->g / 255.0f; - buf_vbo[buf_vbo_len++] = color->b / 255.0f; - buf_vbo[buf_vbo_len++] = color->a / 255.0f;*/ - } - if (++buf_vbo_num_tris == MAX_BUFFERED_TRIANGLES) { - gfx_flush(); - } -} - -static void gfx_sp_geometry_mode(uint32_t clear, uint32_t set) { - rsp.geometry_mode &= ~clear; - rsp.geometry_mode |= set; -} - -static void gfx_calc_and_set_viewport(const Vp_t *viewport) { - // 2 bits fraction - float width = 2.0f * viewport->vscale[0] / 4.0f; - float height = 2.0f * viewport->vscale[1] / 4.0f; - float x = (viewport->vtrans[0] / 4.0f) - width / 2.0f; - float y = SCREEN_HEIGHT - ((viewport->vtrans[1] / 4.0f) + height / 2.0f); - - width *= RATIO_X; - height *= RATIO_Y; - x *= RATIO_X; - y *= RATIO_Y; - - rdp.viewport.x = x; - rdp.viewport.y = y; - rdp.viewport.width = width; - rdp.viewport.height = height; - - rdp.viewport_or_scissor_changed = true; -} - -static void gfx_sp_movemem(uint8_t index, uint8_t offset, const void* data) { - switch (index) { - case G_MV_VIEWPORT: - gfx_calc_and_set_viewport((const Vp_t *) data); - break; -#if 0 - case G_MV_LOOKATY: - case G_MV_LOOKATX: - memcpy(rsp.current_lookat + (index - G_MV_LOOKATY) / 2, data, sizeof(Light_t)); - //rsp.lights_changed = 1; - break; -#endif -#ifdef F3DEX_GBI_2 - case G_MV_LIGHT: { - int lightidx = offset / 24 - 2; - if (lightidx >= 0 && lightidx <= MAX_LIGHTS) { // skip lookat - // NOTE: reads out of bounds if it is an ambient light - memcpy(rsp.current_lights + lightidx, data, sizeof(Light_t)); - } - break; - } -#else - case G_MV_L0: - case G_MV_L1: - case G_MV_L2: - // NOTE: reads out of bounds if it is an ambient light - memcpy(rsp.current_lights + (index - G_MV_L0) / 2, data, sizeof(Light_t)); - break; -#endif - } -} - -static void gfx_sp_moveword(uint8_t index, UNUSED uint16_t offset, uint32_t data) { - switch (index) { - case G_MW_NUMLIGHT: -#ifdef F3DEX_GBI_2 - rsp.current_num_lights = data / 24 + 1; // add ambient light -#else - // Ambient light is included - // The 31th bit is a flag that lights should be recalculated - rsp.current_num_lights = (data - 0x80000000U) / 32; -#endif - rsp.lights_changed = 1; - break; - case G_MW_FOG: - rsp.fog_mul = (int16_t)(data >> 16); - rsp.fog_offset = (int16_t)data; -#ifdef TARGET_N3DS - gfx_rapi->set_fog(rsp.fog_mul, rsp.fog_offset); -#endif - break; - } -} - -static void gfx_sp_texture(uint16_t sc, uint16_t tc, UNUSED uint8_t level, UNUSED uint8_t tile, UNUSED uint8_t on) { - rsp.texture_scaling_factor.s = sc; - rsp.texture_scaling_factor.t = tc; -} - -static void gfx_dp_set_scissor(UNUSED uint32_t mode, uint32_t ulx, uint32_t uly, uint32_t lrx, uint32_t lry) { - float x = ulx / 4.0f * RATIO_X; - float y = (SCREEN_HEIGHT - lry / 4.0f) * RATIO_Y; - float width = (lrx - ulx) / 4.0f * RATIO_X; - float height = (lry - uly) / 4.0f * RATIO_Y; - - rdp.scissor.x = x; - rdp.scissor.y = y; - rdp.scissor.width = width; - rdp.scissor.height = height; - - rdp.viewport_or_scissor_changed = true; -} - -static void gfx_dp_set_texture_image(UNUSED uint32_t format, uint32_t size, UNUSED uint32_t width, const void* addr) { - rdp.texture_to_load.addr = addr; - rdp.texture_to_load.siz = size; -} - -static void gfx_dp_set_tile(uint8_t fmt, uint32_t siz, uint32_t line, uint32_t tmem, uint8_t tile, uint32_t palette, uint32_t cmt, - UNUSED uint32_t maskt, UNUSED uint32_t shiftt, uint32_t cms, UNUSED uint32_t masks, UNUSED uint32_t shifts) { - if (tile == G_TX_RENDERTILE) { - SUPPORT_CHECK(palette == 0); // palette should set upper 4 bits of color index in 4b mode - rdp.texture_tile.fmt = fmt; - rdp.texture_tile.siz = siz; - rdp.texture_tile.cms = cms; - rdp.texture_tile.cmt = cmt; - rdp.texture_tile.line_size_bytes = line * 8; - rdp.textures_changed[0] = true; - rdp.textures_changed[1] = true; - } - - if (tile == G_TX_LOADTILE) { - rdp.texture_to_load.tile_number = tmem / 256; - } -} - -static void gfx_dp_set_tile_size(uint8_t tile, uint16_t uls, uint16_t ult, uint16_t lrs, uint16_t lrt) { - if (tile == G_TX_RENDERTILE) { - rdp.texture_tile.uls = uls; - rdp.texture_tile.ult = ult; - rdp.texture_tile.lrs = lrs; - rdp.texture_tile.lrt = lrt; - rdp.textures_changed[0] = true; - rdp.textures_changed[1] = true; - } -} - -static void gfx_dp_load_tlut(UNUSED uint8_t tile, UNUSED uint32_t high_index) { - SUPPORT_CHECK(rdp.texture_to_load.siz == G_IM_SIZ_16b); - rdp.palette = rdp.texture_to_load.addr; -} - -static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, UNUSED uint32_t dxt) { - if (tile == 1) return; - SUPPORT_CHECK(tile == G_TX_LOADTILE); - SUPPORT_CHECK(uls == 0); - SUPPORT_CHECK(ult == 0); - - // The lrs field rather seems to be number of pixels to load - uint32_t word_size_shift; - switch (rdp.texture_to_load.siz) { - case G_IM_SIZ_4b: - word_size_shift = 0; // Or -1? It's unused in SM64 anyway. - break; - case G_IM_SIZ_8b: - word_size_shift = 0; - break; - case G_IM_SIZ_16b: - word_size_shift = 1; - break; - case G_IM_SIZ_32b: - word_size_shift = 2; - break; - } - uint32_t size_bytes = (lrs + 1) << word_size_shift; - rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = size_bytes; - rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr; - - rdp.textures_changed[rdp.texture_to_load.tile_number] = true; -} - -static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t lrt) { - if (tile == 1) return; - SUPPORT_CHECK(tile == G_TX_LOADTILE); - SUPPORT_CHECK(uls == 0); - SUPPORT_CHECK(ult == 0); - - uint32_t word_size_shift; - switch (rdp.texture_to_load.siz) { - case G_IM_SIZ_4b: - word_size_shift = 0; // Or -1? It's unused in SM64 anyway. - break; - case G_IM_SIZ_8b: - word_size_shift = 0; - break; - case G_IM_SIZ_16b: - word_size_shift = 1; - break; - case G_IM_SIZ_32b: - word_size_shift = 2; - break; - } - - uint32_t size_bytes = (((lrs >> G_TEXTURE_IMAGE_FRAC) + 1) * ((lrt >> G_TEXTURE_IMAGE_FRAC) + 1)) << word_size_shift; - rdp.loaded_texture[rdp.texture_to_load.tile_number].size_bytes = size_bytes; - - rdp.loaded_texture[rdp.texture_to_load.tile_number].addr = rdp.texture_to_load.addr; - rdp.texture_tile.uls = uls; - rdp.texture_tile.ult = ult; - rdp.texture_tile.lrs = lrs; - rdp.texture_tile.lrt = lrt; - - rdp.textures_changed[rdp.texture_to_load.tile_number] = true; -} - -static uint8_t color_comb_component(uint32_t v) { - switch (v) { - case G_CCMUX_TEXEL0: - return CC_TEXEL0; - case G_CCMUX_TEXEL1: - return CC_TEXEL1; - case G_CCMUX_PRIMITIVE: - return CC_PRIM; - case G_CCMUX_SHADE: - return CC_SHADE; - case G_CCMUX_ENVIRONMENT: - return CC_ENV; - case G_CCMUX_TEXEL0_ALPHA: - return CC_TEXEL0A; - case G_CCMUX_LOD_FRACTION: - return CC_LOD; - default: - return CC_0; - } -} - -static inline uint32_t color_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { - return color_comb_component(a) | - (color_comb_component(b) << 3) | - (color_comb_component(c) << 6) | - (color_comb_component(d) << 9); -} - -static void gfx_dp_set_combine_mode(uint32_t rgb, uint32_t alpha) { - rdp.combine_mode = rgb | (alpha << 12); -} - -static void gfx_dp_set_env_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - rdp.env_color.r = r; - rdp.env_color.g = g; - rdp.env_color.b = b; - rdp.env_color.a = a; -} - -static void gfx_dp_set_prim_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - rdp.prim_color.r = r; - rdp.prim_color.g = g; - rdp.prim_color.b = b; - rdp.prim_color.a = a; -} - -static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { -#ifdef TARGET_N3DS - gfx_rapi->set_fog_color(r, g, b, a); -#endif - rdp.fog_color.r = r; - rdp.fog_color.g = g; - rdp.fog_color.b = b; - rdp.fog_color.a = a; -} - -static void gfx_dp_set_fill_color(uint32_t packed_color) { - uint16_t col16 = (uint16_t)packed_color; - uint32_t r = col16 >> 11; - uint32_t g = (col16 >> 6) & 0x1f; - uint32_t b = (col16 >> 1) & 0x1f; - uint32_t a = col16 & 1; - rdp.fill_color.r = SCALE_5_8(r); - rdp.fill_color.g = SCALE_5_8(g); - rdp.fill_color.b = SCALE_5_8(b); - rdp.fill_color.a = a * 255; -} - -static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) { - uint32_t saved_other_mode_h = rdp.other_mode_h; - uint32_t cycle_type = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)); - - if (cycle_type == G_CYC_COPY) { - rdp.other_mode_h = (rdp.other_mode_h & ~(3U << G_MDSFT_TEXTFILT)) | G_TF_POINT; - } - - // U10.2 coordinates - float ulxf = ulx; - float ulyf = uly; - float lrxf = lrx; - float lryf = lry; - - ulxf = ulxf / (4.0f * HALF_SCREEN_WIDTH) - 1.0f; - ulyf = -(ulyf / (4.0f * HALF_SCREEN_HEIGHT)) + 1.0f; - lrxf = lrxf / (4.0f * HALF_SCREEN_WIDTH) - 1.0f; - lryf = -(lryf / (4.0f * HALF_SCREEN_HEIGHT)) + 1.0f; - - ulxf = gfx_adjust_x_for_aspect_ratio(ulxf); - lrxf = gfx_adjust_x_for_aspect_ratio(lrxf); - - struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0]; - struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1]; - struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2]; - struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3]; - - ul->x = ulxf; - ul->y = ulyf; - ul->z = -1.0f; - ul->w = 1.0f; - - ll->x = ulxf; - ll->y = lryf; - ll->z = -1.0f; - ll->w = 1.0f; - - lr->x = lrxf; - lr->y = lryf; - lr->z = -1.0f; - lr->w = 1.0f; - - ur->x = lrxf; - ur->y = ulyf; - ur->z = -1.0f; - ur->w = 1.0f; - - // The coordinates for texture rectangle shall bypass the viewport setting - struct XYWidthHeight default_viewport = {0, 0, gfx_current_dimensions.width, gfx_current_dimensions.height}; - struct XYWidthHeight viewport_saved = rdp.viewport; - uint32_t geometry_mode_saved = rsp.geometry_mode; - - rdp.viewport = default_viewport; - rdp.viewport_or_scissor_changed = true; - rsp.geometry_mode = 0; - - gfx_sp_tri1(MAX_VERTICES + 0, MAX_VERTICES + 1, MAX_VERTICES + 3); - gfx_sp_tri1(MAX_VERTICES + 1, MAX_VERTICES + 2, MAX_VERTICES + 3); - - rsp.geometry_mode = geometry_mode_saved; - rdp.viewport = viewport_saved; - rdp.viewport_or_scissor_changed = true; - - if (cycle_type == G_CYC_COPY) { - rdp.other_mode_h = saved_other_mode_h; - } -} - -static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry, UNUSED uint8_t tile, int16_t uls, int16_t ult, int16_t dsdx, int16_t dtdy, bool flip) { - uint32_t saved_combine_mode = rdp.combine_mode; - if ((rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_COPY) { - // Per RDP Command Summary Set Tile's shift s and this dsdx should be set to 4 texels - // Divide by 4 to get 1 instead - dsdx >>= 2; - - // Color combiner is turned off in copy mode - gfx_dp_set_combine_mode(color_comb(0, 0, 0, G_CCMUX_TEXEL0), color_comb(0, 0, 0, G_ACMUX_TEXEL0)); - - // Per documentation one extra pixel is added in this modes to each edge - lrx += 1 << 2; - lry += 1 << 2; - } - - // uls and ult are S10.5 - // dsdx and dtdy are S5.10 - // lrx, lry, ulx, uly are U10.2 - // lrs, lrt are S10.5 - if (flip) { - dsdx = -dsdx; - dtdy = -dtdy; - } - int16_t width = !flip ? lrx - ulx : lry - uly; - int16_t height = !flip ? lry - uly : lrx - ulx; - float lrs = ((uls << 7) + dsdx * width) >> 7; - float lrt = ((ult << 7) + dtdy * height) >> 7; - - struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0]; - struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1]; - struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2]; - struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3]; - ul->u = uls; - ul->v = ult; - lr->u = lrs; - lr->v = lrt; - if (!flip) { - ll->u = uls; - ll->v = lrt; - ur->u = lrs; - ur->v = ult; - } else { - ll->u = lrs; - ll->v = ult; - ur->u = uls; - ur->v = lrt; - } - - gfx_draw_rectangle(ulx, uly, lrx, lry); - rdp.combine_mode = saved_combine_mode; -} - -static void gfx_dp_fill_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) { - if (rdp.color_image_address == rdp.z_buf_address) { - // Don't clear Z buffer here since we already did it with glClear - return; - } - uint32_t mode = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)); - - if (mode == G_CYC_COPY || mode == G_CYC_FILL) { - // Per documentation one extra pixel is added in this modes to each edge - lrx += 1 << 2; - lry += 1 << 2; - } - - for (int i = MAX_VERTICES; i < MAX_VERTICES + 4; i++) { - struct LoadedVertex* v = &rsp.loaded_vertices[i]; - v->color = rdp.fill_color; - } - - uint32_t saved_combine_mode = rdp.combine_mode; - gfx_draw_rectangle(ulx, uly, lrx, lry); - rdp.combine_mode = saved_combine_mode; -} - -static void gfx_dp_set_z_image(void *z_buf_address) { - rdp.z_buf_address = z_buf_address; -} - -static void gfx_dp_set_color_image(UNUSED uint32_t format, UNUSED uint32_t size, UNUSED uint32_t width, void* address) { - rdp.color_image_address = address; -} - -static void gfx_sp_set_other_mode(uint32_t shift, uint32_t num_bits, uint64_t mode) { - uint64_t mask = (((uint64_t)1 << num_bits) - 1) << shift; - uint64_t om = rdp.other_mode_l | ((uint64_t)rdp.other_mode_h << 32); - om = (om & ~mask) | mode; - rdp.other_mode_l = (uint32_t)om; - rdp.other_mode_h = (uint32_t)(om >> 32); -} - -static inline void *seg_addr(uintptr_t w1) { - return (void *) w1; -} - -#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1)) -#define C1(pos, width) ((cmd->words.w1 >> (pos)) & ((1U << width) - 1)) - -static void gfx_run_dl(Gfx* cmd) { - //int dummy = 0; - for (;;) { - uint32_t opcode = cmd->words.w0 >> 24; - - switch (opcode) { - // RSP commands: - case G_MTX: -#ifdef F3DEX_GBI_2 - gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, (const int32_t *) seg_addr(cmd->words.w1)); -#else - gfx_sp_matrix(C0(16, 8), (const int32_t *) seg_addr(cmd->words.w1)); -#endif - break; - case (uint8_t)G_POPMTX: -#ifdef F3DEX_GBI_2 - gfx_sp_pop_matrix(cmd->words.w1 / 64); -#else - gfx_sp_pop_matrix(1); -#endif - break; - case G_MOVEMEM: -#ifdef F3DEX_GBI_2 - gfx_sp_movemem(C0(0, 8), C0(8, 8) * 8, seg_addr(cmd->words.w1)); -#else - gfx_sp_movemem(C0(16, 8), 0, seg_addr(cmd->words.w1)); -#endif - break; - case (uint8_t)G_MOVEWORD: -#ifdef F3DEX_GBI_2 - gfx_sp_moveword(C0(16, 8), C0(0, 16), cmd->words.w1); -#else - gfx_sp_moveword(C0(0, 8), C0(8, 16), cmd->words.w1); -#endif - break; - case (uint8_t)G_TEXTURE: -#ifdef F3DEX_GBI_2 - gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(1, 7)); -#else - gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(0, 8)); -#endif - break; - case G_VTX: -#ifdef F3DEX_GBI_2 - gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), seg_addr(cmd->words.w1)); -#elif defined(F3DEX_GBI) || defined(F3DLP_GBI) - gfx_sp_vertex(C0(10, 6), C0(16, 8) / 2, seg_addr(cmd->words.w1)); -#else - gfx_sp_vertex((C0(0, 16)) / sizeof(Vtx), C0(16, 4), seg_addr(cmd->words.w1)); -#endif - break; - case G_DL: - if (C0(16, 1) == 0) { - // Push return address - gfx_run_dl((Gfx *)seg_addr(cmd->words.w1)); - } else { - cmd = (Gfx *)seg_addr(cmd->words.w1); - --cmd; // increase after break - } - break; - case (uint8_t)G_ENDDL: - return; -#ifdef F3DEX_GBI_2 - case G_GEOMETRYMODE: - gfx_sp_geometry_mode(~C0(0, 24), cmd->words.w1); - break; -#else - case (uint8_t)G_SETGEOMETRYMODE: - gfx_sp_geometry_mode(0, cmd->words.w1); - break; - case (uint8_t)G_CLEARGEOMETRYMODE: - gfx_sp_geometry_mode(cmd->words.w1, 0); - break; -#endif - case (uint8_t)G_TRI1: -#ifdef F3DEX_GBI_2 - gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2); -#elif defined(F3DEX_GBI) || defined(F3DLP_GBI) - gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2); -#else - gfx_sp_tri1(C1(16, 8) / 10, C1(8, 8) / 10, C1(0, 8) / 10); -#endif - break; -#if defined(F3DEX_GBI) || defined(F3DLP_GBI) - case (uint8_t)G_TRI2: - gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2); - gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2); - break; -#endif - case (uint8_t)G_SETOTHERMODE_L: -#ifdef F3DEX_GBI_2 - gfx_sp_set_other_mode(31 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, cmd->words.w1); -#else - gfx_sp_set_other_mode(C0(8, 8), C0(0, 8), cmd->words.w1); -#endif - break; - case (uint8_t)G_SETOTHERMODE_H: -#ifdef F3DEX_GBI_2 - gfx_sp_set_other_mode(63 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, (uint64_t) cmd->words.w1 << 32); -#else - gfx_sp_set_other_mode(C0(8, 8) + 32, C0(0, 8), (uint64_t) cmd->words.w1 << 32); -#endif - break; -#ifdef F3D_OLD - case (uint8_t)G_RDPHALF_2: -#else - case (uint8_t)G_RDPHALF_1: -#endif - switch (rsp.saved_opcode) { - case G_TEXRECT: - case G_TEXRECTFLIP: -#ifdef F3DEX_GBI_2E - rsp.saved_ulx = (int32_t)(C0(0, 24) << 8) >> 8; -#endif - rsp.saved_uls = (uint16_t)C1(16, 16); - rsp.saved_ult = (uint16_t)C1(0, 16); - break; -#ifdef F3DEX_GBI_2E - case G_FILLRECT: - { - int32_t ulx = (int32_t)(C0(0, 24) << 8) >> 8; - int32_t uly = (int32_t)(C1(0, 24) << 8) >> 8; - gfx_dp_fill_rectangle(ulx, uly, rsp.saved_lrx, rsp.saved_lry); - rsp.saved_opcode = G_NOOP; - break; - } -#endif - } - break; -#ifdef F3D_OLD - case (uint8_t)G_RDPHALF_CONT: -#else - case (uint8_t)G_RDPHALF_2: -#endif - switch (rsp.saved_opcode) { - case G_TEXRECT: - case G_TEXRECTFLIP: - { - uint8_t tile = rsp.saved_tile; - int32_t ulx = rsp.saved_ulx, lrx = rsp.saved_lrx, lry = rsp.saved_lry; - uint16_t uls = rsp.saved_uls, ult = rsp.saved_ult; -#ifdef F3DEX_GBI_2E - int32_t uly = (int32_t)(C0(0, 24) << 8) >> 8; -#else - int32_t uly = rsp.saved_uly; -#endif - uint16_t dsdx = (uint16_t)C1(16, 16); - uint16_t dtdy = (uint16_t)C1(0, 16); - gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, rsp.saved_opcode == G_TEXRECTFLIP); - rsp.saved_opcode = G_NOOP; - break; - } - } - - // RDP Commands: - case G_SETTIMG: - gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), seg_addr(cmd->words.w1)); - break; - case G_LOADBLOCK: - gfx_dp_load_block(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); - break; - case G_LOADTILE: - gfx_dp_load_tile(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); - break; - case G_SETTILE: - gfx_dp_set_tile(C0(21, 3), C0(19, 2), C0(9, 9), C0(0, 9), C1(24, 3), C1(20, 4), C1(18, 2), C1(14, 4), C1(10, 4), C1(8, 2), C1(4, 4), C1(0, 4)); - break; - case G_SETTILESIZE: - gfx_dp_set_tile_size(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); - break; - case G_LOADTLUT: - gfx_dp_load_tlut(C1(24, 3), C1(14, 10)); - break; - case G_SETENVCOLOR: - gfx_dp_set_env_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); - break; - case G_SETPRIMCOLOR: - gfx_dp_set_prim_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); - break; - case G_SETFOGCOLOR: - gfx_dp_set_fog_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); - break; - case G_SETFILLCOLOR: - gfx_dp_set_fill_color(cmd->words.w1); - break; - case G_SETCOMBINE: - gfx_dp_set_combine_mode( - color_comb(C0(20, 4), C1(28, 4), C0(15, 5), C1(15, 3)), - color_comb(C0(12, 3), C1(12, 3), C0(9, 3), C1(9, 3))); - /*color_comb(C0(5, 4), C1(24, 4), C0(0, 5), C1(6, 3)), - color_comb(C1(21, 3), C1(3, 3), C1(18, 3), C1(0, 3)));*/ - break; - // G_SETPRIMCOLOR, G_CCMUX_PRIMITIVE, G_ACMUX_PRIMITIVE, is used by Goddard - // G_CCMUX_TEXEL1, LOD_FRACTION is used in Bowser room 1 - case G_TEXRECT: - case G_TEXRECTFLIP: - { - rsp.saved_opcode = opcode; -#ifdef F3DEX_GBI_2E - rsp.saved_lrx = (int32_t)(C0(0, 24) << 8) >> 8; - rsp.saved_lry = (int32_t)(C1(0, 24) << 8) >> 8; - rsp.saved_tile = (int32_t)C1(24, 3); -#else - rsp.saved_lrx = C0(12, 12); - rsp.saved_lry = C0(0, 12); - rsp.saved_tile = C1(24, 3); - rsp.saved_ulx = C1(12, 12); - rsp.saved_uly = C1(0, 12); -#endif - break; - } - case G_FILLRECT: -#ifdef F3DEX_GBI_2E - { - rsp.saved_opcode = G_FILLRECT; - rsp.saved_lrx = (int32_t)(C0(0, 24) << 8) >> 8; - rsp.saved_lry = (int32_t)(C1(0, 24) << 8) >> 8; - break; - } -#else - gfx_dp_fill_rectangle(C1(12, 12), C1(0, 12), C0(12, 12), C0(0, 12)); - break; -#endif - case G_SETSCISSOR: - gfx_dp_set_scissor(C1(24, 2), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); - break; - case G_SETZIMG: - gfx_dp_set_z_image(seg_addr(cmd->words.w1)); - break; - case G_SETCIMG: - gfx_dp_set_color_image(C0(21, 3), C0(19, 2), C0(0, 11), seg_addr(cmd->words.w1)); - break; -#ifdef TARGET_N3DS - case G_SPECIAL_1: - gfx_set_2d(cmd->words.w1); - break; - case G_SPECIAL_2: - gfx_flush(); - break; - case G_SPECIAL_4: - gfx_set_iod(cmd->words.w1); - break; -#endif - } - ++cmd; - } -} - -static void gfx_sp_reset() { - rsp.modelview_matrix_stack_size = 1; - rsp.current_num_lights = 2; - rsp.lights_changed = true; -} - -void gfx_update_dimensions() { - gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height); - - if (gfx_current_dimensions.height == 0) { - // Avoid division by zero - gfx_current_dimensions.height = 1; - } - - gfx_current_dimensions.aspect_ratio = (float) gfx_current_dimensions.width / (float) gfx_current_dimensions.height; -} - -void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, const char *window_title) { - gfx_wapi = wapi; - gfx_wapi->init(window_title); - - gfx_update_dimensions(); - - gfx_rapi = rapi; - gfx_rapi->init(); - - framebuffer_data = NULL; - requested_framebuffer = false; - - // Used in the 120 star TAS - static uint32_t precomp_shaders[] = { - 0x01200200, - 0x00000045, - 0x00000200, - 0x01200a00, - 0x00000a00, - 0x01a00045, - 0x00000551, - 0x01045045, - 0x05a00a00, - 0x01200045, - 0x05045045, - 0x01045a00, - 0x01a00a00, - 0x0000038d, - 0x01081081, - 0x0120038d, - 0x03200045, - 0x03200a00, - 0x01a00a6f, - 0x01141045, - 0x07a00a00, - 0x05200200, - 0x03200200, - 0x09200200, - 0x0920038d, - 0x09200045, - // Not used in the 120 star TAS, but discovered through testing - 0x09200a00 - }; - - for (size_t i = 0; i < sizeof(precomp_shaders) / sizeof(uint32_t); i++) - gfx_lookup_or_create_shader_program(precomp_shaders[i]); -} - -#ifdef EXTERNAL_DATA -void gfx_precache_textures(void) { - // preload all textures - fs_walk(FS_TEXTUREDIR, preload_texture, NULL, true); -} -#endif - -struct GfxRenderingAPI *gfx_get_current_rendering_api(void) { - return gfx_rapi; -} - -uint16_t *get_framebuffer() { - if (!requested_framebuffer) { - if (framebuffer_data == NULL) { - framebuffer_data = (uint16_t *) malloc(FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * sizeof(uint16_t)); - } - - gfx_get_current_rendering_api()->get_framebuffer(framebuffer_data); - requested_framebuffer = true; - } - - return framebuffer_data; -} - -void gfx_start_frame(void) { - gfx_wapi->handle_events(); -#ifndef TARGET_N3DS - gfx_update_dimensions(); -#endif -} - -void gfx_run(Gfx *commands) { - gfx_sp_reset(); - - if (!gfx_wapi->start_frame()) { - dropped_frame = true; - return; - } - dropped_frame = false; - - gfx_rapi->start_frame(); - gfx_run_dl(commands); - gfx_flush(); - gfx_rapi->end_frame(); - gfx_wapi->swap_buffers_begin(); -} - -void gfx_end_frame(void) { - if (!dropped_frame) { - gfx_rapi->finish_render(); - gfx_wapi->swap_buffers_end(); - requested_framebuffer = false; - } -} - -void gfx_shutdown(void) { - if (gfx_rapi) { - if (gfx_rapi->shutdown) gfx_rapi->shutdown(); - gfx_rapi = NULL; - } - if (gfx_wapi) { - if (gfx_wapi->shutdown) gfx_wapi->shutdown(); - gfx_wapi = NULL; - } - if (framebuffer_data) { - free(framebuffer_data); - framebuffer_data = NULL; - } -} diff --git a/src/pc/gfx/gfx_pc.cpp b/src/pc/gfx/gfx_pc.cpp new file mode 100644 index 000000000..1d794e20d --- /dev/null +++ b/src/pc/gfx/gfx_pc.cpp @@ -0,0 +1,3061 @@ +#define NOMINMAX + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +//#include "misc/Cvar.h" + +#include "gfx_pc.h" +#include "gfx_cc.h" +#include "gfx_window_manager_api.h" +#include "gfx_rendering_api.h" +#include "gfx_screen_config.h" + +#ifndef _LANGUAGE_C +#define _LANGUAGE_C +#endif +#include +#include +#include + +#include "gbi_pc.h" + +/* +#include "misc/Hooks.h" + +#include "log/luslog.h" +#include "menu/ImGuiImpl.h" +#include "resource/GameVersions.h" +#include "resource/ResourceMgr.h" +#include "misc/Utils.h" + +extern "C" { +const char* ResourceMgr_GetNameByCRC(uint64_t crc); +int32_t* ResourceMgr_LoadMtxByCRC(uint64_t crc); +Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc); +Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc); +char* ResourceMgr_LoadTexByCRC(uint64_t crc); +void ResourceMgr_RegisterResourcePatch(uint64_t hash, uint32_t instrIndex, uintptr_t origData); +char* ResourceMgr_LoadTexByName(char* texPath); +int ResourceMgr_OTRSigCheck(char* imgData); +} + */ +uintptr_t gfxFramebuffer; + +using namespace std; + +#define SEG_ADDR(seg, addr) (addr | (seg << 24) | 1) + +#define SUPPORT_CHECK(x) assert(x) + +// SCALE_M_N: upscale/downscale M-bit integer to N-bit +#define SCALE_5_8(VAL_) (((VAL_)*0xFF) / 0x1F) +#define SCALE_8_5(VAL_) ((((VAL_) + 4) * 0x1F) / 0xFF) +#define SCALE_4_8(VAL_) ((VAL_)*0x11) +#define SCALE_8_4(VAL_) ((VAL_) / 0x11) +#define SCALE_3_8(VAL_) ((VAL_)*0x24) +#define SCALE_8_3(VAL_) ((VAL_) / 0x24) + +// SCREEN_WIDTH and SCREEN_HEIGHT are defined in the headerfile +#define HALF_SCREEN_WIDTH (SCREEN_WIDTH / 2) +#define HALF_SCREEN_HEIGHT (SCREEN_HEIGHT / 2) + +#define RATIO_X (gfx_current_dimensions.width / (2.0f * HALF_SCREEN_WIDTH)) +#define RATIO_Y (gfx_current_dimensions.height / (2.0f * HALF_SCREEN_HEIGHT)) + +#define MAX_BUFFERED 256 +// #define MAX_LIGHTS 2 +#define MAX_LIGHTS 32 +#define MAX_VERTICES 64 + +#define TEXTURE_CACHE_MAX_SIZE 500 + +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +struct RGBA { + uint8_t r, g, b, a; +}; + +struct LoadedVertex { + float x, y, z, w; + float u, v; + struct RGBA color; + uint8_t clip_rej; +}; + +static struct { + TextureCacheMap map; + list lru; + vector free_texture_ids; +} gfx_texture_cache; + +struct ColorCombiner { + uint64_t shader_id0; + uint32_t shader_id1; + bool used_textures[2]; + struct ShaderProgram* prg[16]; + uint8_t shader_input_mapping[2][7]; +}; + +static map color_combiner_pool; +static map::iterator prev_combiner = color_combiner_pool.end(); + +static struct RSP { + float modelview_matrix_stack[11][4][4]; + uint8_t modelview_matrix_stack_size; + + float MP_matrix[4][4]; + float P_matrix[4][4]; + + Light_t lookat[2]; + Light_t current_lights[MAX_LIGHTS + 1]; + float current_lights_coeffs[MAX_LIGHTS][3]; + float current_lookat_coeffs[2][3]; // lookat_x, lookat_y + uint8_t current_num_lights; // includes ambient light + bool lights_changed; + + uint32_t geometry_mode; + int16_t fog_mul, fog_offset; + + struct { + // U0.16 + uint16_t s, t; + } texture_scaling_factor; + + struct LoadedVertex loaded_vertices[MAX_VERTICES + 4]; + +// these were removed alongside G_RDPHALF commands in f3dv2, perhaps due to OOT incompatibility? + uint8_t saved_opcode; + uint8_t saved_tile; + uint16_t saved_uls, saved_ult; + int32_t saved_lrx, saved_lry, saved_ulx, saved_uly; +} rsp; + +static struct RDP { + const uint8_t* palettes[2]; + struct { + const uint8_t* addr; + uint8_t siz; + uint32_t width; + //const char* otr_path; + } texture_to_load; + struct { + const uint8_t* addr; + uint32_t size_bytes; + uint32_t full_image_line_size_bytes; + uint32_t line_size_bytes; + //const char* otr_path; + } loaded_texture[2]; + struct { + uint8_t fmt; + uint8_t siz; + uint8_t cms, cmt; + uint8_t shifts, shiftt; + uint16_t uls, ult, lrs, lrt; // U10.2 + uint16_t tmem; // 0-511, in 64-bit word units + uint32_t line_size_bytes; + uint8_t palette; + uint8_t tmem_index; // 0 or 1 for offset 0 kB or offset 2 kB, respectively + } texture_tile[8]; + bool textures_changed[2]; + + uint8_t first_tile_index; + + uint32_t other_mode_l, other_mode_h; + uint64_t combine_mode; + bool grayscale; + + uint8_t prim_lod_fraction; + struct RGBA env_color, prim_color, fog_color, fill_color, grayscale_color, override_color; + struct XYWidthHeight viewport, scissor; + bool viewport_or_scissor_changed; + void* z_buf_address; + void* color_image_address; +} rdp; + +static struct RenderingState { + uint8_t depth_test_and_mask; // 1: depth test, 2: depth mask + bool decal_mode; + bool alpha_blend; + struct XYWidthHeight viewport, scissor; + struct ShaderProgram* shader_program; + TextureCacheNode* textures[2]; +} rendering_state; + +struct GfxDimensions gfx_current_window_dimensions; +struct GfxDimensions gfx_current_dimensions; +static struct GfxDimensions gfx_prev_dimensions; +struct XYWidthHeight gfx_current_game_window_viewport; + +static bool game_renders_to_framebuffer; +static int game_framebuffer; +static int game_framebuffer_msaa_resolved; + +uint32_t gfx_msaa_level = 1; + +//static bool has_drawn_imgui_menu; + +static bool dropped_frame; + +//static const std::unordered_map* current_mtx_replacements; + +static float buf_vbo[MAX_BUFFERED * (32 * 3)]; // 3 vertices in a triangle and 32 floats per vtx +static size_t buf_vbo_len; +static size_t buf_vbo_num_tris; + +static struct GfxWindowManagerAPI* gfx_wapi; +static struct GfxRenderingAPI* gfx_rapi; + +static int markerOn; +static uintptr_t segmentPointers[16]; + +struct FBInfo { + uint32_t orig_width, orig_height; + uint32_t applied_width, applied_height; +}; + +static bool fbActive = 0; +static map::iterator active_fb; +static map framebuffers; + +static set> get_pixel_depth_pending; +static unordered_map, uint16_t, hash_pair_ff> get_pixel_depth_cached; + +#ifdef _WIN32 +// TODO: Properly implement for MSVC +static unsigned long get_time(void) { + return 0; +} +#else +#include +#include +static unsigned long get_time(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (unsigned long)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; +} +#endif + +static void gfx_flush(void) { + if (buf_vbo_len > 0) { + int num = buf_vbo_num_tris; + unsigned long t0 = get_time(); + + if (markerOn) { + int bp = 0; + } + + gfx_rapi->draw_triangles(buf_vbo, buf_vbo_len, buf_vbo_num_tris); + buf_vbo_len = 0; + buf_vbo_num_tris = 0; + unsigned long t1 = get_time(); + /*if (t1 - t0 > 1000) { + printf("f: %d %d\n", num, (int)(t1 - t0)); + }*/ + } +} + +static struct ShaderProgram* gfx_lookup_or_create_shader_program(uint64_t shader_id0, uint32_t shader_id1) { + struct ShaderProgram* prg = gfx_rapi->lookup_shader(shader_id0, shader_id1); + if (prg == NULL) { + gfx_rapi->unload_shader(rendering_state.shader_program); + prg = gfx_rapi->create_and_load_new_shader(shader_id0, shader_id1); + rendering_state.shader_program = prg; + } + return prg; +} + +static const char* ccmux_to_string(uint32_t ccmux) { + static const char* const tbl[] = { + "G_CCMUX_COMBINED", + "G_CCMUX_TEXEL0", + "G_CCMUX_TEXEL1", + "G_CCMUX_PRIMITIVE", + "G_CCMUX_SHADE", + "G_CCMUX_ENVIRONMENT", + "G_CCMUX_1", + "G_CCMUX_COMBINED_ALPHA", + "G_CCMUX_TEXEL0_ALPHA", + "G_CCMUX_TEXEL1_ALPHA", + "G_CCMUX_PRIMITIVE_ALPHA", + "G_CCMUX_SHADE_ALPHA", + "G_CCMUX_ENV_ALPHA", + "G_CCMUX_LOD_FRACTION", + "G_CCMUX_PRIM_LOD_FRAC", + "G_CCMUX_K5", + }; + if (ccmux > 15) { + return "G_CCMUX_0"; + + } else { + return tbl[ccmux]; + } +} + +static const char* acmux_to_string(uint32_t acmux) { + static const char* const tbl[] = { + "G_ACMUX_COMBINED or G_ACMUX_LOD_FRACTION", + "G_ACMUX_TEXEL0", + "G_ACMUX_TEXEL1", + "G_ACMUX_PRIMITIVE", + "G_ACMUX_SHADE", + "G_ACMUX_ENVIRONMENT", + "G_ACMUX_1 or G_ACMUX_PRIM_LOD_FRAC", + "G_ACMUX_0", + }; + return tbl[acmux]; +} + +static void gfx_generate_cc(struct ColorCombiner* comb, uint64_t cc_id) { + if (markerOn) { + int bp = 0; + } + + bool is_2cyc = (cc_id & (uint64_t)SHADER_OPT_2CYC << CC_SHADER_OPT_POS) != 0; + + uint8_t c[2][2][4]; + uint64_t shader_id0 = 0; + uint32_t shader_id1 = (cc_id >> CC_SHADER_OPT_POS); + uint8_t shader_input_mapping[2][7] = { { 0 } }; + bool used_textures[2] = { false, false }; + for (int i = 0; i < 2 && (i == 0 || is_2cyc); i++) { + uint32_t rgb_a = (cc_id >> (i * 28)) & 0xf; + uint32_t rgb_b = (cc_id >> (i * 28 + 4)) & 0xf; + uint32_t rgb_c = (cc_id >> (i * 28 + 8)) & 0x1f; + uint32_t rgb_d = (cc_id >> (i * 28 + 13)) & 7; + uint32_t alpha_a = (cc_id >> (i * 28 + 16)) & 7; + uint32_t alpha_b = (cc_id >> (i * 28 + 16 + 3)) & 7; + uint32_t alpha_c = (cc_id >> (i * 28 + 16 + 6)) & 7; + uint32_t alpha_d = (cc_id >> (i * 28 + 16 + 9)) & 7; + + if (rgb_a >= 8) { + rgb_a = G_CCMUX_0; + } + if (rgb_b >= 8) { + rgb_b = G_CCMUX_0; + } + if (rgb_c >= 16) { + rgb_c = G_CCMUX_0; + } + if (rgb_d == 7) { + rgb_d = G_CCMUX_0; + } + + if (rgb_a == rgb_b || rgb_c == G_CCMUX_0) { + // Normalize + rgb_a = G_CCMUX_0; + rgb_b = G_CCMUX_0; + rgb_c = G_CCMUX_0; + } + if (alpha_a == alpha_b || alpha_c == G_ACMUX_0) { + // Normalize + alpha_a = G_ACMUX_0; + alpha_b = G_ACMUX_0; + alpha_c = G_ACMUX_0; + } + if (i == 1) { + if (rgb_a != G_CCMUX_COMBINED && rgb_b != G_CCMUX_COMBINED && rgb_c != G_CCMUX_COMBINED && + rgb_d != G_CCMUX_COMBINED) { + // First cycle RGB not used, so clear it away + c[0][0][0] = c[0][0][1] = c[0][0][2] = c[0][0][3] = G_CCMUX_0; + } + if (rgb_c != G_CCMUX_COMBINED_ALPHA && alpha_a != G_ACMUX_COMBINED && alpha_b != G_ACMUX_COMBINED && + alpha_d != G_ACMUX_COMBINED) { + // First cycle ALPHA not used, so clear it away + c[0][1][0] = c[0][1][1] = c[0][1][2] = c[0][1][3] = G_ACMUX_0; + } + } + + c[i][0][0] = rgb_a; + c[i][0][1] = rgb_b; + c[i][0][2] = rgb_c; + c[i][0][3] = rgb_d; + c[i][1][0] = alpha_a; + c[i][1][1] = alpha_b; + c[i][1][2] = alpha_c; + c[i][1][3] = alpha_d; + } + if (!is_2cyc) { + for (int i = 0; i < 2; i++) { + for (int k = 0; k < 4; k++) { + c[1][i][k] = i == 0 ? G_CCMUX_0 : G_ACMUX_0; + } + } + } + { + uint8_t input_number[32] = { 0 }; + int next_input_number = SHADER_INPUT_1; + for (int i = 0; i < 2 && (i == 0 || is_2cyc); i++) { + for (int j = 0; j < 4; j++) { + uint32_t val = 0; + switch (c[i][0][j]) { + case G_CCMUX_0: + val = SHADER_0; + break; + case G_CCMUX_1: + val = SHADER_1; + break; + case G_CCMUX_TEXEL0: + val = SHADER_TEXEL0; + used_textures[0] = true; + break; + case G_CCMUX_TEXEL1: + val = SHADER_TEXEL1; + used_textures[1] = true; + break; + case G_CCMUX_TEXEL0_ALPHA: + val = SHADER_TEXEL0A; + used_textures[0] = true; + break; + case G_CCMUX_TEXEL1_ALPHA: + val = SHADER_TEXEL1A; + used_textures[1] = true; + break; + case G_CCMUX_NOISE: + val = SHADER_NOISE; + break; + case G_CCMUX_PRIMITIVE: + case G_CCMUX_PRIMITIVE_ALPHA: + case G_CCMUX_PRIM_LOD_FRAC: + case G_CCMUX_SHADE: + case G_CCMUX_ENVIRONMENT: + case G_CCMUX_ENV_ALPHA: + case G_CCMUX_LOD_FRACTION: + if (input_number[c[i][0][j]] == 0) { + shader_input_mapping[0][next_input_number - 1] = c[i][0][j]; + input_number[c[i][0][j]] = next_input_number++; + } + val = input_number[c[i][0][j]]; + break; + case G_CCMUX_COMBINED: + val = SHADER_COMBINED; + break; + default: + fprintf(stderr, "Unsupported ccmux: %d\n", c[i][0][j]); + break; + } + shader_id0 |= (uint64_t)val << (i * 32 + j * 4); + } + } + } + { + uint8_t input_number[16] = { 0 }; + int next_input_number = SHADER_INPUT_1; + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 4; j++) { + uint32_t val = 0; + switch (c[i][1][j]) { + case G_ACMUX_0: + val = SHADER_0; + break; + case G_ACMUX_TEXEL0: + val = SHADER_TEXEL0; + used_textures[0] = true; + break; + case G_ACMUX_TEXEL1: + val = SHADER_TEXEL1; + used_textures[1] = true; + break; + case G_ACMUX_LOD_FRACTION: + // case G_ACMUX_COMBINED: same numerical value + if (j != 2) { + val = SHADER_COMBINED; + break; + } + c[i][1][j] = G_CCMUX_LOD_FRACTION; + [[fallthrough]]; // for G_ACMUX_LOD_FRACTION + case G_ACMUX_1: + // case G_ACMUX_PRIM_LOD_FRAC: same numerical value + if (j != 2) { + val = SHADER_1; + break; + } + [[fallthrough]]; // for G_ACMUX_PRIM_LOD_FRAC + case G_ACMUX_PRIMITIVE: + case G_ACMUX_SHADE: + case G_ACMUX_ENVIRONMENT: + if (input_number[c[i][1][j]] == 0) { + shader_input_mapping[1][next_input_number - 1] = c[i][1][j]; + input_number[c[i][1][j]] = next_input_number++; + } + val = input_number[c[i][1][j]]; + break; + } + shader_id0 |= (uint64_t)val << (i * 32 + 16 + j * 4); + } + } + } + comb->shader_id0 = shader_id0; + comb->shader_id1 = shader_id1; + comb->used_textures[0] = used_textures[0]; + comb->used_textures[1] = used_textures[1]; + // comb->prg = gfx_lookup_or_create_shader_program(shader_id0, shader_id1); + memcpy(comb->shader_input_mapping, shader_input_mapping, sizeof(shader_input_mapping)); +} + +static struct ColorCombiner* gfx_lookup_or_create_color_combiner(uint64_t cc_id) { + if (prev_combiner != color_combiner_pool.end() && prev_combiner->first == cc_id) { + return &prev_combiner->second; + } + + prev_combiner = color_combiner_pool.find(cc_id); + if (prev_combiner != color_combiner_pool.end()) { + return &prev_combiner->second; + } + gfx_flush(); + prev_combiner = color_combiner_pool.insert(make_pair(cc_id, ColorCombiner())).first; + gfx_generate_cc(&prev_combiner->second, cc_id); + return &prev_combiner->second; +} + +void gfx_texture_cache_clear() { + for (const auto& entry : gfx_texture_cache.map) { + gfx_texture_cache.free_texture_ids.push_back(entry.second.texture_id); + } + gfx_texture_cache.map.clear(); + gfx_texture_cache.lru.clear(); +} + +static bool gfx_texture_cache_lookup(int i, int tile) { + uint8_t fmt = rdp.texture_tile[tile].fmt; + uint8_t siz = rdp.texture_tile[tile].siz; + uint32_t tmem_index = rdp.texture_tile[tile].tmem_index; + + TextureCacheNode** n = &rendering_state.textures[i]; + const uint8_t* orig_addr = rdp.loaded_texture[tmem_index].addr; + uint8_t palette_index = rdp.texture_tile[tile].palette; + + TextureCacheKey key; + if (fmt == G_IM_FMT_CI) { + key = { orig_addr, { rdp.palettes[0], rdp.palettes[1] }, fmt, siz, palette_index }; + } else { + key = { orig_addr, {}, fmt, siz, palette_index }; + } + + TextureCacheMap::iterator it = gfx_texture_cache.map.find(key); + + if (it != gfx_texture_cache.map.end()) { + gfx_rapi->select_texture(i, it->second.texture_id); + *n = &*it; + gfx_texture_cache.lru.splice(gfx_texture_cache.lru.end(), gfx_texture_cache.lru, + it->second.lru_location); // move to back + return true; + } + + if (gfx_texture_cache.map.size() >= TEXTURE_CACHE_MAX_SIZE) { + // Remove the texture that was least recently used + it = gfx_texture_cache.lru.front().it; + gfx_texture_cache.free_texture_ids.push_back(it->second.texture_id); + gfx_texture_cache.map.erase(it); + gfx_texture_cache.lru.pop_front(); + } + + uint32_t texture_id; + if (!gfx_texture_cache.free_texture_ids.empty()) { + texture_id = gfx_texture_cache.free_texture_ids.back(); + gfx_texture_cache.free_texture_ids.pop_back(); + } else { + texture_id = gfx_rapi->new_texture(); + } + + it = gfx_texture_cache.map.insert(make_pair(key, TextureCacheValue())).first; + TextureCacheNode* node = &*it; + node->second.texture_id = texture_id; + node->second.lru_location = gfx_texture_cache.lru.insert(gfx_texture_cache.lru.end(), { it }); + + gfx_rapi->select_texture(i, texture_id); + gfx_rapi->set_sampler_parameters(i, false, 0, 0); + *n = node; + return false; +} + +static void gfx_texture_cache_delete(const uint8_t* orig_addr) { + while (gfx_texture_cache.map.bucket_count() > 0) { + TextureCacheKey key = { orig_addr, { 0 }, 0, 0 }; // bucket index only depends on the address + size_t bucket = gfx_texture_cache.map.bucket(key); + bool again = false; + for (auto it = gfx_texture_cache.map.begin(bucket); it != gfx_texture_cache.map.end(bucket); ++it) { + if (it->first.texture_addr == orig_addr) { + gfx_texture_cache.lru.erase(it->second.lru_location); + gfx_texture_cache.free_texture_ids.push_back(it->second.texture_id); + gfx_texture_cache.map.erase(it->first); + again = true; + break; + } + } + if (!again) { + break; + } + } +} + +static void import_texture_rgba16(int tile) { + uint8_t rgba32_buf[480 * 240 * 4]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + // SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes / 2; i++) { + uint16_t col16 = (addr[2 * i] << 8) | addr[2 * i + 1]; + uint8_t a = col16 & 1; + uint8_t r = col16 >> 11; + uint8_t g = (col16 >> 6) & 0x1f; + uint8_t b = (col16 >> 1) & 0x1f; + rgba32_buf[4 * i + 0] = SCALE_5_8(r); + rgba32_buf[4 * i + 1] = SCALE_5_8(g); + rgba32_buf[4 * i + 2] = SCALE_5_8(b); + rgba32_buf[4 * i + 3] = a ? 255 : 0; + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes / 2; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_rgba32(int tile) { + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + uint32_t width = rdp.texture_tile[tile].line_size_bytes / 2; + uint32_t height = (size_bytes / 2) / rdp.texture_tile[tile].line_size_bytes; + gfx_rapi->upload_texture(addr, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, addr, width, height); +} + +static void import_texture_ia4(int tile) { + uint8_t rgba32_buf[32768]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes * 2; i++) { + uint8_t byte = addr[i / 2]; + uint8_t part = (byte >> (4 - (i % 2) * 4)) & 0xf; + uint8_t intensity = part >> 1; + uint8_t alpha = part & 1; + uint8_t r = intensity; + uint8_t g = intensity; + uint8_t b = intensity; + rgba32_buf[4 * i + 0] = SCALE_3_8(r); + rgba32_buf[4 * i + 1] = SCALE_3_8(g); + rgba32_buf[4 * i + 2] = SCALE_3_8(b); + rgba32_buf[4 * i + 3] = alpha ? 255 : 0; + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes * 2; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_ia8(int tile) { + uint8_t rgba32_buf[16384]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes; i++) { + uint8_t intensity = addr[i] >> 4; + uint8_t alpha = addr[i] & 0xf; + uint8_t r = intensity; + uint8_t g = intensity; + uint8_t b = intensity; + rgba32_buf[4 * i + 0] = SCALE_4_8(r); + rgba32_buf[4 * i + 1] = SCALE_4_8(g); + rgba32_buf[4 * i + 2] = SCALE_4_8(b); + rgba32_buf[4 * i + 3] = SCALE_4_8(alpha); + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_ia16(int tile) { + uint8_t rgba32_buf[8192]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes / 2; i++) { + uint8_t intensity = addr[2 * i]; + uint8_t alpha = addr[2 * i + 1]; + uint8_t r = intensity; + uint8_t g = intensity; + uint8_t b = intensity; + rgba32_buf[4 * i + 0] = r; + rgba32_buf[4 * i + 1] = g; + rgba32_buf[4 * i + 2] = b; + rgba32_buf[4 * i + 3] = alpha; + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes / 2; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_i4(int tile) { + uint8_t rgba32_buf[32768]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + // SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes * 2; i++) { + uint8_t byte = addr[i / 2]; + uint8_t part = (byte >> (4 - (i % 2) * 4)) & 0xf; + uint8_t intensity = part; + uint8_t r = intensity; + uint8_t g = intensity; + uint8_t b = intensity; + uint8_t a = intensity; + rgba32_buf[4 * i + 0] = SCALE_4_8(r); + rgba32_buf[4 * i + 1] = SCALE_4_8(g); + rgba32_buf[4 * i + 2] = SCALE_4_8(b); + rgba32_buf[4 * i + 3] = SCALE_4_8(a); + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes * 2; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_i8(int tile) { + uint8_t rgba32_buf[16384]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + // SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes; i++) { + uint8_t intensity = addr[i]; + uint8_t r = intensity; + uint8_t g = intensity; + uint8_t b = intensity; + uint8_t a = intensity; + rgba32_buf[4 * i + 0] = r; + rgba32_buf[4 * i + 1] = g; + rgba32_buf[4 * i + 2] = b; + rgba32_buf[4 * i + 3] = a; + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_ci4(int tile) { + uint8_t rgba32_buf[32768]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + uint32_t pal_idx = rdp.texture_tile[tile].palette; // 0-15 + const uint8_t* palette = rdp.palettes[pal_idx / 8] + (pal_idx % 8) * 16 * 2; // 16 pixel entries, 16 bits each + SUPPORT_CHECK(full_image_line_size_bytes == line_size_bytes); + + for (uint32_t i = 0; i < size_bytes * 2; i++) { + uint8_t byte = addr[i / 2]; + uint8_t idx = (byte >> (4 - (i % 2) * 4)) & 0xf; + uint16_t col16 = (palette[idx * 2] << 8) | palette[idx * 2 + 1]; // Big endian load + uint8_t a = col16 & 1; + uint8_t r = col16 >> 11; + uint8_t g = (col16 >> 6) & 0x1f; + uint8_t b = (col16 >> 1) & 0x1f; + rgba32_buf[4 * i + 0] = SCALE_5_8(r); + rgba32_buf[4 * i + 1] = SCALE_5_8(g); + rgba32_buf[4 * i + 2] = SCALE_5_8(b); + rgba32_buf[4 * i + 3] = a ? 255 : 0; + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes * 2; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture_ci8(int tile) { + uint8_t rgba32_buf[16384]; + const uint8_t* addr = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr; + uint32_t size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t full_image_line_size_bytes = + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes; + uint32_t line_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes; + + for (uint32_t i = 0, j = 0; i < size_bytes; j += full_image_line_size_bytes - line_size_bytes) { + for (uint32_t k = 0; k < line_size_bytes; i++, k++, j++) { + uint8_t idx = addr[j]; + uint16_t col16 = (rdp.palettes[idx / 128][(idx % 128) * 2] << 8) | + rdp.palettes[idx / 128][(idx % 128) * 2 + 1]; // Big endian load + uint8_t a = col16 & 1; + uint8_t r = col16 >> 11; + uint8_t g = (col16 >> 6) & 0x1f; + uint8_t b = (col16 >> 1) & 0x1f; + rgba32_buf[4 * i + 0] = SCALE_5_8(r); + rgba32_buf[4 * i + 1] = SCALE_5_8(g); + rgba32_buf[4 * i + 2] = SCALE_5_8(b); + rgba32_buf[4 * i + 3] = a ? 255 : 0; + } + } + + uint32_t width = rdp.texture_tile[tile].line_size_bytes; + uint32_t height = size_bytes / rdp.texture_tile[tile].line_size_bytes; + + if (size_bytes > 15000) { + int bp = 0; + } + + gfx_rapi->upload_texture(rgba32_buf, width, height); + // DumpTexture(rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path, rgba32_buf, width, height); +} + +static void import_texture(int i, int tile) { + uint8_t fmt = rdp.texture_tile[tile].fmt; + uint8_t siz = rdp.texture_tile[tile].siz; + uint32_t tmem_index = rdp.texture_tile[tile].tmem_index; + + if (gfx_texture_cache_lookup(i, tile)) { + return; + } + + int t0 = get_time(); + if (fmt == G_IM_FMT_RGBA) { + if (siz == G_IM_SIZ_16b) { + import_texture_rgba16(tile); + } else if (siz == G_IM_SIZ_32b) { + import_texture_rgba32(tile); + } else { + // abort(); // OTRTODO: Sometimes, seemingly randomly, we end up here. Could be a bad dlist, could be + // something F3D does not have supported. Further investigation is needed. + } + } else if (fmt == G_IM_FMT_IA) { + if (siz == G_IM_SIZ_4b) { + import_texture_ia4(tile); + } else if (siz == G_IM_SIZ_8b) { + import_texture_ia8(tile); + } else if (siz == G_IM_SIZ_16b) { + import_texture_ia16(tile); + } else { + abort(); + } + } else if (fmt == G_IM_FMT_CI) { + if (siz == G_IM_SIZ_4b) { + import_texture_ci4(tile); + } else if (siz == G_IM_SIZ_8b) { + import_texture_ci8(tile); + } else { + abort(); + } + } else if (fmt == G_IM_FMT_I) { + if (siz == G_IM_SIZ_4b) { + import_texture_i4(tile); + } else if (siz == G_IM_SIZ_8b) { + import_texture_i8(tile); + } else { + abort(); + } + } else { + abort(); + } + int t1 = get_time(); + // printf("Time diff: %d\n", t1 - t0); +} + +static void gfx_normalize_vector(float v[3]) { + float s = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + v[0] /= s; + v[1] /= s; + v[2] /= s; +} + +static void gfx_transposed_matrix_mul(float res[3], const float a[3], const float b[4][4]) { + res[0] = a[0] * b[0][0] + a[1] * b[0][1] + a[2] * b[0][2]; + res[1] = a[0] * b[1][0] + a[1] * b[1][1] + a[2] * b[1][2]; + res[2] = a[0] * b[2][0] + a[1] * b[2][1] + a[2] * b[2][2]; +} + +static void calculate_normal_dir(const Light_t* light, float coeffs[3]) { + float light_dir[3] = { light->dir[0] / 127.0f, light->dir[1] / 127.0f, light->dir[2] / 127.0f }; + + gfx_transposed_matrix_mul(coeffs, light_dir, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1]); + gfx_normalize_vector(coeffs); +} + +static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4][4]) { + float tmp[4][4]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + tmp[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j] + a[i][3] * b[3][j]; + } + } + memcpy(res, tmp, sizeof(tmp)); +} + +static void gfx_sp_matrix(uint8_t parameters, const int32_t* addr) { + float matrix[4][4]; + +#if 0 + if (auto it = current_mtx_replacements->find((Mtx*)addr); it != current_mtx_replacements->end()) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float v = it->second.mf[i][j]; + int as_int = (int)(v * 65536.0f); + matrix[i][j] = as_int * (1.0f / 65536.0f); + } + } + } else { +#endif +#ifndef GBI_FLOATS + // Original GBI where fixed point matrices are used + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j += 2) { + int32_t int_part = addr[i * 2 + j / 2]; + uint32_t frac_part = addr[8 + i * 2 + j / 2]; + matrix[i][j] = (int32_t)((int_part & 0xffff0000) | (frac_part >> 16)) / 65536.0f; + matrix[i][j + 1] = (int32_t)((int_part << 16) | (frac_part & 0xffff)) / 65536.0f; + } + } +#else + // For a modified GBI where fixed point values are replaced with floats + memcpy(matrix, addr, sizeof(matrix)); +#endif + //} + + if (parameters & G_MTX_PROJECTION) { + if (parameters & G_MTX_LOAD) { + memcpy(rsp.P_matrix, matrix, sizeof(matrix)); + } else { + gfx_matrix_mul(rsp.P_matrix, matrix, rsp.P_matrix); + } + } else { // G_MTX_MODELVIEW + if ((parameters & G_MTX_PUSH) && rsp.modelview_matrix_stack_size < 11) { + ++rsp.modelview_matrix_stack_size; + memcpy(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], + rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 2], sizeof(matrix)); + } + if (parameters & G_MTX_LOAD) { + memcpy(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], matrix, sizeof(matrix)); + } else { + gfx_matrix_mul(rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], matrix, + rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1]); + } + rsp.lights_changed = 1; + } + gfx_matrix_mul(rsp.MP_matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], rsp.P_matrix); +} + +static void gfx_sp_pop_matrix(uint32_t count) { + while (count--) { + if (rsp.modelview_matrix_stack_size > 0) { + --rsp.modelview_matrix_stack_size; + if (rsp.modelview_matrix_stack_size > 0) { + gfx_matrix_mul(rsp.MP_matrix, rsp.modelview_matrix_stack[rsp.modelview_matrix_stack_size - 1], + rsp.P_matrix); + } + } + } +} + +static float gfx_adjust_x_for_aspect_ratio(float x) { + if (fbActive) { + return x; + } else { + return x * (4.0f / 3.0f) / ((float)gfx_current_dimensions.width / (float)gfx_current_dimensions.height); + } +} + +static void gfx_adjust_width_height_for_scale(uint32_t& width, uint32_t& height) { + width = round(width * RATIO_X); + height = round(height * RATIO_Y); + if (width == 0) { + width = 1; + } + if (height == 0) { + height = 1; + } +} + +static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* vertices) { + for (size_t i = 0; i < n_vertices; i++, dest_index++) { + const Vtx_t* v = &vertices[i].v; + const Vtx_tn* vn = &vertices[i].n; + struct LoadedVertex* d = &rsp.loaded_vertices[dest_index]; + + if (v == NULL) { + return; + } + + float x = v->ob[0] * rsp.MP_matrix[0][0] + v->ob[1] * rsp.MP_matrix[1][0] + v->ob[2] * rsp.MP_matrix[2][0] + + rsp.MP_matrix[3][0]; + float y = v->ob[0] * rsp.MP_matrix[0][1] + v->ob[1] * rsp.MP_matrix[1][1] + v->ob[2] * rsp.MP_matrix[2][1] + + rsp.MP_matrix[3][1]; + float z = v->ob[0] * rsp.MP_matrix[0][2] + v->ob[1] * rsp.MP_matrix[1][2] + v->ob[2] * rsp.MP_matrix[2][2] + + rsp.MP_matrix[3][2]; + float w = v->ob[0] * rsp.MP_matrix[0][3] + v->ob[1] * rsp.MP_matrix[1][3] + v->ob[2] * rsp.MP_matrix[2][3] + + rsp.MP_matrix[3][3]; + + x = gfx_adjust_x_for_aspect_ratio(x); + + short U = v->tc[0] * rsp.texture_scaling_factor.s >> 16; + short V = v->tc[1] * rsp.texture_scaling_factor.t >> 16; + + if (rsp.geometry_mode & G_LIGHTING) { + if (rsp.lights_changed) { + for (int i = 0; i < rsp.current_num_lights - 1; i++) { + calculate_normal_dir(&rsp.current_lights[i], rsp.current_lights_coeffs[i]); + } + /*static const Light_t lookat_x = {{0, 0, 0}, 0, {0, 0, 0}, 0, {127, 0, 0}, 0}; + static const Light_t lookat_y = {{0, 0, 0}, 0, {0, 0, 0}, 0, {0, 127, 0}, 0};*/ + calculate_normal_dir(&rsp.lookat[0], rsp.current_lookat_coeffs[0]); + calculate_normal_dir(&rsp.lookat[1], rsp.current_lookat_coeffs[1]); + rsp.lights_changed = false; + } + + int r = rsp.current_lights[rsp.current_num_lights - 1].col[0]; + int g = rsp.current_lights[rsp.current_num_lights - 1].col[1]; + int b = rsp.current_lights[rsp.current_num_lights - 1].col[2]; + + for (int i = 0; i < rsp.current_num_lights - 1; i++) { + float intensity = 0; + intensity += vn->n[0] * rsp.current_lights_coeffs[i][0]; + intensity += vn->n[1] * rsp.current_lights_coeffs[i][1]; + intensity += vn->n[2] * rsp.current_lights_coeffs[i][2]; + intensity /= 127.0f; + if (intensity > 0.0f) { + r += intensity * rsp.current_lights[i].col[0]; + g += intensity * rsp.current_lights[i].col[1]; + b += intensity * rsp.current_lights[i].col[2]; + } + } + + d->color.r = r > 255 ? 255 : r; + d->color.g = g > 255 ? 255 : g; + d->color.b = b > 255 ? 255 : b; + + if (rsp.geometry_mode & G_TEXTURE_GEN) { + float dotx = 0, doty = 0; + dotx += vn->n[0] * rsp.current_lookat_coeffs[0][0]; + dotx += vn->n[1] * rsp.current_lookat_coeffs[0][1]; + dotx += vn->n[2] * rsp.current_lookat_coeffs[0][2]; + doty += vn->n[0] * rsp.current_lookat_coeffs[1][0]; + doty += vn->n[1] * rsp.current_lookat_coeffs[1][1]; + doty += vn->n[2] * rsp.current_lookat_coeffs[1][2]; + + dotx /= 127.0f; + doty /= 127.0f; + + dotx = CLAMP(dotx, -1.0f, 1.0f); + doty = CLAMP(doty, -1.0f, 1.0f); + + if (rsp.geometry_mode & G_TEXTURE_GEN_LINEAR) { + // Not sure exactly what formula we should use to get accurate values + /*dotx = (2.906921f * dotx * dotx + 1.36114f) * dotx; + doty = (2.906921f * doty * doty + 1.36114f) * doty; + dotx = (dotx + 1.0f) / 4.0f; + doty = (doty + 1.0f) / 4.0f;*/ + dotx = acosf(-dotx) /* M_PI */ / 4.0f; + doty = acosf(-doty) /* M_PI */ / 4.0f; + } else { + dotx = (dotx + 1.0f) / 4.0f; + doty = (doty + 1.0f) / 4.0f; + } + + U = (int32_t)(dotx * rsp.texture_scaling_factor.s); + V = (int32_t)(doty * rsp.texture_scaling_factor.t); + } + } else { + d->color.r = v->cn[0]; + d->color.g = v->cn[1]; + d->color.b = v->cn[2]; + } + + d->u = U; + d->v = V; + + // trivial clip rejection + d->clip_rej = 0; + if (x < -w) { + d->clip_rej |= 1; // CLIP_LEFT + } + if (x > w) { + d->clip_rej |= 2; // CLIP_RIGHT + } + if (y < -w) { + d->clip_rej |= 4; // CLIP_BOTTOM + } + if (y > w) { + d->clip_rej |= 8; // CLIP_TOP + } + // if (z < -w) d->clip_rej |= 16; // CLIP_NEAR + if (z > w) { + d->clip_rej |= 32; // CLIP_FAR + } + + d->x = x; + d->y = y; + d->z = z; + d->w = w; + + if (rsp.geometry_mode & G_FOG) { + if (fabsf(w) < 0.001f) { + // To avoid division by zero + w = 0.001f; + } + + float winv = 1.0f / w; + if (winv < 0.0f) { + winv = std::numeric_limits::max(); + } + + float fog_z = z * winv * rsp.fog_mul + rsp.fog_offset; + fog_z = CLAMP(fog_z, 0.0f, 255.0f); + d->color.a = fog_z; // Use alpha variable to store fog factor + } else { + d->color.a = v->cn[3]; + } + } +} + +static void gfx_sp_modify_vertex(uint16_t vtx_idx, uint8_t where, uint32_t val) { + SUPPORT_CHECK(where == G_MWO_POINT_ST); + + int16_t s = (int16_t)(val >> 16); + int16_t t = (int16_t)val; + + struct LoadedVertex* v = &rsp.loaded_vertices[vtx_idx]; + v->u = s; + v->v = t; +} + +static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bool is_rect) { + struct LoadedVertex* v1 = &rsp.loaded_vertices[vtx1_idx]; + struct LoadedVertex* v2 = &rsp.loaded_vertices[vtx2_idx]; + struct LoadedVertex* v3 = &rsp.loaded_vertices[vtx3_idx]; + struct LoadedVertex* v_arr[3] = { v1, v2, v3 }; + + // if (rand()%2) return; + + if (v1->clip_rej & v2->clip_rej & v3->clip_rej) { + // The whole triangle lies outside the visible area + return; + } + + if ((rsp.geometry_mode & G_CULL_BOTH) != 0) { + float dx1 = v1->x / (v1->w) - v2->x / (v2->w); + float dy1 = v1->y / (v1->w) - v2->y / (v2->w); + float dx2 = v3->x / (v3->w) - v2->x / (v2->w); + float dy2 = v3->y / (v3->w) - v2->y / (v2->w); + float cross = dx1 * dy2 - dy1 * dx2; + + if ((v1->w < 0) ^ (v2->w < 0) ^ (v3->w < 0)) { + // If one vertex lies behind the eye, negating cross will give the correct result. + // If all vertices lie behind the eye, the triangle will be rejected anyway. + cross = -cross; + } + + switch (rsp.geometry_mode & G_CULL_BOTH) { + case G_CULL_FRONT: + if (cross <= 0) { + return; + } + break; + case G_CULL_BACK: + if (cross >= 0) { + return; + } + break; + case G_CULL_BOTH: + // Why is this even an option? + return; + } + } + + bool depth_test = (rsp.geometry_mode & G_ZBUFFER) == G_ZBUFFER; + bool depth_mask = (rdp.other_mode_l & Z_UPD) == Z_UPD; + uint8_t depth_test_and_mask = (depth_test ? 1 : 0) | (depth_mask ? 2 : 0); + if (depth_test_and_mask != rendering_state.depth_test_and_mask) { + gfx_flush(); + gfx_rapi->set_depth_test_and_mask(depth_test, depth_mask); + rendering_state.depth_test_and_mask = depth_test_and_mask; + } + + bool zmode_decal = (rdp.other_mode_l & ZMODE_DEC) == ZMODE_DEC; + if (zmode_decal != rendering_state.decal_mode) { + gfx_flush(); + gfx_rapi->set_zmode_decal(zmode_decal); + rendering_state.decal_mode = zmode_decal; + } + + if (rdp.viewport_or_scissor_changed) { + if (memcmp(&rdp.viewport, &rendering_state.viewport, sizeof(rdp.viewport)) != 0) { + gfx_flush(); + gfx_rapi->set_viewport(rdp.viewport.x, rdp.viewport.y, rdp.viewport.width, rdp.viewport.height); + rendering_state.viewport = rdp.viewport; + } + if (memcmp(&rdp.scissor, &rendering_state.scissor, sizeof(rdp.scissor)) != 0) { + gfx_flush(); + gfx_rapi->set_scissor(rdp.scissor.x, rdp.scissor.y, rdp.scissor.width, rdp.scissor.height); + rendering_state.scissor = rdp.scissor; + } + rdp.viewport_or_scissor_changed = false; + } + + uint64_t cc_id = rdp.combine_mode; + bool use_fog = (rdp.other_mode_l >> 30) == G_BL_CLR_FOG; + bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; + bool use_noise = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_DITHER; + bool use_2cyc = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE; + // TODO Alo: This use_alpha check was taken from aglab2 fork, works in SM64, needs testing in OOT + bool use_alpha = use_2cyc ? ((rdp.other_mode_l & (G_BL_A_MEM << 16)) == 0) : ((rdp.other_mode_l & (G_BL_A_MEM << 18)) == 0); + bool alpha_threshold = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_THRESHOLD; + bool invisible = + (rdp.other_mode_l & (3 << 24)) == (G_BL_0 << 24) && (rdp.other_mode_l & (3 << 20)) == (G_BL_CLR_MEM << 20); + bool use_grayscale = rdp.grayscale; + + if (texture_edge) { + use_alpha = true; + } + + if (use_alpha) { + cc_id |= (uint64_t)SHADER_OPT_ALPHA << CC_SHADER_OPT_POS; + } + if (use_fog) { + cc_id |= (uint64_t)SHADER_OPT_FOG << CC_SHADER_OPT_POS; + } + if (texture_edge) { + cc_id |= (uint64_t)SHADER_OPT_TEXTURE_EDGE << CC_SHADER_OPT_POS; + } + if (use_noise) { + cc_id |= (uint64_t)SHADER_OPT_NOISE << CC_SHADER_OPT_POS; + } + if (use_2cyc) { + cc_id |= (uint64_t)SHADER_OPT_2CYC << CC_SHADER_OPT_POS; + } + if (alpha_threshold) { + cc_id |= (uint64_t)SHADER_OPT_ALPHA_THRESHOLD << CC_SHADER_OPT_POS; + } + if (invisible) { + cc_id |= (uint64_t)SHADER_OPT_INVISIBLE << CC_SHADER_OPT_POS; + } + if (use_grayscale) { + cc_id |= (uint64_t)SHADER_OPT_GRAYSCALE << CC_SHADER_OPT_POS; + } + + if (!use_alpha) { + cc_id &= ~((0xfff << 16) | ((uint64_t)0xfff << 44)); + } + + if (rdp.override_color.a > 0) { + rdp.prim_color = rdp.override_color; + // rdp.fog_color = { 0 }; + // rdp.fill_color = { 0 }; + // rdp.env_color = { 0 }; + + uint64_t new_cc_id = (cc_id >> CC_SHADER_OPT_POS) << CC_SHADER_OPT_POS; + + new_cc_id &= ~((uint64_t)SHADER_OPT_FOG << CC_SHADER_OPT_POS); + use_fog = false; + + if (use_alpha) { + new_cc_id |= (uint64_t)SHADER_OPT_TEXTURE_EDGE << CC_SHADER_OPT_POS; + //new_cc_id &= ~((uint64_t)SHADER_OPT_NOISE << CC_SHADER_OPT_POS); //needed? + } + + //NOTE: temporary while experimenting with draw settings + for (int i = 0; i < 2; i++) { + uint32_t rgb_a = (cc_id >> (i * 28)) & 0xf; + uint32_t rgb_b = (cc_id >> (i * 28 + 4)) & 0xf; + uint32_t rgb_c = (cc_id >> (i * 28 + 8)) & 0x1f; + uint32_t rgb_d = (cc_id >> (i * 28 + 13)) & 7; + uint32_t alpha_a = (cc_id >> (i * 28 + 16)) & 7; + uint32_t alpha_b = (cc_id >> (i * 28 + 16 + 3)) & 7; + uint32_t alpha_c = (cc_id >> (i * 28 + 16 + 6)) & 7; + uint32_t alpha_d = (cc_id >> (i * 28 + 16 + 9)) & 7; + + //rgb_a = (rgb_a == G_CCMUX_TEXEL0 || rgb_a == G_CCMUX_TEXEL1 || + // rgb_a == G_CCMUX_TEXEL0_ALPHA || rgb_a == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_a; + //rgb_b = (rgb_b == G_CCMUX_TEXEL0 || rgb_b == G_CCMUX_TEXEL1 || + // rgb_b == G_CCMUX_TEXEL0_ALPHA || rgb_b == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_b; + //rgb_c = (rgb_c == G_CCMUX_TEXEL0 || rgb_c == G_CCMUX_TEXEL1 || + // rgb_c == G_CCMUX_TEXEL0_ALPHA || rgb_c == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_c; + //rgb_d = (rgb_d == G_CCMUX_TEXEL0 || rgb_d == G_CCMUX_TEXEL1 || + // rgb_d == G_CCMUX_TEXEL0_ALPHA || rgb_d == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_d; + + rgb_a = G_CCMUX_PRIMITIVE; + rgb_b = G_CCMUX_PRIMITIVE; + rgb_c = G_CCMUX_PRIMITIVE; + rgb_d = G_CCMUX_PRIMITIVE; + + //alpha_a = (alpha_a == G_ACMUX_TEXEL0 || alpha_a == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_a; + //alpha_b = (alpha_b == G_ACMUX_TEXEL0 || alpha_b == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_b; + //alpha_c = (alpha_c == G_ACMUX_TEXEL0 || alpha_c == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_c; + //alpha_d = (alpha_d == G_ACMUX_TEXEL0 || alpha_d == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_d; + + //alpha_a = G_ACMUX_PRIMITIVE; + //alpha_b = G_ACMUX_PRIMITIVE; + //alpha_c = G_ACMUX_PRIMITIVE; + //alpha_d = G_ACMUX_PRIMITIVE; + + new_cc_id |= (uint64_t)(rgb_a & 0xf) << (i * 28); + new_cc_id |= (uint64_t)(rgb_b & 0xf) << (i * 28 + 4); + new_cc_id |= (uint64_t)(rgb_c & 0x1f) << (i * 28 + 8); + new_cc_id |= (uint64_t)(rgb_d & 7) << (i * 28 + 13); + new_cc_id |= (uint64_t)(alpha_a & 7) << (i * 28 + 16); + new_cc_id |= (uint64_t)(alpha_b & 7) << (i * 28 + 16 + 3); + new_cc_id |= (uint64_t)(alpha_c & 7) << (i * 28 + 16 + 6); + new_cc_id |= (uint64_t)(alpha_d & 7) << (i * 28 + 16 + 9); + } + + + cc_id = new_cc_id; + } + + ColorCombiner* comb = gfx_lookup_or_create_color_combiner(cc_id); + + uint32_t tm = 0; + uint32_t tex_width[2], tex_height[2], tex_width2[2], tex_height2[2]; + + for (int i = 0; i < 2; i++) { + uint32_t tile = rdp.first_tile_index + i; + if (comb->used_textures[i]) { + if (rdp.textures_changed[i]) { + gfx_flush(); + import_texture(i, tile); + rdp.textures_changed[i] = false; + } + + uint8_t cms = rdp.texture_tile[tile].cms; + uint8_t cmt = rdp.texture_tile[tile].cmt; + + uint32_t tex_size_bytes = rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes; + uint32_t line_size = rdp.texture_tile[tile].line_size_bytes; + + if (line_size == 0) { + line_size = 1; + } + + tex_height[i] = tex_size_bytes / line_size; + switch (rdp.texture_tile[tile].siz) { + case G_IM_SIZ_4b: + line_size <<= 1; + break; + case G_IM_SIZ_8b: + break; + case G_IM_SIZ_16b: + line_size /= G_IM_SIZ_16b_LINE_BYTES; + break; + case G_IM_SIZ_32b: + line_size /= G_IM_SIZ_32b_LINE_BYTES; // this is 2! + tex_height[i] /= 2; + break; + } + tex_width[i] = line_size; + + tex_width2[i] = (rdp.texture_tile[tile].lrs - rdp.texture_tile[tile].uls + 4) / 4; + tex_height2[i] = (rdp.texture_tile[tile].lrt - rdp.texture_tile[tile].ult + 4) / 4; + + uint32_t tex_width1 = tex_width[i] << (cms & G_TX_MIRROR); + uint32_t tex_height1 = tex_height[i] << (cmt & G_TX_MIRROR); + + if ((cms & G_TX_CLAMP) && ((cms & G_TX_MIRROR) || tex_width1 != tex_width2[i])) { + tm |= 1 << 2 * i; + cms &= ~G_TX_CLAMP; + } + if ((cmt & G_TX_CLAMP) && ((cmt & G_TX_MIRROR) || tex_height1 != tex_height2[i])) { + tm |= 1 << 2 * i + 1; + cmt &= ~G_TX_CLAMP; + } + + bool linear_filter = (rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT; + if (linear_filter != rendering_state.textures[i]->second.linear_filter || + cms != rendering_state.textures[i]->second.cms || cmt != rendering_state.textures[i]->second.cmt) { + gfx_flush(); + gfx_rapi->set_sampler_parameters(i, linear_filter, cms, cmt); + rendering_state.textures[i]->second.linear_filter = linear_filter; + rendering_state.textures[i]->second.cms = cms; + rendering_state.textures[i]->second.cmt = cmt; + } + } + } + + struct ShaderProgram* prg = comb->prg[tm]; + if (prg == NULL) { + comb->prg[tm] = prg = + gfx_lookup_or_create_shader_program(comb->shader_id0, comb->shader_id1 | (tm * SHADER_OPT_TEXEL0_CLAMP_S)); + } + if (prg != rendering_state.shader_program) { + gfx_flush(); + gfx_rapi->unload_shader(rendering_state.shader_program); + gfx_rapi->load_shader(prg); + rendering_state.shader_program = prg; + } + if (use_alpha != rendering_state.alpha_blend) { + gfx_flush(); + gfx_rapi->set_use_alpha(use_alpha); + rendering_state.alpha_blend = use_alpha; + } + uint8_t num_inputs; + bool used_textures[2]; + + if (markerOn) { + int bp = 0; + } + + gfx_rapi->shader_get_info(prg, &num_inputs, used_textures); + + struct GfxClipParameters clip_parameters = gfx_rapi->get_clip_parameters(); + + for (int i = 0; i < 3; i++) { + float z = v_arr[i]->z, w = v_arr[i]->w; + if (clip_parameters.z_is_from_0_to_1) { + z = (z + w) / 2.0f; + } + + if (markerOn) { + // z = 10; + } + + buf_vbo[buf_vbo_len++] = v_arr[i]->x; + buf_vbo[buf_vbo_len++] = clip_parameters.invert_y ? -v_arr[i]->y : v_arr[i]->y; + buf_vbo[buf_vbo_len++] = z; + buf_vbo[buf_vbo_len++] = w; + + for (int t = 0; t < 2; t++) { + if (!used_textures[t]) { + continue; + } + float u = v_arr[i]->u / 32.0f; + float v = v_arr[i]->v / 32.0f; + int shifts = rdp.texture_tile[rdp.first_tile_index + t].shifts; + int shiftt = rdp.texture_tile[rdp.first_tile_index + t].shiftt; + if (shifts != 0) { + if (shifts <= 10) { + u /= 1 << shifts; + } else { + u *= 1 << (16 - shifts); + } + } + if (shiftt != 0) { + if (shiftt <= 10) { + v /= 1 << shiftt; + } else { + v *= 1 << (16 - shiftt); + } + } + + u -= rdp.texture_tile[rdp.first_tile_index + t].uls / 4.0f; + v -= rdp.texture_tile[rdp.first_tile_index + t].ult / 4.0f; + + if ((rdp.other_mode_h & (3U << G_MDSFT_TEXTFILT)) != G_TF_POINT) { + // Linear filter adds 0.5f to the coordinates + if (!is_rect) { + u += 0.5f; + v += 0.5f; + } + } + + buf_vbo[buf_vbo_len++] = u / tex_width[t]; + buf_vbo[buf_vbo_len++] = v / tex_height[t]; + + bool clampS = tm & (1 << 2 * t); + bool clampT = tm & (1 << 2 * t + 1); + + if (clampS) { + buf_vbo[buf_vbo_len++] = (tex_width2[t] - 0.5f) / tex_width[t]; + } +#ifdef __WIIU__ + else { + buf_vbo[buf_vbo_len++] = 0.0f; + } +#endif + if (clampT) { + buf_vbo[buf_vbo_len++] = (tex_height2[t] - 0.5f) / tex_height[t]; + } +#ifdef __WIIU__ + else { + buf_vbo[buf_vbo_len++] = 0.0f; + } +#endif + } + + if (use_fog) { + buf_vbo[buf_vbo_len++] = rdp.fog_color.r / 255.0f; + buf_vbo[buf_vbo_len++] = rdp.fog_color.g / 255.0f; + buf_vbo[buf_vbo_len++] = rdp.fog_color.b / 255.0f; + buf_vbo[buf_vbo_len++] = v_arr[i]->color.a / 255.0f; // fog factor (not alpha) + } + + if (use_grayscale) { + buf_vbo[buf_vbo_len++] = rdp.grayscale_color.r / 255.0f; + buf_vbo[buf_vbo_len++] = rdp.grayscale_color.g / 255.0f; + buf_vbo[buf_vbo_len++] = rdp.grayscale_color.b / 255.0f; + buf_vbo[buf_vbo_len++] = rdp.grayscale_color.a / 255.0f; // lerp interpolation factor (not alpha) + } + + for (int j = 0; j < num_inputs; j++) { + struct RGBA* color = 0; + struct RGBA tmp; + for (int k = 0; k < 1 + (use_alpha ? 1 : 0); k++) { + switch (comb->shader_input_mapping[k][j]) { + // Note: CCMUX constants and ACMUX constants used here have same value, which is why this works + // (except LOD fraction). + case G_CCMUX_PRIMITIVE: + color = &rdp.prim_color; + break; + case G_CCMUX_SHADE: + color = &v_arr[i]->color; + break; + case G_CCMUX_ENVIRONMENT: + color = &rdp.env_color; + break; + case G_CCMUX_PRIMITIVE_ALPHA: { + tmp.r = tmp.g = tmp.b = rdp.prim_color.a; + color = &tmp; + break; + } + case G_CCMUX_ENV_ALPHA: { + tmp.r = tmp.g = tmp.b = rdp.env_color.a; + color = &tmp; + break; + } + case G_CCMUX_PRIM_LOD_FRAC: { + tmp.r = tmp.g = tmp.b = rdp.prim_lod_fraction; + color = &tmp; + break; + } + case G_CCMUX_LOD_FRACTION: { + // Todo Alo: Doesn't work, stays at Bowser + if (rdp.other_mode_h & G_TL_LOD) { + // "Hack" that works for Bowser - Peach painting + float distance_frac = (v1->w - 3000.0f) / 3000.0f; + if (distance_frac < 0.0f) { + distance_frac = 0.0f; + } + if (distance_frac > 1.0f) { + distance_frac = 1.0f; + } + tmp.r = tmp.g = tmp.b = tmp.a = distance_frac * 255.0f; + } else { + tmp.r = tmp.g = tmp.b = tmp.a = 255.0f; + } + color = &tmp; + break; + } + case G_ACMUX_PRIM_LOD_FRAC: + tmp.a = rdp.prim_lod_fraction; + color = &tmp; + break; + default: + memset(&tmp, 0, sizeof(tmp)); + color = &tmp; + break; + } + if (k == 0) { + buf_vbo[buf_vbo_len++] = color->r / 255.0f; + buf_vbo[buf_vbo_len++] = color->g / 255.0f; + buf_vbo[buf_vbo_len++] = color->b / 255.0f; +#ifdef __WIIU__ + // padding + if (!use_alpha) { + buf_vbo[buf_vbo_len++] = 1.0f; + } +#endif + } else { + if (use_fog && color == &v_arr[i]->color) { + // Shade alpha is 100% for fog + buf_vbo[buf_vbo_len++] = 1.0f; + } else { + buf_vbo[buf_vbo_len++] = color->a / 255.0f; + } + } + } + } + // struct RGBA *color = &v_arr[i]->color; + // buf_vbo[buf_vbo_len++] = color->r / 255.0f; + // buf_vbo[buf_vbo_len++] = color->g / 255.0f; + // buf_vbo[buf_vbo_len++] = color->b / 255.0f; + // buf_vbo[buf_vbo_len++] = color->a / 255.0f; + } + + if (++buf_vbo_num_tris == MAX_BUFFERED) { + // if (++buf_vbo_num_tris == 1) { + if (markerOn) { + int bp = 0; + } + gfx_flush(); + } +} + +static void gfx_sp_geometry_mode(uint32_t clear, uint32_t set) { + rsp.geometry_mode &= ~clear; + rsp.geometry_mode |= set; +} + +static void gfx_adjust_viewport_or_scissor(XYWidthHeight* area) { + if (!fbActive) { + area->width *= RATIO_X; + area->height *= RATIO_Y; + area->x *= RATIO_X; + area->y = SCREEN_HEIGHT - area->y; + area->y *= RATIO_Y; + + if (!game_renders_to_framebuffer || + (gfx_msaa_level > 1 && gfx_current_dimensions.width == gfx_current_game_window_viewport.width && + gfx_current_dimensions.height == gfx_current_game_window_viewport.height)) { + area->x += gfx_current_game_window_viewport.x; + area->y += gfx_current_window_dimensions.height - + (gfx_current_game_window_viewport.y + gfx_current_game_window_viewport.height); + } + } else { + area->width *= RATIO_Y; + area->height *= RATIO_Y; + area->x *= RATIO_Y; + area->y = active_fb->second.orig_height - area->y; + area->y *= RATIO_Y; + } +} + +static void gfx_calc_and_set_viewport(const Vp_t* viewport) { + // 2 bits fraction + float width = 2.0f * viewport->vscale[0] / 4.0f; + float height = 2.0f * viewport->vscale[1] / 4.0f; + float x = (viewport->vtrans[0] / 4.0f) - width / 2.0f; + float y = ((viewport->vtrans[1] / 4.0f) + height / 2.0f); + + rdp.viewport.x = x; + rdp.viewport.y = y; + rdp.viewport.width = width; + rdp.viewport.height = height; + + gfx_adjust_viewport_or_scissor(&rdp.viewport); + + rdp.viewport_or_scissor_changed = true; +} + +static void gfx_sp_movemem(uint8_t index, uint8_t offset, const void* data) { + switch (index) { + case G_MV_VIEWPORT: + gfx_calc_and_set_viewport((const Vp_t*)data); + break; +#if 0 + case G_MV_LOOKATY: + case G_MV_LOOKATX: + memcpy(rsp.current_lookat + (index - G_MV_LOOKATY) / 2, data, sizeof(Light_t)); + //rsp.lights_changed = 1; + break; +#endif +#ifdef F3DEX_GBI_2 + case G_MV_LIGHT: { + int lightidx = offset / 24 - 2; + if (lightidx >= 0 && lightidx <= MAX_LIGHTS) { // skip lookat + // NOTE: reads out of bounds if it is an ambient light + memcpy(rsp.current_lights + lightidx, data, sizeof(Light_t)); + } else if (lightidx < 0) { + memcpy(rsp.lookat + offset / 24, data, sizeof(Light_t)); + } + break; + } +#else + case G_MV_L0: + case G_MV_L1: + case G_MV_L2: + // NOTE: reads out of bounds if it is an ambient light + memcpy(rsp.current_lights + (index - G_MV_L0) / 2, data, sizeof(Light_t)); + break; +#endif + } +} + +static void gfx_sp_moveword(uint8_t index, uint16_t offset, uintptr_t data) { + switch (index) { + case G_MW_NUMLIGHT: +#ifdef F3DEX_GBI_2 + rsp.current_num_lights = data / 24 + 1; // add ambient light +#else + // Ambient light is included + // The 31th bit is a flag that lights should be recalculated + rsp.current_num_lights = (data - 0x80000000U) / 32; +#endif + rsp.lights_changed = 1; + break; + case G_MW_FOG: + rsp.fog_mul = (int16_t)(data >> 16); + rsp.fog_offset = (int16_t)data; + break; + case G_MW_SEGMENT: + int segNumber = offset / 4; + segmentPointers[segNumber] = data; + break; + } +} + +static void gfx_sp_texture(uint16_t sc, uint16_t tc, uint8_t level, uint8_t tile, uint8_t on) { + rsp.texture_scaling_factor.s = sc; + rsp.texture_scaling_factor.t = tc; + if (rdp.first_tile_index != tile) { + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } + + rdp.first_tile_index = tile; +} + +static void gfx_dp_set_scissor(uint32_t mode, uint32_t ulx, uint32_t uly, uint32_t lrx, uint32_t lry) { + float x = ulx / 4.0f; + float y = lry / 4.0f; + float width = (lrx - ulx) / 4.0f; + float height = (lry - uly) / 4.0f; + + rdp.scissor.x = x; + rdp.scissor.y = y; + rdp.scissor.width = width; + rdp.scissor.height = height; + + gfx_adjust_viewport_or_scissor(&rdp.scissor); + + rdp.viewport_or_scissor_changed = true; +} + +static void gfx_dp_set_texture_image(uint32_t format, uint32_t size, uint32_t width, const void* addr) { + rdp.texture_to_load.addr = (const uint8_t*)addr; + rdp.texture_to_load.siz = size; + rdp.texture_to_load.width = width; + //if (otr_path != nullptr && !strncmp(otr_path, "__OTR__", 7)) { + // otr_path = otr_path + 7; + //} + //rdp.texture_to_load.otr_path = otr_path; +} + +static void gfx_dp_set_tile(uint8_t fmt, uint32_t siz, uint32_t line, uint32_t tmem, uint8_t tile, uint32_t palette, + uint32_t cmt, uint32_t maskt, uint32_t shiftt, uint32_t cms, uint32_t masks, + uint32_t shifts) { + // OTRTODO: + // SUPPORT_CHECK(tmem == 0 || tmem == 256); + + if (cms == G_TX_WRAP && masks == G_TX_NOMASK) { + cms = G_TX_CLAMP; + } + if (cmt == G_TX_WRAP && maskt == G_TX_NOMASK) { + cmt = G_TX_CLAMP; + } + + rdp.texture_tile[tile].palette = palette; // palette should set upper 4 bits of color index in 4b mode + rdp.texture_tile[tile].fmt = fmt; + rdp.texture_tile[tile].siz = siz; + rdp.texture_tile[tile].cms = cms; + rdp.texture_tile[tile].cmt = cmt; + rdp.texture_tile[tile].shifts = shifts; + rdp.texture_tile[tile].shiftt = shiftt; + rdp.texture_tile[tile].line_size_bytes = line * 8; + + if (rdp.texture_tile[tile].line_size_bytes > 15000) { + int bp = 0; + } + + rdp.texture_tile[tile].tmem = tmem; + // rdp.texture_tile[tile].tmem_index = tmem / 256; // tmem is the 64-bit word offset, so 256 words means 2 kB + rdp.texture_tile[tile].tmem_index = + tmem != 0; // assume one texture is loaded at address 0 and another texture at any other address + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; +} + +static void gfx_dp_set_tile_size(uint8_t tile, uint16_t uls, uint16_t ult, uint16_t lrs, uint16_t lrt) { + rdp.texture_tile[tile].uls = uls; + rdp.texture_tile[tile].ult = ult; + rdp.texture_tile[tile].lrs = lrs; + rdp.texture_tile[tile].lrt = lrt; + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; +} + +static void gfx_dp_load_tlut(uint8_t tile, uint32_t high_index) { + SUPPORT_CHECK(tile == G_TX_LOADTILE); + SUPPORT_CHECK(rdp.texture_to_load.siz == G_IM_SIZ_16b); + + SUPPORT_CHECK((rdp.texture_tile[tile].tmem == 256 && (high_index <= 127 || high_index == 255)) || + (rdp.texture_tile[tile].tmem == 384 && high_index == 127)); + + if (rdp.texture_tile[tile].tmem == 256) { + rdp.palettes[0] = rdp.texture_to_load.addr; + if (high_index == 255) { + rdp.palettes[1] = rdp.texture_to_load.addr + 2 * 128; + } + } else { + rdp.palettes[1] = rdp.texture_to_load.addr; + } +} + +static void gfx_dp_load_block(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t dxt) { + if (markerOn) { + int bp = 0; + } + +// SUPPORT_CHECK(tile == G_TX_LOADTILE); + SUPPORT_CHECK(uls == 0); + SUPPORT_CHECK(ult == 0); + + // The lrs field rather seems to be number of pixels to load + uint32_t word_size_shift = 0; + switch (rdp.texture_to_load.siz) { + case G_IM_SIZ_4b: + word_size_shift = 0; // Or -1? It's unused in SM64 anyway. + break; + case G_IM_SIZ_8b: + word_size_shift = 0; + break; + case G_IM_SIZ_16b: + word_size_shift = 1; + break; + case G_IM_SIZ_32b: + word_size_shift = 2; + break; + } + uint32_t size_bytes = (lrs + 1) << word_size_shift; + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes = size_bytes; + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes = size_bytes; + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes = size_bytes; + // assert(size_bytes <= 4096 && "bug: too big texture"); + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr = rdp.texture_to_load.addr; + //rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path = rdp.texture_to_load.otr_path; + rdp.textures_changed[rdp.texture_tile[tile].tmem_index] = true; +} + +static void gfx_dp_load_tile(uint8_t tile, uint32_t uls, uint32_t ult, uint32_t lrs, uint32_t lrt) { + SUPPORT_CHECK(tile == G_TX_LOADTILE); + + uint32_t word_size_shift = 0; + switch (rdp.texture_to_load.siz) { + case G_IM_SIZ_4b: + word_size_shift = 0; + break; + case G_IM_SIZ_8b: + word_size_shift = 0; + break; + case G_IM_SIZ_16b: + word_size_shift = 1; + break; + case G_IM_SIZ_32b: + word_size_shift = 2; + break; + } + + uint32_t size_bytes = ((((lrs - uls) >> G_TEXTURE_IMAGE_FRAC) + 1) * (((lrt - ult) >> G_TEXTURE_IMAGE_FRAC) + 1)) + << word_size_shift; + uint32_t full_image_line_size_bytes = (rdp.texture_to_load.width + 1) << word_size_shift; + uint32_t line_size_bytes = (((lrs - uls) >> G_TEXTURE_IMAGE_FRAC) + 1) << word_size_shift; + uint32_t start_offset = + full_image_line_size_bytes * (ult >> G_TEXTURE_IMAGE_FRAC) + ((uls >> G_TEXTURE_IMAGE_FRAC) << word_size_shift); + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].size_bytes = size_bytes; + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].full_image_line_size_bytes = full_image_line_size_bytes; + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].line_size_bytes = line_size_bytes; + + assert(size_bytes <= 4096 && "bug: too big texture"); + rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].addr = rdp.texture_to_load.addr + start_offset; + //rdp.loaded_texture[rdp.texture_tile[tile].tmem_index].otr_path = rdp.texture_to_load.otr_path; + rdp.texture_tile[tile].uls = uls; + rdp.texture_tile[tile].ult = ult; + rdp.texture_tile[tile].lrs = lrs; + rdp.texture_tile[tile].lrt = lrt; + + rdp.textures_changed[rdp.texture_tile[tile].tmem_index] = true; +} + +/*static uint8_t color_comb_component(uint32_t v) { + switch (v) { + case G_CCMUX_TEXEL0: + return CC_TEXEL0; + case G_CCMUX_TEXEL1: + return CC_TEXEL1; + case G_CCMUX_PRIMITIVE: + return CC_PRIM; + case G_CCMUX_SHADE: + return CC_SHADE; + case G_CCMUX_ENVIRONMENT: + return CC_ENV; + case G_CCMUX_TEXEL0_ALPHA: + return CC_TEXEL0A; + case G_CCMUX_LOD_FRACTION: + return CC_LOD; + default: + return CC_0; + } +} + +static inline uint32_t color_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return color_comb_component(a) | + (color_comb_component(b) << 3) | + (color_comb_component(c) << 6) | + (color_comb_component(d) << 9); +} + +static void gfx_dp_set_combine_mode(uint32_t rgb, uint32_t alpha) { + rdp.combine_mode = rgb | (alpha << 12); +}*/ + +static void gfx_dp_set_combine_mode(uint32_t rgb, uint32_t alpha, uint32_t rgb_cyc2, uint32_t alpha_cyc2) { + rdp.combine_mode = rgb | (alpha << 16) | ((uint64_t)rgb_cyc2 << 28) | ((uint64_t)alpha_cyc2 << 44); +} + +static inline uint32_t color_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return (a & 0xf) | ((b & 0xf) << 4) | ((c & 0x1f) << 8) | ((d & 7) << 13); +} + +static inline uint32_t alpha_comb(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { + return (a & 7) | ((b & 7) << 3) | ((c & 7) << 6) | ((d & 7) << 9); +} + +static void gfx_dp_set_grayscale_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + rdp.grayscale_color.r = r; + rdp.grayscale_color.g = g; + rdp.grayscale_color.b = b; + rdp.grayscale_color.a = a; +} + +static void gfx_dp_set_env_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + rdp.env_color.r = r; + rdp.env_color.g = g; + rdp.env_color.b = b; + rdp.env_color.a = a; +} + +static void gfx_dp_set_prim_color(uint8_t m, uint8_t l, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + rdp.prim_lod_fraction = l; + rdp.prim_color.r = r; + rdp.prim_color.g = g; + rdp.prim_color.b = b; + rdp.prim_color.a = a; +} + +static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + rdp.fog_color.r = r; + rdp.fog_color.g = g; + rdp.fog_color.b = b; + rdp.fog_color.a = a; +} + +static void gfx_dp_set_override_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + rdp.override_color.r = r; + rdp.override_color.g = g; + rdp.override_color.b = b; + rdp.override_color.a = a; +} + +static void gfx_dp_set_fill_color(uint32_t packed_color) { + uint16_t col16 = (uint16_t)packed_color; + uint32_t r = col16 >> 11; + uint32_t g = (col16 >> 6) & 0x1f; + uint32_t b = (col16 >> 1) & 0x1f; + uint32_t a = col16 & 1; + rdp.fill_color.r = SCALE_5_8(r); + rdp.fill_color.g = SCALE_5_8(g); + rdp.fill_color.b = SCALE_5_8(b); + rdp.fill_color.a = a * 255; +} + +static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) { + uint32_t saved_other_mode_h = rdp.other_mode_h; + uint32_t cycle_type = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)); + + if (cycle_type == G_CYC_COPY) { + rdp.other_mode_h = (rdp.other_mode_h & ~(3U << G_MDSFT_TEXTFILT)) | G_TF_POINT; + } + + // U10.2 coordinates + float ulxf = ulx; + float ulyf = uly; + float lrxf = lrx; + float lryf = lry; + + ulxf = ulxf / (4.0f * HALF_SCREEN_WIDTH) - 1.0f; + ulyf = -(ulyf / (4.0f * HALF_SCREEN_HEIGHT)) + 1.0f; + lrxf = lrxf / (4.0f * HALF_SCREEN_WIDTH) - 1.0f; + lryf = -(lryf / (4.0f * HALF_SCREEN_HEIGHT)) + 1.0f; + + ulxf = gfx_adjust_x_for_aspect_ratio(ulxf); + lrxf = gfx_adjust_x_for_aspect_ratio(lrxf); + + struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0]; + struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1]; + struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2]; + struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3]; + + ul->x = ulxf; + ul->y = ulyf; + ul->z = -1.0f; + ul->w = 1.0f; + + ll->x = ulxf; + ll->y = lryf; + ll->z = -1.0f; + ll->w = 1.0f; + + lr->x = lrxf; + lr->y = lryf; + lr->z = -1.0f; + lr->w = 1.0f; + + ur->x = lrxf; + ur->y = ulyf; + ur->z = -1.0f; + ur->w = 1.0f; + + // The coordinates for texture rectangle shall bypass the viewport setting +#if ENABLE_FRAMEBUFFER + struct XYWidthHeight default_viewport = { 0, SCREEN_HEIGHT, SCREEN_WIDTH, SCREEN_HEIGHT }; +#else + struct XYWidthHeight default_viewport = { 0, 0, gfx_current_dimensions.width, gfx_current_dimensions.height }; +#endif + + struct XYWidthHeight viewport_saved = rdp.viewport; + uint32_t geometry_mode_saved = rsp.geometry_mode; + +#if ENABLE_FRAMEBUFFER + gfx_adjust_viewport_or_scissor(&default_viewport); +#endif + + rdp.viewport = default_viewport; + rdp.viewport_or_scissor_changed = true; + rsp.geometry_mode = 0; + + gfx_sp_tri1(MAX_VERTICES + 0, MAX_VERTICES + 1, MAX_VERTICES + 3, true); + gfx_sp_tri1(MAX_VERTICES + 1, MAX_VERTICES + 2, MAX_VERTICES + 3, true); + + rsp.geometry_mode = geometry_mode_saved; + rdp.viewport = viewport_saved; + rdp.viewport_or_scissor_changed = true; + + if (cycle_type == G_CYC_COPY) { + rdp.other_mode_h = saved_other_mode_h; + } +} + +static void gfx_dp_texture_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry, uint8_t tile, int16_t uls, + int16_t ult, int16_t dsdx, int16_t dtdy, bool flip) { + // printf("render %d at %d\n", tile, lrx); + uint64_t saved_combine_mode = rdp.combine_mode; + if ((rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_COPY) { + // Per RDP Command Summary Set Tile's shift s and this dsdx should be set to 4 texels + // Divide by 4 to get 1 instead + dsdx >>= 2; + + // Color combiner is turned off in copy mode + gfx_dp_set_combine_mode(color_comb(0, 0, 0, G_CCMUX_TEXEL0), alpha_comb(0, 0, 0, G_ACMUX_TEXEL0), 0, 0); + + // Per documentation one extra pixel is added in this modes to each edge + lrx += 1 << 2; + lry += 1 << 2; + } + + // uls and ult are S10.5 + // dsdx and dtdy are S5.10 + // lrx, lry, ulx, uly are U10.2 + // lrs, lrt are S10.5 + if (flip) { + dsdx = -dsdx; + dtdy = -dtdy; + } + int16_t width = !flip ? lrx - ulx : lry - uly; + int16_t height = !flip ? lry - uly : lrx - ulx; + float lrs = ((uls << 7) + dsdx * width) >> 7; + float lrt = ((ult << 7) + dtdy * height) >> 7; + + struct LoadedVertex* ul = &rsp.loaded_vertices[MAX_VERTICES + 0]; + struct LoadedVertex* ll = &rsp.loaded_vertices[MAX_VERTICES + 1]; + struct LoadedVertex* lr = &rsp.loaded_vertices[MAX_VERTICES + 2]; + struct LoadedVertex* ur = &rsp.loaded_vertices[MAX_VERTICES + 3]; + ul->u = uls; + ul->v = ult; + lr->u = lrs; + lr->v = lrt; + if (!flip) { + ll->u = uls; + ll->v = lrt; + ur->u = lrs; + ur->v = ult; + } else { + ll->u = lrs; + ll->v = ult; + ur->u = uls; + ur->v = lrt; + } + + uint8_t saved_tile = rdp.first_tile_index; + if (saved_tile != tile) { + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } + rdp.first_tile_index = tile; + + gfx_draw_rectangle(ulx, uly, lrx, lry); + if (saved_tile != tile) { + rdp.textures_changed[0] = true; + rdp.textures_changed[1] = true; + } + rdp.first_tile_index = saved_tile; + rdp.combine_mode = saved_combine_mode; +} + +static void gfx_dp_fill_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lry) { + if (rdp.color_image_address == rdp.z_buf_address) { + // Don't clear Z buffer here since we already did it with glClear + return; + } + uint32_t mode = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)); + + // OTRTODO: This is a bit of a hack for widescreen screen fades, but it'll work for now... + if (ulx == 0 && uly == 0 && lrx == (319 * 4) && lry == (239 * 4)) { + ulx = -1024; + uly = -1024; + lrx = 2048; + lry = 2048; + } + + if (mode == G_CYC_COPY || mode == G_CYC_FILL) { + // Per documentation one extra pixel is added in this modes to each edge + lrx += 1 << 2; + lry += 1 << 2; + } + + for (int i = MAX_VERTICES; i < MAX_VERTICES + 4; i++) { + struct LoadedVertex* v = &rsp.loaded_vertices[i]; + v->color = rdp.fill_color; + } + + uint64_t saved_combine_mode = rdp.combine_mode; + + if (mode == G_CYC_FILL) { + gfx_dp_set_combine_mode(color_comb(0, 0, 0, G_CCMUX_SHADE), alpha_comb(0, 0, 0, G_ACMUX_SHADE), 0, 0); + } + + gfx_draw_rectangle(ulx, uly, lrx, lry); + rdp.combine_mode = saved_combine_mode; +} + +static void gfx_dp_set_z_image(void* z_buf_address) { + rdp.z_buf_address = z_buf_address; +} + +static void gfx_dp_set_color_image(uint32_t format, uint32_t size, uint32_t width, void* address) { + rdp.color_image_address = address; +} + +static void gfx_sp_set_other_mode(uint32_t shift, uint32_t num_bits, uint64_t mode) { + uint64_t mask = (((uint64_t)1 << num_bits) - 1) << shift; + uint64_t om = rdp.other_mode_l | ((uint64_t)rdp.other_mode_h << 32); + om = (om & ~mask) | mode; + rdp.other_mode_l = (uint32_t)om; + rdp.other_mode_h = (uint32_t)(om >> 32); +} + +static void gfx_dp_set_other_mode(uint32_t h, uint32_t l) { + rdp.other_mode_h = h; + rdp.other_mode_l = l; +} + +static void gfx_s2dex_bg_copy(const uObjBg* bg) { + /* + bg->b.imageX = 0; + bg->b.imageW = width * 4; + bg->b.frameX = frameX * 4; + bg->b.imageY = 0; + bg->b.imageH = height * 4; + bg->b.frameY = frameY * 4; + bg->b.imagePtr = source; + bg->b.imageLoad = G_BGLT_LOADTILE; + bg->b.imageFmt = fmt; + bg->b.imageSiz = siz; + bg->b.imagePal = 0; + bg->b.imageFlip = 0; + */ + SUPPORT_CHECK(bg->b.imageSiz == G_IM_SIZ_16b); + gfx_dp_set_texture_image(G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, bg->b.imagePtr); + gfx_dp_set_tile(G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, 0, 0, 0, 0, 0, 0); + gfx_dp_load_block(G_TX_LOADTILE, 0, 0, (bg->b.imageW * bg->b.imageH >> 4) - 1, 0); + gfx_dp_set_tile(bg->b.imageFmt, G_IM_SIZ_16b, bg->b.imageW >> 4, 0, G_TX_RENDERTILE, bg->b.imagePal, 0, 0, 0, 0, 0, + 0); + gfx_dp_set_tile_size(G_TX_RENDERTILE, 0, 0, bg->b.imageW, bg->b.imageH); + gfx_dp_texture_rectangle(bg->b.frameX, bg->b.frameY, bg->b.frameX + bg->b.imageW - 4, + bg->b.frameY + bg->b.imageH - 4, G_TX_RENDERTILE, bg->b.imageX << 3, bg->b.imageY << 3, + 4 << 10, 1 << 10, false); +} + +static inline void* seg_addr(uintptr_t w1) { + // Segmented? + if (w1 & 1) { + uint32_t segNum = (w1 >> 24); + + uint32_t offset = w1 & 0x00FFFFFE; + // offset = 0; // Cursed Malon bug + + if (segmentPointers[segNum] != 0) { + return (void*)(segmentPointers[segNum] + offset); + } else { + return (void*)w1; + } + } else { + return (void*)w1; + } +} + +#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1)) +#define C1(pos, width) ((cmd->words.w1 >> (pos)) & ((1U << width) - 1)) + +unsigned int dListBP; +int matrixBP; +uintptr_t clearMtx; + +static void gfx_run_dl(Gfx* cmd) { + // puts("dl"); + int dummy = 0; + char dlName[128]; + const char* fileName; + + Gfx* dListStart = cmd; + uint64_t ourHash = -1; + + for (;;) { + uint32_t opcode = cmd->words.w0 >> 24; + // uint32_t opcode = cmd->words.w0 & 0xFF; + + // if (markerOn) + // printf("OP: %02X\n", opcode); + + switch (opcode) { + // RSP commands: + case G_LOAD_UCODE: + rsp.fog_mul = 0; + rsp.fog_offset = 0; + break; + case G_MARKER: { + cmd++; + + ourHash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + +#if _DEBUG + // uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + // ResourceMgr_GetNameByCRC(hash, dlName); + // lusprintf(__FILE__, __LINE__, 6, "G_MARKER: %s\n", dlName); +#endif + + markerOn = true; + } break; + case G_INVALTEXCACHE: { + uintptr_t texAddr = cmd->words.w1; + + if (texAddr == 0) { + gfx_texture_cache_clear(); + } else { + gfx_texture_cache_delete((const uint8_t*)texAddr); + } + } break; + case G_NOOP: { + uint32_t index = C0(0, 16); + uint32_t type = C0(16, 8); + if (type == 2) { + const char* str = (const char*)cmd->words.w1; + // printf("%s, %u\n", str, index); + } + } break; + case G_MTX: { + uintptr_t mtxAddr = cmd->words.w1; + + // OTRTODO: Temp way of dealing with gMtxClear. Need something more elegant in the future... +/* + uint32_t gameVersion = Ship::Window::GetInstance()->GetResourceManager()->GetGameVersion(); + if (gameVersion == OOT_PAL_GC) { + if (mtxAddr == SEG_ADDR(0, 0x0FBC20)) { + mtxAddr = clearMtx; + } + } else { + if (mtxAddr == SEG_ADDR(0, 0x12DB20) || mtxAddr == SEG_ADDR(0, 0x12DB40)) { + mtxAddr = clearMtx; + } + } + */ +#ifdef F3DEX_GBI_2 + gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, (const int32_t*)seg_addr(mtxAddr)); +#else + gfx_sp_matrix(C0(16, 8), (const int32_t*)seg_addr(cmd->words.w1)); +#endif + break; + } +/* + case G_MTX_OTR: { + cmd++; + + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + +#if _DEBUG + // char fileName[4096]; + // ResourceMgr_GetNameByCRC(hash, fileName); + + // printf("G_MTX_OTR: %s\n", fileName); +#endif + + int32_t* mtx = ResourceMgr_LoadMtxByCRC(hash); + +#ifdef F3DEX_GBI_2 + if (mtx != NULL) { + cmd--; + gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, mtx); + cmd++; + } +#else + gfx_sp_matrix(C0(16, 8), (const int32_t*)seg_addr(cmd->words.w1)); +#endif + break; + } + */ + case (uint8_t)G_POPMTX: +#ifdef F3DEX_GBI_2 + gfx_sp_pop_matrix(cmd->words.w1 / 64); +#else + gfx_sp_pop_matrix(1); +#endif + break; + case G_MOVEMEM: +#ifdef F3DEX_GBI_2 + gfx_sp_movemem(C0(0, 8), C0(8, 8) * 8, seg_addr(cmd->words.w1)); +#else + gfx_sp_movemem(C0(16, 8), 0, seg_addr(cmd->words.w1)); +#endif + break; + case (uint8_t)G_MOVEWORD: +#ifdef F3DEX_GBI_2 + gfx_sp_moveword(C0(16, 8), C0(0, 16), cmd->words.w1); +#else + gfx_sp_moveword(C0(0, 8), C0(8, 16), cmd->words.w1); +#endif + break; + case (uint8_t)G_TEXTURE: +#ifdef F3DEX_GBI_2 + gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(1, 7)); +#else + gfx_sp_texture(C1(16, 16), C1(0, 16), C0(11, 3), C0(8, 3), C0(0, 8)); +#endif + break; + case G_VTX: +#ifdef F3DEX_GBI_2 + gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), (const Vtx*)seg_addr(cmd->words.w1)); +#elif defined(F3DEX_GBI) || defined(F3DLP_GBI) + gfx_sp_vertex(C0(10, 6), C0(16, 8) / 2, seg_addr(cmd->words.w1)); +#else + gfx_sp_vertex((C0(0, 16)) / sizeof(Vtx), C0(16, 4), seg_addr(cmd->words.w1)); +#endif + break; +/* + case G_VTX_OTR: { + uintptr_t offset = cmd->words.w1; + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + +#if _DEBUG + // char fileName[4096]; + // ResourceMgr_GetNameByCRC(hash, fileName); + + // printf("G_VTX_OTR: %s, 0x%08X\n", fileName, hash); +#endif + if (offset > 0xFFFFF) { + cmd--; + gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), (Vtx*)offset); + cmd++; + } else { + Vtx* vtx = ResourceMgr_LoadVtxByCRC(hash); + + if (vtx != NULL) { + vtx = (Vtx*)((char*)vtx + offset); + + cmd--; + + if (ourHash != (uint64_t)-1) { + ResourceMgr_RegisterResourcePatch(ourHash, cmd - dListStart, cmd->words.w1); + } + + cmd->words.w1 = (uintptr_t)vtx; + + gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), vtx); + cmd++; + } + } + } + break; + */ + case G_MODIFYVTX: + gfx_sp_modify_vertex(C0(1, 15), C0(16, 8), cmd->words.w1); + break; + case G_DL: + if (cmd->words.w1 == dListBP) { + int bp = 0; + } + + if (C0(16, 1) == 0) { + // Push return address + gfx_run_dl((Gfx*)seg_addr(cmd->words.w1)); + } else { + cmd = (Gfx*)seg_addr(cmd->words.w1); + --cmd; // increase after break + } + break; +/* + case G_DL_OTR: + if (C0(16, 1) == 0) { + // Push return address + + cmd++; + + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + +#if _DEBUG + // char fileName[4096]; + // ResourceMgr_GetNameByCRC(hash, fileName); + + // printf("G_DL_OTR: %s\n", fileName); +#endif + + Gfx* gfx = ResourceMgr_LoadGfxByCRC(hash); + + if (gfx != 0) { + gfx_run_dl(gfx); + } + } else { + cmd = (Gfx*)seg_addr(cmd->words.w1); + cmd++; + --cmd; // increase after break + } + break; + */ +/* + case G_BRANCH_Z_OTR: { + // Push return address + + uint8_t vbidx = cmd->words.w0 & 0x00000FFF; + uint32_t zval = cmd->words.w1; + + cmd++; + + if (rsp.loaded_vertices[vbidx].z <= zval) { + + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; + +#if _DEBUG + // char fileName[4096]; + // ResourceMgr_GetNameByCRC(hash, fileName); + + // printf("G_BRANCH_Z_OTR: %s\n", fileName); +#endif + + Gfx* gfx = ResourceMgr_LoadGfxByCRC(hash); + + if (gfx != 0) { + cmd = gfx; + --cmd; // increase after break + } + } + } break; + */ + case (uint8_t)G_ENDDL: + + // if (markerOn) + // printf("END DL ON MARKER\n"); + + markerOn = false; + return; +#ifdef F3DEX_GBI_2 + case G_GEOMETRYMODE: + gfx_sp_geometry_mode(~C0(0, 24), cmd->words.w1); + break; +#else + case (uint8_t)G_SETGEOMETRYMODE: + gfx_sp_geometry_mode(0, cmd->words.w1); + break; + case (uint8_t)G_CLEARGEOMETRYMODE: + gfx_sp_geometry_mode(cmd->words.w1, 0); + break; +#endif + case (uint8_t)G_TRI1: +#ifdef F3DEX_GBI_2 + gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2, false); +#elif defined(F3DEX_GBI) || defined(F3DLP_GBI) + gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2, false); +#else + gfx_sp_tri1(C1(16, 8) / 10, C1(8, 8) / 10, C1(0, 8) / 10, false); +#endif + break; +#ifdef F3DEX_GBI_2 + case G_QUAD: { + int bp = 0; + [[fallthrough]]; + } +#endif +#if defined(F3DEX_GBI) || defined(F3DLP_GBI) + case (uint8_t)G_TRI2: + gfx_sp_tri1(C0(16, 8) / 2, C0(8, 8) / 2, C0(0, 8) / 2, false); + gfx_sp_tri1(C1(16, 8) / 2, C1(8, 8) / 2, C1(0, 8) / 2, false); + break; +#endif + case (uint8_t)G_SETOTHERMODE_L: +#ifdef F3DEX_GBI_2 + gfx_sp_set_other_mode(31 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, cmd->words.w1); +#else + gfx_sp_set_other_mode(C0(8, 8), C0(0, 8), cmd->words.w1); +#endif + break; + case (uint8_t)G_SETOTHERMODE_H: +#ifdef F3DEX_GBI_2 + gfx_sp_set_other_mode(63 - C0(8, 8) - C0(0, 8), C0(0, 8) + 1, (uint64_t)cmd->words.w1 << 32); +#else + gfx_sp_set_other_mode(C0(8, 8) + 32, C0(0, 8), (uint64_t)cmd->words.w1 << 32); +#endif + break; +// G_RDPHALF_CONT, G_RDPHALF_2 and G_RDPHALF_1 were removed from f3dv2, perhaps due to OOT incompatibility? +#ifdef F3D_OLD + case (uint8_t)G_RDPHALF_2: +#else + case (uint8_t)G_RDPHALF_1: +#endif + switch (rsp.saved_opcode) { + case G_TEXRECT: + case G_TEXRECTFLIP: +#ifdef F3DEX_GBI_2E + rsp.saved_ulx = (int32_t)(C0(0, 24) << 8) >> 8; +#endif + rsp.saved_uls = (uint16_t)C1(16, 16); + rsp.saved_ult = (uint16_t)C1(0, 16); + break; +#ifdef F3DEX_GBI_2E + case G_FILLRECT: + { + int32_t ulx = (int32_t)(C0(0, 24) << 8) >> 8; + int32_t uly = (int32_t)(C1(0, 24) << 8) >> 8; + gfx_dp_fill_rectangle(ulx, uly, rsp.saved_lrx, rsp.saved_lry); + rsp.saved_opcode = G_NOOP; + break; + } +#endif + } + break; +#ifdef F3D_OLD + case (uint8_t)G_RDPHALF_CONT: +#else + case (uint8_t)G_RDPHALF_2: +#endif + switch (rsp.saved_opcode) { + case G_TEXRECT: + case G_TEXRECTFLIP: + { + uint8_t tile = rsp.saved_tile; + int32_t ulx = rsp.saved_ulx, lrx = rsp.saved_lrx, lry = rsp.saved_lry; + uint16_t uls = rsp.saved_uls, ult = rsp.saved_ult; +#ifdef F3DEX_GBI_2E + int32_t uly = (int32_t)(C0(0, 24) << 8) >> 8; +#else + int32_t uly = rsp.saved_uly; +#endif + uint16_t dsdx = (uint16_t)C1(16, 16); + uint16_t dtdy = (uint16_t)C1(0, 16); + gfx_dp_texture_rectangle(ulx, uly, lrx, lry, tile, uls, ult, dsdx, dtdy, rsp.saved_opcode == G_TEXRECTFLIP); + rsp.saved_opcode = G_NOOP; + break; + } + } + // RDP Commands: + case G_SETTIMG: { + /* + uintptr_t i = (uintptr_t)seg_addr(cmd->words.w1); + + char* imgData = (char*)i; + + if ((i & 1) != 1) { + if (ResourceMgr_OTRSigCheck(imgData) == 1) { + i = (uintptr_t)ResourceMgr_LoadTexByName(imgData); + } + } + + gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), (void*)i, imgData); + */ + gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), seg_addr(cmd->words.w1)); + break; + } +/* + case G_SETTIMG_OTR: { + uintptr_t addr = cmd->words.w1; + cmd++; + uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + (uint64_t)cmd->words.w1; + fileName = ResourceMgr_GetNameByCRC(hash); +#if _DEBUG && 0 + char* tex = ResourceMgr_LoadTexByCRC(hash); + ResourceMgr_GetNameByCRC(hash, fileName); + printf("G_SETTIMG_OTR: %s, %08X\n", fileName, hash); +#else + char* tex = NULL; +#endif + + if (addr != 0) { + tex = (char*)addr; + } else { + tex = ResourceMgr_LoadTexByCRC(hash); + + if (tex != nullptr) { + cmd--; + uintptr_t oldData = cmd->words.w1; + cmd->words.w1 = (uintptr_t)tex; + + if (ourHash != (uint64_t)-1) { + ResourceMgr_RegisterResourcePatch(ourHash, cmd - dListStart, oldData); + } + + cmd++; + } + } + + cmd--; + + uint32_t fmt = C0(21, 3); + uint32_t size = C0(19, 2); + uint32_t width = C0(0, 10); + + if (tex != NULL) { + gfx_dp_set_texture_image(fmt, size, width, tex, fileName); + } + + cmd++; + break; + } + */ + case G_SETFB: { + gfx_flush(); + fbActive = 1; + active_fb = framebuffers.find(cmd->words.w1); + gfx_rapi->start_draw_to_framebuffer(active_fb->first, (float)active_fb->second.applied_height / + active_fb->second.orig_height); + gfx_rapi->clear_framebuffer(); + break; + } + case G_RESETFB: { + gfx_flush(); + fbActive = 0; + gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, + (float)gfx_current_dimensions.height / SCREEN_HEIGHT); + break; + } + case G_SETTIMG_FB: { + gfx_flush(); + gfx_rapi->select_texture_fb(cmd->words.w1); + rdp.textures_changed[0] = false; + rdp.textures_changed[1] = false; + + // if (texPtr != NULL) + // gfx_dp_set_texture_image(C0(21, 3), C0(19, 2), C0(0, 10), texPtr); + break; + } + case G_SETGRAYSCALE: { + rdp.grayscale = cmd->words.w1; + break; + } + case G_LOADBLOCK: + gfx_dp_load_block(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); + break; + case G_LOADTILE: + gfx_dp_load_tile(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); + break; + case G_SETTILE: + gfx_dp_set_tile(C0(21, 3), C0(19, 2), C0(9, 9), C0(0, 9), C1(24, 3), C1(20, 4), C1(18, 2), C1(14, 4), + C1(10, 4), C1(8, 2), C1(4, 4), C1(0, 4)); + break; + case G_SETTILESIZE: + gfx_dp_set_tile_size(C1(24, 3), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); + break; + case G_LOADTLUT: + gfx_dp_load_tlut(C1(24, 3), C1(14, 10)); + break; + case G_SETENVCOLOR: + gfx_dp_set_env_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); + break; + case G_SETOVERRIDECOLOR: + gfx_dp_set_override_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); + break; + case G_SETPRIMCOLOR: + gfx_dp_set_prim_color(C0(8, 8), C0(0, 8), C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); + break; + case G_SETFOGCOLOR: + gfx_dp_set_fog_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); + break; + case G_SETFILLCOLOR: + gfx_dp_set_fill_color(cmd->words.w1); + break; + case G_SETINTENSITY: + gfx_dp_set_grayscale_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); + break; + case G_SETCOMBINE: + gfx_dp_set_combine_mode(color_comb(C0(20, 4), C1(28, 4), C0(15, 5), C1(15, 3)), + alpha_comb(C0(12, 3), C1(12, 3), C0(9, 3), C1(9, 3)), + color_comb(C0(5, 4), C1(24, 4), C0(0, 5), C1(6, 3)), + alpha_comb(C1(21, 3), C1(3, 3), C1(18, 3), C1(0, 3))); + break; + // G_SETPRIMCOLOR, G_CCMUX_PRIMITIVE, G_ACMUX_PRIMITIVE, is used by Goddard + // G_CCMUX_TEXEL1, LOD_FRACTION is used in Bowser room 1 + case G_TEXRECT: + case G_TEXRECTFLIP: { + rsp.saved_opcode = opcode; +#ifdef F3DEX_GBI_2E + rsp.saved_lrx = (int32_t)(C0(0, 24) << 8) >> 8; + rsp.saved_lry = (int32_t)(C1(0, 24) << 8) >> 8; + rsp.saved_tile = (int32_t)C1(24, 3); +#else + rsp.saved_lrx = C0(12, 12); + rsp.saved_lry = C0(0, 12); + rsp.saved_tile = C1(24, 3); + rsp.saved_ulx = C1(12, 12); + rsp.saved_uly = C1(0, 12); +#endif + break; + } + case G_FILLRECT: +#ifdef F3DEX_GBI_2E + { + rsp.saved_opcode = G_FILLRECT; + rsp.saved_lrx = (int32_t)(C0(0, 24) << 8) >> 8; + rsp.saved_lry = (int32_t)(C1(0, 24) << 8) >> 8; + break; + } +#else + gfx_dp_fill_rectangle(C1(12, 12), C1(0, 12), C0(12, 12), C0(0, 12)); + break; +#endif + case G_SETSCISSOR: + gfx_dp_set_scissor(C1(24, 2), C0(12, 12), C0(0, 12), C1(12, 12), C1(0, 12)); + break; + case G_SETZIMG: + gfx_dp_set_z_image(seg_addr(cmd->words.w1)); + break; + case G_SETCIMG: + gfx_dp_set_color_image(C0(21, 3), C0(19, 2), C0(0, 11), seg_addr(cmd->words.w1)); + break; + case G_RDPSETOTHERMODE: + gfx_dp_set_other_mode(C0(0, 24), cmd->words.w1); + break; + // S2DEX + case G_BG_COPY: + if (!markerOn) { + gfx_s2dex_bg_copy((const uObjBg*)cmd->words.w1); // not seg_addr here it seems + } + + break; + } + ++cmd; + } +} + +static void gfx_sp_reset() { + rsp.modelview_matrix_stack_size = 1; + rsp.current_num_lights = 2; + rsp.lights_changed = true; +} + +void gfx_get_dimensions(uint32_t* width, uint32_t* height) { + gfx_wapi->get_dimensions(width, height); +} + +void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, + bool start_in_fullscreen, uint32_t width, uint32_t height) { + gfx_wapi = wapi; + gfx_rapi = rapi; + gfx_wapi->init(game_name, start_in_fullscreen, width, height); + gfx_rapi->init(); +#if ENABLE_FRAMEBUFFER + gfx_rapi->update_framebuffer_parameters(0, width, height, 1, false, true, true, true); +#endif +#if defined(__APPLE__) || 1 + gfx_current_dimensions.internal_mul = 1; +#else + gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); +#endif +#if 1 + gfx_msaa_level = 1; +#else + gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); +#endif +#ifndef __WIIU__ // Wii U overrides dimentions in gfx_wapi->init to match framebuffer size + gfx_current_dimensions.width = width; + gfx_current_dimensions.height = height; +#endif +#if ENABLE_FRAMEBUFFER + game_framebuffer = gfx_rapi->create_framebuffer(); + game_framebuffer_msaa_resolved = gfx_rapi->create_framebuffer(); +#endif + for (int i = 0; i < 16; i++) { + segmentPointers[i] = 0; + } + + // Used in the 120 star TAS + static uint32_t precomp_shaders[] = { 0x01200200, 0x00000045, 0x00000200, 0x01200a00, 0x00000a00, 0x01a00045, + 0x00000551, 0x01045045, 0x05a00a00, 0x01200045, 0x05045045, 0x01045a00, + 0x01a00a00, 0x0000038d, 0x01081081, 0x0120038d, 0x03200045, 0x03200a00, + 0x01a00a6f, 0x01141045, 0x07a00a00, 0x05200200, 0x03200200, 0x09200200, + 0x0920038d, 0x09200045 }; + for (size_t i = 0; i < sizeof(precomp_shaders) / sizeof(uint32_t); i++) { + // gfx_lookup_or_create_shader_program(precomp_shaders[i]); + } + + //Ship::ExecuteHooks(); +} + +struct GfxRenderingAPI* gfx_get_current_rendering_api(void) { + return gfx_rapi; +} + +void gfx_start_frame(void) { + gfx_wapi->handle_events(); +#if ENABLE_FRAMEBUFFER && !RENDER_TO_SCREEN + gfx_wapi->get_dimensions(&gfx_current_window_dimensions.width, &gfx_current_window_dimensions.height); +#else + gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height); +#endif + //SohImGui::DrawMainMenuAndCalculateGameSize(); + //has_drawn_imgui_menu = true; + if (gfx_current_dimensions.height == 0) { + // Avoid division by zero + gfx_current_dimensions.height = 1; + } + gfx_current_dimensions.aspect_ratio = (float)gfx_current_dimensions.width / (float)gfx_current_dimensions.height; + + if (gfx_current_dimensions.height != gfx_prev_dimensions.height) { + for (auto& fb : framebuffers) { + uint32_t width = fb.second.orig_width, height = fb.second.orig_height; + gfx_adjust_width_height_for_scale(width, height); + if (width != fb.second.applied_width || height != fb.second.applied_height) { + gfx_rapi->update_framebuffer_parameters(fb.first, width, height, 1, true, true, true, true); + fb.second.applied_width = width; + fb.second.applied_height = height; + } + } + } + gfx_prev_dimensions = gfx_current_dimensions; + +#if ENABLE_FRAMEBUFFER + bool different_size = gfx_current_dimensions.width != gfx_current_game_window_viewport.width || + gfx_current_dimensions.height != gfx_current_game_window_viewport.height; + if (different_size || gfx_msaa_level > 1) { + game_renders_to_framebuffer = true; + if (different_size) { + gfx_rapi->update_framebuffer_parameters(game_framebuffer, gfx_current_dimensions.width, + gfx_current_dimensions.height, gfx_msaa_level, true, true, true, + true); + } else { + // MSAA framebuffer needs to be resolved to an equally sized target when complete, which must therefore + // match the window size + gfx_rapi->update_framebuffer_parameters(game_framebuffer, gfx_current_window_dimensions.width, + gfx_current_window_dimensions.height, gfx_msaa_level, false, true, + true, true); + } + if (gfx_msaa_level > 1 && different_size) { + gfx_rapi->update_framebuffer_parameters(game_framebuffer_msaa_resolved, gfx_current_dimensions.width, + gfx_current_dimensions.height, 1, false, false, false, false); + } + } else { + game_renders_to_framebuffer = false; + } +#else + game_renders_to_framebuffer = false; +#endif + + fbActive = 0; +} + +//void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacements) { +void gfx_run(Gfx* commands) { + gfx_sp_reset(); + + // puts("New frame"); + get_pixel_depth_pending.clear(); + get_pixel_depth_cached.clear(); + + if (!gfx_wapi->start_frame()) { + dropped_frame = true; + //if (has_drawn_imgui_menu) { + // SohImGui::DrawFramebufferAndGameInput(); + // SohImGui::CancelFrame(); + // has_drawn_imgui_menu = false; + //} + return; + } + dropped_frame = false; + rdp.override_color.a = 0; + + //if (!has_drawn_imgui_menu) { + // SohImGui::DrawMainMenuAndCalculateGameSize(); + //} + + //current_mtx_replacements = &mtx_replacements; + +#if ENABLE_FRAMEBUFFER && RENDER_TO_SCREEN + game_renders_to_framebuffer = false; +#endif + + double t0 = gfx_wapi->get_time(); + gfx_rapi->update_framebuffer_parameters(0, gfx_current_window_dimensions.width, + gfx_current_window_dimensions.height, 1, false, true, true, + !game_renders_to_framebuffer); + gfx_rapi->start_frame(); + gfx_rapi->start_draw_to_framebuffer(game_renders_to_framebuffer ? game_framebuffer : 0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT); + gfx_rapi->clear_framebuffer(); + rdp.viewport_or_scissor_changed = true; + rendering_state.viewport = {}; + rendering_state.scissor = {}; + gfx_run_dl(commands); + gfx_flush(); + gfxFramebuffer = 0; + if (game_renders_to_framebuffer) { + gfx_rapi->start_draw_to_framebuffer(0, 1); + gfx_rapi->clear_framebuffer(); + + if (gfx_msaa_level > 1) { + bool different_size = gfx_current_dimensions.width != gfx_current_game_window_viewport.width || + gfx_current_dimensions.height != gfx_current_game_window_viewport.height; + + if (different_size) { + gfx_rapi->resolve_msaa_color_buffer(game_framebuffer_msaa_resolved, game_framebuffer); + gfxFramebuffer = (uintptr_t)gfx_rapi->get_framebuffer_texture_id(game_framebuffer_msaa_resolved); + } else { + gfx_rapi->resolve_msaa_color_buffer(0, game_framebuffer); + } + } else { + gfxFramebuffer = (uintptr_t)gfx_rapi->get_framebuffer_texture_id(game_framebuffer); + } + } + //SohImGui::DrawFramebufferAndGameInput(); + //SohImGui::Render(); + double t1 = gfx_wapi->get_time(); + // printf("Process %f %f\n", t1, t1 - t0); + gfx_rapi->end_frame(); + gfx_wapi->swap_buffers_begin(); + //has_drawn_imgui_menu = false; +} + +void gfx_end_frame(void) { + if (!dropped_frame) { + gfx_rapi->finish_render(); + gfx_wapi->swap_buffers_end(); + } +} + +void gfx_set_target_fps(int fps) { + gfx_wapi->set_target_fps(fps); +} + +void gfx_set_maximum_frame_latency(int latency) { + gfx_wapi->set_maximum_frame_latency(latency); +} + +float gfx_get_detected_hz(void) { + return gfx_wapi->get_detected_hz(); +} + +int gfx_create_framebuffer(uint32_t width, uint32_t height) { + uint32_t orig_width = width, orig_height = height; + gfx_adjust_width_height_for_scale(width, height); + int fb = gfx_rapi->create_framebuffer(); + gfx_rapi->update_framebuffer_parameters(fb, width, height, 1, true, true, true, true); + framebuffers[fb] = { orig_width, orig_height, width, height }; + return fb; +} + +void gfx_set_framebuffer(int fb, float noise_scale) { + gfx_rapi->start_draw_to_framebuffer(fb, noise_scale); + gfx_rapi->clear_framebuffer(); +} + +void gfx_reset_framebuffer() { + gfx_rapi->start_draw_to_framebuffer(0, (float)gfx_current_dimensions.height / SCREEN_HEIGHT); +} + +static void adjust_pixel_depth_coordinates(float& x, float& y) { + x = x * RATIO_Y - (SCREEN_WIDTH * RATIO_Y - gfx_current_dimensions.width) / 2; + y *= RATIO_Y; + if (!game_renders_to_framebuffer || + (gfx_msaa_level > 1 && gfx_current_dimensions.width == gfx_current_game_window_viewport.width && + gfx_current_dimensions.height == gfx_current_game_window_viewport.height)) { + x += gfx_current_game_window_viewport.x; + y += gfx_current_window_dimensions.height - + (gfx_current_game_window_viewport.y + gfx_current_game_window_viewport.height); + } +} + +void gfx_get_pixel_depth_prepare(float x, float y) { + adjust_pixel_depth_coordinates(x, y); + get_pixel_depth_pending.emplace(x, y); +} + +uint16_t gfx_get_pixel_depth(float x, float y) { + adjust_pixel_depth_coordinates(x, y); + + if (auto it = get_pixel_depth_cached.find(make_pair(x, y)); it != get_pixel_depth_cached.end()) { + return it->second; + } + + get_pixel_depth_pending.emplace(x, y); + + unordered_map, uint16_t, hash_pair_ff> res = + gfx_rapi->get_pixel_depth(game_renders_to_framebuffer ? game_framebuffer : 0, get_pixel_depth_pending); + get_pixel_depth_cached.merge(res); + get_pixel_depth_pending.clear(); + + return get_pixel_depth_cached.find(make_pair(x, y))->second; +} + +void gfx_get_fb_dimensions(int fb, uint32_t *width, uint32_t *height) { + *width = framebuffers[fb].applied_width; + *height = framebuffers[fb].applied_height; +} + +void gfx_read_pixels(int fb, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t type, void* data) { + return gfx_rapi->read_pixels(fb, x, y, width, height, type, data); +} diff --git a/src/pc/gfx/gfx_pc.h b/src/pc/gfx/gfx_pc.h index 0089201fe..4008467ce 100644 --- a/src/pc/gfx/gfx_pc.h +++ b/src/pc/gfx/gfx_pc.h @@ -1,38 +1,115 @@ #ifndef GFX_PC_H #define GFX_PC_H -#define MAX_BUFFERED_TRIANGLES 256 -#ifdef TARGET_WII_U -#define VERTEX_BUFFER_SIZE MAX_BUFFERED_TRIANGLES * 28 * 3 // 3 vertices in a triangle and 28 floats per vtx -#else -#define VERTEX_BUFFER_SIZE MAX_BUFFERED_TRIANGLES * 26 * 3 // 3 vertices in a triangle and 26 floats per vtx +#include +#include +#include +#ifdef __cplusplus +#include +#include +#include #endif +#include +#include + +typedef float MtxF_t[4][4]; +typedef union { + MtxF_t mf; + struct { + float xx, yx, zx, wx, xy, yy, zy, wy, xz, yz, zz, wz, xw, yw, zw, ww; + }; +} MtxF; + +// TODO figure out why changing these to 640x480 makes the game only render in a quarter of the window +#define SCREEN_WIDTH 320 +#define SCREEN_HEIGHT 240 + +// SoH uses ImGui to render using framebuffers +#define ENABLE_FRAMEBUFFER 1 +// Without the requisite above nothing renders, so make extra changes to render directly to the screen +#define RENDER_TO_SCREEN 1 + +extern uintptr_t gfxFramebuffer; + struct GfxRenderingAPI; struct GfxWindowManagerAPI; +struct XYWidthHeight { + int16_t x, y; + uint32_t width, height; +}; + struct GfxDimensions { + float internal_mul; uint32_t width, height; float aspect_ratio; }; -extern struct GfxDimensions gfx_current_dimensions; +#ifdef __cplusplus +struct TextureCacheKey { + const uint8_t* texture_addr; + const uint8_t* palette_addrs[2]; + uint8_t fmt, siz; + uint8_t palette_index; + + bool operator==(const TextureCacheKey&) const noexcept = default; + + struct Hasher { + size_t operator()(const TextureCacheKey& key) const noexcept { + uintptr_t addr = (uintptr_t)key.texture_addr; + return (size_t)(addr ^ (addr >> 5)); + } + }; +}; + +typedef std::unordered_map TextureCacheMap; +typedef std::pair TextureCacheNode; + +struct TextureCacheValue { + uint32_t texture_id; + uint8_t cms, cmt; + bool linear_filter; + + std::list::iterator lru_location; +}; + +struct TextureCacheMapIter { + TextureCacheMap::iterator it; +}; +#endif #ifdef __cplusplus extern "C" { #endif +extern struct GfxDimensions gfx_current_window_dimensions; // The dimensions of the window +extern struct GfxDimensions + gfx_current_dimensions; // The dimensions of the draw area the game draws to, before scaling (if applicable) +extern struct XYWidthHeight + gfx_current_game_window_viewport; // The area of the window the game is drawn to, (0, 0) is top-left corner +extern uint32_t gfx_msaa_level; +#ifdef __cplusplus +} +#endif -void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, const char *window_title); -struct GfxRenderingAPI *gfx_get_current_rendering_api(void); -uint16_t *get_framebuffer(); -void gfx_start_frame(void); -void gfx_run(Gfx *commands); -void gfx_end_frame(void); -void gfx_precache_textures(void); -void gfx_shutdown(void); +void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name, + bool start_in_fullscreen, uint32_t width, uint32_t height); +struct GfxRenderingAPI* gfx_get_current_rendering_api(void); +void gfx_start_frame(void); +//void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacements); +void gfx_run(Gfx* commands); +void gfx_end_frame(void); +void gfx_set_target_fps(int); +void gfx_set_maximum_frame_latency(int latency); +float gfx_get_detected_hz(void); +void gfx_texture_cache_clear(); #ifdef __cplusplus -} +extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height); +#else +int gfx_create_framebuffer(uint32_t width, uint32_t height); #endif +void gfx_get_pixel_depth_prepare(float x, float y); +uint16_t gfx_get_pixel_depth(float x, float y); #endif diff --git a/src/pc/gfx/gfx_rendering_api.h b/src/pc/gfx/gfx_rendering_api.h index cb304fc20..74e6d979d 100644 --- a/src/pc/gfx/gfx_rendering_api.h +++ b/src/pc/gfx/gfx_rendering_api.h @@ -5,39 +5,67 @@ #include #include +#include +#include +#include + struct ShaderProgram; +struct GfxClipParameters { + bool z_is_from_0_to_1; + bool invert_y; +}; + +enum FilteringMode { FILTER_THREE_POINT, FILTER_LINEAR, FILTER_NONE }; + +// A hash function used to hash a: pair +struct hash_pair_ff { + size_t operator()(const std::pair& p) const { + auto hash1 = std::hash{}(p.first); + auto hash2 = std::hash{}(p.second); + + // If hash1 == hash2, their XOR is zero. + return (hash1 != hash2) ? hash1 ^ hash2 : hash1; + } +}; + struct GfxRenderingAPI { - bool (*z_is_from_0_to_1)(void); - void (*unload_shader)(struct ShaderProgram *old_prg); - void (*load_shader)(struct ShaderProgram *new_prg); - struct ShaderProgram *(*create_and_load_new_shader)(uint32_t shader_id); - struct ShaderProgram *(*lookup_shader)(uint32_t shader_id); - void (*shader_get_info)(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]); + struct GfxClipParameters (*get_clip_parameters)(void); + void (*unload_shader)(struct ShaderProgram* old_prg); + void (*load_shader)(struct ShaderProgram* new_prg); + struct ShaderProgram* (*create_and_load_new_shader)(uint64_t shader_id0, uint32_t shader_id1); + struct ShaderProgram* (*lookup_shader)(uint64_t shader_id0, uint32_t shader_id1); + void (*shader_get_info)(struct ShaderProgram* prg, uint8_t* num_inputs, bool used_textures[2]); uint32_t (*new_texture)(void); void (*select_texture)(int tile, uint32_t texture_id); - void (*upload_texture)(const uint8_t *rgba32_buf, int width, int height); + void (*upload_texture)(const uint8_t* rgba32_buf, uint32_t width, uint32_t height); void (*set_sampler_parameters)(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt); - void (*set_depth_test)(bool depth_test); - void (*set_depth_mask)(bool z_upd); + void (*set_depth_test_and_mask)(bool depth_test, bool z_upd); + void (*read_pixels)(int fb, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t type, void* data); void (*set_zmode_decal)(bool zmode_decal); void (*set_viewport)(int x, int y, int width, int height); void (*set_scissor)(int x, int y, int width, int height); void (*set_use_alpha)(bool use_alpha); void (*draw_triangles)(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris); - void (*get_framebuffer)(uint16_t *buffer); void (*init)(void); void (*on_resize)(void); void (*start_frame)(void); void (*end_frame)(void); void (*finish_render)(void); - void (*shutdown)(void); -#ifdef TARGET_N3DS - void (*set_fog)(uint16_t from, uint16_t to); - void (*set_fog_color)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - void (*set_2d)(int mode_2d); - void (*set_iod)(float z, float w); -#endif + int (*create_framebuffer)(); + void (*update_framebuffer_parameters)(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invert_y, bool render_target, bool has_depth_buffer, + bool can_extract_depth); + void (*start_draw_to_framebuffer)(int fb_id, float noise_scale); + void (*clear_framebuffer)(void); + void (*resolve_msaa_color_buffer)(int fb_id_target, int fb_id_source); + std::unordered_map, uint16_t, hash_pair_ff> (*get_pixel_depth)( + int fb_id, const std::set>& coordinates); + void* (*get_framebuffer_texture_id)(int fb_id); + void (*select_texture_fb)(int fb_id); + void (*delete_texture)(uint32_t texID); + void (*set_texture_filter)(FilteringMode mode); + FilteringMode (*get_texture_filter)(void); }; #endif diff --git a/src/pc/gfx/gfx_screen_config.h b/src/pc/gfx/gfx_screen_config.h index 1b0830489..5b933f853 100644 --- a/src/pc/gfx/gfx_screen_config.h +++ b/src/pc/gfx/gfx_screen_config.h @@ -4,7 +4,4 @@ #define DESIRED_SCREEN_WIDTH 640 #define DESIRED_SCREEN_HEIGHT 480 -#define FRAMEBUFFER_WIDTH 320 -#define FRAMEBUFFER_HEIGHT 240 - #endif diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c deleted file mode 100644 index 7614b5c7b..000000000 --- a/src/pc/gfx/gfx_sdl2.c +++ /dev/null @@ -1,487 +0,0 @@ -#ifdef WAPI_SDL2 - -#ifdef __MINGW32__ -#define FOR_WINDOWS 1 -#else -#define FOR_WINDOWS 0 -#endif - -#if FOR_WINDOWS -unsigned int timeBeginPeriod(unsigned int uPeriod); -unsigned int timeEndPeriod(unsigned int uPeriod); -unsigned long GetPriorityClass(void* hProcess); -unsigned long SetPriorityClass(void* hProcess, unsigned long dwPriorityClass); -void Sleep(unsigned long ms); -#define GLEW_STATIC -#include -#include -#define GL_GLEXT_PROTOTYPES 1 -#include -#else -#include -#define GL_GLEXT_PROTOTYPES 1 - -#ifdef OSX_BUILD -#include -#else -#include -#endif - -#endif // End of OS-Specific GL defines - -#include -#include -#include - -#include "gfx_window_manager_api.h" -#include "gfx_screen_config.h" -#include "../pc_main.h" -#include "../configfile.h" - -#ifdef COMMAND_LINE_OPTIONS -#include "../cliopts.h" -#endif - -#include "src/pc/controller/controller_keyboard.h" -#include "src/pc/controller/controller_touchscreen.h" - -#ifdef TARGET_SWITCH -#include -#endif -// PAL framerate things -#ifdef VERSION_EU -#define FRAMERATE 25 -#else -#define FRAMERATE 30 -#endif - -// Only used on console targets -#ifdef HIGH_FPS_PC -#define SWAP_FRAME 1 -#else -#define SWAP_FRAME 2 -#endif - -static SDL_Window *wnd; -static SDL_GLContext ctx = NULL; -static int inverted_scancode_table[512]; - -static kb_callback_t kb_key_down = NULL; -static kb_callback_t kb_key_up = NULL; -static void (*kb_all_keys_up)(void) = NULL; -static void (*touch_down_callback)(void* event); -static void (*touch_motion_callback)(void* event); -static void (*touch_up_callback)(void* event); - -// time between consequtive game frames, in perf counter ticks -static double frame_rate = 0.0; // set in init() -// time in which a frame began, in perf counter ticks -static double frame_time = 0.0; // updated in start_frame() -// GetPerformanceFrequency -static double perf_freq = 0.0; - -const SDL_Scancode windows_scancode_table[] = { - /* 0 1 2 3 4 5 6 7 */ - /* 8 9 A B C D E F */ - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, /* 0 */ - SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0, SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB, /* 0 */ - - SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I, /* 1 */ - SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S, /* 1 */ - - SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON, /* 2 */ - SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, /* 2 */ - - SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_PRINTSCREEN,/* 3 */ - SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, /* 3 */ - - SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_HOME, /* 4 */ - SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_LEFT, SDL_SCANCODE_KP_5, SDL_SCANCODE_RIGHT, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_END, /* 4 */ - - SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_NONUSBACKSLASH, SDL_SCANCODE_F11, /* 5 */ - SDL_SCANCODE_F12, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_APPLICATION, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 5 */ - - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16, /* 6 */ - SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 6 */ - - SDL_SCANCODE_INTERNATIONAL2, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL1, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 7 */ - SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL4, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL5, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL3, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN /* 7 */ -}; - -const SDL_Scancode scancode_rmapping_extended[][2] = { - {SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RETURN}, - {SDL_SCANCODE_RALT, SDL_SCANCODE_LALT}, - {SDL_SCANCODE_RCTRL, SDL_SCANCODE_LCTRL}, - {SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_SLASH}, - //{SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_CAPSLOCK} -}; - -const SDL_Scancode scancode_rmapping_nonextended[][2] = { - {SDL_SCANCODE_KP_7, SDL_SCANCODE_HOME}, - {SDL_SCANCODE_KP_8, SDL_SCANCODE_UP}, - {SDL_SCANCODE_KP_9, SDL_SCANCODE_PAGEUP}, - {SDL_SCANCODE_KP_4, SDL_SCANCODE_LEFT}, - {SDL_SCANCODE_KP_6, SDL_SCANCODE_RIGHT}, - {SDL_SCANCODE_KP_1, SDL_SCANCODE_END}, - {SDL_SCANCODE_KP_2, SDL_SCANCODE_DOWN}, - {SDL_SCANCODE_KP_3, SDL_SCANCODE_PAGEDOWN}, - {SDL_SCANCODE_KP_0, SDL_SCANCODE_INSERT}, - {SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_DELETE}, - {SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN} -}; - -static void gfx_sdl_set_framerate(void) { -#ifdef TARGET_PORT_CONSOLE - SDL_GL_SetSwapInterval(SWAP_FRAME); -#else - SDL_GL_SetSwapInterval(configWindow.vsync); -#endif -} - -#define IS_FULLSCREEN() ((SDL_GetWindowFlags(wnd) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) - -static void gfx_sdl_set_fullscreen(void) { - if (configWindow.reset) - configWindow.fullscreen = false; - if (configWindow.fullscreen == IS_FULLSCREEN()) - return; - if (configWindow.fullscreen) { - SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP); - SDL_ShowCursor(SDL_DISABLE); - } else { - SDL_SetWindowFullscreen(wnd, 0); - SDL_ShowCursor(SDL_ENABLE); - configWindow.exiting_fullscreen = true; - } -} - -static void gfx_sdl_reset_dimension_and_pos(void) { - if (configWindow.exiting_fullscreen) - configWindow.exiting_fullscreen = false; - - if (configWindow.reset) { - configWindow.x = WAPI_WIN_CENTERPOS; - configWindow.y = WAPI_WIN_CENTERPOS; - configWindow.w = DESIRED_SCREEN_WIDTH; - configWindow.h = DESIRED_SCREEN_HEIGHT; - configWindow.reset = false; - } else if (!configWindow.settings_changed) { - return; - } - - int xpos = (configWindow.x == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.x; - int ypos = (configWindow.y == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.y; - - SDL_SetWindowSize(wnd, configWindow.w, configWindow.h); - SDL_SetWindowPosition(wnd, xpos, ypos); - // in case vsync changed - gfx_sdl_set_framerate(); -} - -static void gfx_sdl_init(const char *window_title) { -#if FOR_WINDOWS - if (GetPriorityClass((void*)-1) == 0x20) SetPriorityClass((void*)-1, 0x8000); - timeBeginPeriod(1); -#endif - SDL_Init(SDL_INIT_VIDEO); - - #ifdef __ANDROID__ - SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight"); - #endif - - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - - // SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); - - #ifdef USE_GLES - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); // These attributes allow for hardware acceleration on RPis. - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - #endif - - //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); - - #ifdef TARGET_SWITCH - configWindow.fullscreen = false; - // if docked, set 1920x1080 - if (appletGetOperationMode() == 1) { - configWindow.w = 1920; - configWindow.h = 1080; - } else { - configWindow.w = 1280; - configWindow.h = 720; - } - int xpos = 0; - int ypos = 0; - #else - int xpos = (configWindow.x == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.x; - int ypos = (configWindow.y == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.y; - #endif - - wnd = SDL_CreateWindow( - window_title, - xpos, ypos, configWindow.w, configWindow.h, - SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN - #ifndef TARGET_PORT_CONSOLE - | SDL_WINDOW_RESIZABLE - #endif - ); - ctx = SDL_GL_CreateContext(wnd); - - gfx_sdl_set_framerate(); - - gfx_sdl_set_fullscreen(); - - perf_freq = SDL_GetPerformanceFrequency(); - - #ifdef HIGH_FPS_PC - frame_rate = perf_freq / (2 * FRAMERATE); - #else - frame_rate = perf_freq / FRAMERATE; - #endif - - frame_time = SDL_GetPerformanceCounter(); - - for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { - inverted_scancode_table[windows_scancode_table[i]] = i; - } - - for (size_t i = 0; i < sizeof(scancode_rmapping_extended) / sizeof(scancode_rmapping_extended[0]); i++) { - inverted_scancode_table[scancode_rmapping_extended[i][0]] = inverted_scancode_table[scancode_rmapping_extended[i][1]] + 0x100; - } - - for (size_t i = 0; i < sizeof(scancode_rmapping_nonextended) / sizeof(scancode_rmapping_nonextended[0]); i++) { - inverted_scancode_table[scancode_rmapping_nonextended[i][0]] = inverted_scancode_table[scancode_rmapping_nonextended[i][1]]; - inverted_scancode_table[scancode_rmapping_nonextended[i][1]] += 0x100; - } -} - -static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { - run_one_game_iter(); -} - -static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) { - int w, h; - SDL_GetWindowSize(wnd, &w, &h); - if (width) *width = w; - if (height) *height = h; -} - -static int translate_scancode(int scancode) { - if (scancode < 512) { - return inverted_scancode_table[scancode]; - } else { - return 0; - } -} - -static void gfx_sdl_onkeydown(int scancode) { - if (kb_key_down) - kb_key_down(translate_scancode(scancode)); - - const Uint8 *state = SDL_GetKeyboardState(NULL); - - if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN]) { - configWindow.fullscreen = !configWindow.fullscreen; - configWindow.settings_changed = true; - } -} - -static void gfx_sdl_onkeyup(int scancode) { - if (kb_key_up) - kb_key_up(translate_scancode(scancode)); -} - -#ifdef TOUCH_CONTROLS -static void gfx_sdl_fingerdown(SDL_TouchFingerEvent sdl_event) { - struct TouchEvent event; - event.x = sdl_event.x; - event.y = sdl_event.y; - event.touchID = sdl_event.fingerId + 1; - if (touch_down_callback != NULL) { - touch_down_callback((void*)&event); - } -} - -static void gfx_sdl_fingermotion(SDL_TouchFingerEvent sdl_event) { - struct TouchEvent event; - event.x = sdl_event.x; - event.y = sdl_event.y; - event.touchID = sdl_event.fingerId + 1; - if (touch_motion_callback != NULL) { - touch_motion_callback((void*)&event); - } -} - -static void gfx_sdl_fingerup(SDL_TouchFingerEvent sdl_event) { - struct TouchEvent event; - event.x = sdl_event.x; - event.y = sdl_event.y; - event.touchID = sdl_event.fingerId + 1; - if (touch_up_callback != NULL) { - touch_up_callback((void*)&event); - } -} -#endif - -static void gfx_sdl_handle_events(void) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - switch (event.type) { -#ifndef TARGET_WEB - // Scancodes are broken in Emscripten SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3259 - case SDL_KEYDOWN: - gfx_sdl_onkeydown(event.key.keysym.scancode); - break; - case SDL_KEYUP: - gfx_sdl_onkeyup(event.key.keysym.scancode); - break; -#endif -#ifdef TOUCH_CONTROLS - case SDL_FINGERDOWN: - gfx_sdl_fingerdown(event.tfinger); - break; - case SDL_FINGERMOTION: - gfx_sdl_fingermotion(event.tfinger); - break; - case SDL_FINGERUP: - gfx_sdl_fingerup(event.tfinger); - break; -#endif - case SDL_WINDOWEVENT: // TODO: Check if this makes sense to be included in the Web build - if (!IS_FULLSCREEN()) { - switch (event.window.event) { - case SDL_WINDOWEVENT_MOVED: - if (!configWindow.exiting_fullscreen) { - if (event.window.data1 >= 0) configWindow.x = event.window.data1; - if (event.window.data2 >= 0) configWindow.y = event.window.data2; - } - break; - case SDL_WINDOWEVENT_SIZE_CHANGED: - configWindow.w = event.window.data1; - configWindow.h = event.window.data2; - break; - } - } - break; - case SDL_QUIT: - game_exit(); - break; - } - } - - if (configWindow.settings_changed) { - gfx_sdl_set_fullscreen(); - gfx_sdl_reset_dimension_and_pos(); - configWindow.settings_changed = false; - } -} - -#ifndef TARGET_PORT_CONSOLE -static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void)) { - kb_key_down = on_key_down; - kb_key_up = on_key_up; - kb_all_keys_up = on_all_keys_up; -} -#endif - -#ifdef TOUCH_CONTROLS -static void gfx_sdl_set_touchscreen_callbacks(void (*down)(void* event), void (*motion)(void* event), void (*up)(void* event)) { - touch_down_callback = down; - touch_motion_callback = motion; - touch_up_callback = up; -} -#endif - -static bool gfx_sdl_start_frame(void) { - return true; -} - -#if FOR_WINDOWS -static inline void sync_framerate_with_timer(void) { - // calculate how long it took for the frame to render - double now = SDL_GetPerformanceCounter(); - double frame_length = now - frame_time; - - // Only sleep if we have time to spare - while (frame_length < frame_rate) { - double remain = (frame_rate - frame_length) / perf_freq * 1000.0; - if (remain > 2.0) // > 2ms - Sleep(1); // sleep for ~1ms - else - Sleep(0); // yield thread's time-slice (does not actually sleep) - - now = SDL_GetPerformanceCounter(); - frame_length = now - frame_time; - } - frame_time = now; -} -#else -static inline void sync_framerate_with_timer(void) { - // calculate how long it took for the frame to render - const double now = SDL_GetPerformanceCounter(); - const double frame_length = now - frame_time; - - if (frame_length < frame_rate) { - // Only sleep if we have time to spare - const double remain = frame_rate - frame_length; - // Sleep remaining time away (convert remain to microseconds) - usleep(remain / perf_freq * 1000000.0); - // Assume we slept the required amount of time to keep the timer stable - frame_time = now + remain; - } else { - frame_time = now; - } -} -#endif - -static void gfx_sdl_swap_buffers_begin(void) { - // Swap after we finished rendering, only if this frame wasn't dropped. - // This will wait for vblank if vsync is enabled and then update our window with our render. - SDL_GL_SwapWindow(wnd); -} - -static void gfx_sdl_swap_buffers_end(void) { - // The game isn't always going to run at a consistent rate, - // with frame pacing going up and down depending on hardware performance. - // Sleep off any remaining time to make the main loop iteration be called at a consistent frane rate. - // We do this after our swap, because it actually will take the time to swap into account. - sync_framerate_with_timer(); -} - -static double gfx_sdl_get_time(void) { - return 0.0; -} - -static void gfx_sdl_shutdown(void) { - if (SDL_WasInit(0)) { - if (ctx) { SDL_GL_DeleteContext(ctx); ctx = NULL; } - if (wnd) { SDL_DestroyWindow(wnd); wnd = NULL; } - SDL_Quit(); - } -#if FOR_WINDOWS - timeEndPeriod(1); -#endif -} - -struct GfxWindowManagerAPI gfx_sdl = { - gfx_sdl_init, -#ifndef TARGET_PORT_CONSOLE - gfx_sdl_set_keyboard_callbacks, -#endif -#ifdef TOUCH_CONTROLS - gfx_sdl_set_touchscreen_callbacks, -#endif - gfx_sdl_main_loop, - gfx_sdl_get_dimensions, - gfx_sdl_handle_events, - gfx_sdl_start_frame, - gfx_sdl_swap_buffers_begin, - gfx_sdl_swap_buffers_end, - gfx_sdl_get_time, - gfx_sdl_shutdown -}; - -#endif // BACKEND_WM diff --git a/src/pc/gfx/gfx_sdl2.cpp b/src/pc/gfx/gfx_sdl2.cpp new file mode 100644 index 000000000..2ffa14871 --- /dev/null +++ b/src/pc/gfx/gfx_sdl2.cpp @@ -0,0 +1,519 @@ +#include + +#if 1 + +#ifdef __MINGW32__ +#define FOR_WINDOWS 1 +#else +#define FOR_WINDOWS 0 +#endif + +#if FOR_WINDOWS +#include +#define GL_GLEXT_PROTOTYPES 1 +#include +#include "SDL2/SDL_opengl.h" +#elif __APPLE__ +#include +#elif __SWITCH__ +#include +#include +#include +#include "SwitchImpl.h" +#else +#include +#define GL_GLEXT_PROTOTYPES 1 +#include +#endif + +//#include "menu/ImGuiImpl.h" +//#include "misc/Cvar.h" +//#include "misc/Hooks.h" + +#include "gfx_window_manager_api.h" +#include "gfx_screen_config.h" +#ifdef _WIN32 +#include +#endif + +#define GFX_API_NAME "SDL2 - OpenGL" + +static SDL_Window* wnd; +static SDL_GLContext ctx; +static int inverted_scancode_table[512]; +static int vsync_enabled = 0; +static int window_width = DESIRED_SCREEN_WIDTH; +static int window_height = DESIRED_SCREEN_HEIGHT; +static bool fullscreen_state; +static bool is_running = true; +static void (*on_fullscreen_changed_callback)(bool is_now_fullscreen); +static bool (*on_key_down_callback)(int scancode); +static bool (*on_key_up_callback)(int scancode); +static void (*on_all_keys_up_callback)(void); + +const SDL_Scancode windows_scancode_table[] = { + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_ESCAPE, + SDL_SCANCODE_1, + SDL_SCANCODE_2, + SDL_SCANCODE_3, + SDL_SCANCODE_4, + SDL_SCANCODE_5, + SDL_SCANCODE_6, /* 0 */ + SDL_SCANCODE_7, + SDL_SCANCODE_8, + SDL_SCANCODE_9, + SDL_SCANCODE_0, + SDL_SCANCODE_MINUS, + SDL_SCANCODE_EQUALS, + SDL_SCANCODE_BACKSPACE, + SDL_SCANCODE_TAB, /* 0 */ + + SDL_SCANCODE_Q, + SDL_SCANCODE_W, + SDL_SCANCODE_E, + SDL_SCANCODE_R, + SDL_SCANCODE_T, + SDL_SCANCODE_Y, + SDL_SCANCODE_U, + SDL_SCANCODE_I, /* 1 */ + SDL_SCANCODE_O, + SDL_SCANCODE_P, + SDL_SCANCODE_LEFTBRACKET, + SDL_SCANCODE_RIGHTBRACKET, + SDL_SCANCODE_RETURN, + SDL_SCANCODE_LCTRL, + SDL_SCANCODE_A, + SDL_SCANCODE_S, /* 1 */ + + SDL_SCANCODE_D, + SDL_SCANCODE_F, + SDL_SCANCODE_G, + SDL_SCANCODE_H, + SDL_SCANCODE_J, + SDL_SCANCODE_K, + SDL_SCANCODE_L, + SDL_SCANCODE_SEMICOLON, /* 2 */ + SDL_SCANCODE_APOSTROPHE, + SDL_SCANCODE_GRAVE, + SDL_SCANCODE_LSHIFT, + SDL_SCANCODE_BACKSLASH, + SDL_SCANCODE_Z, + SDL_SCANCODE_X, + SDL_SCANCODE_C, + SDL_SCANCODE_V, /* 2 */ + + SDL_SCANCODE_B, + SDL_SCANCODE_N, + SDL_SCANCODE_M, + SDL_SCANCODE_COMMA, + SDL_SCANCODE_PERIOD, + SDL_SCANCODE_SLASH, + SDL_SCANCODE_RSHIFT, + SDL_SCANCODE_PRINTSCREEN, /* 3 */ + SDL_SCANCODE_LALT, + SDL_SCANCODE_SPACE, + SDL_SCANCODE_CAPSLOCK, + SDL_SCANCODE_F1, + SDL_SCANCODE_F2, + SDL_SCANCODE_F3, + SDL_SCANCODE_F4, + SDL_SCANCODE_F5, /* 3 */ + + SDL_SCANCODE_F6, + SDL_SCANCODE_F7, + SDL_SCANCODE_F8, + SDL_SCANCODE_F9, + SDL_SCANCODE_F10, + SDL_SCANCODE_NUMLOCKCLEAR, + SDL_SCANCODE_SCROLLLOCK, + SDL_SCANCODE_HOME, /* 4 */ + SDL_SCANCODE_UP, + SDL_SCANCODE_PAGEUP, + SDL_SCANCODE_KP_MINUS, + SDL_SCANCODE_LEFT, + SDL_SCANCODE_KP_5, + SDL_SCANCODE_RIGHT, + SDL_SCANCODE_KP_PLUS, + SDL_SCANCODE_END, /* 4 */ + + SDL_SCANCODE_DOWN, + SDL_SCANCODE_PAGEDOWN, + SDL_SCANCODE_INSERT, + SDL_SCANCODE_DELETE, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_NONUSBACKSLASH, + SDL_SCANCODE_F11, /* 5 */ + SDL_SCANCODE_F12, + SDL_SCANCODE_PAUSE, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_LGUI, + SDL_SCANCODE_RGUI, + SDL_SCANCODE_APPLICATION, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, /* 5 */ + + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_F13, + SDL_SCANCODE_F14, + SDL_SCANCODE_F15, + SDL_SCANCODE_F16, /* 6 */ + SDL_SCANCODE_F17, + SDL_SCANCODE_F18, + SDL_SCANCODE_F19, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, /* 6 */ + + SDL_SCANCODE_INTERNATIONAL2, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_INTERNATIONAL1, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN, /* 7 */ + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_INTERNATIONAL4, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_INTERNATIONAL5, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_INTERNATIONAL3, + SDL_SCANCODE_UNKNOWN, + SDL_SCANCODE_UNKNOWN /* 7 */ +}; + +const SDL_Scancode scancode_rmapping_extended[][2] = { + { SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RETURN }, + { SDL_SCANCODE_RALT, SDL_SCANCODE_LALT }, + { SDL_SCANCODE_RCTRL, SDL_SCANCODE_LCTRL }, + { SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_SLASH }, + //{SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_CAPSLOCK} +}; + +const SDL_Scancode scancode_rmapping_nonextended[][2] = { { SDL_SCANCODE_KP_7, SDL_SCANCODE_HOME }, + { SDL_SCANCODE_KP_8, SDL_SCANCODE_UP }, + { SDL_SCANCODE_KP_9, SDL_SCANCODE_PAGEUP }, + { SDL_SCANCODE_KP_4, SDL_SCANCODE_LEFT }, + { SDL_SCANCODE_KP_6, SDL_SCANCODE_RIGHT }, + { SDL_SCANCODE_KP_1, SDL_SCANCODE_END }, + { SDL_SCANCODE_KP_2, SDL_SCANCODE_DOWN }, + { SDL_SCANCODE_KP_3, SDL_SCANCODE_PAGEDOWN }, + { SDL_SCANCODE_KP_0, SDL_SCANCODE_INSERT }, + { SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_DELETE }, + { SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN } }; + +static void set_fullscreen(bool on, bool call_callback) { + if (fullscreen_state == on) { + return; + } + fullscreen_state = on; + + if (on) { + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(0, &mode); + window_width = mode.w; + window_height = mode.h; + SDL_ShowCursor(false); + } else { + window_width = DESIRED_SCREEN_WIDTH; + window_height = DESIRED_SCREEN_HEIGHT; + } + SDL_SetWindowSize(wnd, window_width, window_height); + SDL_SetWindowFullscreen(wnd, on ? SDL_WINDOW_FULLSCREEN : 0); + SDL_SetCursor(SDL_DISABLE); + + if (on_fullscreen_changed_callback != NULL && call_callback) { + on_fullscreen_changed_callback(on); + } +} + +static uint64_t previous_time; +#ifdef _WIN32 +static HANDLE timer; +#endif + +static int target_fps = 30; + +#define FRAME_INTERVAL_US_NUMERATOR 1000000 +#define FRAME_INTERVAL_US_DENOMINATOR (target_fps) + +static void gfx_sdl_init(const char* game_name, bool start_in_fullscreen, uint32_t width, uint32_t height) { + SDL_Init(SDL_INIT_VIDEO); + + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + +#if defined(__APPLE__) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); +#elif defined(__SWITCH__) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif + +#ifdef _WIN32 + timer = CreateWaitableTimer(nullptr, false, nullptr); +#endif + + char title[512]; + int len = sprintf(title, "%s (%s)", game_name, GFX_API_NAME); + +#ifdef __SWITCH__ + // For Switch we need to set the window width before creating the window + Ship::Switch::GetDisplaySize(&window_width, &window_height); + width = window_width; + height = window_height; +#endif + + wnd = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, + SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); + +#ifndef __SWITCH__ + SDL_GL_GetDrawableSize(wnd, &window_width, &window_height); + + if (start_in_fullscreen) { + set_fullscreen(true, false); + } +#endif + + ctx = SDL_GL_CreateContext(wnd); + +#ifdef __SWITCH__ + if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) { + printf("Failed to initialize glad\n"); + } +#endif + + SDL_GL_MakeCurrent(wnd, ctx); + SDL_GL_SetSwapInterval(1); + + //SohImGui::WindowImpl window_impl; + //window_impl.backend = SohImGui::Backend::SDL; + //window_impl.sdl = { wnd, ctx }; + //SohImGui::Init(window_impl); + + for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { + inverted_scancode_table[windows_scancode_table[i]] = i; + } + + for (size_t i = 0; i < sizeof(scancode_rmapping_extended) / sizeof(scancode_rmapping_extended[0]); i++) { + inverted_scancode_table[scancode_rmapping_extended[i][0]] = + inverted_scancode_table[scancode_rmapping_extended[i][1]] + 0x100; + } + + for (size_t i = 0; i < sizeof(scancode_rmapping_nonextended) / sizeof(scancode_rmapping_nonextended[0]); i++) { + inverted_scancode_table[scancode_rmapping_nonextended[i][0]] = + inverted_scancode_table[scancode_rmapping_nonextended[i][1]]; + inverted_scancode_table[scancode_rmapping_nonextended[i][1]] += 0x100; + } +} + +static void gfx_sdl_set_fullscreen_changed_callback(void (*on_fullscreen_changed)(bool is_now_fullscreen)) { + on_fullscreen_changed_callback = on_fullscreen_changed; +} + +static void gfx_sdl_set_fullscreen(bool enable) { + set_fullscreen(enable, true); +} + +static void gfx_sdl_show_cursor(bool hide) { + SDL_ShowCursor(hide); +} + +static void gfx_sdl_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), + void (*on_all_keys_up)(void)) { + on_key_down_callback = on_key_down; + on_key_up_callback = on_key_up; + on_all_keys_up_callback = on_all_keys_up; +} + +static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { +#ifdef __SWITCH__ + while (Ship::Switch::IsRunning()) { +#else + while (is_running) { +#endif + run_one_game_iter(); + } +#ifdef __SWITCH__ + Ship::Switch::Exit(); +#endif + //Ship::ExecuteHooks(); + + SDL_Quit(); +} + +static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height) { + *width = window_width; + *height = window_height; +} + +static int translate_scancode(int scancode) { + if (scancode < 512) { + return inverted_scancode_table[scancode]; + } else { + return 0; + } +} + +static int untranslate_scancode(int translatedScancode) { + for (int i = 0; i < 512; i++) { + if (inverted_scancode_table[i] == translatedScancode) { + return i; + } + } + return 0; +} + +static void gfx_sdl_onkeydown(int scancode) { + int key = translate_scancode(scancode); + if (on_key_down_callback != NULL) { + on_key_down_callback(key); + } +} + +static void gfx_sdl_onkeyup(int scancode) { + int key = translate_scancode(scancode); + if (on_key_up_callback != NULL) { + on_key_up_callback(key); + } +} + +static void gfx_sdl_handle_events(void) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + //SohImGui::EventImpl event_impl; + //event_impl.sdl = { &event }; + //SohImGui::Update(event_impl); + switch (event.type) { +#ifndef TARGET_WEB + // Scancodes are broken in Emscripten SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3259 + case SDL_KEYDOWN: + gfx_sdl_onkeydown(event.key.keysym.scancode); + break; + case SDL_KEYUP: + gfx_sdl_onkeyup(event.key.keysym.scancode); + break; +#endif + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { +#ifdef __SWITCH__ + Ship::Switch::GetDisplaySize(&window_width, &window_height); +#else + SDL_GL_GetDrawableSize(wnd, &window_width, &window_height); +#endif + } else if (event.window.event == SDL_WINDOWEVENT_CLOSE && + event.window.windowID == SDL_GetWindowID(wnd)) { + // We listen specifically for main window close because closing main window + // on macOS does not trigger SDL_Quit. + is_running = false; + } + break; + case SDL_DROPFILE: + //CVar_SetString("gDroppedFile", event.drop.file); + //CVar_SetS32("gNewFileDropped", 1); + //CVar_Save(); + break; + case SDL_QUIT: + is_running = false; + break; + } + } +} + +static bool gfx_sdl_start_frame(void) { + return true; +} + +static uint64_t qpc_to_100ns(uint64_t qpc) { + const uint64_t qpc_freq = SDL_GetPerformanceFrequency(); + return qpc / qpc_freq * 10000000 + qpc % qpc_freq * 10000000 / qpc_freq; +} + +static inline void sync_framerate_with_timer(void) { + uint64_t t; + t = qpc_to_100ns(SDL_GetPerformanceCounter()); + + const int64_t next = previous_time + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR; + const int64_t left = next - t; + if (left > 0) { +#ifndef _WIN32 + const timespec spec = { 0, left * 100 }; + nanosleep(&spec, nullptr); +#else + // The accuracy of this timer seems to usually be within +- 1.0 ms + LARGE_INTEGER li; + li.QuadPart = -left; + SetWaitableTimer(timer, &li, 0, nullptr, nullptr, false); + WaitForSingleObject(timer, INFINITE); +#endif + } + + t = qpc_to_100ns(SDL_GetPerformanceCounter()); + if (left > 0 && t - next < 10000) { + // In case it takes some time for the application to wake up after sleep, + // or inaccurate timer, + // don't let that slow down the framerate. + t = next; + } + previous_time = t; +} + +static void gfx_sdl_swap_buffers_begin(void) { + sync_framerate_with_timer(); + SDL_GL_SwapWindow(wnd); +} + +static void gfx_sdl_swap_buffers_end(void) { +} + +static double gfx_sdl_get_time(void) { + return 0.0; +} + +static void gfx_sdl_set_target_fps(int fps) { + target_fps = fps; +} + +static void gfx_sdl_set_maximum_frame_latency(int latency) { + // Not supported by SDL :( +} + +static float gfx_sdl_get_detected_hz(void) { + return 0; +} + +static const char* gfx_sdl_get_key_name(int scancode) { + return SDL_GetScancodeName((SDL_Scancode)untranslate_scancode(scancode)); +} + +struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, + gfx_sdl_set_keyboard_callbacks, + gfx_sdl_set_fullscreen_changed_callback, + gfx_sdl_set_fullscreen, + gfx_sdl_show_cursor, + gfx_sdl_main_loop, + gfx_sdl_get_dimensions, + gfx_sdl_handle_events, + gfx_sdl_start_frame, + gfx_sdl_swap_buffers_begin, + gfx_sdl_swap_buffers_end, + gfx_sdl_get_time, + gfx_sdl_set_target_fps, + gfx_sdl_set_maximum_frame_latency, + gfx_sdl_get_detected_hz, + gfx_sdl_get_key_name }; + +#endif diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 58c83ecad..7ddec83eb 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -4,27 +4,24 @@ #include #include -// special value for window position that signifies centered position -#define WAPI_WIN_CENTERPOS 0xFFFFFFFF - -typedef bool (*kb_callback_t)(int code); - struct GfxWindowManagerAPI { - void (*init)(const char *window_title); -#ifndef TARGET_PORT_CONSOLE - void (*set_keyboard_callbacks)(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void)); -#endif -#ifdef TOUCH_CONTROLS - void (*set_touchscreen_callbacks)(void (*down)(void* event), void (*motion)(void* event), void (*up)(void* event)); -#endif + void (*init)(const char* game_name, bool start_in_fullscreen, uint32_t width, uint32_t height); + void (*set_keyboard_callbacks)(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), + void (*on_all_keys_up)(void)); + void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); + void (*set_fullscreen)(bool enable); + void (*show_cursor)(bool hide); void (*main_loop)(void (*run_one_game_iter)(void)); - void (*get_dimensions)(uint32_t *width, uint32_t *height); + void (*get_dimensions)(uint32_t* width, uint32_t* height); void (*handle_events)(void); bool (*start_frame)(void); void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug - void (*shutdown)(void); + void (*set_target_fps)(int fps); + void (*set_maximum_frame_latency)(int latency); + float (*get_detected_hz)(void); + const char* (*get_key_name)(int scancode); }; #endif diff --git a/src/pc/pc_main.c b/src/pc/pc_main.cpp similarity index 93% rename from src/pc/pc_main.c rename to src/pc/pc_main.cpp index 9d4d4e122..5da68f717 100644 --- a/src/pc/pc_main.c +++ b/src/pc/pc_main.cpp @@ -10,32 +10,42 @@ #include #endif +extern "C" { #include "sm64.h" #include "game/memory.h" #include "game/sound_init.h" #include "audio/external.h" +} #include "gfx/gfx_pc.h" +#if 0 +extern "C" { +#endif #include "gfx/gfx_opengl.h" -#include "gfx/gfx_direct3d11.h" -#include "gfx/gfx_direct3d12.h" -#include "gfx/gfx_dxgi.h" +//#include "gfx/gfx_direct3d11.h" +//#include "gfx/gfx_direct3d12.h" +//#include "gfx/gfx_dxgi.h" #include "gfx/gfx_sdl.h" -#include "gfx/gfx_gx2.h" -#include "gfx/gfx_3ds.h" -#include "gfx/gfx_citro3d.h" -#include "gfx/gfx_dummy.h" +//#include "gfx/gfx_gx2.h" +//#include "gfx/gfx_3ds.h" +//#include "gfx/gfx_citro3d.h" +//#include "gfx/gfx_dummy.h" +#if 0 +} +#endif -#include "audio/audio_api.h" +extern "C" { +//#include "audio/audio_api.h" #include "audio/audio_sdl.h" #include "audio/audio_null.h" -#include "audio/audio_3ds.h" -#include "audio/audio_3ds_threading.h" +//#include "audio/audio_3ds.h" +//#include "audio/audio_3ds_threading.h" #include "pc_main.h" + #ifdef COMMAND_LINE_OPTIONS #include "cliopts.h" #endif @@ -60,6 +70,7 @@ #ifdef DISCORDRPC #include "pc/discord/discordrpc.h" #endif +} OSMesg gMainReceivedMesg; OSMesgQueue gSIEventMesgQueue; @@ -75,9 +86,12 @@ static struct GfxWindowManagerAPI *wm_api; static struct GfxRenderingAPI *rendering_api; extern void gfx_run(Gfx *commands); + +extern "C" { extern void thread5_game_loop(void *arg); extern void create_next_audio_buffer(s16 *samples, u32 num_samples); void game_loop_one_iteration(void); +} void dispatch_audio_sptask(UNUSED struct SPTask *spTask) { } @@ -172,7 +186,7 @@ void game_deinit(void) { discord_shutdown(); #endif configfile_save(configfile_name()); -#if defined(TARGET_SWITCH) || !defined(TARGET_PORT_CONSOLE) +#if 0 controller_shutdown(); audio_shutdown(); gfx_shutdown(); @@ -181,12 +195,13 @@ void game_deinit(void) { } void game_exit(void) { -#if defined(TARGET_SWITCH) || !defined(TARGET_PORT_CONSOLE) - game_deinit(); -#ifndef TARGET_WEB +//#if defined(TARGET_SWITCH) || !defined(TARGET_PORT_CONSOLE) +// game_deinit(); +//#ifndef TARGET_WEB +// exit(0); +//#endif +//#endif exit(0); -#endif -#endif } #ifdef TARGET_WEB @@ -362,10 +377,10 @@ void main_func(void) { #endif char window_title[96] = - "Super Mario 64 EX (" RAPI_NAME ")" + "Super Mario 64 EX" ; - gfx_init(wm_api, rendering_api, window_title); + gfx_init(wm_api, rendering_api, window_title, 0, configWindow.w, configWindow.h); #ifndef TARGET_PORT_CONSOLE wm_api->set_keyboard_callbacks(keyboard_on_key_down, keyboard_on_key_up, keyboard_on_all_keys_up); #endif @@ -431,6 +446,7 @@ void main_func(void) { #endif } +/** #ifdef TARGET_PORT_CONSOLE int main(UNUSED int argc, UNUSED char *argv[]) { #ifdef TARGET_SWITCH @@ -453,3 +469,14 @@ int main(int argc, char *argv[]) { return 0; } #endif + */ + +extern "C" +{ + int main(int argc, char* argv[]); +} + +int main(int argc, char *argv[]) { + main_func(); + return 0; +} \ No newline at end of file From 578f38c22fc1a36e51cbbb6cb9e798d91e019b0d Mon Sep 17 00:00:00 2001 From: AloXado320 Date: Mon, 2 Dec 2024 21:28:40 -0500 Subject: [PATCH 2/6] Fix test build --- src/pc/controller/controller_sdl1.c | 4 ++-- src/pc/controller/controller_sdl2.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pc/controller/controller_sdl1.c b/src/pc/controller/controller_sdl1.c index 750d904ab..e0b1dc387 100644 --- a/src/pc/controller/controller_sdl1.c +++ b/src/pc/controller/controller_sdl1.c @@ -129,7 +129,7 @@ static void controller_sdl_init(void) { controller_sdl_bind(); init_ok = true; - mouse_init_ok = true; + //mouse_init_ok = true; } static inline void update_button(const int i, const bool new) { @@ -294,7 +294,7 @@ static void controller_sdl_shutdown(void) { } init_ok = false; - mouse_init_ok = false; + //mouse_init_ok = false; } struct ControllerAPI controller_sdl = { diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 6cd8f0835..08e21dbf5 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -127,7 +127,7 @@ static void controller_sdl_init(void) { controller_sdl_bind(); init_ok = true; - mouse_init_ok = true; + //mouse_init_ok = true; } static SDL_Haptic *controller_sdl_init_haptics(const int joy) { @@ -339,7 +339,7 @@ static void controller_sdl_shutdown(void) { haptics_enabled = false; init_ok = false; - mouse_init_ok = false; + //mouse_init_ok = false; } struct ControllerAPI controller_sdl = { From 5d0e41a5d581af206cde02e62b44b3f67aec67cb Mon Sep 17 00:00:00 2001 From: AloXado320 Date: Tue, 3 Dec 2024 21:37:33 -0500 Subject: [PATCH 3/6] Legacy defines, restore gfx_sdl2 --- src/pc/gfx/gfx_defines.h | 12 + src/pc/gfx/gfx_pc.cpp | 7 + src/pc/gfx/gfx_pc.h | 9 +- src/pc/gfx/gfx_rendering_api.h | 2 + src/pc/gfx/gfx_sdl2.c | 487 ++++++++++++++++++++++++++ src/pc/gfx/gfx_sdl2.cpp | 519 ---------------------------- src/pc/gfx/gfx_window_manager_api.h | 27 +- 7 files changed, 536 insertions(+), 527 deletions(-) create mode 100644 src/pc/gfx/gfx_defines.h create mode 100644 src/pc/gfx/gfx_sdl2.c delete mode 100644 src/pc/gfx/gfx_sdl2.cpp diff --git a/src/pc/gfx/gfx_defines.h b/src/pc/gfx/gfx_defines.h new file mode 100644 index 000000000..2edb752f0 --- /dev/null +++ b/src/pc/gfx/gfx_defines.h @@ -0,0 +1,12 @@ +#pragma once + +#define LEGACY_GFX_WIDOW_API + +//#define LEGACY_GFX_RENDER_API + +/* Essential, do not edit unless you know what are you doing */ + +// SoH uses ImGui to render using framebuffers +#define ENABLE_FRAMEBUFFER 1 +// Without the requisite above nothing renders, so make extra changes to render directly to the screen +#define RENDER_TO_SCREEN 1 diff --git a/src/pc/gfx/gfx_pc.cpp b/src/pc/gfx/gfx_pc.cpp index 1d794e20d..e4fd4b0fc 100644 --- a/src/pc/gfx/gfx_pc.cpp +++ b/src/pc/gfx/gfx_pc.cpp @@ -2808,7 +2808,12 @@ void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, co bool start_in_fullscreen, uint32_t width, uint32_t height) { gfx_wapi = wapi; gfx_rapi = rapi; +#ifdef LEGACY_GFX_WIDOW_API + gfx_wapi->init(game_name); +#else gfx_wapi->init(game_name, start_in_fullscreen, width, height); +#endif + gfx_rapi->init(); #if ENABLE_FRAMEBUFFER gfx_rapi->update_framebuffer_parameters(0, width, height, 1, false, true, true, true); @@ -2987,6 +2992,7 @@ void gfx_end_frame(void) { } } +#ifndef LEGACY_GFX_WIDOW_API void gfx_set_target_fps(int fps) { gfx_wapi->set_target_fps(fps); } @@ -2998,6 +3004,7 @@ void gfx_set_maximum_frame_latency(int latency) { float gfx_get_detected_hz(void) { return gfx_wapi->get_detected_hz(); } +#endif int gfx_create_framebuffer(uint32_t width, uint32_t height) { uint32_t orig_width = width, orig_height = height; diff --git a/src/pc/gfx/gfx_pc.h b/src/pc/gfx/gfx_pc.h index 4008467ce..46bb26028 100644 --- a/src/pc/gfx/gfx_pc.h +++ b/src/pc/gfx/gfx_pc.h @@ -25,10 +25,7 @@ typedef union { #define SCREEN_WIDTH 320 #define SCREEN_HEIGHT 240 -// SoH uses ImGui to render using framebuffers -#define ENABLE_FRAMEBUFFER 1 -// Without the requisite above nothing renders, so make extra changes to render directly to the screen -#define RENDER_TO_SCREEN 1 +#include "gfx_defines.h" extern uintptr_t gfxFramebuffer; @@ -99,10 +96,12 @@ struct GfxRenderingAPI* gfx_get_current_rendering_api(void); void gfx_start_frame(void); //void gfx_run(Gfx* commands, const std::unordered_map& mtx_replacements); void gfx_run(Gfx* commands); -void gfx_end_frame(void); +void gfx_end_frame(void); +#ifndef LEGACY_GFX_WIDOW_API void gfx_set_target_fps(int); void gfx_set_maximum_frame_latency(int latency); float gfx_get_detected_hz(void); +#endif void gfx_texture_cache_clear(); #ifdef __cplusplus extern "C" int gfx_create_framebuffer(uint32_t width, uint32_t height); diff --git a/src/pc/gfx/gfx_rendering_api.h b/src/pc/gfx/gfx_rendering_api.h index 74e6d979d..28fecc21e 100644 --- a/src/pc/gfx/gfx_rendering_api.h +++ b/src/pc/gfx/gfx_rendering_api.h @@ -9,6 +9,8 @@ #include #include +#include "gfx_defines.h" + struct ShaderProgram; struct GfxClipParameters { diff --git a/src/pc/gfx/gfx_sdl2.c b/src/pc/gfx/gfx_sdl2.c new file mode 100644 index 000000000..7614b5c7b --- /dev/null +++ b/src/pc/gfx/gfx_sdl2.c @@ -0,0 +1,487 @@ +#ifdef WAPI_SDL2 + +#ifdef __MINGW32__ +#define FOR_WINDOWS 1 +#else +#define FOR_WINDOWS 0 +#endif + +#if FOR_WINDOWS +unsigned int timeBeginPeriod(unsigned int uPeriod); +unsigned int timeEndPeriod(unsigned int uPeriod); +unsigned long GetPriorityClass(void* hProcess); +unsigned long SetPriorityClass(void* hProcess, unsigned long dwPriorityClass); +void Sleep(unsigned long ms); +#define GLEW_STATIC +#include +#include +#define GL_GLEXT_PROTOTYPES 1 +#include +#else +#include +#define GL_GLEXT_PROTOTYPES 1 + +#ifdef OSX_BUILD +#include +#else +#include +#endif + +#endif // End of OS-Specific GL defines + +#include +#include +#include + +#include "gfx_window_manager_api.h" +#include "gfx_screen_config.h" +#include "../pc_main.h" +#include "../configfile.h" + +#ifdef COMMAND_LINE_OPTIONS +#include "../cliopts.h" +#endif + +#include "src/pc/controller/controller_keyboard.h" +#include "src/pc/controller/controller_touchscreen.h" + +#ifdef TARGET_SWITCH +#include +#endif +// PAL framerate things +#ifdef VERSION_EU +#define FRAMERATE 25 +#else +#define FRAMERATE 30 +#endif + +// Only used on console targets +#ifdef HIGH_FPS_PC +#define SWAP_FRAME 1 +#else +#define SWAP_FRAME 2 +#endif + +static SDL_Window *wnd; +static SDL_GLContext ctx = NULL; +static int inverted_scancode_table[512]; + +static kb_callback_t kb_key_down = NULL; +static kb_callback_t kb_key_up = NULL; +static void (*kb_all_keys_up)(void) = NULL; +static void (*touch_down_callback)(void* event); +static void (*touch_motion_callback)(void* event); +static void (*touch_up_callback)(void* event); + +// time between consequtive game frames, in perf counter ticks +static double frame_rate = 0.0; // set in init() +// time in which a frame began, in perf counter ticks +static double frame_time = 0.0; // updated in start_frame() +// GetPerformanceFrequency +static double perf_freq = 0.0; + +const SDL_Scancode windows_scancode_table[] = { + /* 0 1 2 3 4 5 6 7 */ + /* 8 9 A B C D E F */ + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_ESCAPE, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_3, SDL_SCANCODE_4, SDL_SCANCODE_5, SDL_SCANCODE_6, /* 0 */ + SDL_SCANCODE_7, SDL_SCANCODE_8, SDL_SCANCODE_9, SDL_SCANCODE_0, SDL_SCANCODE_MINUS, SDL_SCANCODE_EQUALS, SDL_SCANCODE_BACKSPACE, SDL_SCANCODE_TAB, /* 0 */ + + SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_E, SDL_SCANCODE_R, SDL_SCANCODE_T, SDL_SCANCODE_Y, SDL_SCANCODE_U, SDL_SCANCODE_I, /* 1 */ + SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_LEFTBRACKET, SDL_SCANCODE_RIGHTBRACKET, SDL_SCANCODE_RETURN, SDL_SCANCODE_LCTRL, SDL_SCANCODE_A, SDL_SCANCODE_S, /* 1 */ + + SDL_SCANCODE_D, SDL_SCANCODE_F, SDL_SCANCODE_G, SDL_SCANCODE_H, SDL_SCANCODE_J, SDL_SCANCODE_K, SDL_SCANCODE_L, SDL_SCANCODE_SEMICOLON, /* 2 */ + SDL_SCANCODE_APOSTROPHE, SDL_SCANCODE_GRAVE, SDL_SCANCODE_LSHIFT, SDL_SCANCODE_BACKSLASH, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_C, SDL_SCANCODE_V, /* 2 */ + + SDL_SCANCODE_B, SDL_SCANCODE_N, SDL_SCANCODE_M, SDL_SCANCODE_COMMA, SDL_SCANCODE_PERIOD, SDL_SCANCODE_SLASH, SDL_SCANCODE_RSHIFT, SDL_SCANCODE_PRINTSCREEN,/* 3 */ + SDL_SCANCODE_LALT, SDL_SCANCODE_SPACE, SDL_SCANCODE_CAPSLOCK, SDL_SCANCODE_F1, SDL_SCANCODE_F2, SDL_SCANCODE_F3, SDL_SCANCODE_F4, SDL_SCANCODE_F5, /* 3 */ + + SDL_SCANCODE_F6, SDL_SCANCODE_F7, SDL_SCANCODE_F8, SDL_SCANCODE_F9, SDL_SCANCODE_F10, SDL_SCANCODE_NUMLOCKCLEAR, SDL_SCANCODE_SCROLLLOCK, SDL_SCANCODE_HOME, /* 4 */ + SDL_SCANCODE_UP, SDL_SCANCODE_PAGEUP, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_LEFT, SDL_SCANCODE_KP_5, SDL_SCANCODE_RIGHT, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_END, /* 4 */ + + SDL_SCANCODE_DOWN, SDL_SCANCODE_PAGEDOWN, SDL_SCANCODE_INSERT, SDL_SCANCODE_DELETE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_NONUSBACKSLASH, SDL_SCANCODE_F11, /* 5 */ + SDL_SCANCODE_F12, SDL_SCANCODE_PAUSE, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_LGUI, SDL_SCANCODE_RGUI, SDL_SCANCODE_APPLICATION, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 5 */ + + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16, /* 6 */ + SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 6 */ + + SDL_SCANCODE_INTERNATIONAL2, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL1, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, /* 7 */ + SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL4, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL5, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_INTERNATIONAL3, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN /* 7 */ +}; + +const SDL_Scancode scancode_rmapping_extended[][2] = { + {SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RETURN}, + {SDL_SCANCODE_RALT, SDL_SCANCODE_LALT}, + {SDL_SCANCODE_RCTRL, SDL_SCANCODE_LCTRL}, + {SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_SLASH}, + //{SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_CAPSLOCK} +}; + +const SDL_Scancode scancode_rmapping_nonextended[][2] = { + {SDL_SCANCODE_KP_7, SDL_SCANCODE_HOME}, + {SDL_SCANCODE_KP_8, SDL_SCANCODE_UP}, + {SDL_SCANCODE_KP_9, SDL_SCANCODE_PAGEUP}, + {SDL_SCANCODE_KP_4, SDL_SCANCODE_LEFT}, + {SDL_SCANCODE_KP_6, SDL_SCANCODE_RIGHT}, + {SDL_SCANCODE_KP_1, SDL_SCANCODE_END}, + {SDL_SCANCODE_KP_2, SDL_SCANCODE_DOWN}, + {SDL_SCANCODE_KP_3, SDL_SCANCODE_PAGEDOWN}, + {SDL_SCANCODE_KP_0, SDL_SCANCODE_INSERT}, + {SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_DELETE}, + {SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN} +}; + +static void gfx_sdl_set_framerate(void) { +#ifdef TARGET_PORT_CONSOLE + SDL_GL_SetSwapInterval(SWAP_FRAME); +#else + SDL_GL_SetSwapInterval(configWindow.vsync); +#endif +} + +#define IS_FULLSCREEN() ((SDL_GetWindowFlags(wnd) & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0) + +static void gfx_sdl_set_fullscreen(void) { + if (configWindow.reset) + configWindow.fullscreen = false; + if (configWindow.fullscreen == IS_FULLSCREEN()) + return; + if (configWindow.fullscreen) { + SDL_SetWindowFullscreen(wnd, SDL_WINDOW_FULLSCREEN_DESKTOP); + SDL_ShowCursor(SDL_DISABLE); + } else { + SDL_SetWindowFullscreen(wnd, 0); + SDL_ShowCursor(SDL_ENABLE); + configWindow.exiting_fullscreen = true; + } +} + +static void gfx_sdl_reset_dimension_and_pos(void) { + if (configWindow.exiting_fullscreen) + configWindow.exiting_fullscreen = false; + + if (configWindow.reset) { + configWindow.x = WAPI_WIN_CENTERPOS; + configWindow.y = WAPI_WIN_CENTERPOS; + configWindow.w = DESIRED_SCREEN_WIDTH; + configWindow.h = DESIRED_SCREEN_HEIGHT; + configWindow.reset = false; + } else if (!configWindow.settings_changed) { + return; + } + + int xpos = (configWindow.x == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.x; + int ypos = (configWindow.y == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.y; + + SDL_SetWindowSize(wnd, configWindow.w, configWindow.h); + SDL_SetWindowPosition(wnd, xpos, ypos); + // in case vsync changed + gfx_sdl_set_framerate(); +} + +static void gfx_sdl_init(const char *window_title) { +#if FOR_WINDOWS + if (GetPriorityClass((void*)-1) == 0x20) SetPriorityClass((void*)-1, 0x8000); + timeBeginPeriod(1); +#endif + SDL_Init(SDL_INIT_VIDEO); + + #ifdef __ANDROID__ + SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight"); + #endif + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + // SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); + + #ifdef USE_GLES + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); // These attributes allow for hardware acceleration on RPis. + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + #endif + + //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + //SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + + #ifdef TARGET_SWITCH + configWindow.fullscreen = false; + // if docked, set 1920x1080 + if (appletGetOperationMode() == 1) { + configWindow.w = 1920; + configWindow.h = 1080; + } else { + configWindow.w = 1280; + configWindow.h = 720; + } + int xpos = 0; + int ypos = 0; + #else + int xpos = (configWindow.x == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.x; + int ypos = (configWindow.y == WAPI_WIN_CENTERPOS) ? SDL_WINDOWPOS_CENTERED : configWindow.y; + #endif + + wnd = SDL_CreateWindow( + window_title, + xpos, ypos, configWindow.w, configWindow.h, + SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN + #ifndef TARGET_PORT_CONSOLE + | SDL_WINDOW_RESIZABLE + #endif + ); + ctx = SDL_GL_CreateContext(wnd); + + gfx_sdl_set_framerate(); + + gfx_sdl_set_fullscreen(); + + perf_freq = SDL_GetPerformanceFrequency(); + + #ifdef HIGH_FPS_PC + frame_rate = perf_freq / (2 * FRAMERATE); + #else + frame_rate = perf_freq / FRAMERATE; + #endif + + frame_time = SDL_GetPerformanceCounter(); + + for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { + inverted_scancode_table[windows_scancode_table[i]] = i; + } + + for (size_t i = 0; i < sizeof(scancode_rmapping_extended) / sizeof(scancode_rmapping_extended[0]); i++) { + inverted_scancode_table[scancode_rmapping_extended[i][0]] = inverted_scancode_table[scancode_rmapping_extended[i][1]] + 0x100; + } + + for (size_t i = 0; i < sizeof(scancode_rmapping_nonextended) / sizeof(scancode_rmapping_nonextended[0]); i++) { + inverted_scancode_table[scancode_rmapping_nonextended[i][0]] = inverted_scancode_table[scancode_rmapping_nonextended[i][1]]; + inverted_scancode_table[scancode_rmapping_nonextended[i][1]] += 0x100; + } +} + +static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { + run_one_game_iter(); +} + +static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) { + int w, h; + SDL_GetWindowSize(wnd, &w, &h); + if (width) *width = w; + if (height) *height = h; +} + +static int translate_scancode(int scancode) { + if (scancode < 512) { + return inverted_scancode_table[scancode]; + } else { + return 0; + } +} + +static void gfx_sdl_onkeydown(int scancode) { + if (kb_key_down) + kb_key_down(translate_scancode(scancode)); + + const Uint8 *state = SDL_GetKeyboardState(NULL); + + if (state[SDL_SCANCODE_LALT] && state[SDL_SCANCODE_RETURN]) { + configWindow.fullscreen = !configWindow.fullscreen; + configWindow.settings_changed = true; + } +} + +static void gfx_sdl_onkeyup(int scancode) { + if (kb_key_up) + kb_key_up(translate_scancode(scancode)); +} + +#ifdef TOUCH_CONTROLS +static void gfx_sdl_fingerdown(SDL_TouchFingerEvent sdl_event) { + struct TouchEvent event; + event.x = sdl_event.x; + event.y = sdl_event.y; + event.touchID = sdl_event.fingerId + 1; + if (touch_down_callback != NULL) { + touch_down_callback((void*)&event); + } +} + +static void gfx_sdl_fingermotion(SDL_TouchFingerEvent sdl_event) { + struct TouchEvent event; + event.x = sdl_event.x; + event.y = sdl_event.y; + event.touchID = sdl_event.fingerId + 1; + if (touch_motion_callback != NULL) { + touch_motion_callback((void*)&event); + } +} + +static void gfx_sdl_fingerup(SDL_TouchFingerEvent sdl_event) { + struct TouchEvent event; + event.x = sdl_event.x; + event.y = sdl_event.y; + event.touchID = sdl_event.fingerId + 1; + if (touch_up_callback != NULL) { + touch_up_callback((void*)&event); + } +} +#endif + +static void gfx_sdl_handle_events(void) { + SDL_Event event; + while (SDL_PollEvent(&event)) { + switch (event.type) { +#ifndef TARGET_WEB + // Scancodes are broken in Emscripten SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3259 + case SDL_KEYDOWN: + gfx_sdl_onkeydown(event.key.keysym.scancode); + break; + case SDL_KEYUP: + gfx_sdl_onkeyup(event.key.keysym.scancode); + break; +#endif +#ifdef TOUCH_CONTROLS + case SDL_FINGERDOWN: + gfx_sdl_fingerdown(event.tfinger); + break; + case SDL_FINGERMOTION: + gfx_sdl_fingermotion(event.tfinger); + break; + case SDL_FINGERUP: + gfx_sdl_fingerup(event.tfinger); + break; +#endif + case SDL_WINDOWEVENT: // TODO: Check if this makes sense to be included in the Web build + if (!IS_FULLSCREEN()) { + switch (event.window.event) { + case SDL_WINDOWEVENT_MOVED: + if (!configWindow.exiting_fullscreen) { + if (event.window.data1 >= 0) configWindow.x = event.window.data1; + if (event.window.data2 >= 0) configWindow.y = event.window.data2; + } + break; + case SDL_WINDOWEVENT_SIZE_CHANGED: + configWindow.w = event.window.data1; + configWindow.h = event.window.data2; + break; + } + } + break; + case SDL_QUIT: + game_exit(); + break; + } + } + + if (configWindow.settings_changed) { + gfx_sdl_set_fullscreen(); + gfx_sdl_reset_dimension_and_pos(); + configWindow.settings_changed = false; + } +} + +#ifndef TARGET_PORT_CONSOLE +static void gfx_sdl_set_keyboard_callbacks(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void)) { + kb_key_down = on_key_down; + kb_key_up = on_key_up; + kb_all_keys_up = on_all_keys_up; +} +#endif + +#ifdef TOUCH_CONTROLS +static void gfx_sdl_set_touchscreen_callbacks(void (*down)(void* event), void (*motion)(void* event), void (*up)(void* event)) { + touch_down_callback = down; + touch_motion_callback = motion; + touch_up_callback = up; +} +#endif + +static bool gfx_sdl_start_frame(void) { + return true; +} + +#if FOR_WINDOWS +static inline void sync_framerate_with_timer(void) { + // calculate how long it took for the frame to render + double now = SDL_GetPerformanceCounter(); + double frame_length = now - frame_time; + + // Only sleep if we have time to spare + while (frame_length < frame_rate) { + double remain = (frame_rate - frame_length) / perf_freq * 1000.0; + if (remain > 2.0) // > 2ms + Sleep(1); // sleep for ~1ms + else + Sleep(0); // yield thread's time-slice (does not actually sleep) + + now = SDL_GetPerformanceCounter(); + frame_length = now - frame_time; + } + frame_time = now; +} +#else +static inline void sync_framerate_with_timer(void) { + // calculate how long it took for the frame to render + const double now = SDL_GetPerformanceCounter(); + const double frame_length = now - frame_time; + + if (frame_length < frame_rate) { + // Only sleep if we have time to spare + const double remain = frame_rate - frame_length; + // Sleep remaining time away (convert remain to microseconds) + usleep(remain / perf_freq * 1000000.0); + // Assume we slept the required amount of time to keep the timer stable + frame_time = now + remain; + } else { + frame_time = now; + } +} +#endif + +static void gfx_sdl_swap_buffers_begin(void) { + // Swap after we finished rendering, only if this frame wasn't dropped. + // This will wait for vblank if vsync is enabled and then update our window with our render. + SDL_GL_SwapWindow(wnd); +} + +static void gfx_sdl_swap_buffers_end(void) { + // The game isn't always going to run at a consistent rate, + // with frame pacing going up and down depending on hardware performance. + // Sleep off any remaining time to make the main loop iteration be called at a consistent frane rate. + // We do this after our swap, because it actually will take the time to swap into account. + sync_framerate_with_timer(); +} + +static double gfx_sdl_get_time(void) { + return 0.0; +} + +static void gfx_sdl_shutdown(void) { + if (SDL_WasInit(0)) { + if (ctx) { SDL_GL_DeleteContext(ctx); ctx = NULL; } + if (wnd) { SDL_DestroyWindow(wnd); wnd = NULL; } + SDL_Quit(); + } +#if FOR_WINDOWS + timeEndPeriod(1); +#endif +} + +struct GfxWindowManagerAPI gfx_sdl = { + gfx_sdl_init, +#ifndef TARGET_PORT_CONSOLE + gfx_sdl_set_keyboard_callbacks, +#endif +#ifdef TOUCH_CONTROLS + gfx_sdl_set_touchscreen_callbacks, +#endif + gfx_sdl_main_loop, + gfx_sdl_get_dimensions, + gfx_sdl_handle_events, + gfx_sdl_start_frame, + gfx_sdl_swap_buffers_begin, + gfx_sdl_swap_buffers_end, + gfx_sdl_get_time, + gfx_sdl_shutdown +}; + +#endif // BACKEND_WM diff --git a/src/pc/gfx/gfx_sdl2.cpp b/src/pc/gfx/gfx_sdl2.cpp deleted file mode 100644 index 2ffa14871..000000000 --- a/src/pc/gfx/gfx_sdl2.cpp +++ /dev/null @@ -1,519 +0,0 @@ -#include - -#if 1 - -#ifdef __MINGW32__ -#define FOR_WINDOWS 1 -#else -#define FOR_WINDOWS 0 -#endif - -#if FOR_WINDOWS -#include -#define GL_GLEXT_PROTOTYPES 1 -#include -#include "SDL2/SDL_opengl.h" -#elif __APPLE__ -#include -#elif __SWITCH__ -#include -#include -#include -#include "SwitchImpl.h" -#else -#include -#define GL_GLEXT_PROTOTYPES 1 -#include -#endif - -//#include "menu/ImGuiImpl.h" -//#include "misc/Cvar.h" -//#include "misc/Hooks.h" - -#include "gfx_window_manager_api.h" -#include "gfx_screen_config.h" -#ifdef _WIN32 -#include -#endif - -#define GFX_API_NAME "SDL2 - OpenGL" - -static SDL_Window* wnd; -static SDL_GLContext ctx; -static int inverted_scancode_table[512]; -static int vsync_enabled = 0; -static int window_width = DESIRED_SCREEN_WIDTH; -static int window_height = DESIRED_SCREEN_HEIGHT; -static bool fullscreen_state; -static bool is_running = true; -static void (*on_fullscreen_changed_callback)(bool is_now_fullscreen); -static bool (*on_key_down_callback)(int scancode); -static bool (*on_key_up_callback)(int scancode); -static void (*on_all_keys_up_callback)(void); - -const SDL_Scancode windows_scancode_table[] = { - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_ESCAPE, - SDL_SCANCODE_1, - SDL_SCANCODE_2, - SDL_SCANCODE_3, - SDL_SCANCODE_4, - SDL_SCANCODE_5, - SDL_SCANCODE_6, /* 0 */ - SDL_SCANCODE_7, - SDL_SCANCODE_8, - SDL_SCANCODE_9, - SDL_SCANCODE_0, - SDL_SCANCODE_MINUS, - SDL_SCANCODE_EQUALS, - SDL_SCANCODE_BACKSPACE, - SDL_SCANCODE_TAB, /* 0 */ - - SDL_SCANCODE_Q, - SDL_SCANCODE_W, - SDL_SCANCODE_E, - SDL_SCANCODE_R, - SDL_SCANCODE_T, - SDL_SCANCODE_Y, - SDL_SCANCODE_U, - SDL_SCANCODE_I, /* 1 */ - SDL_SCANCODE_O, - SDL_SCANCODE_P, - SDL_SCANCODE_LEFTBRACKET, - SDL_SCANCODE_RIGHTBRACKET, - SDL_SCANCODE_RETURN, - SDL_SCANCODE_LCTRL, - SDL_SCANCODE_A, - SDL_SCANCODE_S, /* 1 */ - - SDL_SCANCODE_D, - SDL_SCANCODE_F, - SDL_SCANCODE_G, - SDL_SCANCODE_H, - SDL_SCANCODE_J, - SDL_SCANCODE_K, - SDL_SCANCODE_L, - SDL_SCANCODE_SEMICOLON, /* 2 */ - SDL_SCANCODE_APOSTROPHE, - SDL_SCANCODE_GRAVE, - SDL_SCANCODE_LSHIFT, - SDL_SCANCODE_BACKSLASH, - SDL_SCANCODE_Z, - SDL_SCANCODE_X, - SDL_SCANCODE_C, - SDL_SCANCODE_V, /* 2 */ - - SDL_SCANCODE_B, - SDL_SCANCODE_N, - SDL_SCANCODE_M, - SDL_SCANCODE_COMMA, - SDL_SCANCODE_PERIOD, - SDL_SCANCODE_SLASH, - SDL_SCANCODE_RSHIFT, - SDL_SCANCODE_PRINTSCREEN, /* 3 */ - SDL_SCANCODE_LALT, - SDL_SCANCODE_SPACE, - SDL_SCANCODE_CAPSLOCK, - SDL_SCANCODE_F1, - SDL_SCANCODE_F2, - SDL_SCANCODE_F3, - SDL_SCANCODE_F4, - SDL_SCANCODE_F5, /* 3 */ - - SDL_SCANCODE_F6, - SDL_SCANCODE_F7, - SDL_SCANCODE_F8, - SDL_SCANCODE_F9, - SDL_SCANCODE_F10, - SDL_SCANCODE_NUMLOCKCLEAR, - SDL_SCANCODE_SCROLLLOCK, - SDL_SCANCODE_HOME, /* 4 */ - SDL_SCANCODE_UP, - SDL_SCANCODE_PAGEUP, - SDL_SCANCODE_KP_MINUS, - SDL_SCANCODE_LEFT, - SDL_SCANCODE_KP_5, - SDL_SCANCODE_RIGHT, - SDL_SCANCODE_KP_PLUS, - SDL_SCANCODE_END, /* 4 */ - - SDL_SCANCODE_DOWN, - SDL_SCANCODE_PAGEDOWN, - SDL_SCANCODE_INSERT, - SDL_SCANCODE_DELETE, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_NONUSBACKSLASH, - SDL_SCANCODE_F11, /* 5 */ - SDL_SCANCODE_F12, - SDL_SCANCODE_PAUSE, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_LGUI, - SDL_SCANCODE_RGUI, - SDL_SCANCODE_APPLICATION, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, /* 5 */ - - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_F13, - SDL_SCANCODE_F14, - SDL_SCANCODE_F15, - SDL_SCANCODE_F16, /* 6 */ - SDL_SCANCODE_F17, - SDL_SCANCODE_F18, - SDL_SCANCODE_F19, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, /* 6 */ - - SDL_SCANCODE_INTERNATIONAL2, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_INTERNATIONAL1, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN, /* 7 */ - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_INTERNATIONAL4, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_INTERNATIONAL5, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_INTERNATIONAL3, - SDL_SCANCODE_UNKNOWN, - SDL_SCANCODE_UNKNOWN /* 7 */ -}; - -const SDL_Scancode scancode_rmapping_extended[][2] = { - { SDL_SCANCODE_KP_ENTER, SDL_SCANCODE_RETURN }, - { SDL_SCANCODE_RALT, SDL_SCANCODE_LALT }, - { SDL_SCANCODE_RCTRL, SDL_SCANCODE_LCTRL }, - { SDL_SCANCODE_KP_DIVIDE, SDL_SCANCODE_SLASH }, - //{SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_CAPSLOCK} -}; - -const SDL_Scancode scancode_rmapping_nonextended[][2] = { { SDL_SCANCODE_KP_7, SDL_SCANCODE_HOME }, - { SDL_SCANCODE_KP_8, SDL_SCANCODE_UP }, - { SDL_SCANCODE_KP_9, SDL_SCANCODE_PAGEUP }, - { SDL_SCANCODE_KP_4, SDL_SCANCODE_LEFT }, - { SDL_SCANCODE_KP_6, SDL_SCANCODE_RIGHT }, - { SDL_SCANCODE_KP_1, SDL_SCANCODE_END }, - { SDL_SCANCODE_KP_2, SDL_SCANCODE_DOWN }, - { SDL_SCANCODE_KP_3, SDL_SCANCODE_PAGEDOWN }, - { SDL_SCANCODE_KP_0, SDL_SCANCODE_INSERT }, - { SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_DELETE }, - { SDL_SCANCODE_KP_MULTIPLY, SDL_SCANCODE_PRINTSCREEN } }; - -static void set_fullscreen(bool on, bool call_callback) { - if (fullscreen_state == on) { - return; - } - fullscreen_state = on; - - if (on) { - SDL_DisplayMode mode; - SDL_GetDesktopDisplayMode(0, &mode); - window_width = mode.w; - window_height = mode.h; - SDL_ShowCursor(false); - } else { - window_width = DESIRED_SCREEN_WIDTH; - window_height = DESIRED_SCREEN_HEIGHT; - } - SDL_SetWindowSize(wnd, window_width, window_height); - SDL_SetWindowFullscreen(wnd, on ? SDL_WINDOW_FULLSCREEN : 0); - SDL_SetCursor(SDL_DISABLE); - - if (on_fullscreen_changed_callback != NULL && call_callback) { - on_fullscreen_changed_callback(on); - } -} - -static uint64_t previous_time; -#ifdef _WIN32 -static HANDLE timer; -#endif - -static int target_fps = 30; - -#define FRAME_INTERVAL_US_NUMERATOR 1000000 -#define FRAME_INTERVAL_US_DENOMINATOR (target_fps) - -static void gfx_sdl_init(const char* game_name, bool start_in_fullscreen, uint32_t width, uint32_t height) { - SDL_Init(SDL_INIT_VIDEO); - - SDL_EventState(SDL_DROPFILE, SDL_ENABLE); - - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - -#if defined(__APPLE__) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); -#elif defined(__SWITCH__) - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); -#endif - -#ifdef _WIN32 - timer = CreateWaitableTimer(nullptr, false, nullptr); -#endif - - char title[512]; - int len = sprintf(title, "%s (%s)", game_name, GFX_API_NAME); - -#ifdef __SWITCH__ - // For Switch we need to set the window width before creating the window - Ship::Switch::GetDisplaySize(&window_width, &window_height); - width = window_width; - height = window_height; -#endif - - wnd = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, - SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); - -#ifndef __SWITCH__ - SDL_GL_GetDrawableSize(wnd, &window_width, &window_height); - - if (start_in_fullscreen) { - set_fullscreen(true, false); - } -#endif - - ctx = SDL_GL_CreateContext(wnd); - -#ifdef __SWITCH__ - if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) { - printf("Failed to initialize glad\n"); - } -#endif - - SDL_GL_MakeCurrent(wnd, ctx); - SDL_GL_SetSwapInterval(1); - - //SohImGui::WindowImpl window_impl; - //window_impl.backend = SohImGui::Backend::SDL; - //window_impl.sdl = { wnd, ctx }; - //SohImGui::Init(window_impl); - - for (size_t i = 0; i < sizeof(windows_scancode_table) / sizeof(SDL_Scancode); i++) { - inverted_scancode_table[windows_scancode_table[i]] = i; - } - - for (size_t i = 0; i < sizeof(scancode_rmapping_extended) / sizeof(scancode_rmapping_extended[0]); i++) { - inverted_scancode_table[scancode_rmapping_extended[i][0]] = - inverted_scancode_table[scancode_rmapping_extended[i][1]] + 0x100; - } - - for (size_t i = 0; i < sizeof(scancode_rmapping_nonextended) / sizeof(scancode_rmapping_nonextended[0]); i++) { - inverted_scancode_table[scancode_rmapping_nonextended[i][0]] = - inverted_scancode_table[scancode_rmapping_nonextended[i][1]]; - inverted_scancode_table[scancode_rmapping_nonextended[i][1]] += 0x100; - } -} - -static void gfx_sdl_set_fullscreen_changed_callback(void (*on_fullscreen_changed)(bool is_now_fullscreen)) { - on_fullscreen_changed_callback = on_fullscreen_changed; -} - -static void gfx_sdl_set_fullscreen(bool enable) { - set_fullscreen(enable, true); -} - -static void gfx_sdl_show_cursor(bool hide) { - SDL_ShowCursor(hide); -} - -static void gfx_sdl_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), - void (*on_all_keys_up)(void)) { - on_key_down_callback = on_key_down; - on_key_up_callback = on_key_up; - on_all_keys_up_callback = on_all_keys_up; -} - -static void gfx_sdl_main_loop(void (*run_one_game_iter)(void)) { -#ifdef __SWITCH__ - while (Ship::Switch::IsRunning()) { -#else - while (is_running) { -#endif - run_one_game_iter(); - } -#ifdef __SWITCH__ - Ship::Switch::Exit(); -#endif - //Ship::ExecuteHooks(); - - SDL_Quit(); -} - -static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height) { - *width = window_width; - *height = window_height; -} - -static int translate_scancode(int scancode) { - if (scancode < 512) { - return inverted_scancode_table[scancode]; - } else { - return 0; - } -} - -static int untranslate_scancode(int translatedScancode) { - for (int i = 0; i < 512; i++) { - if (inverted_scancode_table[i] == translatedScancode) { - return i; - } - } - return 0; -} - -static void gfx_sdl_onkeydown(int scancode) { - int key = translate_scancode(scancode); - if (on_key_down_callback != NULL) { - on_key_down_callback(key); - } -} - -static void gfx_sdl_onkeyup(int scancode) { - int key = translate_scancode(scancode); - if (on_key_up_callback != NULL) { - on_key_up_callback(key); - } -} - -static void gfx_sdl_handle_events(void) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - //SohImGui::EventImpl event_impl; - //event_impl.sdl = { &event }; - //SohImGui::Update(event_impl); - switch (event.type) { -#ifndef TARGET_WEB - // Scancodes are broken in Emscripten SDL2: https://bugzilla.libsdl.org/show_bug.cgi?id=3259 - case SDL_KEYDOWN: - gfx_sdl_onkeydown(event.key.keysym.scancode); - break; - case SDL_KEYUP: - gfx_sdl_onkeyup(event.key.keysym.scancode); - break; -#endif - case SDL_WINDOWEVENT: - if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { -#ifdef __SWITCH__ - Ship::Switch::GetDisplaySize(&window_width, &window_height); -#else - SDL_GL_GetDrawableSize(wnd, &window_width, &window_height); -#endif - } else if (event.window.event == SDL_WINDOWEVENT_CLOSE && - event.window.windowID == SDL_GetWindowID(wnd)) { - // We listen specifically for main window close because closing main window - // on macOS does not trigger SDL_Quit. - is_running = false; - } - break; - case SDL_DROPFILE: - //CVar_SetString("gDroppedFile", event.drop.file); - //CVar_SetS32("gNewFileDropped", 1); - //CVar_Save(); - break; - case SDL_QUIT: - is_running = false; - break; - } - } -} - -static bool gfx_sdl_start_frame(void) { - return true; -} - -static uint64_t qpc_to_100ns(uint64_t qpc) { - const uint64_t qpc_freq = SDL_GetPerformanceFrequency(); - return qpc / qpc_freq * 10000000 + qpc % qpc_freq * 10000000 / qpc_freq; -} - -static inline void sync_framerate_with_timer(void) { - uint64_t t; - t = qpc_to_100ns(SDL_GetPerformanceCounter()); - - const int64_t next = previous_time + 10 * FRAME_INTERVAL_US_NUMERATOR / FRAME_INTERVAL_US_DENOMINATOR; - const int64_t left = next - t; - if (left > 0) { -#ifndef _WIN32 - const timespec spec = { 0, left * 100 }; - nanosleep(&spec, nullptr); -#else - // The accuracy of this timer seems to usually be within +- 1.0 ms - LARGE_INTEGER li; - li.QuadPart = -left; - SetWaitableTimer(timer, &li, 0, nullptr, nullptr, false); - WaitForSingleObject(timer, INFINITE); -#endif - } - - t = qpc_to_100ns(SDL_GetPerformanceCounter()); - if (left > 0 && t - next < 10000) { - // In case it takes some time for the application to wake up after sleep, - // or inaccurate timer, - // don't let that slow down the framerate. - t = next; - } - previous_time = t; -} - -static void gfx_sdl_swap_buffers_begin(void) { - sync_framerate_with_timer(); - SDL_GL_SwapWindow(wnd); -} - -static void gfx_sdl_swap_buffers_end(void) { -} - -static double gfx_sdl_get_time(void) { - return 0.0; -} - -static void gfx_sdl_set_target_fps(int fps) { - target_fps = fps; -} - -static void gfx_sdl_set_maximum_frame_latency(int latency) { - // Not supported by SDL :( -} - -static float gfx_sdl_get_detected_hz(void) { - return 0; -} - -static const char* gfx_sdl_get_key_name(int scancode) { - return SDL_GetScancodeName((SDL_Scancode)untranslate_scancode(scancode)); -} - -struct GfxWindowManagerAPI gfx_sdl = { gfx_sdl_init, - gfx_sdl_set_keyboard_callbacks, - gfx_sdl_set_fullscreen_changed_callback, - gfx_sdl_set_fullscreen, - gfx_sdl_show_cursor, - gfx_sdl_main_loop, - gfx_sdl_get_dimensions, - gfx_sdl_handle_events, - gfx_sdl_start_frame, - gfx_sdl_swap_buffers_begin, - gfx_sdl_swap_buffers_end, - gfx_sdl_get_time, - gfx_sdl_set_target_fps, - gfx_sdl_set_maximum_frame_latency, - gfx_sdl_get_detected_hz, - gfx_sdl_get_key_name }; - -#endif diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 7ddec83eb..3491ce6d5 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -4,13 +4,30 @@ #include #include +#include "gfx_defines.h" + +// special value for window position that signifies centered position +#define WAPI_WIN_CENTERPOS 0xFFFFFFFF + +typedef bool (*kb_callback_t)(int code); // scancode + struct GfxWindowManagerAPI { +#ifdef LEGACY_GFX_WIDOW_API + void (*init)(const char *window_title); +#else void (*init)(const char* game_name, bool start_in_fullscreen, uint32_t width, uint32_t height); +#endif +#ifdef LEGACY_GFX_WIDOW_API // pretty sure these are the same +#ifndef TARGET_PORT_CONSOLE + void (*set_keyboard_callbacks)(kb_callback_t on_key_down, kb_callback_t on_key_up, void (*on_all_keys_up)(void)); +#endif +#else void (*set_keyboard_callbacks)(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), void (*on_all_keys_up)(void)); - void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); - void (*set_fullscreen)(bool enable); - void (*show_cursor)(bool hide); +#endif + //void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); // config fullscreen replaces it + //void (*set_fullscreen)(bool enable); // config fullscreen replaces it + //void (*show_cursor)(bool hide); // controller_mouse.c takes care of it void (*main_loop)(void (*run_one_game_iter)(void)); void (*get_dimensions)(uint32_t* width, uint32_t* height); void (*handle_events)(void); @@ -18,10 +35,14 @@ struct GfxWindowManagerAPI { void (*swap_buffers_begin)(void); void (*swap_buffers_end)(void); double (*get_time)(void); // For debug +#ifndef LEGACY_GFX_WIDOW_API // TODO: Maybe limit this to non TARGET_PORT_CONSOLE? void (*set_target_fps)(int fps); void (*set_maximum_frame_latency)(int latency); float (*get_detected_hz)(void); const char* (*get_key_name)(int scancode); +#else + void (*shutdown)(void); // modern render doesn't have this, closes window but game still runs +#endif }; #endif From e0f28862f80de484ff33251d24444e342e706912 Mon Sep 17 00:00:00 2001 From: AloXado320 Date: Wed, 4 Dec 2024 02:23:24 -0500 Subject: [PATCH 4/6] Remove unmerged changes from LUS PRs i can't find --- src/pc/gfx/gbi_pc.h | 21 ++----- src/pc/gfx/gfx_opengl.cpp | 7 --- src/pc/gfx/gfx_pc.cpp | 101 ++++----------------------------- src/pc/gfx/gfx_rendering_api.h | 1 - 4 files changed, 16 insertions(+), 114 deletions(-) diff --git a/src/pc/gfx/gbi_pc.h b/src/pc/gfx/gbi_pc.h index 790da65cc..dcea6946f 100644 --- a/src/pc/gfx/gbi_pc.h +++ b/src/pc/gfx/gbi_pc.h @@ -3,17 +3,18 @@ #include "PR/gbi.h" // CUSTOM OTR COMMANDS +// ALONOTE: Not used standalone #define G_SETTIMG_OTR 0x20 -#define G_SETFB 0x21 -#define G_RESETFB 0x22 -#define G_SETTIMG_FB 0x23 #define G_DL_OTR 0x31 #define G_VTX_OTR 0x32 #define G_MARKER 0x33 -#define G_INVALTEXCACHE 0x34 #define G_BRANCH_Z_OTR 0x35 #define G_MTX_OTR 0x36 -#define G_SETOVERRIDECOLOR 0x41 + +#define G_SETFB 0x21 +#define G_RESETFB 0x22 +#define G_SETTIMG_FB 0x23 +#define G_INVALTEXCACHE 0x34 //#define G_TEXRECT_WIDE 0x37 //#define G_FILLWIDERECT 0x38 @@ -38,13 +39,3 @@ } #define gsDPSetGrayscaleColor(pkt, r, g, b, lerp) DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp) - -#define gDPSetOverrideColor(pkt, m, l, r, g, b, a) \ -_DW({ \ - Gfx *_g = (Gfx *)(pkt); \ - \ - _g->words.w0 = (_SHIFTL(G_SETOVERRIDECOLOR, 24, 8) | \ - _SHIFTL(m, 8, 8) | _SHIFTL(l, 0, 8)); \ - _g->words.w1 = (_SHIFTL(r, 24, 8) | _SHIFTL(g, 16, 8) | \ - _SHIFTL(b, 8, 8) | _SHIFTL(a, 0, 8)); \ -}) diff --git a/src/pc/gfx/gfx_opengl.cpp b/src/pc/gfx/gfx_opengl.cpp index c6ae73140..588afd0a9 100644 --- a/src/pc/gfx/gfx_opengl.cpp +++ b/src/pc/gfx/gfx_opengl.cpp @@ -1043,12 +1043,6 @@ FilteringMode gfx_opengl_get_texture_filter(void) { return current_filter_mode; } -void gfx_opengl_read_pixels(int fb, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t type, void* data) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[fb].fbo); - glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); - glBindFramebuffer(GL_FRAMEBUFFER, current_framebuffer); -} - struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_get_clip_parameters, gfx_opengl_unload_shader, @@ -1061,7 +1055,6 @@ struct GfxRenderingAPI gfx_opengl_api = { gfx_opengl_upload_texture, gfx_opengl_set_sampler_parameters, gfx_opengl_set_depth_test_and_mask, - gfx_opengl_read_pixels, gfx_opengl_set_zmode_decal, gfx_opengl_set_viewport, gfx_opengl_set_scissor, diff --git a/src/pc/gfx/gfx_pc.cpp b/src/pc/gfx/gfx_pc.cpp index e4fd4b0fc..3000f246d 100644 --- a/src/pc/gfx/gfx_pc.cpp +++ b/src/pc/gfx/gfx_pc.cpp @@ -14,6 +14,7 @@ #include #include +// ALONOTE: OTR and Imgui functionally was commented out to make this work standalone #include #include @@ -138,7 +139,8 @@ static struct RSP { struct LoadedVertex loaded_vertices[MAX_VERTICES + 4]; -// these were removed alongside G_RDPHALF commands in f3dv2, perhaps due to OOT incompatibility? +// ALONOTE: These were removed alongside G_RDPHALF commands in f3dv2, perhaps due to OOT incompatibility? +// Required to make HUD render, would it be possible to remove this while not breaking HUD? uint8_t saved_opcode; uint8_t saved_tile; uint16_t saved_uls, saved_ult; @@ -180,7 +182,7 @@ static struct RDP { bool grayscale; uint8_t prim_lod_fraction; - struct RGBA env_color, prim_color, fog_color, fill_color, grayscale_color, override_color; + struct RGBA env_color, prim_color, fog_color, fill_color, grayscale_color; struct XYWidthHeight viewport, scissor; bool viewport_or_scissor_changed; void* z_buf_address; @@ -941,7 +943,8 @@ static void gfx_matrix_mul(float res[4][4], const float a[4][4], const float b[4 static void gfx_sp_matrix(uint8_t parameters, const int32_t* addr) { float matrix[4][4]; -#if 0 +// ALOTODO: Used for interpolation, not really sure how to use it +#if 0 if (auto it = current_mtx_replacements->find((Mtx*)addr); it != current_mtx_replacements->end()) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -1252,7 +1255,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo bool texture_edge = (rdp.other_mode_l & CVG_X_ALPHA) == CVG_X_ALPHA; bool use_noise = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_DITHER; bool use_2cyc = (rdp.other_mode_h & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE; - // TODO Alo: This use_alpha check was taken from aglab2 fork, works in SM64, needs testing in OOT + // ALOTODO: This use_alpha check was taken from aglab2 fork, works in SM64, needs testing in OOT bool use_alpha = use_2cyc ? ((rdp.other_mode_l & (G_BL_A_MEM << 16)) == 0) : ((rdp.other_mode_l & (G_BL_A_MEM << 18)) == 0); bool alpha_threshold = (rdp.other_mode_l & (3U << G_MDSFT_ALPHACOMPARE)) == G_AC_THRESHOLD; bool invisible = @@ -1292,71 +1295,6 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo cc_id &= ~((0xfff << 16) | ((uint64_t)0xfff << 44)); } - if (rdp.override_color.a > 0) { - rdp.prim_color = rdp.override_color; - // rdp.fog_color = { 0 }; - // rdp.fill_color = { 0 }; - // rdp.env_color = { 0 }; - - uint64_t new_cc_id = (cc_id >> CC_SHADER_OPT_POS) << CC_SHADER_OPT_POS; - - new_cc_id &= ~((uint64_t)SHADER_OPT_FOG << CC_SHADER_OPT_POS); - use_fog = false; - - if (use_alpha) { - new_cc_id |= (uint64_t)SHADER_OPT_TEXTURE_EDGE << CC_SHADER_OPT_POS; - //new_cc_id &= ~((uint64_t)SHADER_OPT_NOISE << CC_SHADER_OPT_POS); //needed? - } - - //NOTE: temporary while experimenting with draw settings - for (int i = 0; i < 2; i++) { - uint32_t rgb_a = (cc_id >> (i * 28)) & 0xf; - uint32_t rgb_b = (cc_id >> (i * 28 + 4)) & 0xf; - uint32_t rgb_c = (cc_id >> (i * 28 + 8)) & 0x1f; - uint32_t rgb_d = (cc_id >> (i * 28 + 13)) & 7; - uint32_t alpha_a = (cc_id >> (i * 28 + 16)) & 7; - uint32_t alpha_b = (cc_id >> (i * 28 + 16 + 3)) & 7; - uint32_t alpha_c = (cc_id >> (i * 28 + 16 + 6)) & 7; - uint32_t alpha_d = (cc_id >> (i * 28 + 16 + 9)) & 7; - - //rgb_a = (rgb_a == G_CCMUX_TEXEL0 || rgb_a == G_CCMUX_TEXEL1 || - // rgb_a == G_CCMUX_TEXEL0_ALPHA || rgb_a == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_a; - //rgb_b = (rgb_b == G_CCMUX_TEXEL0 || rgb_b == G_CCMUX_TEXEL1 || - // rgb_b == G_CCMUX_TEXEL0_ALPHA || rgb_b == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_b; - //rgb_c = (rgb_c == G_CCMUX_TEXEL0 || rgb_c == G_CCMUX_TEXEL1 || - // rgb_c == G_CCMUX_TEXEL0_ALPHA || rgb_c == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_c; - //rgb_d = (rgb_d == G_CCMUX_TEXEL0 || rgb_d == G_CCMUX_TEXEL1 || - // rgb_d == G_CCMUX_TEXEL0_ALPHA || rgb_d == G_CCMUX_TEXEL1_ALPHA) ? G_CCMUX_0 : rgb_d; - - rgb_a = G_CCMUX_PRIMITIVE; - rgb_b = G_CCMUX_PRIMITIVE; - rgb_c = G_CCMUX_PRIMITIVE; - rgb_d = G_CCMUX_PRIMITIVE; - - //alpha_a = (alpha_a == G_ACMUX_TEXEL0 || alpha_a == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_a; - //alpha_b = (alpha_b == G_ACMUX_TEXEL0 || alpha_b == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_b; - //alpha_c = (alpha_c == G_ACMUX_TEXEL0 || alpha_c == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_c; - //alpha_d = (alpha_d == G_ACMUX_TEXEL0 || alpha_d == G_ACMUX_TEXEL1) ? G_ACMUX_0 : alpha_d; - - //alpha_a = G_ACMUX_PRIMITIVE; - //alpha_b = G_ACMUX_PRIMITIVE; - //alpha_c = G_ACMUX_PRIMITIVE; - //alpha_d = G_ACMUX_PRIMITIVE; - - new_cc_id |= (uint64_t)(rgb_a & 0xf) << (i * 28); - new_cc_id |= (uint64_t)(rgb_b & 0xf) << (i * 28 + 4); - new_cc_id |= (uint64_t)(rgb_c & 0x1f) << (i * 28 + 8); - new_cc_id |= (uint64_t)(rgb_d & 7) << (i * 28 + 13); - new_cc_id |= (uint64_t)(alpha_a & 7) << (i * 28 + 16); - new_cc_id |= (uint64_t)(alpha_b & 7) << (i * 28 + 16 + 3); - new_cc_id |= (uint64_t)(alpha_c & 7) << (i * 28 + 16 + 6); - new_cc_id |= (uint64_t)(alpha_d & 7) << (i * 28 + 16 + 9); - } - - - cc_id = new_cc_id; - } - ColorCombiner* comb = gfx_lookup_or_create_color_combiner(cc_id); uint32_t tm = 0; @@ -1571,7 +1509,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo break; } case G_CCMUX_LOD_FRACTION: { - // Todo Alo: Doesn't work, stays at Bowser + // ALOTODO: Some times it doesn't work depending of Window API? Still a hack nonetheless if (rdp.other_mode_h & G_TL_LOD) { // "Hack" that works for Bowser - Peach painting float distance_frac = (v1->w - 3000.0f) / 3000.0f; @@ -1981,13 +1919,6 @@ static void gfx_dp_set_fog_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { rdp.fog_color.a = a; } -static void gfx_dp_set_override_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { - rdp.override_color.r = r; - rdp.override_color.g = g; - rdp.override_color.b = b; - rdp.override_color.a = a; -} - static void gfx_dp_set_fill_color(uint32_t packed_color) { uint16_t col16 = (uint16_t)packed_color; uint32_t r = col16 >> 11; @@ -2555,7 +2486,7 @@ static void gfx_run_dl(Gfx* cmd) { gfx_sp_set_other_mode(C0(8, 8) + 32, C0(0, 8), (uint64_t)cmd->words.w1 << 32); #endif break; -// G_RDPHALF_CONT, G_RDPHALF_2 and G_RDPHALF_1 were removed from f3dv2, perhaps due to OOT incompatibility? +// ALOTODO: G_RDPHALF_CONT, G_RDPHALF_2 and G_RDPHALF_1 were removed from f3dv2, perhaps due to OOT incompatibility?. See struct comment above #ifdef F3D_OLD case (uint8_t)G_RDPHALF_2: #else @@ -2719,9 +2650,6 @@ static void gfx_run_dl(Gfx* cmd) { case G_SETENVCOLOR: gfx_dp_set_env_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); break; - case G_SETOVERRIDECOLOR: - gfx_dp_set_override_color(C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); - break; case G_SETPRIMCOLOR: gfx_dp_set_prim_color(C0(8, 8), C0(0, 8), C1(24, 8), C1(16, 8), C1(8, 8), C1(0, 8)); break; @@ -2742,6 +2670,7 @@ static void gfx_run_dl(Gfx* cmd) { break; // G_SETPRIMCOLOR, G_CCMUX_PRIMITIVE, G_ACMUX_PRIMITIVE, is used by Goddard // G_CCMUX_TEXEL1, LOD_FRACTION is used in Bowser room 1 + // ALOTOO: Fix this up to match LUS without breaking HUD case G_TEXRECT: case G_TEXRECTFLIP: { rsp.saved_opcode = opcode; @@ -2933,7 +2862,6 @@ void gfx_run(Gfx* commands) { return; } dropped_frame = false; - rdp.override_color.a = 0; //if (!has_drawn_imgui_menu) { // SohImGui::DrawMainMenuAndCalculateGameSize(); @@ -3057,12 +2985,3 @@ uint16_t gfx_get_pixel_depth(float x, float y) { return get_pixel_depth_cached.find(make_pair(x, y))->second; } - -void gfx_get_fb_dimensions(int fb, uint32_t *width, uint32_t *height) { - *width = framebuffers[fb].applied_width; - *height = framebuffers[fb].applied_height; -} - -void gfx_read_pixels(int fb, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t type, void* data) { - return gfx_rapi->read_pixels(fb, x, y, width, height, type, data); -} diff --git a/src/pc/gfx/gfx_rendering_api.h b/src/pc/gfx/gfx_rendering_api.h index 28fecc21e..cd04d711f 100644 --- a/src/pc/gfx/gfx_rendering_api.h +++ b/src/pc/gfx/gfx_rendering_api.h @@ -43,7 +43,6 @@ struct GfxRenderingAPI { void (*upload_texture)(const uint8_t* rgba32_buf, uint32_t width, uint32_t height); void (*set_sampler_parameters)(int sampler, bool linear_filter, uint32_t cms, uint32_t cmt); void (*set_depth_test_and_mask)(bool depth_test, bool z_upd); - void (*read_pixels)(int fb, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t type, void* data); void (*set_zmode_decal)(bool zmode_decal); void (*set_viewport)(int x, int y, int width, int height); void (*set_scissor)(int x, int y, int width, int height); From 264314d0239fa7eb8a399a8d976117f7b7e45127 Mon Sep 17 00:00:00 2001 From: AloXado320 Date: Wed, 4 Dec 2024 19:10:59 -0500 Subject: [PATCH 5/6] Fix framebuffer rendering, clear comments about it --- src/pc/gfx/gfx_defines.h | 6 +++--- src/pc/gfx/gfx_pc.cpp | 28 ++++++++++++++++------------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/pc/gfx/gfx_defines.h b/src/pc/gfx/gfx_defines.h index 2edb752f0..5a8e6a49b 100644 --- a/src/pc/gfx/gfx_defines.h +++ b/src/pc/gfx/gfx_defines.h @@ -6,7 +6,7 @@ /* Essential, do not edit unless you know what are you doing */ -// SoH uses ImGui to render using framebuffers +// SoH uses ImGui to set window dimensions, which are separate to render dimensions +// so options like resolution scale and msaa can work properly +// For now we set these the same if framebuffers are enabled to simply render on screen independently #define ENABLE_FRAMEBUFFER 1 -// Without the requisite above nothing renders, so make extra changes to render directly to the screen -#define RENDER_TO_SCREEN 1 diff --git a/src/pc/gfx/gfx_pc.cpp b/src/pc/gfx/gfx_pc.cpp index 3000f246d..f8fd41df9 100644 --- a/src/pc/gfx/gfx_pc.cpp +++ b/src/pc/gfx/gfx_pc.cpp @@ -2747,16 +2747,13 @@ void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, co #if ENABLE_FRAMEBUFFER gfx_rapi->update_framebuffer_parameters(0, width, height, 1, false, true, true, true); #endif -#if defined(__APPLE__) || 1 - gfx_current_dimensions.internal_mul = 1; -#else - gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); -#endif -#if 1 - gfx_msaa_level = 1; -#else - gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); -#endif +//#if defined(__APPLE__) + gfx_current_dimensions.internal_mul = 1; // ALOTODO: This does nothing +//#else + //gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); +//#endif + //gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); + gfx_msaa_level = 1; // ALONOTE: MSAA works if ENABLE_FRAMEBUFFER is set #ifndef __WIIU__ // Wii U overrides dimentions in gfx_wapi->init to match framebuffer size gfx_current_dimensions.width = width; gfx_current_dimensions.height = height; @@ -2788,11 +2785,18 @@ struct GfxRenderingAPI* gfx_get_current_rendering_api(void) { void gfx_start_frame(void) { gfx_wapi->handle_events(); -#if ENABLE_FRAMEBUFFER && !RENDER_TO_SCREEN +#if ENABLE_FRAMEBUFFER gfx_wapi->get_dimensions(&gfx_current_window_dimensions.width, &gfx_current_window_dimensions.height); + + // ALONOTE: These needs to be properly separated to make it work with resolution scale + // gfx_current_dimensions and gfx_current_game_window_viewport = Game dimensions + // gfx_current_window_dimensions = Window dimensions + gfx_current_dimensions.width = gfx_current_game_window_viewport.width = gfx_current_window_dimensions.width; + gfx_current_dimensions.height = gfx_current_game_window_viewport.height = gfx_current_window_dimensions.height; #else gfx_wapi->get_dimensions(&gfx_current_dimensions.width, &gfx_current_dimensions.height); #endif + //SohImGui::DrawMainMenuAndCalculateGameSize(); //has_drawn_imgui_menu = true; if (gfx_current_dimensions.height == 0) { @@ -2869,7 +2873,7 @@ void gfx_run(Gfx* commands) { //current_mtx_replacements = &mtx_replacements; -#if ENABLE_FRAMEBUFFER && RENDER_TO_SCREEN +#if !ENABLE_FRAMEBUFFER game_renders_to_framebuffer = false; #endif From 773509a7f457503d39ea9287716dcd12a0061d74 Mon Sep 17 00:00:00 2001 From: AloXado320 Date: Thu, 5 Dec 2024 00:22:10 -0500 Subject: [PATCH 6/6] Small updates, make DirectX 11 work --- src/pc/gfx/gbi_pc.h | 8 +- src/pc/gfx/gfx_direct3d11.cpp | 1463 +++++++++++++-------------- src/pc/gfx/gfx_direct3d_common.cpp | 296 ++++-- src/pc/gfx/gfx_dxgi.cpp | 38 +- src/pc/gfx/gfx_dxgi.h | 7 +- src/pc/gfx/gfx_opengl.cpp | 3 +- src/pc/gfx/gfx_pc.cpp | 91 +- src/pc/gfx/gfx_pc.h | 3 + src/pc/gfx/gfx_window_manager_api.h | 2 +- src/pc/pc_main.cpp | 4 +- 10 files changed, 962 insertions(+), 953 deletions(-) diff --git a/src/pc/gfx/gbi_pc.h b/src/pc/gfx/gbi_pc.h index dcea6946f..9cb93bc32 100644 --- a/src/pc/gfx/gbi_pc.h +++ b/src/pc/gfx/gbi_pc.h @@ -30,7 +30,7 @@ /* Private macro to wrap other macros in do {...} while (0) */ #define _DW(macro) do {macro} while (0) -#define gsSPGrayscale(pkt, state) \ +#define gSPGrayscale(pkt, state) \ { \ Gfx* _g = (Gfx*)(pkt); \ \ @@ -38,4 +38,8 @@ _g->words.w1 = state; \ } -#define gsDPSetGrayscaleColor(pkt, r, g, b, lerp) DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp) +#define gsSPGrayscale(state) \ + { (_SHIFTL(G_SETGRAYSCALE, 24, 8)), (state) } + +#define gDPSetGrayscaleColor(pkt, r, g, b, lerp) DPRGBColor(pkt, G_SETINTENSITY, r, g, b, lerp) +#define gsDPSetGrayscaleColor(r, g, b, a) sDPRGBColor(G_SETINTENSITY, r, g, b, a) diff --git a/src/pc/gfx/gfx_direct3d11.cpp b/src/pc/gfx/gfx_direct3d11.cpp index 44c102534..51565ca2e 100644 --- a/src/pc/gfx/gfx_direct3d11.cpp +++ b/src/pc/gfx/gfx_direct3d11.cpp @@ -1,9 +1,12 @@ -#if 0 +#ifdef RAPI_D3D11 #include #include #include +#include +#include + #include #include #include @@ -15,19 +18,21 @@ #ifndef _LANGUAGE_C #define _LANGUAGE_C #endif +//#include "libultraship/libultra/gbi.h" #include -#include "gfx_cc.h" #include "gfx_window_manager_api.h" -#include "gfx_rendering_api.h" #include "gfx_direct3d_common.h" #define DECLARE_GFX_DXGI_FUNCTIONS #include "gfx_dxgi.h" -#include "gfx_screen_config.h" -#include "gfx_pc.h" +//#include "gfx_screen_config.h" +//#include "menu/ImGuiImpl.h" +#include "gfx_cc.h" +#include "gfx_rendering_api.h" +#include "gfx_pc.h" #define DEBUG_D3D 0 using namespace Microsoft::WRL; // For ComPtr @@ -36,9 +41,8 @@ namespace { struct PerFrameCB { uint32_t noise_frame; - float noise_scale_x; - float noise_scale_y; - uint32_t padding; + float noise_scale; + uint32_t padding[2]; // constant buffers must be multiples of 16 bytes in size }; struct PerDrawCB { @@ -50,7 +54,12 @@ struct PerDrawCB { } textures[2]; }; +struct Coord { + int x, y; +}; + struct TextureData { + ComPtr texture; ComPtr resource_view; ComPtr sampler_state; uint32_t width; @@ -58,596 +67,175 @@ struct TextureData { bool linear_filtering; }; +struct Framebuffer { + ComPtr render_target_view; + ComPtr depth_stencil_view; + ComPtr depth_stencil_srv; + uint32_t texture_id; + bool has_depth_buffer; + uint32_t msaa_level; +}; + struct ShaderProgramD3D11 { ComPtr vertex_shader; ComPtr pixel_shader; ComPtr input_layout; ComPtr blend_state; - uint32_t shader_id; + uint64_t shader_id0; + uint32_t shader_id1; uint8_t num_inputs; uint8_t num_floats; bool used_textures[2]; }; -struct RenderTarget { - ComPtr texture; - ComPtr render_target_view; - ComPtr depth_stencil_view; - TextureData texture_data; - ComPtr cpu_readable_texture_copy; -}; - -struct PipelineState { - int32_t viewport_x, viewport_y, viewport_width, viewport_height; - int32_t scissor_x, scissor_y, scissor_width, scissor_height; - int8_t depth_test, depth_mask; - int8_t zmode_decal; - - D3D_PRIMITIVE_TOPOLOGY primitive_topology; - - struct ShaderProgramD3D11 *shader_program; - ComPtr blend_state; - - ComPtr resource_views[2]; - ComPtr sampler_states[2]; - - uint32_t vertex_buffer_stride = 0; -}; - static struct { HMODULE d3d11_module; PFN_D3D11_CREATE_DEVICE D3D11CreateDevice; - + HMODULE d3dcompiler_module; pD3DCompile D3DCompile; - + D3D_FEATURE_LEVEL feature_level; - + uint32_t msaa_num_quality_levels[D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT]; + ComPtr device; - ComPtr swap_chain; ComPtr context; ComPtr rasterizer_state; ComPtr depth_stencil_state; ComPtr vertex_buffer; ComPtr per_frame_cb; ComPtr per_draw_cb; - - RenderTarget backbuffer_rt; - RenderTarget main_rt; - RenderTarget framebuffer_rt; - - struct ShaderProgramD3D11 rt_shader_program; + ComPtr coord_buffer; + ComPtr coord_buffer_srv; + ComPtr depth_value_output_buffer; + ComPtr depth_value_output_buffer_copy; + ComPtr depth_value_output_uav; + ComPtr compute_shader; + ComPtr compute_shader_msaa; + ComPtr compute_shader_msaa_blob; + size_t coord_buffer_size; #if DEBUG_D3D ComPtr debug; #endif - DXGI_SAMPLE_DESC sample_description; - PerFrameCB per_frame_cb_data; PerDrawCB per_draw_cb_data; - struct ShaderProgramD3D11 shader_program_pool[64]; - uint8_t shader_program_pool_size; + std::map, struct ShaderProgramD3D11> shader_program_pool; std::vector textures; - - bool framebuffer_requested; - uint16_t *framebuffer_data; - - // Current values - int current_tile; uint32_t current_texture_ids[2]; - struct ShaderProgramD3D11 *current_shader_program; - - uint32_t current_width, current_height; + std::vector framebuffers; - int8_t current_depth_test, current_depth_mask; - int8_t current_zmode_decal; + // Current state - int32_t current_viewport_x, current_viewport_y, current_viewport_width, current_viewport_height; - int32_t current_scissor_x, current_scissor_y, current_scissor_width, current_scissor_height; + struct ShaderProgramD3D11* shader_program; - // State of the D3D pipeline (to prevent setting states needlessly) + // uint32_t current_width, current_height; + uint32_t render_target_height; + int current_framebuffer; + FilteringMode current_filter_mode = FILTER_NONE; - struct PipelineState state; + int8_t depth_test; + int8_t depth_mask; + int8_t zmode_decal; + // Previous states (to prevent setting states needlessly) + + struct ShaderProgramD3D11* last_shader_program = nullptr; + uint32_t last_vertex_buffer_stride = 0; + ComPtr last_blend_state = nullptr; + ComPtr last_resource_views[2] = { nullptr, nullptr }; + ComPtr last_sampler_states[2] = { nullptr, nullptr }; + int8_t last_depth_test = -1; + int8_t last_depth_mask = -1; + int8_t last_zmode_decal = -1; + D3D_PRIMITIVE_TOPOLOGY last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; } d3d; -static const char rt_shader[1024] = - "struct PSInput {\n" - " float4 position : SV_POSITION;\n" - " float2 uv : TEXCOORD;\n" - "};\n" - "Texture2D texture0 : register(t0);\n" - "SamplerState sampler0 : register(s0);\n" - "PSInput VSMain(float2 position : POSITION, float2 uv : TEXCOORD) {\n" - " PSInput result;\n" - " result.position = float4(position, 0, 1);\n" - " result.uv = uv;\n" - " return result;\n" - "}\n" - "float4 PSMain(PSInput input) : SV_TARGET {\n" - " return float4(texture0.Sample(sampler0, input.uv).rgb, 1);\n" - "}\n"; - static LARGE_INTEGER last_time, accumulated_time, frequency; -static ComPtr compile_shader(const char *vertex_shader, size_t vertex_shader_length, const char *fragment_shader, size_t fragment_shader_length, ShaderProgramD3D11 *shader_program) { - ComPtr vs, ps, error_blob; - -#if DEBUG_D3D - UINT compile_flags = D3DCOMPILE_DEBUG; -#else - UINT compile_flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; -#endif - - HRESULT hr = d3d.D3DCompile(vertex_shader, vertex_shader_length, nullptr, nullptr, nullptr, "VSMain", "vs_4_0_level_9_1", compile_flags, 0, vs.GetAddressOf(), error_blob.GetAddressOf()); - - if (FAILED(hr)) { - char *message = (char *) error_blob->GetBufferPointer(); - MessageBox(gfx_dxgi_get_h_wnd(), message, "Error", MB_OK | MB_ICONERROR); - throw hr; - } - - hr = d3d.D3DCompile(fragment_shader, fragment_shader_length, nullptr, nullptr, nullptr, "PSMain", "ps_4_0_level_9_1", compile_flags, 0, ps.GetAddressOf(), error_blob.GetAddressOf()); - - if (FAILED(hr)) { - char *message = (char *) error_blob->GetBufferPointer(); - MessageBox(gfx_dxgi_get_h_wnd(), message, "Error", MB_OK | MB_ICONERROR); - throw hr; - } - - ThrowIfFailed(d3d.device->CreateVertexShader(vs->GetBufferPointer(), vs->GetBufferSize(), nullptr, shader_program->vertex_shader.GetAddressOf())); - ThrowIfFailed(d3d.device->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), nullptr, shader_program->pixel_shader.GetAddressOf())); - - return vs; -} - -static void set_vertex_buffer(float buffer[], size_t buffer_length, uint32_t stride) { - D3D11_MAPPED_SUBRESOURCE ms; - ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); - - d3d.context->Map(d3d.vertex_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); - memcpy(ms.pData, buffer, buffer_length); - d3d.context->Unmap(d3d.vertex_buffer.Get(), 0); - - if (d3d.state.vertex_buffer_stride == stride) { - return; - } - - d3d.state.vertex_buffer_stride = stride; - - uint32_t offset = 0; - d3d.context->IASetVertexBuffers(0, 1, d3d.vertex_buffer.GetAddressOf(), &stride, &offset); -} - -static void set_viewport(int32_t x, int32_t y, int32_t width, int32_t height) { - if (d3d.state.viewport_x == x && d3d.state.viewport_y == y && d3d.state.viewport_width == width && d3d.state.viewport_height == height) { - return; - } - - d3d.state.viewport_x = x; - d3d.state.viewport_y = y; - d3d.state.viewport_width = width; - d3d.state.viewport_height = height; - - D3D11_VIEWPORT viewport; - viewport.TopLeftX = x; - viewport.TopLeftY = y; - viewport.Width = width; - viewport.Height = height; - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 1.0f; - - d3d.context->RSSetViewports(1, &viewport); -} - -static void set_scissor(int32_t x, int32_t y, int32_t width, int32_t height) { - if (d3d.state.scissor_x == x && d3d.state.scissor_y == y && d3d.state.scissor_width == width && d3d.state.scissor_height == height) { - return; - } - - d3d.state.scissor_x = x; - d3d.state.scissor_y = y; - d3d.state.scissor_width = width; - d3d.state.scissor_height = height; - - D3D11_RECT rect; - rect.left = x; - rect.top = y; - rect.right = width; - rect.bottom = height; - - d3d.context->RSSetScissorRects(1, &rect); -} - -static void set_depth_stencil_state(int8_t depth_test, int8_t depth_mask) { - if (d3d.state.depth_test == depth_test && d3d.state.depth_mask == depth_mask) { - return; - } - - d3d.state.depth_test = depth_test; - d3d.state.depth_mask = depth_mask; - - d3d.depth_stencil_state.Reset(); - - D3D11_DEPTH_STENCIL_DESC depth_stencil_desc; - ZeroMemory(&depth_stencil_desc, sizeof(D3D11_DEPTH_STENCIL_DESC)); - - depth_stencil_desc.DepthEnable = depth_test; - depth_stencil_desc.DepthWriteMask = depth_mask ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; - depth_stencil_desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL; - depth_stencil_desc.StencilEnable = false; - - ThrowIfFailed(d3d.device->CreateDepthStencilState(&depth_stencil_desc, d3d.depth_stencil_state.GetAddressOf()), - gfx_dxgi_get_h_wnd(), "Failed to create depth stencil state."); - - d3d.context->OMSetDepthStencilState(d3d.depth_stencil_state.Get(), 0); -} - -static void set_rasterizer_state(int8_t zmode_decal) { - if (d3d.state.zmode_decal == zmode_decal) { - return; - } - - d3d.state.zmode_decal = zmode_decal; - - d3d.rasterizer_state.Reset(); - - D3D11_RASTERIZER_DESC rasterizer_desc; - ZeroMemory(&rasterizer_desc, sizeof(D3D11_RASTERIZER_DESC)); - - rasterizer_desc.FillMode = D3D11_FILL_SOLID; - rasterizer_desc.CullMode = D3D11_CULL_NONE; - rasterizer_desc.FrontCounterClockwise = true; - rasterizer_desc.DepthBias = 0; - rasterizer_desc.SlopeScaledDepthBias = zmode_decal ? -2.0f : 0.0f; - rasterizer_desc.DepthBiasClamp = 0.0f; - rasterizer_desc.DepthClipEnable = true; - rasterizer_desc.ScissorEnable = true; - rasterizer_desc.MultisampleEnable = false; - rasterizer_desc.AntialiasedLineEnable = false; - - ThrowIfFailed(d3d.device->CreateRasterizerState(&rasterizer_desc, d3d.rasterizer_state.GetAddressOf()), - gfx_dxgi_get_h_wnd(), "Failed to create rasterizer state."); - - d3d.context->RSSetState(d3d.rasterizer_state.Get()); -} - -static void set_shader(ShaderProgramD3D11 *shader_program) { - if (d3d.state.shader_program == shader_program) { - return; - } - - d3d.state.shader_program = shader_program; - - d3d.context->IASetInputLayout(shader_program->input_layout.Get()); - d3d.context->VSSetShader(shader_program->vertex_shader.Get(), 0, 0); - d3d.context->PSSetShader(shader_program->pixel_shader.Get(), 0, 0); - - if (d3d.state.blend_state.Get() != shader_program->blend_state.Get()) { - d3d.state.blend_state = shader_program->blend_state.Get(); - d3d.context->OMSetBlendState(shader_program->blend_state.Get(), 0, 0xFFFFFFFF); - } -} - -static void set_primitive_topology(D3D_PRIMITIVE_TOPOLOGY primitive_topology) { - if (d3d.state.primitive_topology == primitive_topology) { - return; - } - - d3d.state.primitive_topology = primitive_topology; - - d3d.context->IASetPrimitiveTopology(primitive_topology); -} - -static void set_shader_resources(uint8_t slot, const TextureData *texture) { - if (texture == nullptr) { - d3d.state.resource_views[slot] = nullptr; - ID3D11ShaderResourceView *null_srv[1] = { nullptr }; - d3d.context->PSSetShaderResources(slot, 1, null_srv); - return; - } - - if (d3d.state.resource_views[slot].Get() == texture->resource_view.Get()) { - return; - } - - d3d.state.resource_views[slot] = texture->resource_view.Get(); - - d3d.context->PSSetShaderResources(slot, 1, texture->resource_view.GetAddressOf()); - -#ifdef THREE_POINT_FILTERING - d3d.per_draw_cb_data.textures[slot].width = texture->width; - d3d.per_draw_cb_data.textures[slot].height = texture->height; - d3d.per_draw_cb_data.textures[slot].linear_filtering = texture->linear_filtering; -#endif - - if (d3d.state.sampler_states[slot].Get() != texture->sampler_state.Get()) { - d3d.state.sampler_states[slot] = texture->sampler_state.Get(); - d3d.context->PSSetSamplers(slot, 1, texture->sampler_state.GetAddressOf()); - } -} - -static void create_render_target(uint32_t width, uint32_t height, bool has_depth_buffer, bool is_shader_resource, bool create_cpu_readable_copy, RenderTarget *render_target) { - uint32_t bind_flags = D3D11_BIND_RENDER_TARGET; - if (is_shader_resource) - bind_flags |= D3D11_BIND_SHADER_RESOURCE; - - // Create the texture +int gfx_d3d11_create_framebuffer(void); +static void create_depth_stencil_objects(uint32_t width, uint32_t height, uint32_t msaa_count, + ID3D11DepthStencilView** view, ID3D11ShaderResourceView** srv) { D3D11_TEXTURE2D_DESC texture_desc; - ZeroMemory(&texture_desc, sizeof(D3D11_TEXTURE2D_DESC)); - texture_desc.Width = width; texture_desc.Height = height; texture_desc.MipLevels = 1; texture_desc.ArraySize = 1; - texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - texture_desc.SampleDesc = d3d.sample_description; + texture_desc.Format = + d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ? DXGI_FORMAT_R32_TYPELESS : DXGI_FORMAT_R24G8_TYPELESS; + texture_desc.SampleDesc.Count = msaa_count; + texture_desc.SampleDesc.Quality = 0; texture_desc.Usage = D3D11_USAGE_DEFAULT; - texture_desc.BindFlags = bind_flags; + texture_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | (srv != nullptr ? D3D11_BIND_SHADER_RESOURCE : 0); texture_desc.CPUAccessFlags = 0; texture_desc.MiscFlags = 0; - ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, render_target->texture.GetAddressOf())); - - render_target->texture_data.width = width; - render_target->texture_data.height = height; - render_target->texture_data.linear_filtering = true; - - // Create the render target - - D3D11_RENDER_TARGET_VIEW_DESC rtv_desc; - ZeroMemory(&rtv_desc, sizeof(D3D11_RENDER_TARGET_VIEW_DESC)); - - rtv_desc.Format = texture_desc.Format; - rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - rtv_desc.Texture2D.MipSlice = 0; - - ThrowIfFailed(d3d.device->CreateRenderTargetView(render_target->texture.Get(), &rtv_desc, render_target->render_target_view.GetAddressOf())); - - // Create the shader resource view and sampler from the texture - - if (is_shader_resource) { - D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc; - ZeroMemory(&srv_desc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); - - srv_desc.Format = texture_desc.Format; - srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - srv_desc.Texture2D.MostDetailedMip = 0; - srv_desc.Texture2D.MipLevels = 1; - - ThrowIfFailed(d3d.device->CreateShaderResourceView(render_target->texture.Get(), &srv_desc, render_target->texture_data.resource_view.GetAddressOf())); - - D3D11_SAMPLER_DESC sampler_desc; - ZeroMemory(&sampler_desc, sizeof(D3D11_SAMPLER_DESC)); - - sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.MinLOD = 0; - sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; - - ThrowIfFailed(d3d.device->CreateSamplerState(&sampler_desc, render_target->texture_data.sampler_state.GetAddressOf())); - } else { - render_target->texture_data.resource_view = nullptr; - render_target->texture_data.sampler_state = nullptr; - } - - // Create depth buffer - - if (has_depth_buffer) { - D3D11_TEXTURE2D_DESC depth_stencil_texture_desc; - ZeroMemory(&depth_stencil_texture_desc, sizeof(D3D11_TEXTURE2D_DESC)); - - depth_stencil_texture_desc.Width = width; - depth_stencil_texture_desc.Height = height; - depth_stencil_texture_desc.MipLevels = 1; - depth_stencil_texture_desc.ArraySize = 1; - depth_stencil_texture_desc.Format = d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ? DXGI_FORMAT_D32_FLOAT : DXGI_FORMAT_D24_UNORM_S8_UINT; - depth_stencil_texture_desc.SampleDesc = d3d.sample_description; - depth_stencil_texture_desc.Usage = D3D11_USAGE_DEFAULT; - depth_stencil_texture_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; - depth_stencil_texture_desc.CPUAccessFlags = 0; - depth_stencil_texture_desc.MiscFlags = 0; - - ComPtr depth_stencil_texture; - ThrowIfFailed(d3d.device->CreateTexture2D(&depth_stencil_texture_desc, nullptr, depth_stencil_texture.GetAddressOf())); - ThrowIfFailed(d3d.device->CreateDepthStencilView(depth_stencil_texture.Get(), nullptr, render_target->depth_stencil_view.GetAddressOf())); - } else { - render_target->depth_stencil_view = nullptr; - } - - // Create a copy of the framebuffer to be able to read it from CPU - - if (create_cpu_readable_copy) { - texture_desc.Usage = D3D11_USAGE_STAGING; - texture_desc.BindFlags = 0; - texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, render_target->cpu_readable_texture_copy.GetAddressOf())); + ComPtr texture; + ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, texture.GetAddressOf())); + + D3D11_DEPTH_STENCIL_VIEW_DESC view_desc; + view_desc.Format = + d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ? DXGI_FORMAT_D32_FLOAT : DXGI_FORMAT_D24_UNORM_S8_UINT; + view_desc.Flags = 0; + if (msaa_count > 1) { + view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + view_desc.Texture2DMS.UnusedField_NothingToDefine = 0; } else { - render_target->cpu_readable_texture_copy = nullptr; - } -} - -static void draw_render_target(const RenderTarget *dst_render_target, const RenderTarget *src_render_target, bool clear_before_drawing) { - // Set render target - - d3d.context->OMSetRenderTargets(1, dst_render_target->render_target_view.GetAddressOf(), NULL); - - if (clear_before_drawing) { - const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - d3d.context->ClearRenderTargetView(dst_render_target->render_target_view.Get(), clearColor); + view_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + view_desc.Texture2D.MipSlice = 0; } - // Disable depth buffer - - set_depth_stencil_state(false, false); - set_rasterizer_state(false); - - // Viewport and Scissor - - set_viewport(0, 0, dst_render_target->texture_data.width, dst_render_target->texture_data.height); - set_scissor(0, 0, dst_render_target->texture_data.width, dst_render_target->texture_data.height); - - // Set vertex buffer data - - float rt_aspect = (float) dst_render_target->texture_data.width / (float) dst_render_target->texture_data.height; - float current_aspect = (float) d3d.current_width / (float) d3d.current_height; - float w = current_aspect / rt_aspect; - - float buf_vbo[] = { - -w, +1.0, 0.0, 0.0, - -w, -1.0, 0.0, 1.0, - +w, +1.0, 1.0, 0.0, - +w, -1.0, 1.0, 1.0 - }; - - uint32_t stride = 2 * 2 * sizeof(float); - set_vertex_buffer(buf_vbo, 4 * stride, stride); - - // Set shader stuff - - set_shader(&d3d.rt_shader_program); - set_shader_resources(0, &src_render_target->texture_data); - set_primitive_topology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - - d3d.context->Draw(4, 0); - - set_shader_resources(0, nullptr); -} - -static void initialize_framebuffer() { - create_render_target(FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, false, false, true, &d3d.framebuffer_rt); - - // Compile the shader used to draw the fullscreen quad with the render target - - struct ShaderProgramD3D11 *prg = &d3d.rt_shader_program; - size_t shader_length = strlen(rt_shader); - ComPtr vs = compile_shader(rt_shader, shader_length, rt_shader, shader_length, prg); - - // Create input layout - - D3D11_INPUT_ELEMENT_DESC ied[2]; - ied[0] = { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; - ied[1] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; - - ThrowIfFailed(d3d.device->CreateInputLayout(ied, 2, vs->GetBufferPointer(), vs->GetBufferSize(), prg->input_layout.GetAddressOf())); - - // Blend state - - D3D11_BLEND_DESC blend_desc; - ZeroMemory(&blend_desc, sizeof(D3D11_BLEND_DESC)); - - blend_desc.RenderTarget[0].BlendEnable = false; - blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; - - ThrowIfFailed(d3d.device->CreateBlendState(&blend_desc, prg->blend_state.GetAddressOf())); -} - -static void create_render_target_views(bool is_resize) { - DXGI_SWAP_CHAIN_DESC1 desc1; + ThrowIfFailed(d3d.device->CreateDepthStencilView(texture.Get(), &view_desc, view)); - if (is_resize) { - // Release previous stuff (if any) - - d3d.backbuffer_rt.texture.Reset(); - d3d.backbuffer_rt.render_target_view.Reset(); - - d3d.main_rt.texture.Reset(); - d3d.main_rt.render_target_view.Reset(); - d3d.main_rt.depth_stencil_view.Reset(); - d3d.main_rt.texture_data.resource_view.Reset(); - d3d.main_rt.texture_data.sampler_state.Reset(); - - // Resize swap chain buffers + if (srv != nullptr) { + D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc; + srv_desc.Format = + d3d.feature_level >= D3D_FEATURE_LEVEL_10_0 ? DXGI_FORMAT_R32_FLOAT : DXGI_FORMAT_R24_UNORM_X8_TYPELESS; + srv_desc.ViewDimension = msaa_count > 1 ? D3D11_SRV_DIMENSION_TEXTURE2DMS : D3D11_SRV_DIMENSION_TEXTURE2D; + srv_desc.Texture2D.MostDetailedMip = 0; + srv_desc.Texture2D.MipLevels = -1; - ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1)); - ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags), - gfx_dxgi_get_h_wnd(), "Failed to resize IDXGISwapChain buffers."); - } else { - // Only initialize the framebuffer the first time - initialize_framebuffer(); + ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), &srv_desc, srv)); } - - // Get new size - - ThrowIfFailed(d3d.swap_chain->GetDesc1(&desc1)); - - // Create back buffer - - ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *) d3d.backbuffer_rt.texture.GetAddressOf()), - gfx_dxgi_get_h_wnd(), "Failed to get backbuffer from IDXGISwapChain."); - - ThrowIfFailed(d3d.device->CreateRenderTargetView(d3d.backbuffer_rt.texture.Get(), nullptr, d3d.backbuffer_rt.render_target_view.GetAddressOf()), - gfx_dxgi_get_h_wnd(), "Failed to create render target view."); - - d3d.backbuffer_rt.texture_data.width = desc1.Width; - d3d.backbuffer_rt.texture_data.height = desc1.Height; - - // Create the main render target where contents will be rendered - - create_render_target(desc1.Width, desc1.Height, true, true, false, &d3d.main_rt); - - // Save resolution - - d3d.current_width = desc1.Width; - d3d.current_height = desc1.Height; } static void gfx_d3d11_init(void) { // Load d3d11.dll d3d.d3d11_module = LoadLibraryW(L"d3d11.dll"); if (d3d.d3d11_module == nullptr) { - ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()), gfx_dxgi_get_h_wnd(), "d3d11.dll could not be loaded"); + ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()), gfx_dxgi_get_h_wnd(), "d3d11.dll not found"); } d3d.D3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d.d3d11_module, "D3D11CreateDevice"); - // Load D3DCompiler_47.dll or D3DCompiler_43.dll + // Load D3DCompiler_47.dll d3d.d3dcompiler_module = LoadLibraryW(L"D3DCompiler_47.dll"); if (d3d.d3dcompiler_module == nullptr) { - d3d.d3dcompiler_module = LoadLibraryW(L"D3DCompiler_43.dll"); - if (d3d.d3dcompiler_module == nullptr) { - ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()), gfx_dxgi_get_h_wnd(), "D3DCompiler_47.dll or D3DCompiler_43.dll could not be loaded"); - } + ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()), gfx_dxgi_get_h_wnd(), "D3DCompiler_47.dll not found"); } d3d.D3DCompile = (pD3DCompile)GetProcAddress(d3d.d3dcompiler_module, "D3DCompile"); // Create D3D11 device - gfx_dxgi_create_factory_and_device(DEBUG_D3D, 11, [](IDXGIAdapter1 *adapter, bool test_only) { + gfx_dxgi_create_factory_and_device(DEBUG_D3D, 11, [](IDXGIAdapter1* adapter, bool test_only) { #if DEBUG_D3D UINT device_creation_flags = D3D11_CREATE_DEVICE_DEBUG; #else UINT device_creation_flags = 0; #endif - D3D_FEATURE_LEVEL FeatureLevels[] = { - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; + D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; - HRESULT res = d3d.D3D11CreateDevice( - adapter, - D3D_DRIVER_TYPE_UNKNOWN, // since we use a specific adapter - nullptr, - device_creation_flags, - FeatureLevels, - ARRAYSIZE(FeatureLevels), - D3D11_SDK_VERSION, - test_only ? nullptr : d3d.device.GetAddressOf(), - &d3d.feature_level, - test_only ? nullptr : d3d.context.GetAddressOf()); + HRESULT res = d3d.D3D11CreateDevice(adapter, + D3D_DRIVER_TYPE_UNKNOWN, // since we use a specific adapter + nullptr, device_creation_flags, FeatureLevels, ARRAYSIZE(FeatureLevels), + D3D11_SDK_VERSION, test_only ? nullptr : d3d.device.GetAddressOf(), + &d3d.feature_level, test_only ? nullptr : d3d.context.GetAddressOf()); if (test_only) { return SUCCEEDED(res); @@ -657,24 +245,47 @@ static void gfx_d3d11_init(void) { } }); - // Sample description to be used in several textures - - d3d.sample_description.Count = 1; - d3d.sample_description.Quality = 0; - // Create the swap chain - d3d.swap_chain = gfx_dxgi_create_swap_chain(d3d.device.Get()); + gfx_dxgi_create_swap_chain(d3d.device.Get(), []() { + d3d.framebuffers[0].render_target_view.Reset(); + d3d.textures[d3d.framebuffers[0].texture_id].texture.Reset(); + d3d.context->ClearState(); + d3d.context->Flush(); + + d3d.last_shader_program = nullptr; + d3d.last_vertex_buffer_stride = 0; + d3d.last_blend_state.Reset(); + d3d.last_resource_views[0].Reset(); + d3d.last_resource_views[1].Reset(); + d3d.last_sampler_states[0].Reset(); + d3d.last_sampler_states[1].Reset(); + d3d.last_depth_test = -1; + d3d.last_depth_mask = -1; + d3d.last_zmode_decal = -1; + d3d.last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; + }); // Create D3D Debug device if in debug mode #if DEBUG_D3D - ThrowIfFailed(d3d.device->QueryInterface(__uuidof(ID3D11Debug), (void **) d3d.debug.GetAddressOf()), + ThrowIfFailed(d3d.device->QueryInterface(__uuidof(ID3D11Debug), (void**)d3d.debug.GetAddressOf()), gfx_dxgi_get_h_wnd(), "Failed to get ID3D11Debug device."); #endif - // Create views + // Create the default framebuffer which represents the window + Framebuffer& fb = d3d.framebuffers[gfx_d3d11_create_framebuffer()]; + + // Check the size of the window + DXGI_SWAP_CHAIN_DESC1 swap_chain_desc; + ThrowIfFailed(gfx_dxgi_get_swap_chain()->GetDesc1(&swap_chain_desc)); + d3d.textures[fb.texture_id].width = swap_chain_desc.Width; + d3d.textures[fb.texture_id].height = swap_chain_desc.Height; + fb.msaa_level = 1; - create_render_target_views(false); + for (uint32_t sample_count = 1; sample_count <= D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT; sample_count++) { + ThrowIfFailed(d3d.device->CheckMultisampleQualityLevels(DXGI_FORMAT_R8G8B8A8_UNORM, sample_count, + &d3d.msaa_num_quality_levels[sample_count - 1])); + } // Create main vertex buffer @@ -682,7 +293,7 @@ static void gfx_d3d11_init(void) { ZeroMemory(&vertex_buffer_desc, sizeof(D3D11_BUFFER_DESC)); vertex_buffer_desc.Usage = D3D11_USAGE_DYNAMIC; - vertex_buffer_desc.ByteWidth = VERTEX_BUFFER_SIZE * sizeof(float); + vertex_buffer_desc.ByteWidth = 256 * 32 * 3 * sizeof(float); // Same as buf_vbo size in gfx_pc vertex_buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertex_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; vertex_buffer_desc.MiscFlags = 0; @@ -704,8 +315,6 @@ static void gfx_d3d11_init(void) { ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, nullptr, d3d.per_frame_cb.GetAddressOf()), gfx_dxgi_get_h_wnd(), "Failed to create per-frame constant buffer."); - d3d.context->PSSetConstantBuffers(0, 1, d3d.per_frame_cb.GetAddressOf()); - // Create per-draw constant buffer constant_buffer_desc.Usage = D3D11_USAGE_DYNAMIC; @@ -717,62 +326,179 @@ static void gfx_d3d11_init(void) { ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, nullptr, d3d.per_draw_cb.GetAddressOf()), gfx_dxgi_get_h_wnd(), "Failed to create per-draw constant buffer."); - d3d.context->PSSetConstantBuffers(1, 1, d3d.per_draw_cb.GetAddressOf()); + // Create compute shader that can be used to retrieve depth buffer values + + const char* shader_source = R"( +sampler my_sampler : register(s0); +Texture2D tex : register(t0); +StructuredBuffer coord : register(t1); +RWStructuredBuffer output : register(u0); - // Initialize pipeline state +[numthreads(1, 1, 1)] +void CSMain(uint3 DTid : SV_DispatchThreadID) { + output[DTid.x] = tex.Load(int3(coord[DTid.x], 0)); +} +)"; + + const char* shader_source_msaa = R"( +sampler my_sampler : register(s0); +Texture2DMS tex : register(t0); +StructuredBuffer coord : register(t1); +RWStructuredBuffer output : register(u0); - ZeroMemory(&d3d.state, sizeof(PipelineState)); - d3d.state.depth_test = -1; - d3d.state.depth_mask = -1; - d3d.state.zmode_decal = -1; +[numthreads(1, 1, 1)] +void CSMain(uint3 DTid : SV_DispatchThreadID) { + output[DTid.x] = tex.Load(coord[DTid.x], 0); } +)"; -static bool gfx_d3d11_z_is_from_0_to_1(void) { - return true; +#if DEBUG_D3D + UINT compile_flags = D3DCOMPILE_DEBUG; +#else + UINT compile_flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; +#endif + + ComPtr cs, error_blob; + HRESULT hr; + + hr = d3d.D3DCompile(shader_source, strlen(shader_source), nullptr, nullptr, nullptr, "CSMain", "cs_4_0", + compile_flags, 0, cs.GetAddressOf(), error_blob.GetAddressOf()); + + if (FAILED(hr)) { + char* err = (char*)error_blob->GetBufferPointer(); + MessageBoxA(gfx_dxgi_get_h_wnd(), err, "Error", MB_OK | MB_ICONERROR); + throw hr; + } + + ThrowIfFailed(d3d.device->CreateComputeShader(cs->GetBufferPointer(), cs->GetBufferSize(), nullptr, + d3d.compute_shader.GetAddressOf())); + + hr = d3d.D3DCompile(shader_source_msaa, strlen(shader_source_msaa), nullptr, nullptr, nullptr, "CSMain", "cs_4_1", + compile_flags, 0, d3d.compute_shader_msaa_blob.GetAddressOf(), + error_blob.ReleaseAndGetAddressOf()); + + if (FAILED(hr)) { + char* err = (char*)error_blob->GetBufferPointer(); + MessageBoxA(gfx_dxgi_get_h_wnd(), err, "Error", MB_OK | MB_ICONERROR); + throw hr; + } + + // Create ImGui + + //SohImGui::WindowImpl window_impl; + //window_impl.backend = SohImGui::Backend::DX11; + //window_impl.dx11 = { gfx_dxgi_get_h_wnd(), d3d.context.Get(), d3d.device.Get() }; + //SohImGui::Init(window_impl); +} + +static struct GfxClipParameters gfx_d3d11_get_clip_parameters(void) { + return { true, false }; } -static void gfx_d3d11_unload_shader(struct ShaderProgram *old_prg) { +static void gfx_d3d11_unload_shader(struct ShaderProgram* old_prg) { } -static void gfx_d3d11_load_shader(struct ShaderProgram *new_prg) { - d3d.current_shader_program = (struct ShaderProgramD3D11 *)new_prg; +static void gfx_d3d11_load_shader(struct ShaderProgram* new_prg) { + d3d.shader_program = (struct ShaderProgramD3D11*)new_prg; } -static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint32_t shader_id) { +static struct ShaderProgram* gfx_d3d11_create_and_load_new_shader(uint64_t shader_id0, uint32_t shader_id1) { CCFeatures cc_features; - gfx_cc_get_features(shader_id, &cc_features); + gfx_cc_get_features(shader_id0, shader_id1, &cc_features); char buf[4096]; size_t len, num_floats; -#ifdef THREE_POINT_FILTERING - bool three_point_filtering = true; + gfx_direct3d_common_build_shader(buf, len, num_floats, cc_features, false, + d3d.current_filter_mode == FILTER_THREE_POINT); + + ComPtr vs, ps; + ComPtr error_blob; + +#if DEBUG_D3D + UINT compile_flags = D3DCOMPILE_DEBUG; #else - bool three_point_filtering = false; + UINT compile_flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; #endif - gfx_direct3d_common_build_shader(buf, len, num_floats, cc_features, false, three_point_filtering); + HRESULT hr = d3d.D3DCompile(buf, len, nullptr, nullptr, nullptr, "VSMain", "vs_4_0", compile_flags, 0, + vs.GetAddressOf(), error_blob.GetAddressOf()); + + if (FAILED(hr)) { + char* err = (char*)error_blob->GetBufferPointer(); + MessageBoxA(gfx_dxgi_get_h_wnd(), err, "Error", MB_OK | MB_ICONERROR); + throw hr; + } + + hr = d3d.D3DCompile(buf, len, nullptr, nullptr, nullptr, "PSMain", "ps_4_0", compile_flags, 0, ps.GetAddressOf(), + error_blob.GetAddressOf()); + + if (FAILED(hr)) { + char* err = (char*)error_blob->GetBufferPointer(); + MessageBoxA(gfx_dxgi_get_h_wnd(), err, "Error", MB_OK | MB_ICONERROR); + throw hr; + } + + struct ShaderProgramD3D11* prg = &d3d.shader_program_pool[std::make_pair(shader_id0, shader_id1)]; - struct ShaderProgramD3D11 *prg = &d3d.shader_program_pool[d3d.shader_program_pool_size++]; - ComPtr vs = compile_shader(buf, len, buf, len, prg); + ThrowIfFailed(d3d.device->CreateVertexShader(vs->GetBufferPointer(), vs->GetBufferSize(), nullptr, + prg->vertex_shader.GetAddressOf())); + ThrowIfFailed(d3d.device->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), nullptr, + prg->pixel_shader.GetAddressOf())); // Input Layout - D3D11_INPUT_ELEMENT_DESC ied[7]; + D3D11_INPUT_ELEMENT_DESC ied[16]; uint8_t ied_index = 0; - ied[ied_index++] = { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - ied[ied_index++] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; + ied[ied_index++] = { + "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 + }; + for (UINT i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + ied[ied_index++] = { + "TEXCOORD", i, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 + }; + if (cc_features.clamp[i][0]) { + ied[ied_index++] = { "TEXCLAMPS", + i, + DXGI_FORMAT_R32_FLOAT, + 0, + D3D11_APPEND_ALIGNED_ELEMENT, + D3D11_INPUT_PER_VERTEX_DATA, + 0 }; + } + if (cc_features.clamp[i][1]) { + ied[ied_index++] = { "TEXCLAMPT", + i, + DXGI_FORMAT_R32_FLOAT, + 0, + D3D11_APPEND_ALIGNED_ELEMENT, + D3D11_INPUT_PER_VERTEX_DATA, + 0 }; + } + } } if (cc_features.opt_fog) { - ied[ied_index++] = { "FOG", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; + ied[ied_index++] = { + "FOG", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 + }; + } + if (cc_features.opt_grayscale) { + ied[ied_index++] = { "GRAYSCALE", + 0, + DXGI_FORMAT_R32G32B32A32_FLOAT, + 0, + D3D11_APPEND_ALIGNED_ELEMENT, + D3D11_INPUT_PER_VERTEX_DATA, + 0 }; } for (unsigned int i = 0; i < cc_features.num_inputs; i++) { DXGI_FORMAT format = cc_features.opt_alpha ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R32G32B32_FLOAT; ied[ied_index++] = { "INPUT", i, format, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; } - ThrowIfFailed(d3d.device->CreateInputLayout(ied, ied_index, vs->GetBufferPointer(), vs->GetBufferSize(), prg->input_layout.GetAddressOf())); + ThrowIfFailed(d3d.device->CreateInputLayout(ied, ied_index, vs->GetBufferPointer(), vs->GetBufferSize(), + prg->input_layout.GetAddressOf())); // Blend state @@ -784,8 +510,9 @@ static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint32_t shade blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; - blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; - blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO; + blend_desc.RenderTarget[0].DestBlendAlpha = + D3D11_BLEND_ONE; // We initially clear alpha to 1.0f and want to keep it at 1.0f blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; } else { @@ -797,26 +524,23 @@ static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint32_t shade // Save some values - prg->shader_id = shader_id; + prg->shader_id0 = shader_id0; + prg->shader_id1 = shader_id1; prg->num_inputs = cc_features.num_inputs; prg->num_floats = num_floats; prg->used_textures[0] = cc_features.used_textures[0]; prg->used_textures[1] = cc_features.used_textures[1]; - return (struct ShaderProgram *)(d3d.current_shader_program = prg); + return (struct ShaderProgram*)(d3d.shader_program = prg); } -static struct ShaderProgram *gfx_d3d11_lookup_shader(uint32_t shader_id) { - for (size_t i = 0; i < d3d.shader_program_pool_size; i++) { - if (d3d.shader_program_pool[i].shader_id == shader_id) { - return (struct ShaderProgram *)&d3d.shader_program_pool[i]; - } - } - return nullptr; +static struct ShaderProgram* gfx_d3d11_lookup_shader(uint64_t shader_id0, uint32_t shader_id1) { + auto it = d3d.shader_program_pool.find(std::make_pair(shader_id0, shader_id1)); + return it == d3d.shader_program_pool.end() ? nullptr : (struct ShaderProgram*)&it->second; } -static void gfx_d3d11_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { - struct ShaderProgramD3D11 *p = (struct ShaderProgramD3D11 *)prg; +static void gfx_d3d11_shader_get_info(struct ShaderProgram* prg, uint8_t* num_inputs, bool used_textures[2]) { + struct ShaderProgramD3D11* p = (struct ShaderProgramD3D11*)prg; *num_inputs = p->num_inputs; used_textures[0] = p->used_textures[0]; @@ -828,21 +552,30 @@ static uint32_t gfx_d3d11_new_texture(void) { return (uint32_t)(d3d.textures.size() - 1); } +static void gfx_d3d11_delete_texture(uint32_t texID) { + // glDeleteTextures(1, &texID); +} + static void gfx_d3d11_select_texture(int tile, uint32_t texture_id) { d3d.current_tile = tile; d3d.current_texture_ids[tile] = texture_id; } static D3D11_TEXTURE_ADDRESS_MODE gfx_cm_to_d3d11(uint32_t val) { + // TODO: handle G_TX_MIRROR | G_TX_CLAMP if (val & G_TX_CLAMP) { return D3D11_TEXTURE_ADDRESS_CLAMP; } return (val & G_TX_MIRROR) ? D3D11_TEXTURE_ADDRESS_MIRROR : D3D11_TEXTURE_ADDRESS_WRAP; } -static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, int width, int height) { +static void gfx_d3d11_upload_texture(const uint8_t* rgba32_buf, uint32_t width, uint32_t height) { // Create texture + TextureData* texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]]; + texture_data->width = width; + texture_data->height = height; + D3D11_TEXTURE2D_DESC texture_desc; ZeroMemory(&texture_desc, sizeof(D3D11_TEXTURE2D_DESC)); @@ -863,47 +596,29 @@ static void gfx_d3d11_upload_texture(const uint8_t *rgba32_buf, int width, int h resource_data.SysMemPitch = width * 4; resource_data.SysMemSlicePitch = resource_data.SysMemPitch * height; - ComPtr texture; - ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture.GetAddressOf())); + ThrowIfFailed( + d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture_data->texture.ReleaseAndGetAddressOf())); // Create shader resource view from texture - D3D11_SHADER_RESOURCE_VIEW_DESC resource_view_desc; - ZeroMemory(&resource_view_desc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); - - resource_view_desc.Format = texture_desc.Format; - resource_view_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - resource_view_desc.Texture2D.MostDetailedMip = 0; - resource_view_desc.Texture2D.MipLevels = -1; - - TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]]; - texture_data->width = width; - texture_data->height = height; - - if (texture_data->resource_view.Get() != nullptr) { - // Free the previous texture in this slot - texture_data->resource_view.Reset(); - } - - ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), &resource_view_desc, texture_data->resource_view.GetAddressOf())); + ThrowIfFailed(d3d.device->CreateShaderResourceView(texture_data->texture.Get(), nullptr, + texture_data->resource_view.ReleaseAndGetAddressOf())); } static void gfx_d3d11_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { D3D11_SAMPLER_DESC sampler_desc; ZeroMemory(&sampler_desc, sizeof(D3D11_SAMPLER_DESC)); -#ifdef THREE_POINT_FILTERING - sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; -#else - sampler_desc.Filter = linear_filter ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT; -#endif + sampler_desc.Filter = linear_filter && d3d.current_filter_mode == FILTER_LINEAR ? D3D11_FILTER_MIN_MAG_MIP_LINEAR + : D3D11_FILTER_MIN_MAG_MIP_POINT; + sampler_desc.AddressU = gfx_cm_to_d3d11(cms); sampler_desc.AddressV = gfx_cm_to_d3d11(cmt); sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampler_desc.MinLOD = 0; sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; - TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[tile]]; + TextureData* texture_data = &d3d.textures[d3d.current_texture_ids[tile]]; texture_data->linear_filtering = linear_filter; // This function is called twice per texture, the first one only to set default values. @@ -914,30 +629,35 @@ static void gfx_d3d11_set_sampler_parameters(int tile, bool linear_filter, uint3 ThrowIfFailed(d3d.device->CreateSamplerState(&sampler_desc, texture_data->sampler_state.GetAddressOf())); } -static void gfx_d3d11_set_depth_test(bool depth_test) { - d3d.current_depth_test = depth_test; -} - -static void gfx_d3d11_set_depth_mask(bool depth_mask) { - d3d.current_depth_mask = depth_mask; +static void gfx_d3d11_set_depth_test_and_mask(bool depth_test, bool depth_mask) { + d3d.depth_test = depth_test; + d3d.depth_mask = depth_mask; } static void gfx_d3d11_set_zmode_decal(bool zmode_decal) { - d3d.current_zmode_decal = zmode_decal; + d3d.zmode_decal = zmode_decal; } static void gfx_d3d11_set_viewport(int x, int y, int width, int height) { - d3d.current_viewport_x = x; - d3d.current_viewport_y = d3d.current_height - y - height; - d3d.current_viewport_width = width; - d3d.current_viewport_height = height; + D3D11_VIEWPORT viewport; + viewport.TopLeftX = x; + viewport.TopLeftY = d3d.render_target_height - y - height; + viewport.Width = width; + viewport.Height = height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + + d3d.context->RSSetViewports(1, &viewport); } static void gfx_d3d11_set_scissor(int x, int y, int width, int height) { - d3d.current_scissor_x = x; - d3d.current_scissor_y = d3d.current_height - y - height; - d3d.current_scissor_width = x + width; - d3d.current_scissor_height = d3d.current_height - y; + D3D11_RECT rect; + rect.left = x; + rect.top = d3d.render_target_height - y - height; + rect.right = x + width; + rect.bottom = d3d.render_target_height - y; + + d3d.context->RSSetScissorRects(1, &rect); } static void gfx_d3d11_set_use_alpha(bool use_alpha) { @@ -946,77 +666,245 @@ static void gfx_d3d11_set_use_alpha(bool use_alpha) { static void gfx_d3d11_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { - set_depth_stencil_state(d3d.current_depth_test, d3d.current_depth_mask); - set_rasterizer_state(d3d.current_zmode_decal); + if (d3d.last_depth_test != d3d.depth_test || d3d.last_depth_mask != d3d.depth_mask) { + d3d.last_depth_test = d3d.depth_test; + d3d.last_depth_mask = d3d.depth_mask; + + d3d.depth_stencil_state.Reset(); + + D3D11_DEPTH_STENCIL_DESC depth_stencil_desc; + ZeroMemory(&depth_stencil_desc, sizeof(D3D11_DEPTH_STENCIL_DESC)); + + depth_stencil_desc.DepthEnable = d3d.depth_test || d3d.depth_mask; + depth_stencil_desc.DepthWriteMask = d3d.depth_mask ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + depth_stencil_desc.DepthFunc = d3d.depth_test ? D3D11_COMPARISON_LESS_EQUAL : D3D11_COMPARISON_ALWAYS; + depth_stencil_desc.StencilEnable = false; + + ThrowIfFailed(d3d.device->CreateDepthStencilState(&depth_stencil_desc, d3d.depth_stencil_state.GetAddressOf())); + d3d.context->OMSetDepthStencilState(d3d.depth_stencil_state.Get(), 0); + } - // Viewport and Scissor + if (d3d.last_zmode_decal != d3d.zmode_decal) { + d3d.last_zmode_decal = d3d.zmode_decal; - set_viewport(d3d.current_viewport_x, d3d.current_viewport_y, d3d.current_viewport_width, d3d.current_viewport_height); - set_scissor(d3d.current_scissor_x, d3d.current_scissor_y, d3d.current_scissor_width, d3d.current_scissor_height); + d3d.rasterizer_state.Reset(); - // Textures + D3D11_RASTERIZER_DESC rasterizer_desc; + ZeroMemory(&rasterizer_desc, sizeof(D3D11_RASTERIZER_DESC)); - for (uint8_t i = 0; i < 2; i++) { - if (d3d.current_shader_program->used_textures[i]) { - set_shader_resources(i, &d3d.textures[d3d.current_texture_ids[i]]); + rasterizer_desc.FillMode = D3D11_FILL_SOLID; + rasterizer_desc.CullMode = D3D11_CULL_NONE; + rasterizer_desc.FrontCounterClockwise = true; + rasterizer_desc.DepthBias = 0; + rasterizer_desc.SlopeScaledDepthBias = d3d.zmode_decal ? -2.0f : 0.0f; + rasterizer_desc.DepthBiasClamp = 0.0f; + rasterizer_desc.DepthClipEnable = false; + rasterizer_desc.ScissorEnable = true; + rasterizer_desc.MultisampleEnable = false; + rasterizer_desc.AntialiasedLineEnable = false; + + ThrowIfFailed(d3d.device->CreateRasterizerState(&rasterizer_desc, d3d.rasterizer_state.GetAddressOf())); + d3d.context->RSSetState(d3d.rasterizer_state.Get()); + } + + bool textures_changed = false; + + for (int i = 0; i < 2; i++) { + if (d3d.shader_program->used_textures[i]) { + if (d3d.last_resource_views[i].Get() != d3d.textures[d3d.current_texture_ids[i]].resource_view.Get()) { + d3d.last_resource_views[i] = d3d.textures[d3d.current_texture_ids[i]].resource_view.Get(); + d3d.context->PSSetShaderResources( + i, 1, d3d.textures[d3d.current_texture_ids[i]].resource_view.GetAddressOf()); + + if (d3d.current_filter_mode == FILTER_THREE_POINT) { + d3d.per_draw_cb_data.textures[i].width = d3d.textures[d3d.current_texture_ids[i]].width; + d3d.per_draw_cb_data.textures[i].height = d3d.textures[d3d.current_texture_ids[i]].height; + d3d.per_draw_cb_data.textures[i].linear_filtering = + d3d.textures[d3d.current_texture_ids[i]].linear_filtering; + textures_changed = true; + } + + if (d3d.last_sampler_states[i].Get() != d3d.textures[d3d.current_texture_ids[i]].sampler_state.Get()) { + d3d.last_sampler_states[i] = d3d.textures[d3d.current_texture_ids[i]].sampler_state.Get(); + d3d.context->PSSetSamplers(i, 1, + d3d.textures[d3d.current_texture_ids[i]].sampler_state.GetAddressOf()); + } + } } } // Set per-draw constant buffer -#ifdef THREE_POINT_FILTERING + if (textures_changed) { + D3D11_MAPPED_SUBRESOURCE ms; + ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); + d3d.context->Map(d3d.per_draw_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); + memcpy(ms.pData, &d3d.per_draw_cb_data, sizeof(PerDrawCB)); + d3d.context->Unmap(d3d.per_draw_cb.Get(), 0); + } + + // Set vertex buffer data + D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); - d3d.context->Map(d3d.per_draw_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); - memcpy(ms.pData, &d3d.per_draw_cb_data, sizeof(PerDrawCB)); - d3d.context->Unmap(d3d.per_draw_cb.Get(), 0); -#endif + d3d.context->Map(d3d.vertex_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); + memcpy(ms.pData, buf_vbo, buf_vbo_len * sizeof(float)); + d3d.context->Unmap(d3d.vertex_buffer.Get(), 0); - // Set vertex buffer data + uint32_t stride = d3d.shader_program->num_floats * sizeof(float); + uint32_t offset = 0; - size_t buffer_length = buf_vbo_len * sizeof(float); - uint32_t stride = d3d.current_shader_program->num_floats * sizeof(float); - set_vertex_buffer(buf_vbo, buffer_length, stride); + if (d3d.last_vertex_buffer_stride != stride) { + d3d.last_vertex_buffer_stride = stride; + d3d.context->IASetVertexBuffers(0, 1, d3d.vertex_buffer.GetAddressOf(), &stride, &offset); + } - set_shader(d3d.current_shader_program); + if (d3d.last_shader_program != d3d.shader_program) { + d3d.last_shader_program = d3d.shader_program; + d3d.context->IASetInputLayout(d3d.shader_program->input_layout.Get()); + d3d.context->VSSetShader(d3d.shader_program->vertex_shader.Get(), 0, 0); + d3d.context->PSSetShader(d3d.shader_program->pixel_shader.Get(), 0, 0); - set_primitive_topology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + if (d3d.last_blend_state.Get() != d3d.shader_program->blend_state.Get()) { + d3d.last_blend_state = d3d.shader_program->blend_state.Get(); + d3d.context->OMSetBlendState(d3d.shader_program->blend_state.Get(), 0, 0xFFFFFFFF); + } + } - d3d.context->Draw(buf_vbo_num_tris * 3, 0); -} + if (d3d.last_primitive_topology != D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST) { + d3d.last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + d3d.context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + } -static void gfx_d3d11_get_framebuffer(uint16_t *buffer) { - d3d.context->CopyResource(d3d.framebuffer_rt.cpu_readable_texture_copy.Get(), d3d.framebuffer_rt.texture.Get()); - d3d.framebuffer_requested = true; - d3d.framebuffer_data = buffer; + d3d.context->Draw(buf_vbo_num_tris * 3, 0); } static void gfx_d3d11_on_resize(void) { - create_render_target_views(true); + // create_render_target_views(true); } static void gfx_d3d11_start_frame(void) { - // Set render targets - - d3d.context->OMSetRenderTargets(1, d3d.main_rt.render_target_view.GetAddressOf(), d3d.main_rt.depth_stencil_view.Get()); - - // Clear render targets - - const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; - d3d.context->ClearRenderTargetView(d3d.main_rt.render_target_view.Get(), clearColor); - d3d.context->ClearDepthStencilView(d3d.main_rt.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0); - // Set per-frame constant buffer + ID3D11Buffer* buffers[2] = { d3d.per_frame_cb.Get(), d3d.per_draw_cb.Get() }; + d3d.context->PSSetConstantBuffers(0, 2, buffers); d3d.per_frame_cb_data.noise_frame++; if (d3d.per_frame_cb_data.noise_frame > 150) { // No high values, as noise starts to look ugly d3d.per_frame_cb_data.noise_frame = 0; } +} + +static void gfx_d3d11_end_frame(void) { + d3d.context->Flush(); +} + +static void gfx_d3d11_finish_render(void) { + d3d.context->Flush(); +} + +int gfx_d3d11_create_framebuffer(void) { + uint32_t texture_id = gfx_d3d11_new_texture(); + TextureData& t = d3d.textures[texture_id]; + + size_t index = d3d.framebuffers.size(); + d3d.framebuffers.resize(d3d.framebuffers.size() + 1); + Framebuffer& data = d3d.framebuffers.back(); + data.texture_id = texture_id; + + uint32_t tile = 0; + uint32_t saved = d3d.current_texture_ids[tile]; + d3d.current_texture_ids[tile] = texture_id; + gfx_d3d11_set_sampler_parameters(0, true, G_TX_WRAP, G_TX_WRAP); + d3d.current_texture_ids[tile] = saved; + + return (int)index; +} + +static void gfx_d3d11_update_framebuffer_parameters(int fb_id, uint32_t width, uint32_t height, uint32_t msaa_level, + bool opengl_invert_y, bool render_target, bool has_depth_buffer, + bool can_extract_depth) { + Framebuffer& fb = d3d.framebuffers[fb_id]; + TextureData& tex = d3d.textures[fb.texture_id]; + + width = MAX(width, 1U); + height = MAX(height, 1U); + while (msaa_level > 1 && d3d.msaa_num_quality_levels[msaa_level - 1] == 0) { + --msaa_level; + } + + bool diff = tex.width != width || tex.height != height || fb.msaa_level != msaa_level; + + if (diff || (fb.render_target_view.Get() != nullptr) != render_target) { + if (fb_id != 0) { + D3D11_TEXTURE2D_DESC texture_desc; + texture_desc.Width = width; + texture_desc.Height = height; + texture_desc.Usage = D3D11_USAGE_DEFAULT; + texture_desc.BindFlags = + (msaa_level <= 1 ? D3D11_BIND_SHADER_RESOURCE : 0) | (render_target ? D3D11_BIND_RENDER_TARGET : 0); + texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + texture_desc.CPUAccessFlags = 0; + texture_desc.MiscFlags = 0; + texture_desc.ArraySize = 1; + texture_desc.MipLevels = 1; + texture_desc.SampleDesc.Count = msaa_level; + texture_desc.SampleDesc.Quality = 0; + + ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, tex.texture.ReleaseAndGetAddressOf())); + + if (msaa_level <= 1) { + ThrowIfFailed(d3d.device->CreateShaderResourceView(tex.texture.Get(), nullptr, + tex.resource_view.ReleaseAndGetAddressOf())); + } + } else if (diff || (render_target && tex.texture.Get() == nullptr)) { + DXGI_SWAP_CHAIN_DESC1 desc1; + IDXGISwapChain1* swap_chain = gfx_dxgi_get_swap_chain(); + ThrowIfFailed(swap_chain->GetDesc1(&desc1)); + if (desc1.Width != width || desc1.Height != height) { + fb.render_target_view.Reset(); + tex.texture.Reset(); + ThrowIfFailed(swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN, desc1.Flags)); + } + ThrowIfFailed( + swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)tex.texture.ReleaseAndGetAddressOf())); + } + if (render_target) { + ThrowIfFailed(d3d.device->CreateRenderTargetView(tex.texture.Get(), nullptr, + fb.render_target_view.ReleaseAndGetAddressOf())); + } + + tex.width = width; + tex.height = height; + } + + if (has_depth_buffer && + (diff || !fb.has_depth_buffer || (fb.depth_stencil_srv.Get() != nullptr) != can_extract_depth)) { + fb.depth_stencil_srv.Reset(); + create_depth_stencil_objects(width, height, msaa_level, fb.depth_stencil_view.ReleaseAndGetAddressOf(), + can_extract_depth ? fb.depth_stencil_srv.GetAddressOf() : nullptr); + } + if (!has_depth_buffer) { + fb.depth_stencil_view.Reset(); + fb.depth_stencil_srv.Reset(); + } + + fb.has_depth_buffer = has_depth_buffer; + fb.msaa_level = msaa_level; +} - float aspect_ratio = (float) d3d.current_width / (float) d3d.current_height; - d3d.per_frame_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 - d3d.per_frame_cb_data.noise_scale_y = 120; +void gfx_d3d11_start_draw_to_framebuffer(int fb_id, float noise_scale) { + Framebuffer& fb = d3d.framebuffers[fb_id]; + d3d.render_target_height = d3d.textures[fb.texture_id].height; + + d3d.context->OMSetRenderTargets(1, fb.render_target_view.GetAddressOf(), + fb.has_depth_buffer ? fb.depth_stencil_view.Get() : nullptr); + + d3d.current_framebuffer = fb_id; + + if (noise_scale != 0.0f) { + d3d.per_frame_cb_data.noise_scale = 1.0f / noise_scale; + } D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); @@ -1025,134 +913,189 @@ static void gfx_d3d11_start_frame(void) { d3d.context->Unmap(d3d.per_frame_cb.Get(), 0); } -static void gfx_d3d11_end_frame(void) { - draw_render_target(&d3d.backbuffer_rt, &d3d.main_rt, false); - draw_render_target(&d3d.framebuffer_rt, &d3d.main_rt, true); +void gfx_d3d11_clear_framebuffer(void) { + Framebuffer& fb = d3d.framebuffers[d3d.current_framebuffer]; + const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + d3d.context->ClearRenderTargetView(fb.render_target_view.Get(), clearColor); + if (fb.has_depth_buffer) { + d3d.context->ClearDepthStencilView(fb.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0); + } +} - if (d3d.framebuffer_requested) { - D3D11_MAPPED_SUBRESOURCE ms; - ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); - UINT subresource = D3D11CalcSubresource(0, 0, 0); - d3d.context->Map(d3d.framebuffer_rt.cpu_readable_texture_copy.Get(), subresource, D3D11_MAP_READ, 0, &ms); - uint8_t *pixels = (uint8_t *) ms.pData; - - uint32_t bi = 0; - for (uint32_t y = 0; y < FRAMEBUFFER_HEIGHT; y++) { - for (uint32_t x = 0; x < FRAMEBUFFER_WIDTH; x++) { - uint32_t fb_pixel = (y * FRAMEBUFFER_WIDTH + x) * 4; - - uint8_t r = pixels[fb_pixel + 0] >> 3; - uint8_t g = pixels[fb_pixel + 1] >> 3; - uint8_t b = pixels[fb_pixel + 2] >> 3; - uint8_t a = 1; //pixels[fb_pixel + 3] / 255; - - d3d.framebuffer_data[bi] = (r << 11) | (g << 6) | (b << 1) | a; - bi++; - } - } +void gfx_d3d11_resolve_msaa_color_buffer(int fb_id_target, int fb_id_source) { + Framebuffer& fb_dst = d3d.framebuffers[fb_id_target]; + Framebuffer& fb_src = d3d.framebuffers[fb_id_source]; - d3d.context->Unmap(d3d.framebuffer_rt.cpu_readable_texture_copy.Get(), subresource); - d3d.framebuffer_requested = false; - } + d3d.context->ResolveSubresource(d3d.textures[fb_dst.texture_id].texture.Get(), 0, + d3d.textures[fb_src.texture_id].texture.Get(), 0, DXGI_FORMAT_R8G8B8A8_UNORM); } -static void gfx_d3d11_finish_render(void) { +void* gfx_d3d11_get_framebuffer_texture_id(int fb_id) { + return (void*)d3d.textures[d3d.framebuffers[fb_id].texture_id].resource_view.Get(); } -static void gfx_d3d11_shutdown() { -#if DEBUG_D3D +void gfx_d3d11_select_texture_fb(int fbID) { + int tile = 0; + gfx_d3d11_select_texture(tile, d3d.framebuffers[fbID].texture_id); +} + +void gfx_d3d11_set_texture_filter(FilteringMode mode) { + d3d.current_filter_mode = mode; + gfx_texture_cache_clear(); +} - // Check if there are any leaking resources. - // Normally the will be relased automatically thanks to ComPtr, - // but let's release them manually here to see what ReportLiveDeviceObjects() reports. - // ID3D11Device should appear with Refcount: 4, - // IDXGISwapChain should have Refcount: 1 (there's still a reference in gfx_dxgi.cpp that is released later) - // And all the rest should have RefCount: 0. - - d3d.state.blend_state = nullptr; - d3d.state.resource_views[0] = nullptr; - d3d.state.resource_views[1] = nullptr; - d3d.state.sampler_states[0] = nullptr; - d3d.state.sampler_states[1] = nullptr; - - for (int t = 0; t < d3d.textures.size(); t++) { - d3d.textures[t].resource_view.Reset(); - d3d.textures[t].sampler_state.Reset(); +FilteringMode gfx_d3d11_get_texture_filter(void) { + return d3d.current_filter_mode; +} + +std::unordered_map, uint16_t, hash_pair_ff> +gfx_d3d11_get_pixel_depth(int fb_id, const std::set>& coordinates) { + Framebuffer& fb = d3d.framebuffers[fb_id]; + TextureData& td = d3d.textures[fb.texture_id]; + + if (coordinates.size() > d3d.coord_buffer_size) { + d3d.coord_buffer.Reset(); + d3d.coord_buffer_srv.Reset(); + d3d.depth_value_output_buffer.Reset(); + d3d.depth_value_output_uav.Reset(); + d3d.depth_value_output_buffer_copy.Reset(); + + D3D11_BUFFER_DESC coord_buf_desc; + coord_buf_desc.Usage = D3D11_USAGE_DYNAMIC; + coord_buf_desc.ByteWidth = sizeof(Coord) * coordinates.size(); + coord_buf_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + coord_buf_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + coord_buf_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + coord_buf_desc.StructureByteStride = sizeof(Coord); + + ThrowIfFailed(d3d.device->CreateBuffer(&coord_buf_desc, nullptr, d3d.coord_buffer.GetAddressOf())); + + D3D11_SHADER_RESOURCE_VIEW_DESC coord_buf_srv_desc; + coord_buf_srv_desc.Format = DXGI_FORMAT_UNKNOWN; + coord_buf_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; + coord_buf_srv_desc.Buffer.FirstElement = 0; + coord_buf_srv_desc.Buffer.NumElements = coordinates.size(); + + ThrowIfFailed(d3d.device->CreateShaderResourceView(d3d.coord_buffer.Get(), &coord_buf_srv_desc, + d3d.coord_buffer_srv.GetAddressOf())); + + D3D11_BUFFER_DESC output_buffer_desc; + output_buffer_desc.Usage = D3D11_USAGE_DEFAULT; + output_buffer_desc.ByteWidth = sizeof(float) * coordinates.size(); + output_buffer_desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS; + output_buffer_desc.CPUAccessFlags = 0; + output_buffer_desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; + output_buffer_desc.StructureByteStride = sizeof(float); + ThrowIfFailed( + d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer.GetAddressOf())); + + D3D11_UNORDERED_ACCESS_VIEW_DESC output_buffer_uav_desc; + output_buffer_uav_desc.Format = DXGI_FORMAT_UNKNOWN; + output_buffer_uav_desc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; + output_buffer_uav_desc.Buffer.FirstElement = 0; + output_buffer_uav_desc.Buffer.NumElements = coordinates.size(); + output_buffer_uav_desc.Buffer.Flags = 0; + ThrowIfFailed(d3d.device->CreateUnorderedAccessView( + d3d.depth_value_output_buffer.Get(), &output_buffer_uav_desc, d3d.depth_value_output_uav.GetAddressOf())); + + output_buffer_desc.Usage = D3D11_USAGE_STAGING; + output_buffer_desc.BindFlags = 0; + output_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + ThrowIfFailed( + d3d.device->CreateBuffer(&output_buffer_desc, nullptr, d3d.depth_value_output_buffer_copy.GetAddressOf())); + + d3d.coord_buffer_size = coordinates.size(); } - for (int s = 0; s < d3d.shader_program_pool_size; s++) { - d3d.shader_program_pool[s].vertex_shader.Reset(); - d3d.shader_program_pool[s].pixel_shader.Reset(); - d3d.shader_program_pool[s].blend_state.Reset(); - d3d.shader_program_pool[s].input_layout.Reset(); + D3D11_MAPPED_SUBRESOURCE ms; + + if (fb.msaa_level > 1 && d3d.compute_shader_msaa.Get() == nullptr) { + ThrowIfFailed(d3d.device->CreateComputeShader(d3d.compute_shader_msaa_blob->GetBufferPointer(), + d3d.compute_shader_msaa_blob->GetBufferSize(), nullptr, + d3d.compute_shader_msaa.GetAddressOf())); } - d3d.rt_shader_program.vertex_shader.Reset(); - d3d.rt_shader_program.pixel_shader.Reset(); - d3d.rt_shader_program.blend_state.Reset(); - d3d.rt_shader_program.input_layout.Reset(); - - d3d.backbuffer_rt.texture.Reset(); - d3d.backbuffer_rt.render_target_view.Reset(); - d3d.backbuffer_rt.depth_stencil_view.Reset(); - d3d.backbuffer_rt.cpu_readable_texture_copy.Reset(); - d3d.backbuffer_rt.texture_data.resource_view.Reset(); - d3d.backbuffer_rt.texture_data.sampler_state.Reset(); - - d3d.main_rt.texture.Reset(); - d3d.main_rt.render_target_view.Reset(); - d3d.main_rt.depth_stencil_view.Reset(); - d3d.main_rt.cpu_readable_texture_copy.Reset(); - d3d.main_rt.texture_data.resource_view.Reset(); - d3d.main_rt.texture_data.sampler_state.Reset(); - - d3d.framebuffer_rt.texture.Reset(); - d3d.framebuffer_rt.render_target_view.Reset(); - d3d.framebuffer_rt.depth_stencil_view.Reset(); - d3d.framebuffer_rt.cpu_readable_texture_copy.Reset(); - d3d.framebuffer_rt.texture_data.resource_view.Reset(); - d3d.framebuffer_rt.texture_data.sampler_state.Reset(); - - d3d.vertex_buffer.Reset(); - d3d.per_frame_cb.Reset(); - d3d.per_draw_cb.Reset(); - - d3d.depth_stencil_state.Reset(); - d3d.rasterizer_state.Reset(); - d3d.swap_chain.Reset(); - d3d.context.Reset(); - - d3d.debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); -#endif + // ImGui overwrites these values, so we cannot set them once at init + d3d.context->CSSetShader(fb.msaa_level > 1 ? d3d.compute_shader_msaa.Get() : d3d.compute_shader.Get(), nullptr, 0); + d3d.context->CSSetUnorderedAccessViews(0, 1, d3d.depth_value_output_uav.GetAddressOf(), nullptr); + + ThrowIfFailed(d3d.context->Map(d3d.coord_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms)); + Coord* coord_cb = (Coord*)ms.pData; + { + size_t i = 0; + for (const auto& coord : coordinates) { + coord_cb[i].x = coord.first; + // We invert y because the gfx_pc assumes OpenGL coordinates (bottom-left corner is origin), while DX's + // origin is top-left corner + coord_cb[i].y = td.height - 1 - coord.second; + ++i; + } + } + d3d.context->Unmap(d3d.coord_buffer.Get(), 0); + + // The depth stencil texture can only have one mapping at a time, so unbind from the OM + ID3D11RenderTargetView* null_arr1[1] = { nullptr }; + d3d.context->OMSetRenderTargets(1, null_arr1, nullptr); + + ID3D11ShaderResourceView* srvs[] = { fb.depth_stencil_srv.Get(), d3d.coord_buffer_srv.Get() }; + d3d.context->CSSetShaderResources(0, 2, srvs); + + d3d.context->Dispatch(coordinates.size(), 1, 1); + + d3d.context->CopyResource(d3d.depth_value_output_buffer_copy.Get(), d3d.depth_value_output_buffer.Get()); + ThrowIfFailed(d3d.context->Map(d3d.depth_value_output_buffer_copy.Get(), 0, D3D11_MAP_READ, 0, &ms)); + std::unordered_map, uint16_t, hash_pair_ff> res; + { + size_t i = 0; + for (const auto& coord : coordinates) { + res.emplace(coord, ((float*)ms.pData)[i++] * 65532.0f); + } + } + d3d.context->Unmap(d3d.depth_value_output_buffer_copy.Get(), 0); + + ID3D11ShaderResourceView* null_arr[2] = { nullptr, nullptr }; + d3d.context->CSSetShaderResources(0, 2, null_arr); + + return res; } } // namespace -struct GfxRenderingAPI gfx_direct3d11_api = { - gfx_d3d11_z_is_from_0_to_1, - gfx_d3d11_unload_shader, - gfx_d3d11_load_shader, - gfx_d3d11_create_and_load_new_shader, - gfx_d3d11_lookup_shader, - gfx_d3d11_shader_get_info, - gfx_d3d11_new_texture, - gfx_d3d11_select_texture, - gfx_d3d11_upload_texture, - gfx_d3d11_set_sampler_parameters, - gfx_d3d11_set_depth_test, - gfx_d3d11_set_depth_mask, - gfx_d3d11_set_zmode_decal, - gfx_d3d11_set_viewport, - gfx_d3d11_set_scissor, - gfx_d3d11_set_use_alpha, - gfx_d3d11_draw_triangles, - gfx_d3d11_get_framebuffer, - gfx_d3d11_init, - gfx_d3d11_on_resize, - gfx_d3d11_start_frame, - gfx_d3d11_end_frame, - gfx_d3d11_finish_render, - gfx_d3d11_shutdown, -}; +void* gfx_d3d11_get_texture_by_id(int id) { + return d3d.textures[id].resource_view.Get(); +} + +struct GfxRenderingAPI gfx_direct3d11_api = { gfx_d3d11_get_clip_parameters, + gfx_d3d11_unload_shader, + gfx_d3d11_load_shader, + gfx_d3d11_create_and_load_new_shader, + gfx_d3d11_lookup_shader, + gfx_d3d11_shader_get_info, + gfx_d3d11_new_texture, + gfx_d3d11_select_texture, + gfx_d3d11_upload_texture, + gfx_d3d11_set_sampler_parameters, + gfx_d3d11_set_depth_test_and_mask, + gfx_d3d11_set_zmode_decal, + gfx_d3d11_set_viewport, + gfx_d3d11_set_scissor, + gfx_d3d11_set_use_alpha, + gfx_d3d11_draw_triangles, + gfx_d3d11_init, + gfx_d3d11_on_resize, + gfx_d3d11_start_frame, + gfx_d3d11_end_frame, + gfx_d3d11_finish_render, + gfx_d3d11_create_framebuffer, + gfx_d3d11_update_framebuffer_parameters, + gfx_d3d11_start_draw_to_framebuffer, + gfx_d3d11_clear_framebuffer, + gfx_d3d11_resolve_msaa_color_buffer, + gfx_d3d11_get_pixel_depth, + gfx_d3d11_get_framebuffer_texture_id, + gfx_d3d11_select_texture_fb, + gfx_d3d11_delete_texture, + gfx_d3d11_set_texture_filter, + gfx_d3d11_get_texture_filter }; #endif diff --git a/src/pc/gfx/gfx_direct3d_common.cpp b/src/pc/gfx/gfx_direct3d_common.cpp index 62f3879fb..455765248 100644 --- a/src/pc/gfx/gfx_direct3d_common.cpp +++ b/src/pc/gfx/gfx_direct3d_common.cpp @@ -5,62 +5,29 @@ #include "gfx_direct3d_common.h" #include "gfx_cc.h" -void get_cc_features(uint32_t shader_id, CCFeatures *cc_features) { - for (int i = 0; i < 4; i++) { - cc_features->c[0][i] = (shader_id >> (i * 3)) & 7; - cc_features->c[1][i] = (shader_id >> (12 + i * 3)) & 7; - } - - cc_features->opt_alpha = (shader_id & SHADER_OPT_ALPHA) != 0; - cc_features->opt_fog = (shader_id & SHADER_OPT_FOG) != 0; - cc_features->opt_texture_edge = (shader_id & SHADER_OPT_TEXTURE_EDGE) != 0; - cc_features->opt_noise = (shader_id & SHADER_OPT_NOISE) != 0; - - cc_features->used_textures[0] = false; - cc_features->used_textures[1] = false; - cc_features->num_inputs = 0; - - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - if (cc_features->c[i][j] >= SHADER_INPUT_1 && cc_features->c[i][j] <= SHADER_INPUT_4) { - if (cc_features->c[i][j] > cc_features->num_inputs) { - cc_features->num_inputs = cc_features->c[i][j]; - } - } - if (cc_features->c[i][j] == SHADER_TEXEL0 || cc_features->c[i][j] == SHADER_TEXEL0A) { - cc_features->used_textures[0] = true; - } - if (cc_features->c[i][j] == SHADER_TEXEL1) { - cc_features->used_textures[1] = true; - } - } - } - - cc_features->do_single[0] = cc_features->c[0][2] == 0; - cc_features->do_single[1] = cc_features->c[1][2] == 0; - cc_features->do_multiply[0] = cc_features->c[0][1] == 0 && cc_features->c[0][3] == 0; - cc_features->do_multiply[1] = cc_features->c[1][1] == 0 && cc_features->c[1][3] == 0; - cc_features->do_mix[0] = cc_features->c[0][1] == cc_features->c[0][3]; - cc_features->do_mix[1] = cc_features->c[1][1] == cc_features->c[1][3]; - cc_features->color_alpha_same = (shader_id & 0xfff) == ((shader_id >> 12) & 0xfff); +static void append_str(char* buf, size_t* len, const char* str) { + while (*str != '\0') + buf[(*len)++] = *str++; } -static void append_str(char *buf, size_t *len, const char *str) { - while (*str != '\0') buf[(*len)++] = *str++; -} - -static void append_line(char *buf, size_t *len, const char *str) { - while (*str != '\0') buf[(*len)++] = *str++; +static void append_line(char* buf, size_t* len, const char* str) { + while (*str != '\0') + buf[(*len)++] = *str++; buf[(*len)++] = '\r'; buf[(*len)++] = '\n'; } -static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_alpha, bool inputs_have_alpha, bool hint_single_element) { +#define RAND_NOISE "((random(float3(floor(screenSpace.xy * noise_scale), noise_frame)) + 1.0) / 2.0)" + +static const char* shader_item_to_str(uint32_t item, bool with_alpha, bool only_alpha, bool inputs_have_alpha, + bool hint_single_element) { if (!only_alpha) { switch (item) { default: case SHADER_0: return with_alpha ? "float4(0.0, 0.0, 0.0, 0.0)" : "float3(0.0, 0.0, 0.0)"; + case SHADER_1: + return with_alpha ? "float4(1.0, 1.0, 1.0, 1.0)" : "float3(1.0, 1.0, 1.0)"; case SHADER_INPUT_1: return with_alpha || !inputs_have_alpha ? "input.input1" : "input.input1.rgb"; case SHADER_INPUT_2: @@ -72,15 +39,28 @@ static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_ case SHADER_TEXEL0: return with_alpha ? "texVal0" : "texVal0.rgb"; case SHADER_TEXEL0A: - return hint_single_element ? "texVal0.a" : (with_alpha ? "float4(texVal0.a, texVal0.a, texVal0.a, texVal0.a)" : "float3(texVal0.a, texVal0.a, texVal0.a)"); + return hint_single_element ? "texVal0.a" + : (with_alpha ? "float4(texVal0.a, texVal0.a, texVal0.a, texVal0.a)" + : "float3(texVal0.a, texVal0.a, texVal0.a)"); + case SHADER_TEXEL1A: + return hint_single_element ? "texVal1.a" + : (with_alpha ? "float4(texVal1.a, texVal1.a, texVal1.a, texVal1.a)" + : "float3(texVal1.a, texVal1.a, texVal1.a)"); case SHADER_TEXEL1: return with_alpha ? "texVal1" : "texVal1.rgb"; + case SHADER_COMBINED: + return with_alpha ? "texel" : "texel.rgb"; + case SHADER_NOISE: + return with_alpha ? "float4(" RAND_NOISE ", " RAND_NOISE ", " RAND_NOISE ", " RAND_NOISE ")" + : "float3(" RAND_NOISE ", " RAND_NOISE ", " RAND_NOISE ")"; } } else { switch (item) { default: case SHADER_0: return "0.0"; + case SHADER_1: + return "1.0"; case SHADER_INPUT_1: return "input.input1.a"; case SHADER_INPUT_2: @@ -93,13 +73,22 @@ static const char *shader_item_to_str(uint32_t item, bool with_alpha, bool only_ return "texVal0.a"; case SHADER_TEXEL0A: return "texVal0.a"; + case SHADER_TEXEL1A: + return "texVal1.a"; case SHADER_TEXEL1: return "texVal1.a"; + case SHADER_COMBINED: + return "texel.a"; + case SHADER_NOISE: + return RAND_NOISE; } } } -static void append_formula(char *buf, size_t *len, const uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, bool with_alpha, bool only_alpha, bool opt_alpha) { +#undef RAND_NOISE + +static void append_formula(char* buf, size_t* len, const uint8_t c[2][4], bool do_single, bool do_multiply, bool do_mix, + bool with_alpha, bool only_alpha, bool opt_alpha) { if (do_single) { append_str(buf, len, shader_item_to_str(c[only_alpha][3], with_alpha, only_alpha, opt_alpha, false)); } else if (do_multiply) { @@ -126,17 +115,17 @@ static void append_formula(char *buf, size_t *len, const uint8_t c[2][4], bool d } } -void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_floats, const CCFeatures& cc_features, bool include_root_signature, bool three_point_filtering) { +void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_floats, const CCFeatures& cc_features, + bool include_root_signature, bool three_point_filtering) { len = 0; num_floats = 4; // Pixel shader input struct if (include_root_signature) { - append_str(buf, &len, "#define RS \"RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | DENY_VERTEX_SHADER_ROOT_ACCESS)"); - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_str(buf, &len, ",CBV(b0, visibility = SHADER_VISIBILITY_PIXEL)"); - } + append_str(buf, &len, + "#define RS \"RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | DENY_VERTEX_SHADER_ROOT_ACCESS)"); + append_str(buf, &len, ",CBV(b0, visibility = SHADER_VISIBILITY_PIXEL)"); if (cc_features.used_textures[0]) { append_str(buf, &len, ",DescriptorTable(SRV(t0), visibility = SHADER_VISIBILITY_PIXEL)"); append_str(buf, &len, ",DescriptorTable(Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL)"); @@ -150,17 +139,27 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f append_line(buf, &len, "struct PSInput {"); append_line(buf, &len, " float4 position : SV_POSITION;"); - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_line(buf, &len, " float2 uv : TEXCOORD;"); - num_floats += 2; - } - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(buf, &len, " float4 screenPos : TEXCOORD1;"); + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + len += sprintf(buf + len, " float2 uv%d : TEXCOORD%d;\r\n", i, i); + num_floats += 2; + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { + len += sprintf(buf + len, " float texClamp%s%d : TEXCLAMP%s%d;\r\n", j == 0 ? "S" : "T", i, + j == 0 ? "S" : "T", i); + num_floats += 1; + } + } + } } if (cc_features.opt_fog) { append_line(buf, &len, " float4 fog : FOG;"); num_floats += 4; } + if (cc_features.opt_grayscale) { + append_line(buf, &len, " float4 grayscale : GRAYSCALE;"); + num_floats += 4; + } for (int i = 0; i < cc_features.num_inputs; i++) { len += sprintf(buf + len, " float%d input%d : INPUT%d;\r\n", cc_features.opt_alpha ? 4 : 3, i + 1, i); num_floats += cc_features.opt_alpha ? 4 : 3; @@ -180,17 +179,15 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f // Constant buffer and random function - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(buf, &len, "cbuffer PerFrameCB : register(b0) {"); - append_line(buf, &len, " uint noise_frame;"); - append_line(buf, &len, " float2 noise_scale;"); - append_line(buf, &len, "}"); + append_line(buf, &len, "cbuffer PerFrameCB : register(b0) {"); + append_line(buf, &len, " uint noise_frame;"); + append_line(buf, &len, " float noise_scale;"); + append_line(buf, &len, "}"); - append_line(buf, &len, "float random(in float3 value) {"); - append_line(buf, &len, " float random = dot(value, float3(12.9898, 78.233, 37.719));"); - append_line(buf, &len, " return frac(sin(random) * 143758.5453);"); - append_line(buf, &len, "}"); - } + append_line(buf, &len, "float random(in float3 value) {"); + append_line(buf, &len, " float random = dot(value, float3(12.9898, 78.233, 37.719));"); + append_line(buf, &len, " return frac(sin(random) * 143758.5453);"); + append_line(buf, &len, "}"); // 3 point texture filtering // Original author: ArthurCarvalho @@ -204,13 +201,21 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f append_line(buf, &len, " bool linear_filtering;"); append_line(buf, &len, " } textures[2];"); append_line(buf, &len, "}"); - append_line(buf, &len, "#define TEX_OFFSET(tex, tSampler, texCoord, off, texSize) tex.Sample(tSampler, texCoord - off / texSize)"); - append_line(buf, &len, "float4 tex2D3PointFilter(in Texture2D tex, in SamplerState tSampler, in float2 texCoord, in float2 texSize) {"); + append_line( + buf, &len, + "#define TEX_OFFSET(tex, tSampler, texCoord, off, texSize) tex.Sample(tSampler, texCoord - off / texSize)"); + append_line(buf, &len, + "float4 tex2D3PointFilter(in Texture2D tex, in SamplerState tSampler, in float2 texCoord, in " + "float2 texSize) {"); append_line(buf, &len, " float2 offset = frac(texCoord * texSize - float2(0.5, 0.5));"); append_line(buf, &len, " offset -= step(1.0, offset.x + offset.y);"); append_line(buf, &len, " float4 c0 = TEX_OFFSET(tex, tSampler, texCoord, offset, texSize);"); - append_line(buf, &len, " float4 c1 = TEX_OFFSET(tex, tSampler, texCoord, float2(offset.x - sign(offset.x), offset.y), texSize);"); - append_line(buf, &len, " float4 c2 = TEX_OFFSET(tex, tSampler, texCoord, float2(offset.x, offset.y - sign(offset.y)), texSize);"); + append_line(buf, &len, + " float4 c1 = TEX_OFFSET(tex, tSampler, texCoord, float2(offset.x - sign(offset.x), offset.y), " + "texSize);"); + append_line(buf, &len, + " float4 c2 = TEX_OFFSET(tex, tSampler, texCoord, float2(offset.x, offset.y - sign(offset.y)), " + "texSize);"); append_line(buf, &len, " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0);"); append_line(buf, &len, "}"); } @@ -218,27 +223,47 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f // Vertex shader append_str(buf, &len, "PSInput VSMain(float4 position : POSITION"); - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_str(buf, &len, ", float2 uv : TEXCOORD"); + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + len += sprintf(buf + len, ", float2 uv%d : TEXCOORD%d", i, i); + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { + len += sprintf(buf + len, ", float texClamp%s%d : TEXCLAMP%s%d", j == 0 ? "S" : "T", i, + j == 0 ? "S" : "T", i); + } + } + } } if (cc_features.opt_fog) { append_str(buf, &len, ", float4 fog : FOG"); } + if (cc_features.opt_grayscale) { + append_str(buf, &len, ", float4 grayscale : GRAYSCALE"); + } for (int i = 0; i < cc_features.num_inputs; i++) { len += sprintf(buf + len, ", float%d input%d : INPUT%d", cc_features.opt_alpha ? 4 : 3, i + 1, i); } append_line(buf, &len, ") {"); append_line(buf, &len, " PSInput result;"); append_line(buf, &len, " result.position = position;"); - if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(buf, &len, " result.screenPos = position;"); - } - if (cc_features.used_textures[0] || cc_features.used_textures[1]) { - append_line(buf, &len, " result.uv = uv;"); + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + len += sprintf(buf + len, " result.uv%d = uv%d;\r\n", i, i); + for (int j = 0; j < 2; j++) { + if (cc_features.clamp[i][j]) { + len += sprintf(buf + len, " result.texClamp%s%d = texClamp%s%d;\r\n", j == 0 ? "S" : "T", i, + j == 0 ? "S" : "T", i); + } + } + } } + if (cc_features.opt_fog) { append_line(buf, &len, " result.fog = fog;"); } + if (cc_features.opt_grayscale) { + append_line(buf, &len, " result.grayscale = grayscale;"); + } for (int i = 0; i < cc_features.num_inputs; i++) { len += sprintf(buf + len, " result.input%d = input%d;\r\n", i + 1, i + 1); } @@ -249,45 +274,81 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f if (include_root_signature) { append_line(buf, &len, "[RootSignature(RS)]"); } - append_line(buf, &len, "float4 PSMain(PSInput input) : SV_TARGET {"); - if (cc_features.used_textures[0]) { - if (three_point_filtering) { - append_line(buf, &len, " float4 texVal0;"); - append_line(buf, &len, " if (textures[0].linear_filtering)"); - append_line(buf, &len, " texVal0 = tex2D3PointFilter(g_texture0, g_sampler0, input.uv, float2(textures[0].width, textures[0].height));"); - append_line(buf, &len, " else"); - append_line(buf, &len, " texVal0 = g_texture0.Sample(g_sampler0, input.uv);"); - } else { - append_line(buf, &len, " float4 texVal0 = g_texture0.Sample(g_sampler0, input.uv);"); + append_line(buf, &len, "float4 PSMain(PSInput input, float4 screenSpace : SV_Position) : SV_TARGET {"); + + // Reference approach to color wrapping as per GLideN64 + // Return wrapped value of x in interval [low, high) + // Mod implementation of GLSL sourced from https://registry.khronos.org/OpenGL-Refpages/gl4/html/mod.xhtml + append_line(buf, &len, "#define MOD(x, y) ((x) - (y) * floor((x)/(y)))"); + append_line(buf, &len, "#define WRAP(x, low, high) MOD((x)-(low), (high)-(low)) + (low)"); + + for (int i = 0; i < 2; i++) { + if (cc_features.used_textures[i]) { + len += sprintf(buf + len, " float2 tc%d = input.uv%d;\r\n", i, i); + bool s = cc_features.clamp[i][0], t = cc_features.clamp[i][1]; + if (!s && !t) { + } else { + len += sprintf(buf + len, " int2 texSize%d;\r\n", i); + len += sprintf(buf + len, " g_texture%d.GetDimensions(texSize%d.x, texSize%d.y);\r\n", i, i, i); + if (s && t) { + len += sprintf( + buf + len, + " tc%d = clamp(tc%d, 0.5 / texSize%d, float2(input.texClampS%d, input.texClampT%d));\r\n", i, + i, i, i, i); + } else if (s) { + len += sprintf(buf + len, + " tc%d = float2(clamp(tc%d.x, 0.5 / texSize%d.x, input.texClampS%d), tc%d.y);\n", + i, i, i, i, i); + } else { + len += sprintf(buf + len, + " tc%d = float2(tc%d.x, clamp(tc%d.y, 0.5 / texSize%d.y, input.texClampT%d));\n", + i, i, i, i, i); + } + } + if (three_point_filtering) { + len += sprintf(buf + len, " float4 texVal%d;\r\n", i); + len += sprintf(buf + len, " if (textures[%d].linear_filtering)\r\n", i); + len += sprintf(buf + len, + " texVal%d = tex2D3PointFilter(g_texture%d, g_sampler%d, tc%d, " + "float2(textures[%d].width, textures[%d].height));\r\n", + i, i, i, i, i, i); + len += sprintf(buf + len, " else\r\n"); + len += sprintf(buf + len, " texVal%d = g_texture%d.Sample(g_sampler%d, tc%d);\r\n", i, i, i, i); + } else { + len += + sprintf(buf + len, " float4 texVal%d = g_texture%d.Sample(g_sampler%d, tc%d);\r\n", i, i, i, i); + } } } - if (cc_features.used_textures[1]) { - if (three_point_filtering) { - append_line(buf, &len, " float4 texVal1;"); - append_line(buf, &len, " if (textures[1].linear_filtering)"); - append_line(buf, &len, " texVal1 = tex2D3PointFilter(g_texture1, g_sampler1, input.uv, float2(textures[1].width, textures[1].height));"); - append_line(buf, &len, " else"); - append_line(buf, &len, " texVal1 = g_texture1.Sample(g_sampler1, input.uv);"); + + append_str(buf, &len, cc_features.opt_alpha ? " float4 texel;" : " float3 texel;"); + for (int c = 0; c < (cc_features.opt_2cyc ? 2 : 1); c++) { + append_str(buf, &len, "texel = "); + if (!cc_features.color_alpha_same[c] && cc_features.opt_alpha) { + append_str(buf, &len, "float4("); + append_formula(buf, &len, cc_features.c[c], cc_features.do_single[c][0], cc_features.do_multiply[c][0], + cc_features.do_mix[c][0], false, false, true); + append_str(buf, &len, ", "); + append_formula(buf, &len, cc_features.c[c], cc_features.do_single[c][1], cc_features.do_multiply[c][1], + cc_features.do_mix[c][1], true, true, true); + append_str(buf, &len, ")"); } else { - append_line(buf, &len, " float4 texVal1 = g_texture1.Sample(g_sampler1, input.uv);"); + append_formula(buf, &len, cc_features.c[c], cc_features.do_single[c][0], cc_features.do_multiply[c][0], + cc_features.do_mix[c][0], cc_features.opt_alpha, false, cc_features.opt_alpha); } - } + append_line(buf, &len, ";"); - append_str(buf, &len, cc_features.opt_alpha ? " float4 texel = " : " float3 texel = "); - if (!cc_features.color_alpha_same && cc_features.opt_alpha) { - append_str(buf, &len, "float4("); - append_formula(buf, &len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], false, false, true); - append_str(buf, &len, ", "); - append_formula(buf, &len, cc_features.c, cc_features.do_single[1], cc_features.do_multiply[1], cc_features.do_mix[1], true, true, true); - append_str(buf, &len, ")"); - } else { - append_formula(buf, &len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], cc_features.opt_alpha, false, cc_features.opt_alpha); + if (c == 0) { + append_str(buf, &len, "texel = WRAP(texel, -1.01, 1.01);"); + } } - append_line(buf, &len, ";"); if (cc_features.opt_texture_edge && cc_features.opt_alpha) { - append_line(buf, &len, " if (texel.a > 0.3) texel.a = 1.0; else discard;"); + append_line(buf, &len, " if (texel.a > 0.19) texel.a = 1.0; else discard;"); } + + append_str(buf, &len, "texel = WRAP(texel, -0.51, 1.51);"); + append_str(buf, &len, "texel = clamp(texel, 0.0, 1.0);"); // TODO discard if alpha is 0? if (cc_features.opt_fog) { if (cc_features.opt_alpha) { @@ -297,12 +358,25 @@ void gfx_direct3d_common_build_shader(char buf[4096], size_t& len, size_t& num_f } } + if (cc_features.opt_grayscale) { + append_line(buf, &len, "float intensity = (texel.r + texel.g + texel.b) / 3.0;"); + append_line(buf, &len, "float3 new_texel = input.grayscale.rgb * intensity;"); + append_line(buf, &len, "texel.rgb = lerp(texel.rgb, new_texel, input.grayscale.a);"); + } + if (cc_features.opt_alpha && cc_features.opt_noise) { - append_line(buf, &len, " float2 coords = (input.screenPos.xy / input.screenPos.w) * noise_scale;"); - append_line(buf, &len, " texel.a *= round(saturate(random(float3(floor(coords), noise_frame)) + texel.a - 0.5));"); + append_line(buf, &len, " float2 coords = screenSpace.xy * noise_scale;"); + append_line(buf, &len, + " texel.a *= round(saturate(random(float3(floor(coords), noise_frame)) + texel.a - 0.5));"); } if (cc_features.opt_alpha) { + if (cc_features.opt_alpha_threshold) { + append_line(buf, &len, " if (texel.a < 8.0 / 256.0) discard;"); + } + if (cc_features.opt_invisible) { + append_line(buf, &len, " texel.a = 0.0;"); + } append_line(buf, &len, " return texel;"); } else { append_line(buf, &len, " return float4(texel, 1.0);"); diff --git a/src/pc/gfx/gfx_dxgi.cpp b/src/pc/gfx/gfx_dxgi.cpp index 180091fd2..6d2d1386f 100644 --- a/src/pc/gfx/gfx_dxgi.cpp +++ b/src/pc/gfx/gfx_dxgi.cpp @@ -1,4 +1,4 @@ -#if 0 +#ifdef WAPI_DXGI #include #include @@ -78,6 +78,8 @@ static struct { ComPtr factory; ComPtr swap_chain; HANDLE waitable_object; + ComPtr swap_chain_device; // D3D11 Device or D3D12 Command Queue + std::function before_destroy_swap_chain_fn; uint64_t qpc_init, qpc_freq; uint64_t frame_timestamp; // in units of 1/FRAME_INTERVAL_US_DENOMINATOR microseconds std::map frame_stats; @@ -265,21 +267,6 @@ static void gfx_dxgi_set_fullscreen(void) { } } -static void gfx_dxgi_on_resize(void) { - if (dxgi.swap_chain.Get() != nullptr) { - gfx_get_current_rendering_api()->on_resize(); - - DXGI_SWAP_CHAIN_DESC1 desc1; - ThrowIfFailed(dxgi.swap_chain->GetDesc1(&desc1)); - dxgi.current_width = desc1.Width; - dxgi.current_height = desc1.Height; - if (!dxgi.is_full_screen && !gfx_dxgi_is_window_maximized() && !configWindow.exiting_fullscreen) { - configWindow.w = dxgi.current_width; - configWindow.h = dxgi.current_height; - } - } -} - static void onkeydown(WPARAM w_param, LPARAM l_param) { int key = ((l_param >> 16) & 0x1ff); if (dxgi.on_key_down != nullptr) { @@ -296,7 +283,12 @@ static void onkeyup(WPARAM w_param, LPARAM l_param) { static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_param, LPARAM l_param) { switch (message) { case WM_SIZE: - gfx_dxgi_on_resize(); + dxgi.current_width = (uint32_t)(l_param & 0xffff); + dxgi.current_height = (uint32_t)(l_param >> 16); + if (!dxgi.is_full_screen && !gfx_dxgi_is_window_maximized() && !configWindow.exiting_fullscreen) { + configWindow.w = dxgi.current_width; + configWindow.h = dxgi.current_height; + } case WM_MOVE: if (!dxgi.is_full_screen && !gfx_dxgi_is_window_maximized() && !configWindow.exiting_fullscreen) { configWindow.x = GET_X_LPARAM(l_param); @@ -639,12 +631,12 @@ void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*crea SetWindowTextW(dxgi.h_wnd, w_title); } -ComPtr gfx_dxgi_create_swap_chain(IUnknown *device) { +void gfx_dxgi_create_swap_chain(IUnknown* device, std::function&& before_destroy_fn) { bool win8 = IsWindows8OrGreater(); // DXGI_SCALING_NONE is only supported on Win8 and beyond bool dxgi_13 = dxgi.CreateDXGIFactory2 != nullptr; // DXGI 1.3 introduced waitable object DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {}; - swap_chain_desc.BufferCount = 2; + swap_chain_desc.BufferCount = 3; swap_chain_desc.Width = 0; swap_chain_desc.Height = 0; swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; @@ -679,7 +671,9 @@ ComPtr gfx_dxgi_create_swap_chain(IUnknown *device) { dxgi.current_width = swap_chain_desc.Width; dxgi.current_height = swap_chain_desc.Height; - return dxgi.swap_chain; + // ALOTODO: This does nothing, added to make updated DX11 render work + dxgi.swap_chain_device = device; + dxgi.before_destroy_swap_chain_fn = std::move(before_destroy_fn); } extern "C" HWND gfx_dxgi_get_h_wnd(void) { @@ -690,6 +684,10 @@ void gfx_dxgi_shutdown(void) { //dxgi.is_running = false; } +IDXGISwapChain1* gfx_dxgi_get_swap_chain() { + return dxgi.swap_chain.Get(); +} + void ThrowIfFailed(HRESULT res) { if (FAILED(res)) { fprintf(stderr, "Error: 0x%08X\n", res); diff --git a/src/pc/gfx/gfx_dxgi.h b/src/pc/gfx/gfx_dxgi.h index 1c16cb122..390f06412 100644 --- a/src/pc/gfx/gfx_dxgi.h +++ b/src/pc/gfx/gfx_dxgi.h @@ -4,10 +4,15 @@ #include "gfx_rendering_api.h" #ifdef DECLARE_GFX_DXGI_FUNCTIONS +#include + +#include + void gfx_dxgi_create_factory_and_device(bool debug, int d3d_version, bool (*create_device_fn)(IDXGIAdapter1 *adapter, bool test_only)); -Microsoft::WRL::ComPtr gfx_dxgi_create_swap_chain(IUnknown *device); +void gfx_dxgi_create_swap_chain(IUnknown* device, std::function&& before_destroy_fn); extern "C" void gfx_dxgi_set_cursor_visibility(bool visible); extern "C" HWND gfx_dxgi_get_h_wnd(void); +IDXGISwapChain1* gfx_dxgi_get_swap_chain(); void ThrowIfFailed(HRESULT res); void ThrowIfFailed(HRESULT res, HWND h_wnd, const char *message); #endif diff --git a/src/pc/gfx/gfx_opengl.cpp b/src/pc/gfx/gfx_opengl.cpp index 588afd0a9..863a2c611 100644 --- a/src/pc/gfx/gfx_opengl.cpp +++ b/src/pc/gfx/gfx_opengl.cpp @@ -1,5 +1,5 @@ //#include "core/Window.h" -#if 1 +#ifdef RAPI_GL #include #include @@ -52,6 +52,7 @@ #ifndef _LANGUAGE_C #define _LANGUAGE_C #endif +//#include "libultraship/libultra/gbi.h" #include #if defined(__APPLE__) || defined(USE_OPENGLES) diff --git a/src/pc/gfx/gfx_pc.cpp b/src/pc/gfx/gfx_pc.cpp index f8fd41df9..221425b73 100644 --- a/src/pc/gfx/gfx_pc.cpp +++ b/src/pc/gfx/gfx_pc.cpp @@ -14,11 +14,14 @@ #include #include -// ALONOTE: OTR and Imgui functionally was commented out to make this work standalone +// ALONOTE: OTR and Imgui functionally was commented out to make this work standalone +//#include "libultraship/libultra/types.h" +//#include "libultraship/libultra/gbi.h" +//#include "libultraship/libultra/gs2dex.h" #include #include -//#include "misc/Cvar.h" +//#include "core/bridge/consolevariablebridge.h" #include "gfx_pc.h" #include "gfx_cc.h" @@ -44,17 +47,10 @@ #include "resource/ResourceMgr.h" #include "misc/Utils.h" -extern "C" { -const char* ResourceMgr_GetNameByCRC(uint64_t crc); -int32_t* ResourceMgr_LoadMtxByCRC(uint64_t crc); -Vtx* ResourceMgr_LoadVtxByCRC(uint64_t crc); -Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc); -char* ResourceMgr_LoadTexByCRC(uint64_t crc); -void ResourceMgr_RegisterResourcePatch(uint64_t hash, uint32_t instrIndex, uintptr_t origData); -char* ResourceMgr_LoadTexByName(char* texPath); -int ResourceMgr_OTRSigCheck(char* imgData); +#include "libultraship.h" } - */ +*/ + uintptr_t gfxFramebuffer; using namespace std; @@ -2237,20 +2233,12 @@ static void gfx_run_dl(Gfx* cmd) { } break; case G_MTX: { uintptr_t mtxAddr = cmd->words.w1; - - // OTRTODO: Temp way of dealing with gMtxClear. Need something more elegant in the future... /* - uint32_t gameVersion = Ship::Window::GetInstance()->GetResourceManager()->GetGameVersion(); - if (gameVersion == OOT_PAL_GC) { - if (mtxAddr == SEG_ADDR(0, 0x0FBC20)) { - mtxAddr = clearMtx; - } - } else { - if (mtxAddr == SEG_ADDR(0, 0x12DB20) || mtxAddr == SEG_ADDR(0, 0x12DB40)) { - mtxAddr = clearMtx; - } + if (mtxAddr == SEG_ADDR(0, 0x12DB20) || mtxAddr == SEG_ADDR(0, 0x12DB40) || + mtxAddr == SEG_ADDR(0, 0xFBC20)) { + mtxAddr = clearMtx; } - */ +*/ #ifdef F3DEX_GBI_2 gfx_sp_matrix(C0(0, 8) ^ G_MTX_PUSH, (const int32_t*)seg_addr(mtxAddr)); #else @@ -2264,14 +2252,7 @@ static void gfx_run_dl(Gfx* cmd) { uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; -#if _DEBUG - // char fileName[4096]; - // ResourceMgr_GetNameByCRC(hash, fileName); - - // printf("G_MTX_OTR: %s\n", fileName); -#endif - - int32_t* mtx = ResourceMgr_LoadMtxByCRC(hash); + int32_t* mtx = (int32_t*)GetResourceDataByCrc(hash); #ifdef F3DEX_GBI_2 if (mtx != NULL) { @@ -2324,22 +2305,21 @@ static void gfx_run_dl(Gfx* cmd) { break; /* case G_VTX_OTR: { + // Offset added to the start of the vertices uintptr_t offset = cmd->words.w1; + // This is a two-part display list command, so increment the instruction pointer so we can get the CRC64 + // hash from the second cmd++; uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + cmd->words.w1; -#if _DEBUG - // char fileName[4096]; - // ResourceMgr_GetNameByCRC(hash, fileName); - - // printf("G_VTX_OTR: %s, 0x%08X\n", fileName, hash); -#endif + // We need to know if the offset is a cached pointer or not. An offset greater than one million is not a + // real offset, so it must be a real pointer if (offset > 0xFFFFF) { cmd--; gfx_sp_vertex(C0(12, 8), C0(1, 7) - C0(12, 8), (Vtx*)offset); cmd++; } else { - Vtx* vtx = ResourceMgr_LoadVtxByCRC(hash); + Vtx* vtx = (Vtx*)GetResourceDataByCrc(hash); if (vtx != NULL) { vtx = (Vtx*)((char*)vtx + offset); @@ -2347,7 +2327,10 @@ static void gfx_run_dl(Gfx* cmd) { cmd--; if (ourHash != (uint64_t)-1) { - ResourceMgr_RegisterResourcePatch(ourHash, cmd - dListStart, cmd->words.w1); + auto res = LoadResource(ourHash); + if (res != nullptr) { + res->RegisterResourceAddressPatch(ourHash, cmd - dListStart, offset); + } } cmd->words.w1 = (uintptr_t)vtx; @@ -2391,7 +2374,7 @@ static void gfx_run_dl(Gfx* cmd) { // printf("G_DL_OTR: %s\n", fileName); #endif - Gfx* gfx = ResourceMgr_LoadGfxByCRC(hash); + Gfx* gfx = (Gfx*)GetResourceDataByCrc(hash); if (gfx != 0) { gfx_run_dl(gfx); @@ -2423,7 +2406,7 @@ static void gfx_run_dl(Gfx* cmd) { // printf("G_BRANCH_Z_OTR: %s\n", fileName); #endif - Gfx* gfx = ResourceMgr_LoadGfxByCRC(hash); + Gfx* gfx = (Gfx*)GetResourceDataByCrc(hash); if (gfx != 0) { cmd = gfx; @@ -2545,8 +2528,8 @@ static void gfx_run_dl(Gfx* cmd) { char* imgData = (char*)i; if ((i & 1) != 1) { - if (ResourceMgr_OTRSigCheck(imgData) == 1) { - i = (uintptr_t)ResourceMgr_LoadTexByName(imgData); + if (Ship::Window::GetInstance()->GetResourceManager()->OtrSignatureCheck(imgData) == 1) { + i = (uintptr_t)GetResourceDataByName(imgData); } } @@ -2560,19 +2543,14 @@ static void gfx_run_dl(Gfx* cmd) { uintptr_t addr = cmd->words.w1; cmd++; uint64_t hash = ((uint64_t)cmd->words.w0 << 32) + (uint64_t)cmd->words.w1; - fileName = ResourceMgr_GetNameByCRC(hash); -#if _DEBUG && 0 - char* tex = ResourceMgr_LoadTexByCRC(hash); - ResourceMgr_GetNameByCRC(hash, fileName); - printf("G_SETTIMG_OTR: %s, %08X\n", fileName, hash); -#else + fileName = GetResourceNameByCrc(hash); + char* tex = NULL; -#endif if (addr != 0) { tex = (char*)addr; } else { - tex = ResourceMgr_LoadTexByCRC(hash); + tex = (char*)GetResourceDataByCrc(hash); if (tex != nullptr) { cmd--; @@ -2580,7 +2558,10 @@ static void gfx_run_dl(Gfx* cmd) { cmd->words.w1 = (uintptr_t)tex; if (ourHash != (uint64_t)-1) { - ResourceMgr_RegisterResourcePatch(ourHash, cmd - dListStart, oldData); + auto res = LoadResource(ourHash); + if (res != nullptr) { + res->RegisterResourceAddressPatch(ourHash, cmd - dListStart, oldData); + } } cmd++; @@ -2750,9 +2731,9 @@ void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, co //#if defined(__APPLE__) gfx_current_dimensions.internal_mul = 1; // ALOTODO: This does nothing //#else - //gfx_current_dimensions.internal_mul = CVar_GetFloat("gInternalResolution", 1); + //gfx_current_dimensions.internal_mul = CVarGetFloat("gInternalResolution", 1); //#endif - //gfx_msaa_level = CVar_GetS32("gMSAAValue", 1); + //gfx_msaa_level = CVarGetInteger("gMSAAValue", 1); gfx_msaa_level = 1; // ALONOTE: MSAA works if ENABLE_FRAMEBUFFER is set #ifndef __WIIU__ // Wii U overrides dimentions in gfx_wapi->init to match framebuffer size gfx_current_dimensions.width = width; diff --git a/src/pc/gfx/gfx_pc.h b/src/pc/gfx/gfx_pc.h index 46bb26028..55b3ef424 100644 --- a/src/pc/gfx/gfx_pc.h +++ b/src/pc/gfx/gfx_pc.h @@ -10,6 +10,9 @@ #include #endif +//#include "libultraship/libultra/gbi.h" +//#include "libultraship/libultra/types.h" + #include #include diff --git a/src/pc/gfx/gfx_window_manager_api.h b/src/pc/gfx/gfx_window_manager_api.h index 3491ce6d5..9a0b189a7 100644 --- a/src/pc/gfx/gfx_window_manager_api.h +++ b/src/pc/gfx/gfx_window_manager_api.h @@ -27,7 +27,7 @@ struct GfxWindowManagerAPI { #endif //void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen)); // config fullscreen replaces it //void (*set_fullscreen)(bool enable); // config fullscreen replaces it - //void (*show_cursor)(bool hide); // controller_mouse.c takes care of it + //void (*set_cursor_visibility)(bool visible); // controller_mouse.c takes care of it void (*main_loop)(void (*run_one_game_iter)(void)); void (*get_dimensions)(uint32_t* width, uint32_t* height); void (*handle_events)(void); diff --git a/src/pc/pc_main.cpp b/src/pc/pc_main.cpp index 5da68f717..0e4193129 100644 --- a/src/pc/pc_main.cpp +++ b/src/pc/pc_main.cpp @@ -24,9 +24,9 @@ extern "C" { extern "C" { #endif #include "gfx/gfx_opengl.h" -//#include "gfx/gfx_direct3d11.h" +#include "gfx/gfx_direct3d11.h" //#include "gfx/gfx_direct3d12.h" -//#include "gfx/gfx_dxgi.h" +#include "gfx/gfx_dxgi.h" #include "gfx/gfx_sdl.h" //#include "gfx/gfx_gx2.h" //#include "gfx/gfx_3ds.h"