port: replace old fast3d with libultraship-fast3d

This commit is contained in:
fgsfds 2023-08-01 23:37:49 +02:00
parent 157d46b73a
commit 9508b136ff
17 changed files with 7915 additions and 3279 deletions

View File

@ -1,19 +1,21 @@
MIT License
Copyright (c) 2020 Emill, MaikelChan
Redistribution and use in source forms, with or without modification,
are permitted provided that the following conditions are met:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form are not allowed except in cases where the binary contains no assets you do not have the right to distribute.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

42
port/fast3d/gfx_api.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef GFX_API_H
#define GFX_API_H
#ifndef __cplusplus
#include <stdint.h>
#include <stdbool.h>
#endif
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_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;
void gfx_init(struct GfxWindowManagerAPI* wapi, struct GfxRenderingAPI* rapi, const char* game_name,
bool start_in_fullscreen, uint32_t width, uint32_t height,
uint32_t posX, uint32_t posY);
void gfx_destroy(void);
struct GfxRenderingAPI* gfx_get_current_rendering_api(void);
void gfx_start_frame(void);
void gfx_run(Gfx* commands);
void gfx_end_frame(void);
void gfx_set_target_fps(int);
void gfx_set_maximum_frame_latency(int latency);
void gfx_texture_cache_clear(void);
int gfx_create_framebuffer(uint32_t width, uint32_t height);
void gfx_get_pixel_depth_prepare(float x, float y);
uint16_t gfx_get_pixel_depth(float x, float y);
#endif

View File

@ -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);
}

76
port/fast3d/gfx_cc.cpp Normal file
View File

@ -0,0 +1,76 @@
#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->used_masks[0] = false;
cc_features->used_masks[1] = false;
cc_features->used_blend[0] = false;
cc_features->used_blend[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);
}
if (cc_features->used_textures[0] && (shader_id1 & SHADER_OPT_TEXEL0_MASK)) {
cc_features->used_masks[0] = true;
}
if (cc_features->used_textures[1] && (shader_id1 & SHADER_OPT_TEXEL1_MASK)) {
cc_features->used_masks[1] = true;
}
if (cc_features->used_textures[0] && (shader_id1 & SHADER_OPT_TEXEL0_BLEND)) {
cc_features->used_blend[0] = true;
}
if (cc_features->used_textures[1] && (shader_id1 & SHADER_OPT_TEXEL1_BLEND)) {
cc_features->used_blend[1] = true;
}
}

View File

@ -4,7 +4,11 @@
#include <stdint.h>
#include <stdbool.h>
enum {
#ifdef __cplusplus
#include <compare>
#endif
/*enum {
CC_0,
CC_TEXEL0,
CC_TEXEL1,
@ -13,7 +17,7 @@ enum {
CC_ENV,
CC_TEXEL0A,
CC_LOD
};
};*/
enum {
SHADER_0,
@ -21,38 +25,67 @@ 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 SHADER_OPT_TEXEL0_MASK (1 << 12)
#define SHADER_OPT_TEXEL1_MASK (1 << 13)
#define SHADER_OPT_TEXEL0_BLEND (1 << 14)
#define SHADER_OPT_TEXEL1_BLEND (1 << 15)
struct ColorCombinerKey {
uint64_t combine_mode;
uint64_t options;
auto operator<=>(const ColorCombinerKey&) const = default;
};
#define SHADER_MAX_TEXTURES 6
#define SHADER_FIRST_TEXTURE 0
#define SHADER_FIRST_MASK_TEXTURE 2
#define SHADER_FIRST_REPLACEMENT_TEXTURE 4
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 used_masks[2];
bool used_blend[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

View File

@ -1,510 +0,0 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <SDL.h>
#ifndef _LANGUAGE_C
#define _LANGUAGE_C
#endif
#include <PR/gbi.h>
#include "glad/glad.h"
#include "gfx_cc.h"
#include "gfx_rendering_api.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;
};
static struct ShaderProgram shader_program_pool[64];
static uint8_t shader_program_pool_size;
static GLuint opengl_vbo;
static uint32_t frame_count;
static uint32_t current_height;
static bool gfx_opengl_z_is_from_0_to_1(void) {
return false;
}
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) {
if (prg->used_noise) {
glUniform1i(prg->frame_count_location, frame_count);
glUniform1i(prg->window_height_location, current_height);
}
}
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) {
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';
}
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 "";
}
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[1024];
size_t vs_len = 0;
size_t fs_len = 0;
size_t num_floats = 4;
// Vertex shader
append_line(vs_buf, &vs_len, "#version 110");
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;
}
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);
}
append_line(vs_buf, &vs_len, "gl_Position = aVtxPos;");
append_line(vs_buf, &vs_len, "}");
// Fragment shader
append_line(fs_buf, &fs_len, "#version 110");
//append_line(fs_buf, &fs_len, "precision mediump float;");
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.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 frame_count;");
append_line(fs_buf, &fs_len, "uniform int window_height;");
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, "}");
}
append_line(fs_buf, &fs_len, "void main() {");
if (cc_features.used_textures[0]) {
append_line(fs_buf, &fs_len, "vec4 texVal0 = texture2D(uTex0, vTexCoord);");
}
if (cc_features.used_textures[1]) {
append_line(fs_buf, &fs_len, "vec4 texVal1 = texture2D(uTex1, vTexCoord);");
}
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, "texel.a *= floor(clamp(random(vec3(floor(gl_FragCoord.xy * (240.0 / float(window_height))), float(frame_count))) + 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");*/
const GLchar *sources[2] = { vs_buf, fs_buf };
const GLint lengths[2] = { vs_len, 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[shader_program_pool_size++];
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->frame_count_location = glGetUniformLocation(shader_program, "frame_count");
prg->window_height_location = glGetUniformLocation(shader_program, "window_height");
prg->used_noise = true;
} else {
prg->used_noise = false;
}
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) {
glActiveTexture(GL_TEXTURE0 + tile);
glBindTexture(GL_TEXTURE_2D, texture_id);
}
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);
}
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) {
glActiveTexture(GL_TEXTURE0 + tile);
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);
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) {
if (depth_test) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
}
static void gfx_opengl_set_depth_mask(bool z_upd) {
glDepthMask(z_upd ? GL_TRUE : GL_FALSE);
}
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);
current_height = 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) {
gladLoadGLES2Loader(SDL_GL_GetProcAddress);
glGenBuffers(1, &opengl_vbo);
glBindBuffer(GL_ARRAY_BUFFER, opengl_vbo);
glDepthFunc(GL_LEQUAL);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
static void gfx_opengl_on_resize(void) {
}
static void gfx_opengl_start_frame(void) {
frame_count++;
glDisable(GL_SCISSOR_TEST);
glDepthMask(GL_TRUE); // Must be set to clear Z-buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
}
static void gfx_opengl_end_frame(void) {
}
static void gfx_opengl_finish_render(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_init,
gfx_opengl_on_resize,
gfx_opengl_start_frame,
gfx_opengl_end_frame,
gfx_opengl_finish_render
};

1013
port/fast3d/gfx_opengl.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

2600
port/fast3d/gfx_pc.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,35 +2,57 @@
#define GFX_PC_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <unordered_map>
#include <list>
#include <cstddef>
#ifndef _LANGUAGE_C
#define _LANGUAGE_C
#endif
#include <PR/gbi.h>
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
extern uintptr_t gfxFramebuffer;
struct GfxRenderingAPI;
struct GfxWindowManagerAPI;
struct GfxDimensions {
uint32_t width, height;
float aspect_ratio;
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));
}
};
};
extern struct GfxDimensions gfx_current_dimensions;
typedef std::unordered_map<TextureCacheKey, struct TextureCacheValue, TextureCacheKey::Hasher> TextureCacheMap;
typedef std::pair<const TextureCacheKey, struct TextureCacheValue> TextureCacheNode;
struct TextureCacheValue {
uint32_t texture_id;
uint8_t cms, cmt;
bool linear_filter;
std::list<struct TextureCacheMapIter>::iterator lru_location;
};
struct TextureCacheMapIter {
TextureCacheMap::iterator it;
};
#ifdef __cplusplus
extern "C" {
#endif
void gfx_init(struct GfxWindowManagerAPI *wapi, struct GfxRenderingAPI *rapi, const char *game_name, bool start_in_fullscreen);
struct GfxRenderingAPI *gfx_get_current_rendering_api(void);
void gfx_start_frame(void);
void gfx_run(Gfx *commands);
void gfx_end_frame(void);
#include "gfx_api.h"
#ifdef __cplusplus
}
#endif
#endif

View File

@ -7,19 +7,27 @@
struct ShaderProgram;
struct GfxClipParameters {
bool z_is_from_0_to_1;
bool invert_y;
};
enum FilteringMode { FILTER_THREE_POINT, FILTER_LINEAR, FILTER_NONE };
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]);
const char* (*get_name)(void);
int (*get_max_texture_size)(void);
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 (*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);
@ -30,6 +38,18 @@ struct GfxRenderingAPI {
void (*start_frame)(void);
void (*end_frame)(void);
void (*finish_render)(void);
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);
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)(enum FilteringMode mode);
enum FilteringMode (*get_texture_filter)(void);
};
#endif

View File

@ -1,191 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <SDL.h>
#include "gfx_window_manager_api.h"
#include "gfx_screen_config.h"
#define GFX_API_NAME "SDL2 - OpenGL"
static SDL_Window *wnd;
static int vsync_enabled = 0;
static unsigned int window_width = DESIRED_SCREEN_WIDTH;
static unsigned int window_height = DESIRED_SCREEN_HEIGHT;
static bool fullscreen_state;
static void (*on_fullscreen_changed_callback)(bool is_now_fullscreen);
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;
} 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);
if (on_fullscreen_changed_callback != NULL && call_callback) {
on_fullscreen_changed_callback(on);
}
}
static void test_vsync(void) {
// Even if SDL_GL_SetSwapInterval succeeds, it doesn't mean that VSync actually works.
// A 60 Hz monitor should have a swap interval of 16.67 milliseconds.
// Try to detect the length of a vsync by swapping buffers some times.
// Since the graphics card may enqueue a fixed number of frames,
// first send in four dummy frames to hopefully fill the queue.
// This method will fail if the refresh rate is changed, which, in
// combination with that we can't control the queue size (i.e. lag)
// is a reason this generic SDL2 backend should only be used as last resort.
Uint32 start;
Uint32 end;
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
start = SDL_GetTicks();
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
SDL_GL_SwapWindow(wnd);
end = SDL_GetTicks();
float average = 4.0 * 1000.0 / (end - start);
vsync_enabled = 1;
if (average > 27 && average < 33) {
SDL_GL_SetSwapInterval(1);
} else if (average > 57 && average < 63) {
SDL_GL_SetSwapInterval(2);
} else if (average > 86 && average < 94) {
SDL_GL_SetSwapInterval(3);
} else if (average > 115 && average < 125) {
SDL_GL_SetSwapInterval(4);
} else {
vsync_enabled = 0;
}
}
static void gfx_sdl_init(const char *game_name, bool start_in_fullscreen) {
SDL_Init(SDL_INIT_VIDEO);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
//SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
char title[512];
int len = sprintf(title, "%s (%s)", game_name, GFX_API_NAME);
wnd = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
window_width, window_height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (start_in_fullscreen) {
set_fullscreen(true, false);
}
SDL_GL_CreateContext(wnd);
SDL_GL_SetSwapInterval(1);
test_vsync();
if (!vsync_enabled)
puts("Warning: VSync is not enabled or not working. Falling back to timer for synchronization");
}
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_main_loop(void (*run_one_game_iter)(void)) {
while (1) {
run_one_game_iter();
}
}
static void gfx_sdl_get_dimensions(uint32_t *width, uint32_t *height) {
*width = window_width;
*height = window_height;
}
static void gfx_sdl_handle_events(void) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
window_width = event.window.data1;
window_height = event.window.data2;
}
break;
case SDL_QUIT:
exit(0);
}
}
}
static bool gfx_sdl_start_frame(void) {
return true;
}
static void sync_framerate_with_timer(void) {
// Number of milliseconds a frame should take (60 fps)
const Uint32 FRAME_TIME = 1000 / 60;
static Uint32 last_time;
Uint32 elapsed = SDL_GetTicks() - last_time;
if (elapsed < FRAME_TIME)
SDL_Delay(FRAME_TIME - elapsed);
last_time += FRAME_TIME;
}
static void gfx_sdl_swap_buffers_begin(void) {
if (!vsync_enabled) {
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_get_window_handle(void) {
return wnd;
}
struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_init,
gfx_sdl_set_fullscreen_changed_callback,
gfx_sdl_set_fullscreen,
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_get_window_handle,
};

223
port/fast3d/gfx_sdl2.cpp Normal file
View File

@ -0,0 +1,223 @@
#include <stdio.h>
#include <SDL.h>
#ifdef __MINGW32__
// mingw has its nanosleep implementation in pthread.h for some reson
#include <pthread.h>
#endif
#include "gfx_window_manager_api.h"
#include "gfx_screen_config.h"
#define GFX_BACKEND_NAME "SDL"
static SDL_Window* wnd;
static SDL_GLContext ctx;
static SDL_Renderer* renderer;
static int sdl_to_lus_table[512];
static bool vsync_enabled = true;
// OTRTODO: These are redundant. Info can be queried from SDL.
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 uint64_t previous_time;
static int target_fps = 60;
static uint64_t qpc_freq;
#define FRAME_INTERVAL_US_NUMERATOR 1000000
#define FRAME_INTERVAL_US_DENOMINATOR (target_fps)
static void set_fullscreen(bool on, bool call_callback) {
if (fullscreen_state == on) {
return;
}
fullscreen_state = on;
}
static void gfx_sdl_get_active_window_refresh_rate(uint32_t* refresh_rate) {
int display_in_use = SDL_GetWindowDisplayIndex(wnd);
SDL_DisplayMode mode;
SDL_GetCurrentDisplayMode(display_in_use, &mode);
*refresh_rate = mode.refresh_rate;
}
static inline void do_sleep(const uint64_t ns) {
const timespec spec = { 0, ns };
nanosleep(&spec, nullptr);
}
static void gfx_sdl_init(const char* game_name, const char* gfx_api_name, bool start_in_fullscreen, uint32_t width,
uint32_t height, int32_t posX, int32_t posY) {
window_width = width;
window_height = height;
SDL_Init(SDL_INIT_VIDEO);
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
bool use_opengl = true;
if (use_opengl) {
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
char title[512];
int len = sprintf(title, "%s (%s)", game_name, gfx_api_name);
Uint32 flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI;
if (use_opengl) {
flags = flags | SDL_WINDOW_OPENGL;
} else {
flags = flags | SDL_WINDOW_METAL;
}
int display_in_use = SDL_GetWindowDisplayIndex(wnd);
if (display_in_use < 0) { // Fallback to default if out of bounds
posX = 100;
posY = 100;
}
wnd = SDL_CreateWindow(title, posX, posY, window_width, window_height, flags);
if (use_opengl) {
ctx = SDL_GL_CreateContext(wnd);
SDL_GL_MakeCurrent(wnd, ctx);
SDL_GL_SetSwapInterval(1);
}
qpc_freq = SDL_GetPerformanceFrequency();
}
static void gfx_sdl_close(void) {
is_running = false;
}
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_set_cursor_visibility(bool visible) {
if (visible) {
SDL_ShowCursor(SDL_ENABLE);
} else {
SDL_ShowCursor(SDL_DISABLE);
}
}
static void gfx_sdl_get_dimensions(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY) {
SDL_GL_GetDrawableSize(wnd, static_cast<int*>((void*)width), static_cast<int*>((void*)height));
SDL_GetWindowPosition(wnd, static_cast<int*>(posX), static_cast<int*>(posY));
}
static void gfx_sdl_handle_events(void) {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_WINDOWEVENT:
if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
SDL_GL_GetDrawableSize(wnd, &window_width, &window_height);
} 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.
exit(0);
}
break;
case SDL_QUIT:
exit(0);
break;
}
}
}
static bool gfx_sdl_start_frame(void) {
return true;
}
static uint64_t qpc_to_100ns(uint64_t qpc) {
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) {
do_sleep(left * 100);
}
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 SDL_GetPerformanceCounter() / (double)qpc_freq;
}
static void gfx_sdl_set_target_fps(int fps) {
target_fps = fps;
}
static bool gfx_sdl_can_disable_vsync(void) {
return false;
}
static void *gfx_sdl_get_window_handle(void) {
return (void *)wnd;
}
static void gfx_sdl_set_window_title(const char *title) {
SDL_SetWindowTitle(wnd, title);
}
struct GfxWindowManagerAPI gfx_sdl = {
gfx_sdl_init,
gfx_sdl_close,
gfx_sdl_set_fullscreen_changed_callback,
gfx_sdl_set_fullscreen,
gfx_sdl_get_active_window_refresh_rate,
gfx_sdl_set_cursor_visibility,
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_can_disable_vsync,
gfx_sdl_get_window_handle,
gfx_sdl_set_window_title
};

View File

@ -5,17 +5,23 @@
#include <stdbool.h>
struct GfxWindowManagerAPI {
void (*init)(const char *game_name, bool start_in_fullscreen);
void (*init)(const char* game_name, const char* gfx_api_name, bool start_in_fullscreen, uint32_t width,
uint32_t height, int32_t posX, int32_t posY);
void (*close)(void);
void (*set_fullscreen_changed_callback)(void (*on_fullscreen_changed)(bool is_now_fullscreen));
void (*set_fullscreen)(bool enable);
void (*main_loop)(void (*run_one_game_iter)(void));
void (*get_dimensions)(uint32_t *width, uint32_t *height);
void (*get_active_window_refresh_rate)(uint32_t* refresh_rate);
void (*set_cursor_visibility)(bool visible);
void (*get_dimensions)(uint32_t* width, uint32_t* height, int32_t* posX, int32_t* posY);
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 (*set_target_fps)(int fps);
bool (*can_disable_vsync)(void);
void *(*get_window_handle)(void);
void (*set_window_title)(const char *);
};
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff