Add mipmapping and sRGB FBO support.

This commit is contained in:
Themaister 2014-05-11 13:13:38 +02:00
parent 91a0073f7d
commit bc8bb13050
8 changed files with 184 additions and 31 deletions

137
gfx/gl.c
View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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