diff --git a/gfx/gl.c b/gfx/gl.c index 993fe5dc0b..67f7cb3a20 100644 --- a/gfx/gl.c +++ b/gfx/gl.c @@ -351,6 +351,8 @@ void gl_shader_set_coords(void *data, const struct gl_coords *coords, const math #define gl_shader_num(gl) ((gl->shader) ? gl->shader->num_shaders() : 0) #define gl_shader_filter_type(gl, index, smooth) ((gl->shader) ? gl->shader->filter_type(index, smooth) : false) #define gl_shader_wrap_type(gl, index) ((gl->shader) ? gl->shader->wrap_type(index) : RARCH_WRAP_BORDER) +#define gl_shader_mipmap_input(gl, index) ((gl->shader) ? gl->shader->mipmap_input(index) : false) +#define gl_shader_srgb_output(gl, index) ((gl->shader) ? gl->shader->srgb_output(index) : false) #ifdef IOS // There is no default frame buffer on IOS. @@ -460,6 +462,19 @@ static void gl_compute_fbo_geometry(void *data, unsigned width, unsigned height, } } +static inline GLenum min_filter_to_mag(GLenum type) +{ + switch (type) + { + case GL_LINEAR_MIPMAP_LINEAR: + return GL_LINEAR; + case GL_NEAREST_MIPMAP_NEAREST: + return GL_NEAREST; + default: + return type; + } +} + static void gl_create_fbo_textures(void *data) { int i; @@ -468,33 +483,48 @@ static void gl_create_fbo_textures(void *data) glGenTextures(gl->fbo_pass, gl->fbo_texture); GLuint base_filt = g_settings.video.smooth ? GL_LINEAR : GL_NEAREST; + GLuint base_mip_filt = g_settings.video.smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST; + for (i = 0; i < gl->fbo_pass; i++) { glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[i]); - GLuint filter_type = base_filt; + bool mipmapped = gl_shader_mipmap_input(gl, i + 2); + bool srgb_output = gl_shader_srgb_output(gl, i + 1); // From previous pass. + + GLenum min_filter = mipmapped ? base_mip_filt : base_filt; bool smooth = false; if (gl_shader_filter_type(gl, i + 2, &smooth)) - filter_type = smooth ? GL_LINEAR : GL_NEAREST; + min_filter = mipmapped ? (smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (smooth ? GL_LINEAR : GL_NEAREST); + + GLenum mag_filter = min_filter_to_mag(min_filter); enum gfx_wrap_type wrap = gl_shader_wrap_type(gl, i + 2); GLenum wrap_enum = gl_wrap_type_to_enum(wrap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_type); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_type); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_enum); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_enum); bool fp_fbo = gl->fbo_scale[i].valid && gl->fbo_scale[i].fp_fbo; + bool srgb_fbo = gl->fbo_scale[i].valid && srgb_output; - if (fp_fbo && !gl->has_fp_fbo) - RARCH_ERR("Floating-point FBO was requested, but is not supported. Falling back to UNORM.\n"); + if (srgb_fbo) + { + if (!gl->has_srgb_fbo) + RARCH_ERR("sRGB FBO was requested, but it is not supported. Falling back to UNORM. Result will look odd!\n"); + } + else if (fp_fbo) + { + if (!gl->has_fp_fbo) + RARCH_ERR("Floating-point FBO was requested, but is not supported. Falling back to UNORM.\n"); + } #ifndef HAVE_OPENGLES2 if (fp_fbo && gl->has_fp_fbo) { RARCH_LOG("FBO pass #%d is floating-point.\n", i); - // GLES and GL are inconsistent in which arguments to pass. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); @@ -502,18 +532,35 @@ static void gl_create_fbo_textures(void *data) else #endif { + if (srgb_fbo && gl->has_srgb_fbo) + { #ifdef HAVE_OPENGLES2 - glTexImage2D(GL_TEXTURE_2D, - 0, GL_RGBA, - gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_SRGB_ALPHA_EXT, + gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, + GL_SRGB_ALPHA_EXT, GL_UNSIGNED_BYTE, NULL); #else - // Avoid potential performance reductions on particular platforms. - glTexImage2D(GL_TEXTURE_2D, - 0, RARCH_GL_INTERNAL_FORMAT32, - gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, - RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, NULL); + glTexImage2D(GL_TEXTURE_2D, + 0, GL_SRGB8_ALPHA8, + gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); #endif + } + else + { +#ifdef HAVE_OPENGLES2 + glTexImage2D(GL_TEXTURE_2D, + 0, GL_RGBA, + gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); +#else + // Avoid potential performance reductions on particular platforms. + glTexImage2D(GL_TEXTURE_2D, + 0, RARCH_GL_INTERNAL_FORMAT32, + gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0, + RARCH_GL_TEXTURE_TYPE32, RARCH_GL_FORMAT32, NULL); +#endif + } } } @@ -852,10 +899,8 @@ static void gl_set_rotation(void *data, unsigned rotation) } #ifdef HAVE_FBO -static inline void gl_start_frame_fbo(void *data) +static inline void gl_start_frame_fbo(gl_t *gl) { - gl_t *gl = (gl_t*)data; - glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); glBindFramebuffer(GL_FRAMEBUFFER, gl->fbo[0]); gl_set_viewport(gl, gl->fbo_rect[0].img_width, gl->fbo_rect[0].img_height, true, false); @@ -864,6 +909,11 @@ static inline void gl_start_frame_fbo(void *data) // consistent texture coordinates. // We will "flip" it in place on last pass. gl->coords.vertex = vertexes; + +#ifdef GL_FRAMEBUFFER_SRGB + if (gl->has_srgb_fbo) + glEnable(GL_FRAMEBUFFER_SRGB); +#endif } static void gl_check_fbo_dimensions(void *data) @@ -945,6 +995,11 @@ static void gl_frame_fbo(void *data, const struct gl_tex_info *tex_info) gl->shader->use(gl, i + 1); glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[i - 1]); +#ifndef HAVE_GCMGL + if (gl_shader_mipmap_input(gl, i + 1)) + glGenerateMipmap(GL_TEXTURE_2D); +#endif + glClear(GL_COLOR_BUFFER_BIT); // Render to FBO with certain size. @@ -961,6 +1016,11 @@ static void gl_frame_fbo(void *data, const struct gl_tex_info *tex_info) fbo_tex_info_cnt++; } +#ifdef GL_FRAMEBUFFER_SRGB + if (gl->has_srgb_fbo) + glDisable(GL_FRAMEBUFFER_SRGB); +#endif + // Render our last FBO texture directly to screen. prev_rect = &gl->fbo_rect[gl->fbo_pass - 1]; GLfloat xamt = (GLfloat)prev_rect->img_width / prev_rect->width; @@ -975,6 +1035,11 @@ static void gl_frame_fbo(void *data, const struct gl_tex_info *tex_info) glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[gl->fbo_pass - 1]); +#ifndef HAVE_GCMGL + if (gl_shader_mipmap_input(gl, gl->fbo_pass + 1)) + glGenerateMipmap(GL_TEXTURE_2D); +#endif + glClear(GL_COLOR_BUFFER_BIT); gl_set_viewport(gl, gl->win_width, gl->win_height, false, true); @@ -1174,8 +1239,8 @@ static void gl_init_textures(void *data, const video_info_t *video) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl->wrap_mode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl->wrap_mode); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_min_filter); #ifdef HAVE_PSGL glTextureReferenceSCE(GL_TEXTURE_2D, 1, @@ -1454,6 +1519,11 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei else glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]); +#ifndef HAVE_GCMGL + if (frame && gl->tex_mipmap) // No point regenerating mipmaps if there are no new frames. + glGenerateMipmap(GL_TEXTURE_2D); +#endif + // Have to reset rendering state which libretro core could easily have overridden. #ifdef HAVE_FBO if (gl->hw_render_fbo_init) @@ -1757,10 +1827,12 @@ static bool resolve_extensions(gl_t *gl) gl->support_unpack_row_length = true; } // No extensions for float FBO currently. + gl->has_srgb_fbo = gl_query_extension(gl, "EXT_sRGB"); #else #ifdef HAVE_FBO // Float FBO is core in 3.2. gl->has_fp_fbo = gl->core_context || gl_query_extension(gl, "ARB_texture_float"); + gl->has_srgb_fbo = gl->core_context || (gl_query_extension(gl, "EXT_texture_sRGB") && gl_query_extension(gl, "ARB_framebuffer_sRGB")); #endif #endif @@ -2216,10 +2288,14 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo gl_set_shader_viewport(gl, 1); bool force_smooth = false; + gl->tex_mipmap = gl_shader_mipmap_input(gl, 1); + if (gl_shader_filter_type(gl, 1, &force_smooth)) - gl->tex_filter = force_smooth ? GL_LINEAR : GL_NEAREST; + gl->tex_min_filter = gl->tex_mipmap ? (force_smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (force_smooth ? GL_LINEAR : GL_NEAREST); else - gl->tex_filter = video->smooth ? GL_LINEAR : GL_NEAREST; + gl->tex_min_filter = gl->tex_mipmap ? (video->smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (video->smooth ? GL_LINEAR : GL_NEAREST); + + gl->tex_mag_filter = min_filter_to_mag(gl->tex_min_filter); gl->wrap_mode = gl_wrap_type_to_enum(gl_shader_wrap_type(gl, 1)); gl_set_texture_fmts(gl, video->rgb32); @@ -2327,12 +2403,16 @@ static void gl_update_tex_filter_frame(gl_t *gl) smooth = g_settings.video.smooth; GLenum wrap_mode = gl_wrap_type_to_enum(gl_shader_wrap_type(gl, 1)); + gl->tex_mipmap = gl_shader_mipmap_input(gl, 1); + gl->video_info.smooth = smooth; - GLuint new_filt = smooth ? GL_LINEAR : GL_NEAREST; - if (new_filt == gl->tex_filter && wrap_mode == gl->wrap_mode) + GLuint new_filt = gl->tex_mipmap ? (smooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST) : (smooth ? GL_LINEAR : GL_NEAREST); + if (new_filt == gl->tex_min_filter && wrap_mode == gl->wrap_mode) return; - gl->tex_filter = new_filt; + gl->tex_min_filter = new_filt; + gl->tex_mag_filter = min_filter_to_mag(gl->tex_min_filter); + gl->wrap_mode = wrap_mode; for (i = 0; i < gl->textures; i++) { @@ -2341,8 +2421,8 @@ static void gl_update_tex_filter_frame(gl_t *gl) glBindTexture(GL_TEXTURE_2D, gl->texture[i]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl->wrap_mode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl->wrap_mode); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl->tex_mag_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl->tex_min_filter); } } @@ -2407,6 +2487,7 @@ static bool gl_set_shader(void *data, enum rarch_shader_type type, const char *p if (gl->shader) { unsigned textures = gl->shader->get_prev_textures() + 1; + if (textures > gl->textures) // Have to reinit a bit. { #if defined(HAVE_FBO) && !defined(HAVE_GCMGL) diff --git a/gfx/gl_common.h b/gfx/gl_common.h index 1e09f2df6e..37b3d529be 100644 --- a/gfx/gl_common.h +++ b/gfx/gl_common.h @@ -158,7 +158,9 @@ typedef struct gl unsigned tex_index; // For use with PREV. unsigned textures; struct gl_tex_info prev_info[MAX_TEXTURES]; - GLuint tex_filter; + GLuint tex_mag_filter; + GLuint tex_min_filter; + bool tex_mipmap; void *empty_buf; @@ -181,6 +183,7 @@ typedef struct gl bool hw_render_fbo_init; bool hw_render_depth_init; bool has_fp_fbo; + bool has_srgb_fbo; #endif bool hw_render_use; bool shared_context_use; @@ -341,11 +344,13 @@ extern void glBufferSubDataTextureReferenceRA( GLenum target, GLintptr offset, G #endif #if defined(HAVE_OPENGLES) - #ifndef GL_UNPACK_ROW_LENGTH #define GL_UNPACK_ROW_LENGTH 0x0CF2 #endif +#ifndef GL_SRGB_ALPHA_EXT +#define GL_SRGB_ALPHA_EXT 0x8C42 +#endif #endif void gl_set_projection(void *data, struct gl_ortho *ortho, bool allow_rotate); diff --git a/gfx/shader_cg.c b/gfx/shader_cg.c index 6b724b7417..a8e861e73c 100644 --- a/gfx/shader_cg.c +++ b/gfx/shader_cg.c @@ -890,6 +890,22 @@ static unsigned gl_cg_get_prev_textures(void) return max_prev; } +static bool gl_cg_mipmap_input(unsigned index) +{ + if (cg_active && index) + return cg_shader->pass[index - 1].mipmap; + else + return false; +} + +static bool gl_cg_srgb_output(unsigned index) +{ + if (cg_active && index) + return cg_shader->pass[index - 1].srgb_fbo; + else + return false; +} + void gl_cg_set_compiler_args(const char **argv) { cg_arguments = argv; @@ -912,6 +928,8 @@ const gl_shader_backend_t gl_cg_backend = { gl_cg_set_coords, gl_cg_set_mvp, gl_cg_get_prev_textures, + gl_cg_mipmap_input, + gl_cg_srgb_output, RARCH_SHADER_CG, }; diff --git a/gfx/shader_common.h b/gfx/shader_common.h index f318b3f97c..bcf8b20ec5 100644 --- a/gfx/shader_common.h +++ b/gfx/shader_common.h @@ -52,6 +52,8 @@ struct gl_shader_backend bool (*set_coords)(const struct gl_coords *coords); bool (*set_mvp)(void *data, const math_matrix *mat); unsigned (*get_prev_textures)(void); + bool (*mipmap_input)(unsigned index); + bool (*srgb_output)(unsigned index); enum rarch_shader_type type; }; @@ -60,7 +62,6 @@ struct gl_shader_backend void gl_load_texture_data(GLuint obj, const struct texture_image *img, GLenum wrap, bool linear, bool mipmap); bool gl_load_luts(const struct gfx_shader *generic_shader, GLuint *lut_textures); - #endif #endif diff --git a/gfx/shader_glsl.c b/gfx/shader_glsl.c index d8a5e6e75e..7b6391c5d8 100644 --- a/gfx/shader_glsl.c +++ b/gfx/shader_glsl.c @@ -1158,6 +1158,22 @@ static unsigned gl_glsl_get_prev_textures(void) return max_prev; } +static bool gl_glsl_mipmap_input(unsigned index) +{ + if (glsl_enable && index) + return glsl_shader->pass[index - 1].mipmap; + else + return false; +} + +static bool gl_glsl_srgb_output(unsigned index) +{ + if (glsl_enable && index) + return glsl_shader->pass[index - 1].srgb_fbo; + else + return false; +} + void gl_glsl_set_get_proc_address(gfx_ctx_proc_t (*proc)(const char*)) { glsl_get_proc_address = proc; @@ -1182,6 +1198,8 @@ const gl_shader_backend_t gl_glsl_backend = { gl_glsl_set_coords, gl_glsl_set_mvp, gl_glsl_get_prev_textures, + gl_glsl_mipmap_input, + gl_glsl_srgb_output, RARCH_SHADER_GLSL, }; diff --git a/gfx/shader_hlsl.c b/gfx/shader_hlsl.c index e94428da8d..576f5243ff 100644 --- a/gfx/shader_hlsl.c +++ b/gfx/shader_hlsl.c @@ -429,6 +429,18 @@ static bool hlsl_set_mvp(void *data, const math_matrix *mat) return false; } +static bool hlsl_mipmap_input(unsigned index) +{ + (void)index; + return false; +} + +static bool hlsl_srgb_output(unsigned index) +{ + (void)index; + return false; +} + const gl_shader_backend_t hlsl_backend = { hlsl_init, hlsl_deinit, @@ -441,6 +453,8 @@ const gl_shader_backend_t hlsl_backend = { NULL, /* hlsl_set_coords */ hlsl_set_mvp, NULL, /* hlsl_get_prev_textures */ + hlsl_mipmap_input, + hlsl_srgb_output, RARCH_SHADER_HLSL, }; diff --git a/gfx/shader_parse.c b/gfx/shader_parse.c index 5c58a113e7..c47ef133d5 100644 --- a/gfx/shader_parse.c +++ b/gfx/shader_parse.c @@ -100,6 +100,15 @@ static bool shader_parse_pass(config_file_t *conf, struct gfx_shader_pass *pass, if (config_get_array(conf, frame_count_mod_buf, frame_count_mod, sizeof(frame_count_mod))) pass->frame_count_mod = strtoul(frame_count_mod, NULL, 0); + // SRGB and mipmapping + char srgb_output_buf[64]; + print_buf(srgb_output_buf, "srgb_framebuffer%u", i); + config_get_bool(conf, srgb_output_buf, &pass->srgb_fbo); + + char mipmap_buf[64]; + print_buf(mipmap_buf, "mipmap_input%u", i); + config_get_bool(conf, mipmap_buf, &pass->mipmap); + // Scale struct gfx_fbo_scale *scale = &pass->fbo; char scale_type[64] = {0}; @@ -1065,6 +1074,11 @@ void gfx_shader_write_conf_cgp(config_file_t *conf, const struct gfx_shader *sha config_set_int(conf, key, pass->frame_count_mod); } + print_buf(key, "srgb_framebuffer%u", i); + config_set_bool(conf, key, pass->srgb_fbo); + print_buf(key, "mipmap_input%u", i); + config_set_bool(conf, key, pass->mipmap); + shader_write_fbo(conf, &pass->fbo, i); } diff --git a/gfx/shader_parse.h b/gfx/shader_parse.h index 86eb24e4d7..91da894899 100644 --- a/gfx/shader_parse.h +++ b/gfx/shader_parse.h @@ -80,6 +80,8 @@ struct gfx_shader_pass enum gfx_filter_type filter; enum gfx_wrap_type wrap; unsigned frame_count_mod; + bool srgb_fbo; + bool mipmap; }; struct gfx_shader_lut