Refactor framebuffer-related stuff (#130)

* Renderer: rename render targets to framebuffers

* Refactor framebuffer pair helper and some of the video API

* Remove hardcoded dimensions from draw_framebuffer_tex

* Make viewport a per-framebuffer property rather than a global one

* Handle config updates via the events system. React to viewport fg/bg quality change requests.
This commit is contained in:
Andrei Alexeyev 2018-07-04 11:36:16 +03:00 committed by GitHub
parent 3f82ce892c
commit b16f402040
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
57 changed files with 953 additions and 772 deletions

View file

@ -164,14 +164,6 @@ Music* get_music(const char *name) {
return get_resource_data(RES_BGM, name, RESF_OPTIONAL);
}
static void sfx_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
audio_backend_set_sfx_volume(config_set_float(idx, v.f));
}
static void bgm_cfg_volume_callback(ConfigIndex idx, ConfigValue v) {
audio_backend_set_bgm_volume(config_set_float(idx, v.f));
}
static bool store_sfx_volume(const char *key, const char *val, void *data) {
int vol = atoi(val);
@ -300,14 +292,32 @@ void start_bgm(const char *name) {
log_info("Started %s", (current_bgm.title ? current_bgm.title : current_bgm.name));
}
static bool audio_config_updated(SDL_Event *evt, void *arg) {
ConfigValue *val = evt->user.data1;
switch(evt->user.code) {
case CONFIG_SFX_VOLUME:
audio_backend_set_sfx_volume(val->f);
break;
case CONFIG_BGM_VOLUME:
audio_backend_set_bgm_volume(val->f);
break;
}
return false;
}
void audio_init(void) {
load_config_files();
audio_backend_init();
config_set_callback(CONFIG_SFX_VOLUME, sfx_cfg_volume_callback);
config_set_callback(CONFIG_BGM_VOLUME, bgm_cfg_volume_callback);
events_register_handler(&(EventHandler) {
audio_config_updated, NULL, EPRIO_SYSTEM, MAKE_TAISEI_EVENT(TE_CONFIG_UPDATED)
});
}
void audio_shutdown(void) {
events_unregister_handler(audio_config_updated);
audio_backend_shutdown();
ht_destroy(&sfx_volumes);
}

View file

@ -17,7 +17,7 @@
static bool config_initialized = false;
CONFIGDEFS_EXPORT ConfigEntry configdefs[] = {
#define CONFIGDEF(type,entryname,default,ufield) {type, entryname, {.ufield = default}, NULL},
#define CONFIGDEF(type,entryname,default,ufield) { type, entryname, { .ufield = default } },
#define CONFIGDEF_KEYBINDING(id,entryname,default) CONFIGDEF(CONFIG_TYPE_KEYBINDING, entryname, default, i)
#define CONFIGDEF_GPKEYBINDING(id,entryname,default) CONFIGDEF(CONFIG_TYPE_GPKEYBINDING, entryname, default, i)
#define CONFIGDEF_INT(id,entryname,default) CONFIGDEF(CONFIG_TYPE_INT, entryname, default, i)
@ -66,12 +66,60 @@ void config_init(void) {
static void config_delete_unknown_entries(void);
static void config_copy_value(ConfigEntryType type, ConfigValue *dst, ConfigValue src) {
switch(type) {
case CONFIG_TYPE_INT:
case CONFIG_TYPE_KEYBINDING:
case CONFIG_TYPE_GPKEYBINDING:
dst->i = src.i;
break;
case CONFIG_TYPE_FLOAT:
dst->f = src.f;
break;
case CONFIG_TYPE_STRING:
stralloc(&dst->s, src.s);
break;
default:
UNREACHABLE;
}
}
static int config_compare_values(ConfigEntryType type, ConfigValue val0, ConfigValue val1) {
switch(type) {
case CONFIG_TYPE_INT:
case CONFIG_TYPE_KEYBINDING:
case CONFIG_TYPE_GPKEYBINDING:
return (val0.i > val1.i) - (val0.i < val1.i);
case CONFIG_TYPE_FLOAT:
return (val0.f > val1.f) - (val0.f < val1.f);
case CONFIG_TYPE_STRING:
return strcmp(val0.s, val1.s);
default:
UNREACHABLE;
}
}
static void config_free_value(ConfigEntryType type, ConfigValue *val) {
switch(type) {
case CONFIG_TYPE_STRING:
free(val->s);
val->s = NULL;
break;
default:
break;
}
}
void config_shutdown(void) {
for(ConfigEntry *e = configdefs; e->name; ++e) {
if(e->type == CONFIG_TYPE_STRING) {
free(e->val.s);
e->val.s = NULL;
}
config_free_value(e->type, &e->val);
}
config_delete_unknown_entries();
@ -152,58 +200,16 @@ KeyIndex config_key_from_gamepad_button(int btn) {
static void config_set_val(ConfigIndex idx, ConfigValue v) {
ConfigEntry *e = config_get(idx);
ConfigCallback callback = e->callback;
bool difference = true;
switch(e->type) {
case CONFIG_TYPE_INT:
case CONFIG_TYPE_KEYBINDING:
case CONFIG_TYPE_GPKEYBINDING:
difference = e->val.i != v.i;
break;
case CONFIG_TYPE_FLOAT:
difference = e->val.f != v.f;
break;
case CONFIG_TYPE_STRING:
difference = strcmp(e->val.s, v.s);
break;
}
if(!difference) {
if(!config_compare_values(e->type, e->val, v)) {
return;
}
if(callback) {
e->callback = NULL;
callback(idx, v);
e->callback = callback;
return;
}
#define PRINTVAL(t) log_debug("%s:" #t " = %" #t, e->name, e->val.t);
switch(e->type) {
case CONFIG_TYPE_INT:
case CONFIG_TYPE_KEYBINDING:
case CONFIG_TYPE_GPKEYBINDING:
e->val.i = v.i;
PRINTVAL(i)
break;
case CONFIG_TYPE_FLOAT:
e->val.f = v.f;
PRINTVAL(f)
break;
case CONFIG_TYPE_STRING:
stralloc(&e->val.s, v.s);
PRINTVAL(s)
break;
}
#undef PRINTVAL
ConfigValue oldv = { 0 };
config_copy_value(e->type, &oldv, e->val);
config_copy_value(e->type, &e->val, v);
events_emit(TE_CONFIG_UPDATED, idx, &e->val, &oldv);
config_free_value(e->type, &oldv);
}
int config_set_int(ConfigIndex idx, int val) {
@ -224,12 +230,6 @@ char* config_set_str(ConfigIndex idx, const char *val) {
return config_get_str(idx);
}
void config_set_callback(ConfigIndex idx, ConfigCallback callback) {
ConfigEntry *e = config_get(idx);
assert(e->callback == NULL);
e->callback = callback;
}
static ConfigEntry* config_get_unknown_entry(const char *name) {
ConfigEntry *e;
ConfigEntryList *l;

View file

@ -177,13 +177,10 @@ typedef union ConfigValue {
char *s;
} ConfigValue;
typedef void (*ConfigCallback)(ConfigIndex, ConfigValue);
typedef struct ConfigEntry {
ConfigEntryType type;
char *name;
ConfigValue val;
ConfigCallback callback;
} ConfigEntry;
#define CONFIG_LOAD_BUFSIZE 256
@ -199,7 +196,6 @@ void config_init(void);
void config_shutdown(void);
void config_load(void);
void config_save(void);
void config_set_callback(ConfigIndex idx, ConfigCallback callback);
#ifndef DEBUG
#define CONFIG_RAWACCESS

View file

@ -71,7 +71,6 @@ static bool events_invoke_handler(SDL_Event *event, EventHandler *handler) {
if(!handler->event_type || handler->event_type == event->type) {
bool result = handler->proc(event, handler->arg);
return result;
}
@ -261,6 +260,7 @@ void events_pause_keyrepeat(void) {
*/
static bool events_handler_quit(SDL_Event *event, void *arg);
static bool events_handler_config(SDL_Event *event, void *arg);
static bool events_handler_keyrepeat_workaround(SDL_Event *event, void *arg);
static bool events_handler_clipboard(SDL_Event *event, void *arg);
static bool events_handler_hotkeys(SDL_Event *event, void *arg);
@ -270,6 +270,7 @@ static bool events_handler_key_up(SDL_Event *event, void *arg);
static EventHandler default_handlers[] = {
{ .proc = events_handler_quit, .priority = EPRIO_SYSTEM, .event_type = SDL_QUIT },
{ .proc = events_handler_config, .priority = EPRIO_SYSTEM, .event_type = 0 },
{ .proc = events_handler_keyrepeat_workaround, .priority = EPRIO_CAPTURE, .event_type = 0 },
{ .proc = events_handler_clipboard, .priority = EPRIO_CAPTURE, .event_type = SDL_KEYDOWN },
{ .proc = events_handler_hotkeys, .priority = EPRIO_HOTKEYS, .event_type = SDL_KEYDOWN },
@ -296,6 +297,27 @@ static bool events_handler_quit(SDL_Event *event, void *arg) {
return true;
}
static bool events_handler_config(SDL_Event *event, void *arg) {
if(event->type != MAKE_TAISEI_EVENT(TE_CONFIG_UPDATED)) {
return false;
}
ConfigValue *val = event->user.data1;
switch(event->user.code) {
case CONFIG_GAMEPAD_ENABLED:
// TODO: Refactor gamepad code so that we don't have to do this.
if(val->i) {
gamepad_init();
} else {
gamepad_shutdown();
}
break;
}
return false;
}
static bool events_handler_keyrepeat_workaround(SDL_Event *event, void *arg) {
hrtime_t timenow = time_get();

View file

@ -15,8 +15,8 @@ typedef enum {
TE_INVALID = -1,
TE_FRAME,
TE_RESOURCE_ASYNC_LOADED,
TE_CONFIG_UPDATED,
#define TE_MENU_FIRST TE_MENU_CURSOR_UP
TE_MENU_CURSOR_UP,

109
src/fbo.c
View file

@ -1,109 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "fbo.h"
#include "global.h"
#include "util.h"
// TODO: rename and move this
Resources resources;
static void init_fbo(FBO *fbo, uint width, uint height, TextureType type) {
Texture *rgba = calloc(1, sizeof(Texture));
Texture *depth = calloc(1, sizeof(Texture));
r_texture_create(rgba, &(TextureParams) {
.width = width,
.height = height,
.type = type,
});
r_texture_create(depth, &(TextureParams) {
.width = width,
.height = height,
.type = TEX_TYPE_DEPTH,
});
r_target_create(fbo);
r_target_attach(fbo, rgba, RENDERTARGET_ATTACHMENT_COLOR0);
r_target_attach(fbo, depth, RENDERTARGET_ATTACHMENT_DEPTH);
log_debug("FBO %p: w=%i, h=%i", (void*)fbo, width, height);
}
static void delete_tex(Texture *tex) {
r_texture_destroy(tex);
free(tex);
}
static void delete_fbo(FBO *fbo) {
delete_tex(r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_COLOR0));
delete_tex(r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_DEPTH));
r_target_destroy(fbo);
}
static void reinit_fbo(FBO *fbo, float scale, TextureType type) {
scale = sanitize_scale(scale);
uint w = VIEWPORT_W * scale;
uint h = VIEWPORT_H * scale;
Texture *rgba;
if(fbo->impl) {
rgba = r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_COLOR0);
} else {
rgba = NULL;
}
if(!rgba) {
init_fbo(fbo, w, h, type);
} else if(w != rgba->w || h != rgba->h) {
delete_fbo(fbo);
init_fbo(fbo, w, h, type);
}
}
static void swap_fbos(FBO **fbo1, FBO **fbo2) {
FBO *temp = *fbo1;
*fbo1 = *fbo2;
*fbo2 = temp;
}
void init_fbo_pair(FBOPair *pair, float scale, TextureType type) {
pair->front = pair->_private.targets+0;
pair->back = pair->_private.targets+1;
reinit_fbo(pair->front, scale, type);
reinit_fbo(pair->back, scale, type);
}
void delete_fbo_pair(FBOPair *pair) {
delete_fbo(pair->front);
delete_fbo(pair->back);
}
void swap_fbo_pair(FBOPair *pair) {
swap_fbos(&pair->front, &pair->back);
}
void draw_fbo(FBO *fbo) {
CullFaceMode cull_saved = r_cull_current();
r_cull(CULL_FRONT);
r_mat_push();
r_texture_ptr(0, r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_COLOR0));
r_mat_scale(VIEWPORT_W, VIEWPORT_H, 1);
r_mat_translate(0.5, 0.5, 0);
r_mat_scale(1, -1, 1);
r_draw_quad();
r_mat_pop();
r_cull(cull_saved);
}

View file

@ -1,48 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#pragma once
#include "taisei.h"
#include "renderer/api.h"
typedef RenderTarget FBO;
typedef struct FBOPair {
/*
* Rule of thumb:
* 1. Bind back buffer;
* 2. Draw front buffer;
* 3. Call swap_fbo_pair;
* 4. Rinse, repeat.
*/
FBO *front;
FBO *back;
struct {
FBO targets[2];
} _private;
} FBOPair;
void init_fbo_pair(FBOPair *pair, float scale, TextureType type);
void delete_fbo_pair(FBOPair *pair);
void swap_fbo_pair(FBOPair *pair);
void draw_fbo(FBO *fbo);
// TODO: rename and move this
typedef struct Resources {
struct {
FBOPair bg;
FBOPair fg;
FBOPair rgba;
} fbo_pairs;
} Resources;
extern Resources resources;

View file

@ -198,31 +198,7 @@ static int gamepad_find_device(char *guid_out, size_t guid_out_sz, int *out_loca
return -1;
}
static void gamepad_cfg_enabled_callback(ConfigIndex idx, ConfigValue v) {
config_set_int(idx, v.i);
if(v.i) {
gamepad_init();
} else {
gamepad_shutdown();
}
}
static void gamepad_setup_cfg_callbacks(void) {
static bool done = false;
if(done) {
return;
}
done = true;
config_set_callback(CONFIG_GAMEPAD_ENABLED, gamepad_cfg_enabled_callback);
}
void gamepad_init(void) {
gamepad_setup_cfg_callbacks();
if(!config_get_int(CONFIG_GAMEPAD_ENABLED) || gamepad.initialized) {
return;
}

View file

@ -19,7 +19,6 @@ void init_global(CLIAction *cli) {
tsrand_init(&global.rand_visual, time(0));
tsrand_switch(&global.rand_visual);
memset(&resources, 0, sizeof(Resources));
memset(&global.replay, 0, sizeof(Replay));
global.replaymode = REPLAY_RECORD;

View file

@ -33,7 +33,6 @@
#include "list.h"
#include "refs.h"
#include "config.h"
#include "fbo.h"
#include "resource/resource.h"
#include "replay.h"
#include "random.h"
@ -53,9 +52,6 @@ enum {
RESX = 800,
RESY = 600,
SCREEN_W = 800,
SCREEN_H = 600,
VIEWPORT_X = 40,
VIEWPORT_Y = 20,
VIEWPORT_W = 480,

View file

@ -12,6 +12,7 @@
#include "options.h"
#include "common.h"
#include "global.h"
#include "video.h"
void set_player(MenuData *m, void *p) {
progress.game_settings.character = (CharacterID)(uintptr_t)p;

View file

@ -16,6 +16,7 @@
#include "ending.h"
#include "credits.h"
#include "mainmenu.h"
#include "video.h"
static void start_game_internal(MenuData *menu, StageInfo *info, bool difficulty_menu) {
MenuData m;

View file

@ -13,6 +13,7 @@
#include "options.h"
#include "common.h"
#include "global.h"
#include "video.h"
// FIXME: put this into the menu struct somehow (drawdata is a bad system)
static Color diff_color;

View file

@ -137,10 +137,6 @@ void create_ingame_menu_replay(MenuData *m) {
void draw_ingame_menu_bg(MenuData *menu, float f) {
float rad = f*IMENU_BLUR;
r_target(NULL);
video_set_viewport();
set_ortho(SCREEN_W, SCREEN_H);
r_shader("ingame_menu");
r_uniform_float("rad", rad);
r_uniform_float("phase", menu->frames / 100.0);
@ -154,6 +150,8 @@ void update_ingame_menu(MenuData *menu) {
}
void draw_ingame_menu(MenuData *menu) {
set_ortho(SCREEN_W, SCREEN_H);
r_mat_push();
draw_ingame_menu_bg(menu, 1.0-menu_fade(menu));

View file

@ -15,6 +15,7 @@
#include "replay.h"
#include "plrmodes.h"
#include "common.h"
#include "video.h"
static void do_save_replay(Replay *rpy) {
char strtime[FILENAME_TIMESTAMP_MIN_BUF_SIZE], *name;

View file

@ -58,7 +58,6 @@ taisei_src = files(
'enemy.c',
'entity.c',
'events.c',
'fbo.c',
'framerate.c',
'gamepad.c',
'global.c',

View file

@ -15,6 +15,7 @@
#include "plrmodes.h"
#include "stage.h"
#include "stagetext.h"
#include "stagedraw.h"
#include "entity.h"
void player_init(Player *plr) {
@ -412,8 +413,8 @@ void player_realdeath(Player *plr) {
}
static void player_death_effect_draw_overlay(Projectile *p, int t) {
FBOPair *framebuffers = &resources.fbo_pairs.fg;
r_target(framebuffers->front);
FBPair *framebuffers = stage_get_fbpair(FBPAIR_FG);
r_framebuffer(framebuffers->front);
r_texture(1, "static");
r_uniform_int("noise", 1);
r_uniform_int("frames", global.frames);
@ -421,12 +422,12 @@ static void player_death_effect_draw_overlay(Projectile *p, int t) {
r_uniform_vec2("origin", creal(p->pos), VIEWPORT_H - cimag(p->pos));
r_uniform_vec2("clear_origin", creal(global.plr.pos), VIEWPORT_H - cimag(global.plr.pos));
r_uniform_vec2("viewport", VIEWPORT_W, VIEWPORT_H);
draw_fbo(framebuffers->back);
swap_fbo_pair(framebuffers);
draw_framebuffer_tex(framebuffers->back, VIEWPORT_W, VIEWPORT_H);
fbpair_swap(framebuffers);
// This change must propagate, hence the r_state salsa. Yes, pop then push, I know what I'm doing.
r_state_pop();
r_target(framebuffers->back);
r_framebuffer(framebuffers->back);
r_state_push();
}

View file

@ -12,6 +12,7 @@
#include "plrmodes.h"
#include "marisa.h"
#include "renderer/api.h"
#include "stagedraw.h"
// args are pain
static float global_magicstar_alpha;
@ -235,6 +236,8 @@ static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
Texture *tex0 = get_tex("part/marisa_laser0");
Texture *tex1 = get_tex("part/marisa_laser1");
VertexArray *varr_saved = r_vertex_array_current();
FBPair *fbp_aux = stage_get_fbpair(FBPAIR_FG_AUX);
FBPair *fbp_fg = stage_get_fbpair(FBPAIR_FG);
r_vertex_array(r_vertex_array_static_models());
r_shader_ptr(shader);
@ -242,7 +245,7 @@ static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
r_uniform_ptr(u_clr1, 1, (float[]) { 1, 1, 1, 0.8 });
r_uniform_ptr(u_clr_phase, 1, (float[]) { -1.5 * t/M_PI });
r_uniform_ptr(u_clr_freq, 1, (float[]) { 10.0 });
r_target(resources.fbo_pairs.rgba.front);
r_framebuffer(fbp_aux->front);
r_clear_color4(0, 0, 0, 0);
r_clear(CLEAR_COLOR);
r_clear_color4(0, 0, 0, 1);
@ -259,9 +262,9 @@ static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
}
r_blend(BLEND_ALPHA);
r_target(resources.fbo_pairs.fg.back);
r_framebuffer(fbp_fg->back);
r_shader_standard();
draw_fbo(resources.fbo_pairs.rgba.front);
draw_framebuffer_tex(fbp_aux->front, VIEWPORT_W, VIEWPORT_H);
r_shader_ptr(shader);
r_blend(BLEND_ADD);

View file

@ -377,13 +377,13 @@ static int youmu_split(Enemy *e, int t) {
return 1;
}
static void youmu_mirror_shader(FBO *fbo) {
static void youmu_mirror_shader(Framebuffer *fb) {
ShaderProgram *shader = r_shader_get("youmua_bomb");
double t = player_get_bomb_progress(&global.plr,0);
r_shader_ptr(shader);
r_uniform_float("tbomb", t);
draw_fbo(fbo);
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
colorfill(1,1,1,max(0,1-10*t));

View file

@ -354,29 +354,43 @@ Texture* r_texture_current(uint unit) {
return B.texture_current(unit);
}
void r_target_create(RenderTarget *target) {
B.target_create(target);
void r_framebuffer_create(Framebuffer *fb) {
B.framebuffer_create(fb);
}
void r_target_attach(RenderTarget *target, Texture *tex, RenderTargetAttachment attachment) {
B.target_attach(target, tex, attachment);
void r_framebuffer_attach(Framebuffer *fb, Texture *tex, FramebufferAttachment attachment) {
B.framebuffer_attach(fb, tex, attachment);
}
Texture* r_target_get_attachment(RenderTarget *target, RenderTargetAttachment attachment) {
return B.target_get_attachment(target, attachment);
Texture* r_framebuffer_get_attachment(Framebuffer *fb, FramebufferAttachment attachment) {
return B.framebuffer_get_attachment(fb, attachment);
}
void r_target_destroy(RenderTarget *target) {
B.target_destroy(target);
void r_framebuffer_viewport(Framebuffer *fb, int x, int y, int w, int h) {
r_framebuffer_viewport_rect(fb, (IntRect) { x, y, w, h });
}
void r_target(RenderTarget *target) {
_r_state_touch_target();
B.target(target);
void r_framebuffer_viewport_rect(Framebuffer *fb, IntRect viewport) {
assert(viewport.h > 0);
assert(viewport.w > 0);
B.framebuffer_viewport(fb, viewport);
}
RenderTarget* r_target_current(void) {
return B.target_current();
void r_framebuffer_viewport_current(Framebuffer *fb, IntRect *viewport) {
B.framebuffer_viewport_current(fb, viewport);
}
void r_framebuffer_destroy(Framebuffer *fb) {
B.framebuffer_destroy(fb);
}
void r_framebuffer(Framebuffer *fb) {
_r_state_touch_framebuffer();
B.framebuffer(fb);
}
Framebuffer * r_framebuffer_current(void) {
return B.framebuffer_current();
}
void r_vertex_buffer_create(VertexBuffer *vbuf, size_t capacity, void *data) {
@ -441,15 +455,6 @@ Color r_clear_color_current(void) {
return B.clear_color_current();
}
void r_viewport_rect(IntRect rect) {
_r_state_touch_viewport();
B.viewport_rect(rect);
}
void r_viewport_current(IntRect *out_rect) {
B.viewport_current(out_rect);
}
void r_vsync(VsyncMode mode) {
_r_state_touch_vsync();
B.vsync(mode);

View file

@ -21,7 +21,7 @@ typedef enum RendererFeature {
RFEAT_DRAW_INSTANCED,
RFEAT_DRAW_INSTANCED_BASE_INSTANCE,
RFEAT_DEPTH_TEXTURE,
RFEAT_RENDERTARGET_MULTIPLE_OUTPUTS,
RFEAT_FRAMEBUFFER_MULTIPLE_OUTPUTS,
NUM_RFEATS,
} RendererFeature;
@ -102,22 +102,22 @@ enum {
R_MAX_TEXUNITS = 8,
};
typedef enum RenderTargetAttachment {
RENDERTARGET_ATTACHMENT_DEPTH,
RENDERTARGET_ATTACHMENT_COLOR0,
RENDERTARGET_ATTACHMENT_COLOR1,
RENDERTARGET_ATTACHMENT_COLOR2,
RENDERTARGET_ATTACHMENT_COLOR3,
typedef enum FramebufferAttachment {
FRAMEBUFFER_ATTACH_DEPTH,
FRAMEBUFFER_ATTACH_COLOR0,
FRAMEBUFFER_ATTACH_COLOR1,
FRAMEBUFFER_ATTACH_COLOR2,
FRAMEBUFFER_ATTACH_COLOR3,
RENDERTARGET_MAX_COLOR_ATTACHMENTS = 4,
RENDERTARGET_MAX_ATTACHMENTS = RENDERTARGET_ATTACHMENT_COLOR0 + RENDERTARGET_MAX_COLOR_ATTACHMENTS,
} RenderTargetAttachment;
FRAMEBUFFER_MAX_COLOR_ATTACHMENTS = 4,
FRAMEBUFFER_MAX_ATTACHMENTS = FRAMEBUFFER_ATTACH_COLOR0 + FRAMEBUFFER_MAX_COLOR_ATTACHMENTS,
} FramebufferAttachment;
typedef struct RenderTargetImpl RenderTargetImpl;
typedef struct FramebufferImpl FramebufferImpl;
typedef struct RenderTarget {
RenderTargetImpl *impl;
} RenderTarget;
typedef struct Framebuffer {
FramebufferImpl *impl;
} Framebuffer;
typedef enum Primitive {
PRIM_POINTS,
@ -353,8 +353,6 @@ typedef struct SpriteParams {
vec3 vector;
} rotation;
// FIXME: find a more efficient solution for this?
// this is needed to support some color transforms, but most sprites won't use this attribute
float custom;
struct {
@ -415,13 +413,16 @@ void r_texture_destroy(Texture *tex) attr_nonnull(1);
void r_texture_ptr(uint unit, Texture *tex);
Texture* r_texture_current(uint unit);
void r_target_create(RenderTarget *target) attr_nonnull(1);
void r_target_attach(RenderTarget *target, Texture *tex, RenderTargetAttachment attachment) attr_nonnull(1);
Texture* r_target_get_attachment(RenderTarget *target, RenderTargetAttachment attachment) attr_nonnull(1);
void r_target_destroy(RenderTarget *target) attr_nonnull(1);
void r_framebuffer_create(Framebuffer *fb) attr_nonnull(1);
void r_framebuffer_attach(Framebuffer *fb, Texture *tex, FramebufferAttachment attachment) attr_nonnull(1);
Texture* r_framebuffer_get_attachment(Framebuffer *fb, FramebufferAttachment attachment) attr_nonnull(1);
void r_framebuffer_viewport(Framebuffer *fb, int x, int y, int w, int h);
void r_framebuffer_viewport_rect(Framebuffer *fb, IntRect viewport);
void r_framebuffer_viewport_current(Framebuffer *fb, IntRect *viewport) attr_nonnull(2);
void r_framebuffer_destroy(Framebuffer *fb) attr_nonnull(1);
void r_target(RenderTarget *target);
RenderTarget* r_target_current(void);
void r_framebuffer(Framebuffer *fb);
Framebuffer* r_framebuffer_current(void);
void r_vertex_buffer_create(VertexBuffer *vbuf, size_t capacity, void *data) attr_nonnull(1);
void r_vertex_buffer_destroy(VertexBuffer *vbuf) attr_nonnull(1);
@ -442,9 +443,6 @@ void r_clear(ClearBufferFlags flags);
void r_clear_color4(float r, float g, float b, float a);
Color r_clear_color_current(void);
void r_viewport_rect(IntRect rect);
void r_viewport_current(IntRect *out_rect) attr_nonnull(1);
void r_vsync(VsyncMode mode);
VsyncMode r_vsync_current(void);
@ -465,6 +463,7 @@ void r_mat_perspective(float angle, float aspect, float near, float far);
void r_mat(MatrixMode mode, mat4 mat);
void r_mat_current(MatrixMode mode, mat4 out_mat);
mat4* r_mat_current_ptr(MatrixMode mode);
void r_shader_standard(void);
void r_shader_standard_notex(void);
@ -660,11 +659,6 @@ void r_clear_color(Color c) {
r_clear_color4(r, g, b, a);
}
static inline attr_must_inline
void r_viewport(int x, int y, int w, int h) {
r_viewport_rect((IntRect) { x, y, w, h });
}
static inline attr_must_inline attr_nonnull(1)
void r_draw_model(const char *model) {
r_draw_model_ptr(get_resource_data(RES_MODEL, model, RESF_UNSAFE));

View file

@ -55,13 +55,15 @@ typedef struct RendererFuncs {
void (*texture)(uint unit, Texture *tex);
Texture* (*texture_current)(uint unit);
void (*target_create)(RenderTarget *target);
void (*target_destroy)(RenderTarget *target);
void (*target_attach)(RenderTarget *target, Texture *tex, RenderTargetAttachment attachment);
Texture* (*target_get_attachment)(RenderTarget *target, RenderTargetAttachment attachment);
void (*framebuffer_create)(Framebuffer *framebuffer);
void (*framebuffer_destroy)(Framebuffer *framebuffer);
void (*framebuffer_attach)(Framebuffer *framebuffer, Texture *tex, FramebufferAttachment attachment);
void (*framebuffer_viewport)(Framebuffer *framebuffer, IntRect vp);
void (*framebuffer_viewport_current)(Framebuffer *framebuffer, IntRect *vp);
Texture* (*framebuffer_get_attachment)(Framebuffer *framebuffer, FramebufferAttachment attachment);
void (*target)(RenderTarget *target);
RenderTarget* (*target_current)(void);
void (*framebuffer)(Framebuffer *framebuffer);
Framebuffer* (*framebuffer_current)(void);
void (*vertex_buffer_create)(VertexBuffer *vbuf, size_t capacity, void *data);
void (*vertex_buffer_destroy)(VertexBuffer *vbuf);
@ -82,9 +84,6 @@ typedef struct RendererFuncs {
void (*clear_color4)(float r, float g, float b, float a);
Color (*clear_color_current)(void);
void (*viewport_rect)(IntRect rect);
void (*viewport_current)(IntRect *out_rect);
void (*vsync)(VsyncMode mode);
VsyncMode (*vsync_current)(void);

View file

@ -88,6 +88,11 @@ void r_mat_current(MatrixMode mode, mat4 out_mat) {
glm_mat4_copy(*_r_matrices.indexed[mode].head, out_mat);
}
mat4* r_mat_current_ptr(MatrixMode mode) {
assert((uint)mode < sizeof(_r_matrices.indexed) / sizeof(MatrixStack));
return _r_matrices.indexed[mode].head;
}
void _r_mat_init(void) {
matstack_reset(&_r_matrices.texture);
matstack_reset(&_r_matrices.modelview);

View file

@ -28,9 +28,10 @@ static struct SpriteBatchState {
Texture *tex;
ShaderProgram *shader;
BlendMode blend;
RenderTarget *render_target;
Framebuffer *framebuffer;
CullFaceMode cull_mode;
DepthTestFunc depth_func;
mat4 projection CGLM_ALIGN(32);
uint cull_enabled : 1;
uint depth_test_enabled : 1;
uint depth_write_enabled : 1;
@ -129,23 +130,16 @@ void r_flush_sprites(void) {
_r_sprite_batch.num_pending = 0;
_r_sprite_batch.frame_stats.flushes++;
// log_warn("flush! %u", pending);
r_state_push();
VertexArray *varr_saved = r_vertex_array_current();
Texture *tex_saved = r_texture_current(0);
ShaderProgram *prog_saved = r_shader_current();
RenderTarget *target_saved = r_target_current();
BlendMode blend_saved = r_blend_current();
bool cap_deptp_test_saved = r_capability_current(RCAP_DEPTH_TEST);
bool cap_depth_write_saved = r_capability_current(RCAP_DEPTH_WRITE);
bool cap_cull_saved = r_capability_current(RCAP_CULL_FACE);
DepthTestFunc depth_func_saved = r_depth_func_current();
CullFaceMode cull_mode_saved = r_cull_current();
r_mat_mode(MM_PROJECTION);
r_mat_push();
glm_mat4_copy(_r_sprite_batch.projection, *r_mat_current_ptr(MM_PROJECTION));
r_vertex_array(&_r_sprite_batch.varr);
r_texture_ptr(0, _r_sprite_batch.tex);
r_shader_ptr(_r_sprite_batch.shader);
r_target(_r_sprite_batch.render_target);
r_framebuffer(_r_sprite_batch.framebuffer);
r_blend(_r_sprite_batch.blend);
r_capability(RCAP_DEPTH_TEST, _r_sprite_batch.depth_test_enabled);
r_capability(RCAP_DEPTH_WRITE, _r_sprite_batch.depth_write_enabled);
@ -167,16 +161,8 @@ void r_flush_sprites(void) {
r_vertex_buffer_invalidate(&_r_sprite_batch.vbuf);
}
r_vertex_array(varr_saved);
r_texture_ptr(0, tex_saved);
r_shader_ptr(prog_saved);
r_target(target_saved);
r_blend(blend_saved);
r_capability(RCAP_DEPTH_TEST, cap_deptp_test_saved);
r_capability(RCAP_DEPTH_WRITE, cap_depth_write_saved);
r_capability(RCAP_CULL_FACE, cap_cull_saved);
r_depth_func(depth_func_saved);
r_cull(cull_mode_saved);
r_mat_pop();
r_state_pop();
}
static void _r_sprite_batch_add(Sprite *spr, const SpriteParams *params, VertexBuffer *vbuf) {
@ -266,11 +252,11 @@ void r_draw_sprite(const SpriteParams *params) {
_r_sprite_batch.shader = prog;
}
RenderTarget *target = r_target_current();
Framebuffer *fb = r_framebuffer_current();
if(target != _r_sprite_batch.render_target) {
if(fb != _r_sprite_batch.framebuffer) {
r_flush_sprites();
_r_sprite_batch.render_target = target;
_r_sprite_batch.framebuffer = fb;
}
BlendMode blend = params->blend;
@ -315,6 +301,13 @@ void r_draw_sprite(const SpriteParams *params) {
_r_sprite_batch.cull_mode = cull_mode;
}
mat4 *current_projection = r_mat_current_ptr(MM_PROJECTION);
if(memcmp(*current_projection, _r_sprite_batch.projection, sizeof(mat4))) {
r_flush_sprites();
glm_mat4_copy(*current_projection, _r_sprite_batch.projection);
}
if(_r_sprite_batch.vbuf.size - _r_sprite_batch.vbuf.offset < sizeof(SpriteAttribs)) {
if(!r_supports(RFEAT_DRAW_INSTANCED_BASE_INSTANCE)) {
log_warn("Vertex buffer exhausted (%zu needed for next sprite, %u remaining), flush forced", sizeof(SpriteAttribs), _r_sprite_batch.vbuf.size - _r_sprite_batch.vbuf.offset);

View file

@ -100,17 +100,13 @@ void r_state_pop(void) {
}
RESTORE(RSTATE_RENDERTARGET) {
B.target(S.target);
B.framebuffer(S.framebuffer);
}
RESTORE(RSTATE_VERTEXARRAY) {
B.vertex_array(S.varr);
}
RESTORE(RSTATE_VIEWPORT) {
B.viewport_rect(S.viewport);
}
RESTORE(RSTATE_VSYNC) {
B.vsync(S.vsync);
}
@ -181,9 +177,9 @@ void _r_state_touch_texunit(uint unit) {
});
}
void _r_state_touch_target(void) {
void _r_state_touch_framebuffer(void) {
TAINT(RSTATE_RENDERTARGET, {
S.target = B.target_current();
S.framebuffer = B.framebuffer_current();
});
}
@ -193,12 +189,6 @@ void _r_state_touch_vertex_array(void) {
});
}
void _r_state_touch_viewport(void) {
TAINT(RSTATE_VIEWPORT, {
B.viewport_current(&S.viewport);
});
}
void _r_state_touch_vsync(void) {
TAINT(RSTATE_VSYNC, {
S.vsync = B.vsync_current();

View file

@ -24,7 +24,6 @@
RSTATE(TEXUNITS) \
RSTATE(RENDERTARGET) \
RSTATE(VERTEXARRAY) \
RSTATE(VIEWPORT) \
RSTATE(VSYNC) \
typedef enum RendererStateID {
@ -55,9 +54,9 @@ typedef struct RendererStateRollback {
ShaderProgram *shader;
// TODO uniforms
Texture *texunits[R_MAX_TEXUNITS];
RenderTarget *target;
Framebuffer *framebuffer;
// TODO framebuffer viewports, if we actually ever need to modify them at render time
VertexArray *varr;
IntRect viewport;
VsyncMode vsync;
} RendererStateRollback;
@ -71,9 +70,8 @@ void _r_state_touch_depth_func(void);
void _r_state_touch_shader(void);
void _r_state_touch_uniform(Uniform *uniform);
void _r_state_touch_texunit(uint unit);
void _r_state_touch_target(void);
void _r_state_touch_framebuffer(void);
void _r_state_touch_vertex_array(void);
void _r_state_touch_viewport(void);
void _r_state_touch_vsync(void);
void _r_state_init(void);

View file

@ -15,7 +15,7 @@
#include "texture.h"
#include "shader_object.h"
#include "shader_program.h"
#include "render_target.h"
#include "framebuffer.h"
#include "vertex_buffer.h"
#include "vertex_array.h"
#include "../glcommon/debug.h"
@ -40,9 +40,9 @@ static struct {
} texunits;
struct {
RenderTarget *active;
RenderTarget *pending;
} render_target;
Framebuffer *active;
Framebuffer *pending;
} framebuffer;
struct {
VertexArray *active;
@ -92,9 +92,13 @@ static struct {
r_capability_bits_t pending;
} capabilities;
struct {
IntRect active;
IntRect default_framebuffer;
} viewport;
vec4 color;
vec4 clear_color;
IntRect viewport;
GLuint pbo;
r_feature_bits_t features;
@ -213,7 +217,9 @@ static void gl33_init_context(SDL_Window *window) {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glGetIntegerv(GL_VIEWPORT, &R.viewport.x);
glGetIntegerv(GL_VIEWPORT, &R.viewport.default_framebuffer.x);
R.viewport.active = R.viewport.default_framebuffer;
r_clear_color4(0, 0, 0, 1);
r_clear(CLEAR_ALL);
@ -231,7 +237,7 @@ static void gl33_init_context(SDL_Window *window) {
}
if(glext.draw_buffers) {
R.features |= r_feature_bit(RFEAT_RENDERTARGET_MULTIPLE_OUTPUTS);
R.features |= r_feature_bit(RFEAT_FRAMEBUFFER_MULTIPLE_OUTPUTS);
}
}
@ -253,7 +259,26 @@ static void gl33_apply_capability(RendererCapability cap, bool value) {
}
}
static inline IntRect* get_framebuffer_viewport(Framebuffer *fb) {
if(fb == NULL) {
return &R.viewport.default_framebuffer;
}
assert(fb->impl != NULL);
return &fb->impl->viewport;
}
static void gl33_sync_viewport(void) {
IntRect *vp = get_framebuffer_viewport(R.framebuffer.pending);
if(memcmp(&R.viewport.active, vp, sizeof(IntRect))) {
R.viewport.active = *vp;
glViewport(vp->x, vp->y, vp->w, vp->h);
}
}
static void gl33_sync_state(void) {
gl33_sync_capabilities();
gl33_sync_shader();
r_uniform("r_modelViewMatrix", 1, _r_matrices.modelview.head);
@ -262,7 +287,8 @@ static void gl33_sync_state(void) {
r_uniform("r_color", 1, R.color);
gl33_sync_uniforms(R.progs.active);
gl33_sync_texunits();
gl33_sync_render_target();
gl33_sync_framebuffer();
gl33_sync_viewport();
gl33_sync_vertex_array();
gl33_sync_blend_mode();
@ -372,25 +398,25 @@ void gl33_sync_depth_test_func(void) {
}
}
static inline GLuint fbo_num(RenderTarget *target) {
if(target == NULL) {
static inline GLuint fbo_num(Framebuffer *fb) {
if(fb == NULL) {
return 0;
}
assert(target->impl != NULL);
assert(target->impl->gl_fbo != 0);
assert(fb->impl != NULL);
assert(fb->impl->gl_fbo != 0);
return target->impl->gl_fbo;
return fb->impl->gl_fbo;
}
void gl33_sync_render_target(void) {
if(fbo_num(R.render_target.active) != fbo_num(R.render_target.pending)) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo_num(R.render_target.pending));
R.render_target.active = R.render_target.pending;
void gl33_sync_framebuffer(void) {
if(fbo_num(R.framebuffer.active) != fbo_num(R.framebuffer.pending)) {
glBindFramebuffer(GL_FRAMEBUFFER, fbo_num(R.framebuffer.pending));
R.framebuffer.active = R.framebuffer.pending;
}
if(R.render_target.active) {
gl33_target_initialize(R.render_target.active);
if(R.framebuffer.active) {
gl33_framebuffer_initialize(R.framebuffer.active);
}
}
@ -505,13 +531,13 @@ void gl33_texture_deleted(Texture *tex) {
}
}
void gl33_render_target_deleted(RenderTarget *target) {
if(R.render_target.pending == target) {
R.render_target.pending = NULL;
void gl33_framebuffer_deleted(Framebuffer *fb) {
if(R.framebuffer.pending == fb) {
R.framebuffer.pending = NULL;
}
if(R.render_target.active == target) {
R.render_target.active = NULL;
if(R.framebuffer.active == fb) {
R.framebuffer.active = NULL;
}
}
@ -719,13 +745,21 @@ static Texture* gl33_texture_current(uint unit) {
return R.texunits.indexed[unit].tex2d.pending;
}
static void gl33_target(RenderTarget *target) {
assert(target == NULL || target->impl != NULL);
R.render_target.pending = target;
static void gl33_framebuffer(Framebuffer *fb) {
assert(fb == NULL || fb->impl != NULL);
R.framebuffer.pending = fb;
}
static RenderTarget *gl33_target_current(void) {
return R.render_target.pending;
static Framebuffer *gl33_framebuffer_current(void) {
return R.framebuffer.pending;
}
static void gl33_framebuffer_viewport(Framebuffer *fb, IntRect vp) {
memcpy(get_framebuffer_viewport(fb), &vp, sizeof(vp));
}
static void gl33_framebuffer_viewport_current(Framebuffer *fb, IntRect *out_rect) {
*out_rect = *get_framebuffer_viewport(fb);
}
static void gl33_shader(ShaderProgram *prog) {
@ -750,7 +784,7 @@ static void gl33_clear(ClearBufferFlags flags) {
}
r_flush_sprites();
gl33_sync_render_target();
gl33_sync_framebuffer();
glClear(mask);
}
@ -767,21 +801,9 @@ static Color gl33_clear_color_current(void) {
return rgba(R.clear_color[0], R.clear_color[1], R.clear_color[2], R.clear_color[3]);
}
static void gl33_viewport_rect(IntRect rect) {
if(memcmp(&R.viewport, &rect, sizeof(IntRect))) {
r_flush_sprites(); // FIXME: have the sprite batch track this instead.
memcpy(&R.viewport, &rect, sizeof(IntRect));
glViewport(rect.x, rect.y, rect.w, rect.h);
}
}
static void gl33_viewport_current(IntRect *out_rect) {
memcpy(out_rect, &R.viewport, sizeof(R.viewport));
}
static void gl33_swap(SDL_Window *window) {
r_flush_sprites();
gl33_sync_render_target();
gl33_sync_framebuffer();
SDL_GL_SwapWindow(window);
gl33_stats_post_frame();
}
@ -811,11 +833,25 @@ static DepthTestFunc gl33_depth_func_current(void) {
}
static uint8_t* gl33_screenshot(uint *out_width, uint *out_height) {
uint8_t *pixels = malloc(R.viewport.w * R.viewport.h * 3);
uint fbo = 0;
IntRect *vp = &R.viewport.default_framebuffer;
if(R.framebuffer.active != NULL) {
fbo = R.framebuffer.active->impl->gl_fbo;
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
}
uint8_t *pixels = malloc(vp->w * vp->h * 3);
glReadBuffer(GL_FRONT);
glReadPixels(R.viewport.x, R.viewport.y, R.viewport.w, R.viewport.h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
*out_width = R.viewport.w;
*out_height = R.viewport.h;
glReadPixels(vp->x, vp->y, vp->w, vp->h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
*out_width = vp->w;
*out_height = vp->h;
if(fbo != 0) {
// FIXME: Maybe we should only ever bind FBOs to GL_DRAW_FRAMEBUFFER?
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
}
return pixels;
}
@ -851,12 +887,14 @@ RendererBackend _r_backend_gl33 = {
.texture_replace = gl33_texture_replace,
.texture = gl33_texture,
.texture_current = gl33_texture_current,
.target_create = gl33_target_create,
.target_destroy = gl33_target_destroy,
.target_attach = gl33_target_attach,
.target_get_attachment = gl33_target_get_attachment,
.target = gl33_target,
.target_current = gl33_target_current,
.framebuffer_create = gl33_framebuffer_create,
.framebuffer_destroy = gl33_framebuffer_destroy,
.framebuffer_attach = gl33_framebuffer_attach,
.framebuffer_get_attachment = gl33_framebuffer_get_attachment,
.framebuffer_viewport = gl33_framebuffer_viewport,
.framebuffer_viewport_current = gl33_framebuffer_viewport_current,
.framebuffer = gl33_framebuffer,
.framebuffer_current = gl33_framebuffer_current,
.vertex_buffer_create = gl33_vertex_buffer_create,
.vertex_buffer_destroy = gl33_vertex_buffer_destroy,
.vertex_buffer_invalidate = gl33_vertex_buffer_invalidate,
@ -872,8 +910,6 @@ RendererBackend _r_backend_gl33 = {
.clear = gl33_clear,
.clear_color4 = gl33_clear_color4,
.clear_color_current = gl33_clear_color_current,
.viewport_rect = gl33_viewport_rect,
.viewport_current = gl33_viewport_current,
.vsync = gl33_vsync,
.vsync_current = gl33_vsync_current,
.swap = gl33_swap,

View file

@ -26,7 +26,7 @@ void gl33_bind_vbo(GLuint vbo);
void gl33_sync_shader(void);
void gl33_sync_texunit(uint unit);
void gl33_sync_texunits(void);
void gl33_sync_render_target(void);
void gl33_sync_framebuffer(void);
void gl33_sync_vertex_array(void);
void gl33_sync_blend_mode(void);
void gl33_sync_cull_face_mode(void);
@ -41,7 +41,7 @@ GLuint gl33_vbo_current(void);
void gl33_vertex_buffer_deleted(VertexBuffer *vbuf);
void gl33_vertex_array_deleted(VertexArray *varr);
void gl33_texture_deleted(Texture *tex);
void gl33_render_target_deleted(RenderTarget *target);
void gl33_framebuffer_deleted(Framebuffer *fb);
void gl33_shader_deleted(ShaderProgram *prog);
extern RendererBackend _r_backend_gl33;

View file

@ -0,0 +1,99 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "framebuffer.h"
#include "core.h"
static GLuint r_attachment_to_gl_attachment[] = {
[FRAMEBUFFER_ATTACH_DEPTH] = GL_DEPTH_ATTACHMENT,
[FRAMEBUFFER_ATTACH_COLOR0] = GL_COLOR_ATTACHMENT0,
[FRAMEBUFFER_ATTACH_COLOR1] = GL_COLOR_ATTACHMENT1,
[FRAMEBUFFER_ATTACH_COLOR2] = GL_COLOR_ATTACHMENT2,
[FRAMEBUFFER_ATTACH_COLOR3] = GL_COLOR_ATTACHMENT3,
};
static_assert(sizeof(r_attachment_to_gl_attachment)/sizeof(GLuint) == FRAMEBUFFER_MAX_ATTACHMENTS, "");
void gl33_framebuffer_create(Framebuffer *framebuffer) {
memset(framebuffer, 0, sizeof(Framebuffer));
framebuffer->impl = calloc(1, sizeof(FramebufferImpl));
glGenFramebuffers(1, &framebuffer->impl->gl_fbo);
}
void gl33_framebuffer_attach(Framebuffer *framebuffer, Texture *tex, FramebufferAttachment attachment) {
assert(attachment >= 0 && attachment < FRAMEBUFFER_MAX_ATTACHMENTS);
GLuint gl_tex = tex ? tex->impl->gl_handle : 0;
Framebuffer *prev_fb = r_framebuffer_current();
// make sure gl33_sync_framebuffer doesn't call gl33_framebuffer_initialize here
framebuffer->impl->initialized = true;
r_framebuffer(framebuffer);
gl33_sync_framebuffer();
glFramebufferTexture2D(GL_FRAMEBUFFER, r_attachment_to_gl_attachment[attachment], GL_TEXTURE_2D, gl_tex, 0);
r_framebuffer(prev_fb);
framebuffer->impl->attachments[attachment] = tex;
// need to update draw buffers
framebuffer->impl->initialized = false;
}
Texture* gl33_framebuffer_get_attachment(Framebuffer *framebuffer, FramebufferAttachment attachment) {
assert(framebuffer->impl != NULL);
assert(attachment >= 0 && attachment < FRAMEBUFFER_MAX_ATTACHMENTS);
if(!framebuffer->impl->initialized) {
Framebuffer *prev_fb = r_framebuffer_current();
r_framebuffer(framebuffer);
gl33_sync_framebuffer(); // implicitly initializes!
r_framebuffer(prev_fb);
}
return framebuffer->impl->attachments[attachment];
}
void gl33_framebuffer_destroy(Framebuffer *framebuffer) {
if(framebuffer->impl != NULL) {
gl33_framebuffer_deleted(framebuffer);
glDeleteFramebuffers(1, &framebuffer->impl->gl_fbo);
free(framebuffer->impl);
framebuffer->impl = NULL;
}
}
void gl33_framebuffer_initialize(Framebuffer *framebuffer) {
if(!framebuffer->impl->initialized) {
// NOTE: this framebuffer is guaranteed to be active at this point
if(glext.draw_buffers) {
GLenum drawbufs[FRAMEBUFFER_MAX_COLOR_ATTACHMENTS];
for(int i = 0; i < FRAMEBUFFER_MAX_COLOR_ATTACHMENTS; ++i) {
if(framebuffer->impl->attachments[FRAMEBUFFER_ATTACH_COLOR0 + i] != NULL) {
drawbufs[i] = GL_COLOR_ATTACHMENT0 + i;
} else {
drawbufs[i] = GL_NONE;
}
}
glDrawBuffers(FRAMEBUFFER_MAX_COLOR_ATTACHMENTS, drawbufs);
}
Color cc_saved = r_clear_color_current();
r_clear_color4(0, 0, 0, 0);
// NOTE: r_clear would cause an infinite recursion here
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
r_clear_color(cc_saved);
framebuffer->impl->initialized = true;
}
}

View file

@ -0,0 +1,28 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#pragma once
#include "taisei.h"
#include "../api.h"
#include "opengl.h"
#include "texture.h"
struct FramebufferImpl {
GLuint gl_fbo;
bool initialized;
Texture *attachments[FRAMEBUFFER_MAX_ATTACHMENTS];
IntRect viewport;
};
void gl33_framebuffer_initialize(Framebuffer *framebuffer);
void gl33_framebuffer_create(Framebuffer *framebuffer);
void gl33_framebuffer_attach(Framebuffer *framebuffer, Texture *tex, FramebufferAttachment attachment);
Texture *gl33_framebuffer_get_attachment(Framebuffer *framebuffer, FramebufferAttachment attachment);
void gl33_framebuffer_destroy(Framebuffer *framebuffer);

View file

@ -1,7 +1,7 @@
r_gl33_src = files(
'core.c',
'render_target.c',
'framebuffer.c',
'shader_object.c',
'shader_program.c',
'texture.c',

View file

@ -1,99 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "render_target.h"
#include "core.h"
static GLuint r_attachment_to_gl_attachment[] = {
[RENDERTARGET_ATTACHMENT_DEPTH] = GL_DEPTH_ATTACHMENT,
[RENDERTARGET_ATTACHMENT_COLOR0] = GL_COLOR_ATTACHMENT0,
[RENDERTARGET_ATTACHMENT_COLOR1] = GL_COLOR_ATTACHMENT1,
[RENDERTARGET_ATTACHMENT_COLOR2] = GL_COLOR_ATTACHMENT2,
[RENDERTARGET_ATTACHMENT_COLOR3] = GL_COLOR_ATTACHMENT3,
};
static_assert(sizeof(r_attachment_to_gl_attachment)/sizeof(GLuint) == RENDERTARGET_MAX_ATTACHMENTS, "");
void gl33_target_create(RenderTarget *target) {
memset(target, 0, sizeof(RenderTarget));
target->impl = calloc(1, sizeof(RenderTargetImpl));
glGenFramebuffers(1, &target->impl->gl_fbo);
}
void gl33_target_attach(RenderTarget *target, Texture *tex, RenderTargetAttachment attachment) {
assert(attachment >= 0 && attachment < RENDERTARGET_MAX_ATTACHMENTS);
GLuint gl_tex = tex ? tex->impl->gl_handle : 0;
RenderTarget *prev_target = r_target_current();
// make sure gl33_sync_render_target doesn't call gl33_target_initialize here
target->impl->initialized = true;
r_target(target);
gl33_sync_render_target();
glFramebufferTexture2D(GL_FRAMEBUFFER, r_attachment_to_gl_attachment[attachment], GL_TEXTURE_2D, gl_tex, 0);
r_target(prev_target);
target->impl->attachments[attachment] = tex;
// need to update draw buffers
target->impl->initialized = false;
}
Texture* gl33_target_get_attachment(RenderTarget *target, RenderTargetAttachment attachment) {
assert(target->impl != NULL);
assert(attachment >= 0 && attachment < RENDERTARGET_MAX_ATTACHMENTS);
if(!target->impl->initialized) {
RenderTarget *prev_target = r_target_current();
r_target(target);
gl33_sync_render_target(); // implicitly initializes!
r_target(prev_target);
}
return target->impl->attachments[attachment];
}
void gl33_target_destroy(RenderTarget *target) {
if(target->impl != NULL) {
gl33_render_target_deleted(target);
glDeleteFramebuffers(1, &target->impl->gl_fbo);
free(target->impl);
target->impl = NULL;
}
}
void gl33_target_initialize(RenderTarget *target) {
if(!target->impl->initialized) {
// NOTE: this render target is guaranteed to be active at this point
if(glext.draw_buffers) {
GLenum drawbufs[RENDERTARGET_MAX_COLOR_ATTACHMENTS];
for(int i = 0; i < RENDERTARGET_MAX_COLOR_ATTACHMENTS; ++i) {
if(target->impl->attachments[RENDERTARGET_ATTACHMENT_COLOR0 + i] != NULL) {
drawbufs[i] = GL_COLOR_ATTACHMENT0 + i;
} else {
drawbufs[i] = GL_NONE;
}
}
glDrawBuffers(RENDERTARGET_MAX_COLOR_ATTACHMENTS, drawbufs);
}
Color cc_saved = r_clear_color_current();
r_clear_color4(0, 0, 0, 0);
// NOTE: r_clear would cause an infinite recursion here
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
r_clear_color(cc_saved);
target->impl->initialized = true;
}
}

View file

@ -1,27 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#pragma once
#include "taisei.h"
#include "../api.h"
#include "opengl.h"
#include "texture.h"
struct RenderTargetImpl {
GLuint gl_fbo;
bool initialized;
Texture *attachments[RENDERTARGET_MAX_ATTACHMENTS];
};
void gl33_target_initialize(RenderTarget *target);
void gl33_target_create(RenderTarget *target);
void gl33_target_attach(RenderTarget *target, Texture *tex, RenderTargetAttachment attachment);
Texture* gl33_target_get_attachment(RenderTarget *target, RenderTargetAttachment attachment);
void gl33_target_destroy(RenderTarget *target);

View file

@ -73,29 +73,48 @@ void null_texture_destroy(Texture *tex) {
void null_texture(uint unit, Texture *tex) { }
Texture* null_texture_current(uint unit) { return (void*)&placeholder; }
struct RenderTargetImpl {
Texture *attachments[RENDERTARGET_MAX_ATTACHMENTS];
struct FramebufferImpl {
Texture *attachments[FRAMEBUFFER_MAX_ATTACHMENTS];
IntRect viewport;
};
void null_target_create(RenderTarget *target) {
target->impl = calloc(1, sizeof(RenderTargetImpl));
static IntRect default_fb_viewport;
void null_framebuffer_create(Framebuffer *framebuffer) {
framebuffer->impl = calloc(1, sizeof(FramebufferImpl));
}
void null_target_attach(RenderTarget *target, Texture *tex, RenderTargetAttachment attachment) {
target->impl->attachments[attachment] = tex;
void null_framebuffer_attach(Framebuffer *framebuffer, Texture *tex, FramebufferAttachment attachment) {
framebuffer->impl->attachments[attachment] = tex;
}
Texture* null_target_get_attachment(RenderTarget *target, RenderTargetAttachment attachment) {
return target->impl->attachments[attachment];
Texture* null_framebuffer_attachment(Framebuffer *framebuffer, FramebufferAttachment attachment) {
return framebuffer->impl->attachments[attachment];
}
void null_target_destroy(RenderTarget *target) {
free(target->impl);
memset(target, 0, sizeof(RenderTarget));
void null_framebuffer_destroy(Framebuffer *framebuffer) {
free(framebuffer->impl);
memset(framebuffer, 0, sizeof(Framebuffer));
}
void null_target(RenderTarget *target) { }
RenderTarget* null_target_current(void) { return (void*)&placeholder; }
void null_framebuffer_viewport(Framebuffer *framebuffer, IntRect vp) {
if(framebuffer) {
framebuffer->impl->viewport = vp;
} else {
default_fb_viewport = vp;
}
}
void null_framebuffer_viewport_current(Framebuffer *framebuffer, IntRect *vp) {
if(framebuffer) {
*vp = framebuffer->impl->viewport;
} else {
*vp = default_fb_viewport;
}
}
void null_framebuffer(Framebuffer *framebuffer) { }
Framebuffer* null_framebuffer_current(void) { return (void*)&placeholder; }
void null_vertex_buffer_create(VertexBuffer *vbuf, size_t capacity, void *data) {
vbuf->offset = 0;
@ -140,14 +159,6 @@ void null_clear(ClearBufferFlags flags) { }
void null_clear_color4(float r, float g, float b, float a) { }
Color null_clear_color_current(void) { return rgba(0, 0, 0, 0); }
void null_viewport_rect(IntRect rect) { }
void null_viewport_current(IntRect *out_rect) {
out_rect->x = 0;
out_rect->y = 0;
out_rect->w = 800;
out_rect->h = 600;
}
void null_vsync(VsyncMode mode) { }
VsyncMode null_vsync_current(void) { return VSYNC_NONE; }
@ -245,12 +256,14 @@ RendererBackend _r_backend_null = {
.texture_replace = null_texture_replace,
.texture = null_texture,
.texture_current = null_texture_current,
.target_create = null_target_create,
.target_destroy = null_target_destroy,
.target_attach = null_target_attach,
.target_get_attachment = null_target_get_attachment,
.target = null_target,
.target_current = null_target_current,
.framebuffer_create = null_framebuffer_create,
.framebuffer_destroy = null_framebuffer_destroy,
.framebuffer_attach = null_framebuffer_attach,
.framebuffer_get_attachment = null_framebuffer_attachment,
.framebuffer_viewport = null_framebuffer_viewport,
.framebuffer_viewport_current = null_framebuffer_viewport_current,
.framebuffer = null_framebuffer,
.framebuffer_current = null_framebuffer_current,
.vertex_buffer_create = null_vertex_buffer_create,
.vertex_buffer_destroy = null_vertex_buffer_destroy,
.vertex_buffer_invalidate = null_vertex_buffer_invalidate,
@ -266,8 +279,6 @@ RendererBackend _r_backend_null = {
.clear = null_clear,
.clear_color4 = null_clear_color4,
.clear_color_current = null_clear_color_current,
.viewport_rect = null_viewport_rect,
.viewport_current = null_viewport_current,
.vsync = null_vsync,
.vsync_current = null_vsync_current,
.swap = null_swap,

View file

@ -106,7 +106,7 @@ static struct {
FT_Library lib;
ShaderProgram *default_shader;
Texture render_tex;
RenderTarget render_buf;
Framebuffer render_buf;
struct {
SDL_mutex *new_face;
@ -115,19 +115,36 @@ static struct {
} globals;
static double global_font_scale(void) {
return sanitize_scale(video.quality_factor * config_get_float(CONFIG_TEXT_QUALITY));
int w, h;
video_get_viewport_size(&w, &h);
return sanitize_scale(((double)h / SCREEN_H) * config_get_float(CONFIG_TEXT_QUALITY));
}
static void reload_fonts(double quality);
static bool fonts_event(SDL_Event *event, void *arg) {
reload_fonts(global_font_scale());
return false;
}
if(!IS_TAISEI_EVENT(event->type)) {
return false;
}
static void fonts_quality_config_callback(ConfigIndex idx, ConfigValue v) {
config_set_float(idx, v.f);
reload_fonts(global_font_scale());
switch(TAISEI_EVENT(event->type)) {
case TE_VIDEO_MODE_CHANGED: {
reload_fonts(global_font_scale());
break;
}
case TE_CONFIG_UPDATED: {
if(event->user.code == CONFIG_TEXT_QUALITY) {
ConfigValue *val = event->user.data1;
val->f = sanitize_scale(val->f);
reload_fonts(global_font_scale());
}
return false;
}
}
return false;
}
static void try_create_mutex(SDL_mutex **mtx) {
@ -147,11 +164,9 @@ static void init_fonts(void) {
}
events_register_handler(&(EventHandler) {
fonts_event, NULL, EPRIO_SYSTEM, MAKE_TAISEI_EVENT(TE_VIDEO_MODE_CHANGED)
fonts_event, NULL, EPRIO_SYSTEM,
});
config_set_callback(CONFIG_TEXT_QUALITY, fonts_quality_config_callback);
preload_resources(RES_FONT, RESF_PERMANENT,
"standard",
NULL);
@ -167,8 +182,9 @@ static void init_fonts(void) {
.height = 1024,
});
r_target_create(&globals.render_buf);
r_target_attach(&globals.render_buf, &globals.render_tex, RENDERTARGET_ATTACHMENT_COLOR0);
r_framebuffer_create(&globals.render_buf);
r_framebuffer_attach(&globals.render_buf, &globals.render_tex, FRAMEBUFFER_ATTACH_COLOR0);
r_framebuffer_viewport(&globals.render_buf, 0, 0, globals.render_tex.w, globals.render_tex.h);
}
static void post_init_fonts(void) {
@ -177,7 +193,7 @@ static void post_init_fonts(void) {
static void shutdown_fonts(void) {
r_texture_destroy(&globals.render_tex);
r_target_destroy(&globals.render_buf);
r_framebuffer_destroy(&globals.render_buf);
events_unregister_handler(fonts_event);
FT_Done_FreeType(globals.lib);
SDL_DestroyMutex(globals.mutex.new_face);
@ -317,7 +333,7 @@ static SpriteSheet* add_spritesheet(SpriteSheetAnchor *spritesheets) {
// FIXME:
//
// This code just zeroes the texture out.
// This code just zeroes the texture out.
//
// We should add an r_texture_clear function to hide this monstrosity.
// It would also allow a non-GL backend to have a different implementation,
@ -325,17 +341,17 @@ static SpriteSheet* add_spritesheet(SpriteSheetAnchor *spritesheets) {
//
// To future generations: if such a function is already in the renderer API,
// but this crap is still here, please convert it.
RenderTarget *prev_target = r_target_current();
RenderTarget atlast_rt;
Framebuffer *prev_fb = r_framebuffer_current();
Framebuffer atlast_fb;
Color cc_prev = r_clear_color_current();
r_target_create(&atlast_rt);
r_target_attach(&atlast_rt, &ss->tex, RENDERTARGET_ATTACHMENT_COLOR0);
r_target(&atlast_rt);
r_framebuffer_create(&atlast_fb);
r_framebuffer_attach(&atlast_fb, &ss->tex, FRAMEBUFFER_ATTACH_COLOR0);
r_framebuffer(&atlast_fb);
r_clear_color4(0, 0, 0, 0);
r_clear(CLEAR_COLOR);
r_target(prev_target);
r_framebuffer(prev_fb);
r_clear_color(cc_prev);
r_target_destroy(&atlast_rt);
r_framebuffer_destroy(&atlast_fb);
alist_append(spritesheets, ss);
return ss;
@ -584,8 +600,10 @@ struct rlfonts_arg {
attr_nonnull(1)
static void reload_font(Font *font, double quality) {
wipe_glyph_cache(font);
set_font_size(font, font->base_size, quality);
if(font->metrics.scale != quality) {
wipe_glyph_cache(font);
set_font_size(font, font->base_size, quality);
}
}
static void* reload_font_callback(const char *name, Resource *res, void *varg) {
@ -945,11 +963,12 @@ void text_render(const char *text, Font *font, Sprite *out_sprite, BBox *out_bbo
);
r_texture_replace(tex, TEX_TYPE_R, tex_new_w, tex_new_h, NULL);
r_framebuffer_viewport(&globals.render_buf, 0, 0, tex_new_w, tex_new_h);
}
r_state_push();
r_target(&globals.render_buf);
r_framebuffer(&globals.render_buf);
r_clear_color4(0, 0, 0, 0);
r_clear(CLEAR_COLOR);
@ -967,7 +986,6 @@ void text_render(const char *text, Font *font, Sprite *out_sprite, BBox *out_bbo
r_mat_identity();
// XXX: y-flipped because that's how our textures are...
r_mat_ortho(0, tex->w, 0, tex->h, -100, 100);
r_viewport(0, 0, tex->w, tex->h);
r_mat_mode(MM_TEXTURE);
r_mat_push();

View file

@ -184,7 +184,7 @@ void postprocess_unload(PostprocessShader **list) {
list_foreach(list, delete_shader, NULL);
}
void postprocess(PostprocessShader *ppshaders, FBOPair *fbos, PostprocessPrepareFuncPtr prepare, PostprocessDrawFuncPtr draw) {
void postprocess(PostprocessShader *ppshaders, FBPair *fbos, PostprocessPrepareFuncPtr prepare, PostprocessDrawFuncPtr draw, double width, double height) {
if(!ppshaders) {
return;
}
@ -197,7 +197,7 @@ void postprocess(PostprocessShader *ppshaders, FBOPair *fbos, PostprocessPrepare
for(PostprocessShader *pps = ppshaders; pps; pps = pps->next) {
ShaderProgram *s = pps->shader;
r_target(fbos->back);
r_framebuffer(fbos->back);
r_shader_ptr(s);
if(prepare) {
@ -208,8 +208,8 @@ void postprocess(PostprocessShader *ppshaders, FBOPair *fbos, PostprocessPrepare
r_uniform_ptr(u->uniform, u->elements, u->values);
}
draw(fbos->front);
swap_fbo_pair(fbos);
draw(fbos->front, width, height);
fbpair_swap(fbos);
}
r_shader_ptr(shader_saved);

View file

@ -11,8 +11,8 @@
#include "resource.h"
#include "shader_program.h"
#include "fbo.h"
#include "renderer/api.h"
#include "util/graphics.h"
typedef struct PostprocessShader PostprocessShader;
typedef struct PostprocessShaderUniform PostprocessShaderUniform;
@ -38,14 +38,14 @@ struct PostprocessShaderUniform {
uint elements;
};
typedef void (*PostprocessDrawFuncPtr)(FBO*);
typedef void (*PostprocessPrepareFuncPtr)(FBO*, ShaderProgram*);
typedef void (*PostprocessDrawFuncPtr)(Framebuffer* fb, double w, double h);
typedef void (*PostprocessPrepareFuncPtr)(Framebuffer* fb, ShaderProgram *prog);
char* postprocess_path(const char *path);
PostprocessShader* postprocess_load(const char *path, uint flags);
void postprocess_unload(PostprocessShader **list);
void postprocess(PostprocessShader *ppshaders, FBOPair *fbos, PostprocessPrepareFuncPtr prepare, PostprocessDrawFuncPtr draw);
void postprocess(PostprocessShader *ppshaders, FBPair *fbos, PostprocessPrepareFuncPtr prepare, PostprocessDrawFuncPtr draw, double width, double height);
/*
* Glue for resources api

View file

@ -67,8 +67,6 @@ typedef struct ResourceAsyncLoadData {
void *opaque;
} ResourceAsyncLoadData;
Resources resources;
static SDL_threadID main_thread_id; // TODO: move this somewhere else
static inline ResourceHandler* get_handler(ResourceType type) {
@ -584,10 +582,6 @@ void free_resources(bool all) {
return;
}
delete_fbo_pair(&resources.fbo_pairs.bg);
delete_fbo_pair(&resources.fbo_pairs.fg);
delete_fbo_pair(&resources.fbo_pairs.rgba);
if(!env_get("TAISEI_NOASYNC", 0)) {
events_unregister_handler(resource_asyncload_handler);
}

View file

@ -10,7 +10,6 @@
#include "taisei.h"
#include "hashtable.h"
// #include "fbo.h"
typedef enum ResourceType {
RES_TEXTURE,

View file

@ -171,15 +171,12 @@ static void texture_post_load(Texture *tex) {
ShaderProgram *shader_saved = r_shader_current();
Texture *texture_saved = r_texture_current(0);
RenderTarget *target_saved = r_target_current();
Framebuffer *fb_saved = r_framebuffer_current();
BlendMode blend_saved = r_blend_current();
bool cullcap_saved = r_capability_current(RCAP_CULL_FACE);
IntRect viewport_saved;
r_viewport_current(&viewport_saved);
Texture fbo_tex;
RenderTarget fbo;
Framebuffer fb;
r_blend(BLEND_NONE);
r_disable(RCAP_CULL_FACE);
@ -196,9 +193,10 @@ static void texture_post_load(Texture *tex) {
.t = TEX_WRAP_REPEAT,
},
});
r_target_create(&fbo);
r_target_attach(&fbo, &fbo_tex, RENDERTARGET_ATTACHMENT_COLOR0);
r_target(&fbo);
r_framebuffer_create(&fb);
r_framebuffer_attach(&fb, &fbo_tex, FRAMEBUFFER_ATTACH_COLOR0);
r_framebuffer_viewport(&fb, 0, 0, tex->w, tex->h);
r_framebuffer(&fb);
r_texture_ptr(0, tex);
r_shader("texture_post_load");
r_uniform_int("width", tex->w);
@ -209,7 +207,6 @@ static void texture_post_load(Texture *tex) {
r_mat_push();
r_mat_identity();
r_mat_ortho(0, tex->w, tex->h, 0, -100, 100);
r_viewport(0, 0, tex->w, tex->h);
r_mat_mode(MM_MODELVIEW);
r_mat_scale(tex->w, tex->h, 1);
r_mat_translate(0.5, 0.5, 0);
@ -219,14 +216,13 @@ static void texture_post_load(Texture *tex) {
r_mat_mode(MM_PROJECTION);
r_mat_pop();
r_mat_mode(MM_MODELVIEW);
r_target(target_saved);
r_framebuffer(fb_saved);
r_shader_ptr(shader_saved);
r_texture_ptr(0, texture_saved);
r_blend(blend_saved);
r_capability(RCAP_CULL_FACE, cullcap_saved);
r_target_destroy(&fbo);
r_framebuffer_destroy(&fb);
r_texture_destroy(tex);
r_viewport_rect(viewport_saved);
memcpy(tex, &fbo_tex, sizeof(fbo_tex));
}

View file

@ -631,7 +631,7 @@ void stage_loop(StageInfo *stage) {
stage_objpools_alloc();
stage_preload();
stage_draw_preload();
stage_draw_init();
uint32_t seed = (uint32_t)time(0);
tsrand_switch(&global.rand_game);
@ -696,6 +696,7 @@ void stage_loop(StageInfo *stage) {
}
stage->procs->end();
stage_draw_shutdown();
stage_free();
player_free(&global.plr);
tsrand_switch(&global.rand_visual);

View file

@ -13,7 +13,7 @@
#include "boss.h"
#include "progress.h"
#include "difficulty.h"
#include "fbo.h"
#include "util/graphics.h"
/* taisei's strange macro language.
*
@ -56,7 +56,7 @@
#define FROM_TO_INT_SND(snd,start,end,step,dur,istep) FROM_TO_INT(start,end,step,dur,2) { play_loop(snd); }FROM_TO_INT(start,end,step,dur,istep)
typedef void (*StageProc)(void);
typedef void (*ShaderRule)(FBO*);
typedef void (*ShaderRule)(Framebuffer *);
// two highest bits of uint16_t, WAY higher than the amount of spells in this game can ever possibly be
#define STAGE_SPELL_BIT 0x8000

View file

@ -35,9 +35,11 @@ static struct {
} color;
} hud_text;
PostprocessShader *viewport_pp;
FBPair fb_pairs[NUM_FBPAIRS];
bool framerate_graphs;
bool objpool_stats;
PostprocessShader *viewport_pp;
#ifdef DEBUG
Sprite dummy;
@ -50,7 +52,121 @@ static struct {
}
};
void stage_draw_preload(void) {
static double fb_scale(void) {
int vp_width, vp_height;
video_get_viewport_size(&vp_width, &vp_height);
return (double)vp_height / SCREEN_H;
}
static void set_fb_size(StageFBPair fb_id, int *w, int *h) {
double scale = fb_scale();
switch(fb_id) {
case FBPAIR_BG:
scale *= config_get_float(CONFIG_BG_QUALITY);
break;
default:
scale *= config_get_float(CONFIG_FG_QUALITY);
break;
}
scale = sanitize_scale(scale);
*w = round(VIEWPORT_W * scale);
*h = round(VIEWPORT_H * scale);
}
static void update_fb_size(StageFBPair fb_id) {
int w, h;
set_fb_size(fb_id, &w, &h);
fbpair_resize_all(stagedraw.fb_pairs + fb_id, w, h);
fbpair_viewport(stagedraw.fb_pairs + fb_id, 0, 0, w, h);
}
static bool stage_draw_event(SDL_Event *e, void *arg) {
if(!IS_TAISEI_EVENT(e->type)) {
return false;
}
switch(TAISEI_EVENT(e->type)) {
case TE_VIDEO_MODE_CHANGED: {
for(uint i = 0; i < NUM_FBPAIRS; ++i) {
update_fb_size(i);
}
break;
}
case TE_CONFIG_UPDATED: {
switch(e->user.code) {
case CONFIG_FG_QUALITY: {
update_fb_size(FBPAIR_FG);
update_fb_size(FBPAIR_FG_AUX);
break;
}
case CONFIG_BG_QUALITY: {
update_fb_size(FBPAIR_BG);
break;
}
}
break;
}
}
return false;
}
static void stage_draw_setup_framebuffers(void) {
int fg_width, fg_height, bg_width, bg_height;
FBAttachmentConfig a[2], *a_color, *a_depth;
memset(a, 0, sizeof(a));
a_color = &a[0];
a_depth = &a[1];
a_color->attachment = FRAMEBUFFER_ATTACH_COLOR0;
a_depth->attachment = FRAMEBUFFER_ATTACH_DEPTH;
set_fb_size(FBPAIR_FG, &fg_width, &fg_height);
set_fb_size(FBPAIR_BG, &bg_width, &bg_height);
// Set up some parameters shared by all attachments
TextureParams tex_common = {
.filter.upscale = TEX_FILTER_LINEAR,
.filter.downscale = TEX_FILTER_LINEAR,
.wrap.s = TEX_WRAP_MIRROR,
.wrap.t = TEX_WRAP_MIRROR,
};
memcpy(&a_color->tex_params, &tex_common, sizeof(tex_common));
memcpy(&a_depth->tex_params, &tex_common, sizeof(tex_common));
// Foreground: 1 RGB texture per FB
a_color->tex_params.type = TEX_TYPE_RGB;
a_color->tex_params.width = fg_width;
a_color->tex_params.height = fg_height;
fbpair_create(stagedraw.fb_pairs + FBPAIR_FG, 1, a);
fbpair_viewport(stagedraw.fb_pairs + FBPAIR_FG, 0, 0, fg_width, fg_height);
// Foreground auxiliary: 1 RGBA texture per FB
a_color->tex_params.type = TEX_TYPE_RGBA;
fbpair_create(stagedraw.fb_pairs + FBPAIR_FG_AUX, 1, a);
fbpair_viewport(stagedraw.fb_pairs + FBPAIR_FG_AUX, 0, 0, fg_width, fg_height);
// Background: 1 RGB texture + depth per FB
a_color->tex_params.type = TEX_TYPE_RGB;
a_color->tex_params.width = bg_width;
a_color->tex_params.height = bg_height;
a_depth->tex_params.type = TEX_TYPE_DEPTH;
a_depth->tex_params.width = bg_width;
a_depth->tex_params.height = bg_height;
fbpair_create(stagedraw.fb_pairs + FBPAIR_BG, 2, a);
fbpair_viewport(stagedraw.fb_pairs + FBPAIR_BG, 0, 0, bg_width, bg_height);
}
void stage_draw_init(void) {
preload_resources(RES_POSTPROCESS, RESF_OPTIONAL,
"viewport",
NULL);
@ -107,6 +223,25 @@ void stage_draw_preload(void) {
stagedraw.dummy.w = 1;
stagedraw.dummy.h = 1;
#endif
stage_draw_setup_framebuffers();
events_register_handler(&(EventHandler) {
stage_draw_event, NULL, EPRIO_SYSTEM,
});
}
void stage_draw_shutdown(void) {
events_unregister_handler(stage_draw_event);
for(uint i = 0; i < NUM_FBPAIRS; ++i) {
fbpair_destroy(stagedraw.fb_pairs + i);
}
}
FBPair* stage_get_fbpair(StageFBPair id) {
assert(id >= 0 && id < NUM_FBPAIRS);
return stagedraw.fb_pairs + id;
}
static void stage_draw_collision_areas(void) {
@ -171,15 +306,15 @@ static void stage_draw_collision_areas(void) {
#endif
}
static void apply_shader_rules(ShaderRule *shaderrules, FBOPair *fbos) {
static void apply_shader_rules(ShaderRule *shaderrules, FBPair *fbos) {
if(!shaderrules) {
return;
}
for(ShaderRule *rule = shaderrules; *rule; ++rule) {
r_target(fbos->back);
r_framebuffer(fbos->back);
(*rule)(fbos->front);
swap_fbo_pair(fbos);
fbpair_swap(fbos);
}
return;
@ -265,7 +400,7 @@ static inline bool should_draw_stage_bg(void) {
);
}
static void apply_bg_shaders(ShaderRule *shaderrules, FBOPair *fbos) {
static void apply_bg_shaders(ShaderRule *shaderrules, FBPair *fbos) {
Boss *b = global.boss;
if(b && b->current && b->current->draw_rule) {
int t = global.frames - b->current->starttime;
@ -276,11 +411,11 @@ static void apply_bg_shaders(ShaderRule *shaderrules, FBOPair *fbos) {
apply_shader_rules(shaderrules, fbos);
}
r_target(fbos->back);
draw_fbo(fbos->front);
r_framebuffer(fbos->back);
draw_framebuffer_tex(fbos->front, VIEWPORT_W, VIEWPORT_H);
draw_spellbg(t);
swap_fbo_pair(fbos);
r_target(fbos->back);
fbpair_swap(fbos);
r_framebuffer(fbos->back);
complex pos = b->pos;
float ratio = (float)VIEWPORT_H/VIEWPORT_W;
@ -317,9 +452,9 @@ static void apply_bg_shaders(ShaderRule *shaderrules, FBOPair *fbos) {
r_shader_standard();
}
draw_fbo(fbos->front);
swap_fbo_pair(fbos);
r_target(NULL);
draw_framebuffer_tex(fbos->front, VIEWPORT_W, VIEWPORT_H);
fbpair_swap(fbos);
r_framebuffer(NULL);
r_shader_standard();
} else if(should_draw_stage_bg()) {
set_ortho(VIEWPORT_W, VIEWPORT_H);
@ -362,9 +497,9 @@ static void apply_zoom_shader(void) {
}
static void stage_render_bg(StageInfo *stage) {
r_target(resources.fbo_pairs.bg.back);
Texture *bg_tex = r_target_get_attachment(resources.fbo_pairs.bg.back, RENDERTARGET_ATTACHMENT_COLOR0);
r_viewport(0, 0, bg_tex->w, bg_tex->h);
FBPair *background = stage_get_fbpair(FBPAIR_BG);
r_framebuffer(background->back);
r_clear(CLEAR_ALL);
if(should_draw_stage_bg()) {
@ -373,10 +508,10 @@ static void stage_render_bg(StageInfo *stage) {
r_enable(RCAP_DEPTH_TEST);
stage->procs->draw();
r_mat_pop();
swap_fbo_pair(&resources.fbo_pairs.bg);
fbpair_swap(background);
}
apply_bg_shaders(stage->procs->shader_rules, &resources.fbo_pairs.bg);
apply_bg_shaders(stage->procs->shader_rules, background);
return;
}
@ -418,7 +553,7 @@ static void stage_draw_objects(void) {
stagetext_draw();
}
static void postprocess_prepare(FBO *fbo, ShaderProgram *s) {
static void postprocess_prepare(Framebuffer *fb, ShaderProgram *s) {
r_uniform_int("frames", global.frames);
r_uniform_vec2("viewport", VIEWPORT_W, VIEWPORT_H);
r_uniform_vec2("player", creal(global.plr.pos), VIEWPORT_H - cimag(global.plr.pos));
@ -434,7 +569,7 @@ void stage_draw_foreground(void) {
// confer video_update_quality to understand why this is fach. fach is equal to facw up to roundoff error.
float scale = fach;
// draw the foreground FBO
// draw the foreground Framebuffer
r_mat_push();
r_mat_scale(1/facw,1/fach,1);
r_mat_translate(floorf(facw*VIEWPORT_X), floorf(fach*VIEWPORT_Y), 0);
@ -452,7 +587,7 @@ void stage_draw_foreground(void) {
global.shake_view = global.shake_view_fade = 0;
}
}
draw_fbo(resources.fbo_pairs.fg.front);
draw_framebuffer_tex(stage_get_fbpair(FBPAIR_FG)->front, VIEWPORT_W, VIEWPORT_H);
r_mat_pop();
}
@ -463,6 +598,9 @@ void stage_draw_scene(StageInfo *stage) {
bool key_nobg = false;
#endif
FBPair *background = stage_get_fbpair(FBPAIR_BG);
FBPair *foreground = stage_get_fbpair(FBPAIR_FG);
bool draw_bg = !config_get_int(CONFIG_NO_STAGEBG) && !key_nobg;
if(draw_bg) {
@ -471,9 +609,7 @@ void stage_draw_scene(StageInfo *stage) {
}
// prepare for 2D rendering into the game viewport framebuffer
r_target(resources.fbo_pairs.fg.back);
Texture *fg_tex = r_target_get_attachment(resources.fbo_pairs.fg.back, RENDERTARGET_ATTACHMENT_COLOR0);
r_viewport(0, 0, fg_tex->w, fg_tex->h);
r_framebuffer(foreground->back);
set_ortho(VIEWPORT_W, VIEWPORT_H);
r_disable(RCAP_DEPTH_TEST);
@ -484,7 +620,7 @@ void stage_draw_scene(StageInfo *stage) {
}
// draw the 3D background
draw_fbo(resources.fbo_pairs.bg.front);
draw_framebuffer_tex(background->front, VIEWPORT_W, VIEWPORT_H);
// disable boss background distortion
r_shader_standard();
@ -501,28 +637,29 @@ void stage_draw_scene(StageInfo *stage) {
stage_draw_objects();
// everything drawn, now apply postprocessing
swap_fbo_pair(&resources.fbo_pairs.fg);
fbpair_swap(foreground);
// stage postprocessing
apply_shader_rules(global.stage->procs->postprocess_rules, &resources.fbo_pairs.fg);
apply_shader_rules(global.stage->procs->postprocess_rules, foreground);
// bomb effects shader if present and player bombing
if(global.frames - global.plr.recovery < 0 && global.plr.mode->procs.bomb_shader) {
ShaderRule rules[] = { global.plr.mode->procs.bomb_shader, NULL };
apply_shader_rules(rules, &resources.fbo_pairs.fg);
apply_shader_rules(rules, foreground);
}
// custom postprocessing
postprocess(
stagedraw.viewport_pp,
&resources.fbo_pairs.fg,
foreground,
postprocess_prepare,
draw_fbo
draw_framebuffer_tex,
VIEWPORT_W,
VIEWPORT_H
);
// prepare for 2D rendering into the main framebuffer (actual screen)
r_target(NULL);
video_set_viewport();
r_framebuffer(NULL);
set_ortho(SCREEN_W, SCREEN_H);
// draw the game viewport and HUD

View file

@ -10,9 +10,20 @@
#include "taisei.h"
#include "stage.h"
#include "util/graphics.h"
void stage_draw_preload(void);
typedef enum StageFBPair {
FBPAIR_BG,
FBPAIR_FG,
FBPAIR_FG_AUX,
NUM_FBPAIRS,
} StageFBPair;
void stage_draw_init(void);
void stage_draw_shutdown(void);
void stage_draw_hud(void);
void stage_draw_foreground(void);
void stage_draw_scene(StageInfo *stage);
bool stage_should_draw_particle(Projectile *p);
FBPair* stage_get_fbpair(StageFBPair id) attr_returns_nonnull;

View file

@ -150,7 +150,7 @@ static vec3 **stage1_smoke_pos(vec3 p, float maxrange) {
return linear3dpos(p, maxrange/2.0, q, r);
}
static void stage1_fog(FBO *fbo) {
static void stage1_fog(Framebuffer *fb) {
r_shader("zbuf_fog");
r_uniform_int("tex", 0);
r_uniform_int("depth", 1);
@ -159,8 +159,8 @@ static void stage1_fog(FBO *fbo) {
r_uniform_float("end", 0.8);
r_uniform_float("exponent", 3.0);
r_uniform_float("sphereness", 0.2);
r_texture_ptr(1, r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_DEPTH));
draw_fbo(fbo);
r_texture_ptr(1, r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}

View file

@ -150,7 +150,7 @@ static vec3 **stage2_bg_grass_pos2(vec3 pos, float maxrange) {
return linear3dpos(pos, maxrange, p, r);
}
static void stage2_fog(FBO *fbo) {
static void stage2_fog(Framebuffer *fb) {
r_shader("zbuf_fog");
r_uniform_int("tex", 0);
r_uniform_int("depth", 2);
@ -159,17 +159,17 @@ static void stage2_fog(FBO *fbo) {
r_uniform_float("end", 0.8);
r_uniform_float("exponent", 3.0);
r_uniform_float("sphereness", 0);
r_texture_ptr(2, r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_DEPTH));
draw_fbo(fbo);
r_texture_ptr(2, r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}
static void stage2_bloom(FBO *fbo) {
static void stage2_bloom(Framebuffer *fb) {
r_shader("bloom");
r_uniform_int("samples", 10);
r_uniform_float("intensity", 0.05);
r_uniform_float("radius", 0.03);
draw_fbo(fbo);
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}

View file

@ -97,16 +97,16 @@ static void stage3_bg_tunnel_draw(vec3 pos) {
r_mat_pop();
}
static void stage3_tunnel(FBO *fbo) {
static void stage3_tunnel(Framebuffer *fb) {
r_shader("tunnel");
r_uniform_vec3("color", stgstate.clr_r, stgstate.clr_g, stgstate.clr_b);
r_uniform_float("mixfactor", stgstate.clr_mixfactor);
r_texture_ptr(2, r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_DEPTH));
draw_fbo(fbo);
r_texture_ptr(2, r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}
static void stage3_fog(FBO *fbo) {
static void stage3_fog(Framebuffer *fb) {
r_shader("zbuf_fog");
r_uniform_int("tex", 0);
r_uniform_int("depth", 2);
@ -115,12 +115,12 @@ static void stage3_fog(FBO *fbo) {
r_uniform_float("end", 0.8);
r_uniform_float("exponent", stgstate.fog_exp/2);
r_uniform_float("sphereness", 0);
r_texture_ptr(2, r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_DEPTH));
draw_fbo(fbo);
r_texture_ptr(2, r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}
static void stage3_glitch(FBO *fbo) {
static void stage3_glitch(Framebuffer *fb) {
float strength;
if(global.boss && global.boss->current && ATTACK_IS_SPELL(global.boss->current->type) && !strcmp(global.boss->name, "Scuttle")) {
@ -138,7 +138,7 @@ static void stage3_glitch(FBO *fbo) {
r_color4(1, 1, 1, 1);
}
draw_fbo(fbo);
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}

View file

@ -63,7 +63,7 @@ struct stage4_spells_s stage4_spells = {
},
};
static void stage4_fog(FBO *fbo) {
static void stage4_fog(Framebuffer *fb) {
float f = 0;
int redtime = 5100 + STAGE4_MIDBOSS_MUSIC_TIME;
@ -80,8 +80,8 @@ static void stage4_fog(FBO *fbo) {
r_uniform_float("end", 0.8);
r_uniform_float("exponent", 4.0);
r_uniform_float("sphereness", 0);
r_texture_ptr(2, r_target_get_attachment(fbo, RENDERTARGET_ATTACHMENT_DEPTH));
draw_fbo(fbo);
r_texture_ptr(2, r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
}

View file

@ -13,6 +13,7 @@
#include <stdlib.h>
#include "global.h"
#include "util/glm.h"
#include "video.h"
Stage3D stage_3d_context;

View file

@ -15,7 +15,7 @@
#include "util/env.h"
#include "util/geometry.h"
// #include "util/glm.h"
#include "util/graphics.h"
// #include "util/graphics.h"
#include "util/io.h"
#include "util/kvparser.h"
#include "util/miscmath.h"

92
src/util/fbpair.c Normal file
View file

@ -0,0 +1,92 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "fbpair.h"
#include "global.h"
#include "util.h"
static void fbpair_create_fb(Framebuffer *fb, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) {
r_framebuffer_create(fb);
for(uint i = 0; i < num_attachments; ++i) {
Texture *tex = calloc(1, sizeof(Texture));
r_texture_create(tex, &attachments[i].tex_params);
r_framebuffer_attach(fb, tex, attachments[i].attachment);
}
}
static void fbpair_destroy_fb(Framebuffer *fb) {
for(uint i = 0; i < FRAMEBUFFER_MAX_ATTACHMENTS; ++i) {
Texture *tex = r_framebuffer_get_attachment(fb, i);
if(tex != NULL) {
r_texture_destroy(tex);
free(tex);
}
}
r_framebuffer_destroy(fb);
}
static void fbpair_resize_fb(Framebuffer *fb, FramebufferAttachment attachment, uint width, uint height) {
Texture *tex = r_framebuffer_get_attachment(fb, attachment);
if(tex == NULL || (tex->w == width && tex->h == height)) {
return;
}
// TODO: We could render a rescaled version of the old texture contents here
r_texture_replace(tex, tex->type, width, height, NULL);
r_state_push();
r_framebuffer(fb);
r_clear_color4(0, 0, 0, 0);
r_clear(CLEAR_ALL);
r_state_pop();
}
void fbpair_create(FBPair *pair, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) {
assert(num_attachments > 0 && num_attachments <= FRAMEBUFFER_MAX_ATTACHMENTS);
memset(pair, 0, sizeof(*pair));
fbpair_create_fb(*(void**)&pair->front = pair->framebuffers + 0, num_attachments, attachments);
fbpair_create_fb(*(void**)&pair->back = pair->framebuffers + 1, num_attachments, attachments);
}
void fbpair_destroy(FBPair *pair) {
fbpair_destroy_fb(pair->framebuffers + 0);
fbpair_destroy_fb(pair->framebuffers + 1);
}
void fbpair_swap(FBPair *pair) {
void *tmp = pair->front;
*(void**)&pair->front = pair->back;
*(void**)&pair->back = tmp;
}
void fbpair_resize(FBPair *pair, FramebufferAttachment attachment, uint width, uint height) {
fbpair_resize_fb(pair->framebuffers + 0, attachment, width, height);
fbpair_resize_fb(pair->framebuffers + 1, attachment, width, height);
}
void fbpair_resize_all(FBPair *pair, uint width, uint height) {
for(uint i = 0; i < FRAMEBUFFER_MAX_ATTACHMENTS; ++i) {
fbpair_resize(pair, i, width, height);
}
}
void fbpair_viewport(FBPair *pair, int x, int y, int w, int h) {
r_framebuffer_viewport(pair->front, x, y, w, h);
r_framebuffer_viewport(pair->back, x, y, w, h);
}
void fbpair_viewport_rect(FBPair *pair, IntRect vp) {
r_framebuffer_viewport_rect(pair->front, vp);
r_framebuffer_viewport_rect(pair->back, vp);
}

40
src/util/fbpair.h Normal file
View file

@ -0,0 +1,40 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#pragma once
#include "taisei.h"
#include "renderer/api.h"
typedef struct FBPair {
/*
* Rule of thumb:
* 1. Bind back buffer;
* 2. Draw front buffer;
* 3. Call fbpair_swap;
* 4. Rinse, repeat.
*/
Framebuffer *const front;
Framebuffer *const back;
Framebuffer framebuffers[2];
} FBPair;
typedef struct FBAttachmentConfig {
FramebufferAttachment attachment;
TextureParams tex_params;
} FBAttachmentConfig;
void fbpair_create(FBPair *pair, uint num_attachments, FBAttachmentConfig attachments[num_attachments]) attr_nonnull(1, 3);
void fbpair_resize(FBPair *pair, FramebufferAttachment attachment, uint width, uint height) attr_nonnull(1);
void fbpair_resize_all(FBPair *pair, uint width, uint height) attr_nonnull(1);
void fbpair_destroy(FBPair *pair) attr_nonnull(1);
void fbpair_swap(FBPair *pair) attr_nonnull(1);
void fbpair_viewport(FBPair *pair, int x, int y, int w, int h) attr_nonnull(1);
void fbpair_viewport_rect(FBPair *pair, IntRect vp) attr_nonnull(1);

View file

@ -10,6 +10,7 @@
#include "graphics.h"
#include "global.h"
#include "video.h"
void set_ortho(float w, float h) {
r_mat_mode(MM_PROJECTION);
@ -98,3 +99,17 @@ void draw_stars(int x, int y, int numstars, int numfrags, int maxstars, int maxf
r_shader_ptr(prog_saved);
}
void draw_framebuffer_tex(Framebuffer *fb, double width, double height) {
CullFaceMode cull_saved = r_cull_current();
r_cull(CULL_FRONT);
r_mat_push();
r_texture_ptr(0, r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_COLOR0));
r_mat_scale(width, height, 1);
r_mat_translate(0.5, 0.5, 0);
r_mat_scale(1, -1, 1);
r_draw_quad();
r_mat_pop();
r_cull(cull_saved);
}

View file

@ -9,7 +9,10 @@
#pragma once
#include "taisei.h"
#include "fbpair.h"
void set_ortho(float w, float h);
void colorfill(float r, float g, float b, float a);
void fade_out(float f);
void draw_stars(int x, int y, int numstars, int numfrags, int maxstars, int maxfrags, float alpha, float star_width);
void draw_framebuffer_tex(Framebuffer *fb, double width, double height);

View file

@ -3,6 +3,7 @@ util_src = files(
'assert.c',
'crap.c',
'env.c',
'fbpair.c',
'geometry.c',
'graphics.c',
'io.c',

View file

@ -27,10 +27,12 @@ typedef struct ScreenshotTaskData {
static void video_add_mode(int width, int height) {
if(video.modes) {
int i; for(i = 0; i < video.mcount; ++i) {
VideoMode *m = &(video.modes[i]);
if(m->width == width && m->height == height)
for(uint i = 0; i < video.mcount; ++i) {
VideoMode *m = video.modes + i;
if(m->width == width && m->height == height) {
return;
}
}
}
@ -60,10 +62,16 @@ void video_get_viewport_size(int *width, int *height) {
*height = h;
}
void video_set_viewport(void) {
int w, h;
video_get_viewport_size(&w,&h);
r_viewport((video.current.width - w) / 2, (video.current.height - h) / 2, w, h);
void video_get_viewport(IntRect *vp) {
video_get_viewport_size(&vp->w, &vp->h);
vp->x = (video.current.width - vp->w) / 2;
vp->y = (video.current.height - vp->h) / 2;
}
static void video_set_viewport(void) {
IntRect vp;
video_get_viewport(&vp);
r_framebuffer_viewport_rect(NULL, vp);
}
static void video_update_vsync(void) {
@ -77,23 +85,6 @@ static void video_update_vsync(void) {
}
}
static void video_update_quality(void) {
int vw, vh;
video_get_viewport_size(&vw, &vh);
float q = (float)vh / SCREEN_H;
video.quality_factor = q;
float fg = q * config_get_float(CONFIG_FG_QUALITY);
float bg = q * config_get_float(CONFIG_BG_QUALITY);
float text = q * config_get_float(CONFIG_TEXT_QUALITY);
log_debug("q:%f, fg:%f, bg:%f, text:%f", q, fg, bg, text);
init_fbo_pair(&resources.fbo_pairs.bg, bg, TEX_TYPE_RGB);
init_fbo_pair(&resources.fbo_pairs.fg, fg, TEX_TYPE_RGB);
init_fbo_pair(&resources.fbo_pairs.rgba, fg, TEX_TYPE_RGBA);
}
static uint32_t get_fullscreen_flag(void) {
if(config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
return SDL_WINDOW_FULLSCREEN_DESKTOP;
@ -120,10 +111,7 @@ static void video_update_mode_settings(void) {
SDL_ShowCursor(false);
video_update_vsync();
SDL_GetWindowSize(video.window, &video.current.width, &video.current.height);
video.real.width = video.current.width;
video.real.height = video.current.height;
video_set_viewport();
video_update_quality();
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
if(video_is_fullscreen() && !config_get_int(CONFIG_FULLSCREEN_DESKTOP)) {
@ -344,29 +332,6 @@ bool video_can_change_resolution(void) {
return !video_is_fullscreen() || !config_get_int(CONFIG_FULLSCREEN_DESKTOP);
}
static void video_cfg_fullscreen_callback(ConfigIndex idx, ConfigValue v) {
video_set_mode(
config_get_int(CONFIG_VID_WIDTH),
config_get_int(CONFIG_VID_HEIGHT),
config_set_int(idx, v.i),
config_get_int(CONFIG_VID_RESIZABLE)
);
}
static void video_cfg_vsync_callback(ConfigIndex idx, ConfigValue v) {
config_set_int(idx, v.i);
video_update_vsync();
}
static void video_cfg_resizable_callback(ConfigIndex idx, ConfigValue v) {
SDL_SetWindowResizable(video.window, config_set_int(idx, v.i));
}
static void video_quality_callback(ConfigIndex idx, ConfigValue v) {
config_set_float(idx, v.f);
video_update_quality();
}
static void video_init_sdl(void) {
// XXX: workaround for an SDL bug: https://bugzilla.libsdl.org/show_bug.cgi?id=4127
SDL_SetHintWithPriority(SDL_HINT_FRAMEBUFFER_ACCELERATION, "0", SDL_HINT_OVERRIDE);
@ -429,10 +394,10 @@ static void video_init_sdl(void) {
}
static void video_handle_resize(int w, int h) {
log_debug("%ix%i --> %ix%i", video.current.width, video.current.height, w, h);
video.current.width = w;
video.current.height = h;
video_set_viewport();
video_update_quality();
events_emit(TE_VIDEO_MODE_CHANGED, 0, NULL, NULL);
}
@ -452,12 +417,34 @@ static bool video_handle_window_event(SDL_Event *event, void *arg) {
return true;
}
static bool video_handle_config_event(SDL_Event *evt, void *arg) {
ConfigValue *val = evt->user.data1;
switch(evt->user.code) {
case CONFIG_FULLSCREEN:
video_set_mode(
config_get_int(CONFIG_VID_WIDTH),
config_get_int(CONFIG_VID_HEIGHT),
val->i,
config_get_int(CONFIG_VID_RESIZABLE)
);
break;
case CONFIG_VID_RESIZABLE:
SDL_SetWindowResizable(video.window, val->i);
break;
case CONFIG_VSYNC:
video_update_vsync();
break;
}
return false;
}
void video_init(void) {
bool fullscreen_available = false;
memset(&video, 0, sizeof(video));
memset(&resources.fbo_pairs, 0, sizeof(resources.fbo_pairs));
video_init_sdl();
log_info("Using driver '%s'", SDL_GetCurrentVideoDriver());
@ -487,6 +474,7 @@ void video_init(void) {
// This is required for some multihead setups.
VideoMode common_modes[] = {
{RESX, RESY},
{SCREEN_W, SCREEN_H},
{640, 480},
{800, 600},
@ -513,24 +501,24 @@ void video_init(void) {
config_get_int(CONFIG_VID_RESIZABLE)
);
config_set_callback(CONFIG_FULLSCREEN, video_cfg_fullscreen_callback);
config_set_callback(CONFIG_VSYNC, video_cfg_vsync_callback);
config_set_callback(CONFIG_VID_RESIZABLE, video_cfg_resizable_callback);
config_set_callback(CONFIG_FG_QUALITY, video_quality_callback);
config_set_callback(CONFIG_BG_QUALITY, video_quality_callback);
EventHandler h = {
events_register_handler(&(EventHandler) {
.proc = video_handle_window_event,
.priority = EPRIO_SYSTEM,
.event_type = SDL_WINDOWEVENT,
};
});
events_register_handler(&(EventHandler) {
.proc = video_handle_config_event,
.priority = EPRIO_SYSTEM,
.event_type = MAKE_TAISEI_EVENT(TE_CONFIG_UPDATED),
});
events_register_handler(&h);
log_info("Video subsystem initialized");
}
void video_shutdown(void) {
events_unregister_handler(video_handle_window_event);
events_unregister_handler(video_handle_config_event);
SDL_DestroyWindow(video.window);
r_shutdown();
free(video.modes);
@ -538,7 +526,7 @@ void video_shutdown(void) {
}
void video_swap_buffers(void) {
r_target(NULL);
r_framebuffer(NULL);
r_swap(video.window);
r_clear(CLEAR_COLOR);
}

View file

@ -9,8 +9,8 @@
#pragma once
#include "taisei.h"
#define WINDOW_TITLE "Taisei Project"
#define VIDEO_ASPECT_RATIO ((double)SCREEN_W/SCREEN_H)
// FIXME: This is just for IntRect, which probably should be placed elsewhere.
#include "util/geometry.h"
#include <SDL.h>
@ -18,6 +18,14 @@
#define WINFLAGS_IS_FAKE_FULLSCREEN(f) (WINFLAGS_IS_FULLSCREEN(f) == SDL_WINDOW_FULLSCREEN_DESKTOP)
#define WINFLAGS_IS_REAL_FULLSCREEN(f) (WINFLAGS_IS_FULLSCREEN(f) == SDL_WINDOW_FULLSCREEN)
#define WINDOW_TITLE "Taisei Project"
#define VIDEO_ASPECT_RATIO ((double)SCREEN_W/SCREEN_H)
enum {
SCREEN_W = 800,
SCREEN_H = 600,
};
typedef struct VideoMode {
int width;
int height;
@ -28,9 +36,7 @@ typedef struct {
int mcount;
VideoMode intended;
VideoMode current;
VideoMode real;
SDL_Window *window;
float quality_factor;
} Video;
extern Video video;
@ -38,8 +44,8 @@ extern Video video;
void video_init(void);
void video_shutdown(void);
void video_set_mode(int w, int h, bool fs, bool resizable);
void video_get_viewport(IntRect *vp);
void video_get_viewport_size(int *width, int *height);
void video_set_viewport(void);
bool video_is_fullscreen(void);
bool video_is_resizable(void);
bool video_can_change_resolution(void);