port: replace old fast3d with libultraship-fast3d
This commit is contained in:
parent
157d46b73a
commit
9508b136ff
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
1805
port/fast3d/gfx_pc.c
1805
port/fast3d/gfx_pc.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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
Loading…
Reference in New Issue