From 29808c4421660304c52ae096870c369882571372 Mon Sep 17 00:00:00 2001 From: Themaister Date: Sun, 27 Mar 2011 20:29:47 +0200 Subject: [PATCH] Start on outscale/size support. --- gfx/gl.c | 199 +++++++++++++++++++++++++++++++++++++++++----- gfx/gl_common.h | 11 +++ gfx/shader_glsl.c | 88 ++++++++++++++++++++ 3 files changed, 276 insertions(+), 22 deletions(-) diff --git a/gfx/gl.c b/gfx/gl.c index 9c6ea42476..7d39349807 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -130,8 +130,8 @@ typedef struct gl unsigned win_width; unsigned win_height; - unsigned vp_width; - unsigned vp_height; + unsigned vp_width, vp_out_width; + unsigned vp_height, vp_out_height; unsigned last_width; unsigned last_height; unsigned tex_w, tex_h; @@ -316,6 +316,7 @@ static inline void gl_init_font(gl_t *gl, const char *font_path, unsigned font_s #endif } +// Horribly long and complex FBO init :D static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height) { #ifdef HAVE_FBO @@ -340,11 +341,51 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height) { scale.scale_x = g_settings.video.fbo_scale_x; scale.scale_y = g_settings.video.fbo_scale_y; + scale.type_x = scale.type_y = SSNES_SCALE_INPUT; + } + + float accum_scale_x = 1.0, accum_scale_y = 1.0; + + switch (gl->fbo_scale[0].type_x) + { + case SSNES_SCALE_INPUT: + accum_scale_x *= scale.scale_x; + gl->fbo_rect[0].width = width * next_pow2(ceil(accum_scale_x)); + break; + + case SSNES_SCALE_ABSOLUTE: + gl->fbo_rect[0].width = next_pow2(gl->fbo_scale[0].abs_x); + break; + + case SSNES_SCALE_VIEWPORT: + gl->fbo_rect[0].width = next_pow2(gl->win_width); + break; + + default: + break; + } + + switch (gl->fbo_scale[0].type_y) + { + case SSNES_SCALE_INPUT: + accum_scale_y *= scale.scale_y; + gl->fbo_rect[0].height = height * next_pow2(ceil(accum_scale_y)); + break; + + case SSNES_SCALE_ABSOLUTE: + gl->fbo_rect[0].height = next_pow2(gl->fbo_scale[0].abs_y); + break; + + case SSNES_SCALE_VIEWPORT: + gl->fbo_rect[0].height = next_pow2(gl->win_height); + break; + + default: + break; } - gl->fbo_rect[0].width = width * next_pow2(ceil(scale.scale_x)); - gl->fbo_rect[0].height = height * next_pow2(ceil(scale.scale_y)); gl->fbo_scale[0] = scale; + SSNES_LOG("Creating FBO 0 @ %ux%u\n", gl->fbo_rect[0].width, gl->fbo_rect[0].height); if (gl->fbo_pass <= 0) @@ -355,17 +396,50 @@ static void gl_init_fbo(gl_t *gl, unsigned width, unsigned height) gl_shader_scale(i + 1, &gl->fbo_scale[i]); if (gl->fbo_scale[i].valid) { - gl->fbo_scale[i].scale_x *= gl->fbo_scale[i - 1].scale_x; - gl->fbo_scale[i].scale_y *= gl->fbo_scale[i - 1].scale_y; + switch (gl->fbo_scale[i].type_x) + { + case SSNES_SCALE_INPUT: + accum_scale_x *= gl->fbo_scale[i].scale_x; + gl->fbo_rect[i].width = width * next_pow2(ceil(accum_scale_x)); + break; - gl->fbo_rect[i].width = width * next_pow2(ceil(gl->fbo_scale[i].scale_x)); - gl->fbo_rect[i].height = height * next_pow2(ceil(gl->fbo_scale[i].scale_y)); + case SSNES_SCALE_ABSOLUTE: + gl->fbo_rect[i].width = next_pow2(gl->fbo_scale[i].abs_x); + break; + + case SSNES_SCALE_VIEWPORT: + gl->fbo_rect[i].width = next_pow2(gl->win_width); + break; + + default: + break; + } + + switch (gl->fbo_scale[i].type_y) + { + case SSNES_SCALE_INPUT: + accum_scale_y *= gl->fbo_scale[i].scale_y; + gl->fbo_rect[i].height = height * next_pow2(ceil(accum_scale_y)); + break; + + case SSNES_SCALE_ABSOLUTE: + gl->fbo_rect[i].height = next_pow2(gl->fbo_scale[i].abs_y); + break; + + case SSNES_SCALE_VIEWPORT: + gl->fbo_rect[i].height = next_pow2(gl->win_height); + break; + + default: + break; + } } else { // Use previous values, essentially a 1x scale compared to last shader in chain. gl->fbo_rect[i] = gl->fbo_rect[i - 1]; - gl->fbo_scale[i] = gl->fbo_scale[i - 1]; + gl->fbo_scale[i].scale_x = gl->fbo_scale[i].scale_y = 1.0; + gl->fbo_scale[i].type_x = gl->fbo_scale[i].type_y = SSNES_SCALE_INPUT; } SSNES_LOG("Creating FBO %d @ %ux%u\n", i, gl->fbo_rect[i].width, gl->fbo_rect[i].height); @@ -484,6 +558,13 @@ static void set_viewport(gl_t *gl, unsigned width, unsigned height, bool force_f gl->vp_width = width; gl->vp_height = height; + // Set last backbuffer viewport. + if (!force_full) + { + gl->vp_out_width = width; + gl->vp_out_height = height; + } + //SSNES_LOG("Setting viewport @ %ux%u\n", width, height); } @@ -555,11 +636,49 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei // Render to texture in first pass. if (gl->fbo_inited) { + unsigned last_width = width; + unsigned last_height = height; // Calculate viewports for FBOs. for (int i = 0; i < gl->fbo_pass; i++) { - gl->fbo_rect[i].img_width = width * gl->fbo_scale[i].scale_x; - gl->fbo_rect[i].img_height = height * gl->fbo_scale[i].scale_y; + switch (gl->fbo_scale[i].type_x) + { + case SSNES_SCALE_INPUT: + gl->fbo_rect[i].img_width = last_width * gl->fbo_scale[i].scale_x; + break; + + case SSNES_SCALE_ABSOLUTE: + gl->fbo_rect[i].img_width = gl->fbo_scale[i].abs_x; + break; + + case SSNES_SCALE_VIEWPORT: + gl->fbo_rect[i].img_width = gl->fbo_scale[i].scale_x * gl->vp_out_width; + break; + + default: + break; + } + + switch (gl->fbo_scale[i].type_y) + { + case SSNES_SCALE_INPUT: + gl->fbo_rect[i].img_height = last_height * gl->fbo_scale[i].scale_y; + break; + + case SSNES_SCALE_ABSOLUTE: + gl->fbo_rect[i].img_height = gl->fbo_scale[i].abs_y; + break; + + case SSNES_SCALE_VIEWPORT: + gl->fbo_rect[i].img_height = gl->fbo_scale[i].scale_y * gl->vp_out_height; + break; + + default: + break; + } + + last_width = gl->fbo_rect[i].img_width; + last_height = gl->fbo_rect[i].img_height; } glBindTexture(GL_TEXTURE_2D, gl->texture); @@ -576,26 +695,62 @@ static bool gl_frame(void *data, const void* frame, unsigned width, unsigned hei #ifdef HAVE_FBO if (!gl->render_to_tex) -#endif set_viewport(gl, gl->win_width, gl->win_height, false); + else + { + unsigned factor_x = 512 / width; + unsigned factor_y = 512 / height; + + // Check if we have to recreate our FBO textures. + for (int i = 0; i < gl->fbo_pass; i++) + { + // Check proactively since we might suddently get sizes of 512 width. + if (factor_x * gl->fbo_rect[i].img_width > gl->fbo_rect[i].width || + factor_y * gl->fbo_rect[i].img_height > gl->fbo_rect[i].height) + { + unsigned img_width = gl->fbo_rect[i].img_width * factor_x; + unsigned img_height = gl->fbo_rect[i].img_height * factor_y; + unsigned max = img_width > img_height ? img_width : img_height; + unsigned pow2_size = next_pow2(max); + gl->fbo_rect[i].width = gl->fbo_rect[i].height = pow2_size; + + pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[i]); + glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[i]); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_RGBA, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_RGBA, + GL_UNSIGNED_INT_8_8_8_8, NULL); + + pglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->fbo_texture[i], 0); + + GLenum status = pglCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) + SSNES_WARN("Failed to reinit FBO texture!\n"); + } + } + + // Go back to what we're supposed to do, render to FBO #0 :D + glBindTexture(GL_TEXTURE_2D, gl->texture); + pglBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]); + set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true); + } +#else + set_viewport(gl, gl->win_width, gl->win_height, false); +#endif } glClear(GL_COLOR_BUFFER_BIT); gl_shader_set_params(width, height, gl->tex_w, gl->tex_h, gl->vp_width, gl->vp_height); - if (width != gl->last_width || height != gl->last_height) // res change. need to clear out texture. + if (width != gl->last_width || height != gl->last_height) // Res change. need to clear out texture. { gl->last_width = width; gl->last_height = height; glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(pitch)); glPixelStorei(GL_UNPACK_ROW_LENGTH, gl->tex_w); - // Can we pass NULL here, hmm? - void *tmp = calloc(1, gl->tex_w * gl->tex_h * gl->base_size); - glTexSubImage2D(GL_TEXTURE_2D, - 0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type, - gl->texture_fmt, tmp); - free(tmp); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_RGBA, gl->tex_w, gl->tex_h, 0, gl->texture_type, + gl->texture_fmt, NULL); GLfloat x = (GLfloat)width / gl->tex_w; GLfloat y = (GLfloat)height / gl->tex_h; @@ -792,14 +947,14 @@ static void* gl_init(video_info_t *video, const input_driver_t **input, void **i // Set up render to texture. gl_init_fbo(gl, 256 * video->input_scale, 256 * video->input_scale); - gl->vsync = video->vsync; - gl->keep_aspect = video->force_aspect; - // Apparently need to set viewport for passes when we aren't using FBOs. set_viewport(gl, gl->win_width, gl->win_height, false); gl_shader_use(1); set_viewport(gl, gl->win_width, gl->win_height, false); + gl->vsync = video->vsync; + gl->keep_aspect = video->force_aspect; + bool force_smooth; if (gl_shader_filter_type(1, &force_smooth)) gl->tex_filter = force_smooth ? GL_LINEAR : GL_NEAREST; diff --git a/gfx/gl_common.h b/gfx/gl_common.h index 0ce02fb027..9e102fe36d 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -73,10 +73,21 @@ struct gl_fbo_rect unsigned height; }; +enum gl_scale_type +{ + SSNES_SCALE_ABSOLUTE, + SSNES_SCALE_INPUT, + SSNES_SCALE_VIEWPORT +}; + struct gl_fbo_scale { + enum gl_scale_type type_x; + enum gl_scale_type type_y; float scale_x; float scale_y; + unsigned abs_x; + unsigned abs_y; bool valid; }; diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index 2de3fce655..388a9c4843 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -100,6 +100,11 @@ struct shader_program float scale_x; float scale_y; + unsigned abs_x; + unsigned abs_y; + enum gl_scale_type type_x; + enum gl_scale_type type_y; + bool valid_scale; }; @@ -107,6 +112,9 @@ static void get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr) { prog->scale_x = 1.0; prog->scale_y = 1.0; + prog->abs_x = 512; + prog->abs_y = 512; + prog->type_x = prog->type_y = SSNES_SCALE_INPUT; prog->valid_scale = false; // Check if shader forces a certain texture filtering. @@ -131,9 +139,16 @@ static void get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr) else prog->filter = SSNES_GL_NOFORCE; + // Check for scaling attributes *lots of code <_<* xmlChar *attr_scale = xmlGetProp(ptr, (const xmlChar*)"scale"); xmlChar *attr_scale_x = xmlGetProp(ptr, (const xmlChar*)"scale_x"); xmlChar *attr_scale_y = xmlGetProp(ptr, (const xmlChar*)"scale_y"); + xmlChar *attr_size = xmlGetProp(ptr, (const xmlChar*)"size"); + xmlChar *attr_size_x = xmlGetProp(ptr, (const xmlChar*)"size_x"); + xmlChar *attr_size_y = xmlGetProp(ptr, (const xmlChar*)"size_y"); + xmlChar *attr_outscale = xmlGetProp(ptr, (const xmlChar*)"outscale"); + xmlChar *attr_outscale_x = xmlGetProp(ptr, (const xmlChar*)"outscale_x"); + xmlChar *attr_outscale_y = xmlGetProp(ptr, (const xmlChar*)"outscale_y"); if (attr_scale) { @@ -141,6 +156,7 @@ static void get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr) prog->scale_x = scale; prog->scale_y = scale; prog->valid_scale = true; + prog->type_x = prog->type_y = SSNES_SCALE_INPUT; SSNES_LOG("Got scale attr: %.1f\n", scale); } @@ -149,6 +165,7 @@ static void get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr) float scale = strtod((const char*)attr_scale_x, NULL); prog->scale_x = scale; prog->valid_scale = true; + prog->type_x = SSNES_SCALE_INPUT; SSNES_LOG("Got scale_x attr: %.1f\n", scale); } @@ -157,15 +174,82 @@ static void get_xml_attrs(struct shader_program *prog, xmlNodePtr ptr) float scale = strtod((const char*)attr_scale_y, NULL); prog->scale_y = scale; prog->valid_scale = true; + prog->type_y = SSNES_SCALE_INPUT; SSNES_LOG("Got scale_y attr: %.1f\n", scale); } + if (attr_size) + { + prog->abs_x = prog->abs_y = strtoul((const char*)attr_scale, NULL, 0); + prog->valid_scale = true; + prog->type_x = prog->type_y = SSNES_SCALE_ABSOLUTE; + SSNES_LOG("Got size attr: %u\n", prog->abs_x); + } + + if (attr_size_x) + { + prog->abs_x = strtoul((const char*)attr_size_x, NULL, 0); + prog->valid_scale = true; + prog->type_x = SSNES_SCALE_ABSOLUTE; + SSNES_LOG("Got size_x attr: %u\n", prog->abs_x); + } + + if (attr_size_y) + { + prog->abs_y = strtoul((const char*)attr_size_y, NULL, 0); + prog->valid_scale = true; + prog->type_y = SSNES_SCALE_ABSOLUTE; + SSNES_LOG("Got size_y attr: %u\n", prog->abs_y); + } + + if (attr_outscale) + { + float scale = strtod((const char*)attr_outscale, NULL); + prog->scale_x = scale; + prog->scale_y = scale; + prog->valid_scale = true; + prog->type_x = prog->type_y = SSNES_SCALE_VIEWPORT; + SSNES_LOG("Got outscale attr: %.1f\n", scale); + } + + if (attr_outscale_x) + { + float scale = strtod((const char*)attr_outscale_x, NULL); + prog->scale_x = scale; + prog->valid_scale = true; + prog->type_x = SSNES_SCALE_VIEWPORT; + SSNES_LOG("Got outscale_x attr: %.1f\n", scale); + } + + if (attr_outscale_y) + { + float scale = strtod((const char*)attr_outscale_y, NULL); + prog->scale_y = scale; + prog->valid_scale = true; + prog->type_y = SSNES_SCALE_VIEWPORT; + SSNES_LOG("Got outscale_y attr: %.1f\n", scale); + } + + if (attr_scale) xmlFree(attr_scale); if (attr_scale_x) xmlFree(attr_scale_x); if (attr_scale_y) xmlFree(attr_scale_y); + if (attr_size) + xmlFree(attr_size); + if (attr_size_x) + xmlFree(attr_size_x); + if (attr_size_y) + xmlFree(attr_size_y); + if (attr_outscale) + xmlFree(attr_outscale); + if (attr_outscale_x) + xmlFree(attr_outscale_x); + if (attr_outscale_y) + xmlFree(attr_outscale_y); + } static unsigned get_xml_shaders(const char *path, struct shader_program *prog, size_t size) @@ -389,8 +473,12 @@ bool gl_glsl_init(const char *path) for (unsigned i = 0; i < num_progs; i++) { gl_filter_type[i + 1] = progs[i].filter; + gl_scale[i + 1].type_x = progs[i].type_x; + gl_scale[i + 1].type_y = progs[i].type_y; gl_scale[i + 1].scale_x = progs[i].scale_x; gl_scale[i + 1].scale_y = progs[i].scale_y; + gl_scale[i + 1].abs_x = progs[i].abs_x; + gl_scale[i + 1].abs_y = progs[i].abs_y; gl_scale[i + 1].valid = progs[i].valid_scale; }