mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +00:00
(GL) Move more renderchain code over to render_chain_gl_legacy
This commit is contained in:
parent
78806bf33c
commit
85ec02ba6b
479
gfx/drivers/gl.c
479
gfx/drivers/gl.c
@ -740,7 +740,7 @@ void cocoagl_bind_game_view_fbo(void);
|
||||
#define gl_bind_backbuffer() glBindFramebuffer(RARCH_GL_FRAMEBUFFER, 0)
|
||||
#endif
|
||||
|
||||
static INLINE GLenum min_filter_to_mag(GLenum type)
|
||||
GLenum min_filter_to_mag(GLenum type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
@ -762,250 +762,8 @@ static uintptr_t gl_get_current_framebuffer(void *data)
|
||||
return gl->hw_render_fbo[(gl->tex_index + 1) % gl->textures];
|
||||
}
|
||||
|
||||
/* Compute FBO geometry.
|
||||
* When width/height changes or window sizes change,
|
||||
* we have to recalculate geometry of our FBO. */
|
||||
|
||||
static void gl_recompute_pass_sizes(gl_t *gl,
|
||||
unsigned width, unsigned height,
|
||||
unsigned vp_width, unsigned vp_height)
|
||||
{
|
||||
int i;
|
||||
bool size_modified = false;
|
||||
GLint max_size = 0;
|
||||
unsigned last_width = width;
|
||||
unsigned last_height = height;
|
||||
unsigned last_max_width = gl->tex_w;
|
||||
unsigned last_max_height = gl->tex_h;
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
|
||||
|
||||
/* Calculate viewports for FBOs. */
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
struct video_fbo_rect *fbo_rect = &gl->fbo_rect[i];
|
||||
struct gfx_fbo_scale *fbo_scale = &gl->fbo_scale[i];
|
||||
|
||||
gl_renderchain_convert_geometry(gl, fbo_rect, fbo_scale,
|
||||
last_width, last_max_width,
|
||||
last_height, last_max_height,
|
||||
vp_width, vp_height
|
||||
);
|
||||
|
||||
|
||||
if (fbo_rect->img_width > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->img_width = max_size;
|
||||
}
|
||||
|
||||
if (fbo_rect->img_height > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->img_height = max_size;
|
||||
}
|
||||
|
||||
if (fbo_rect->max_img_width > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->max_img_width = max_size;
|
||||
}
|
||||
|
||||
if (fbo_rect->max_img_height > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->max_img_height = max_size;
|
||||
}
|
||||
|
||||
if (size_modified)
|
||||
RARCH_WARN("FBO textures exceeded maximum size of GPU (%dx%d). Resizing to fit.\n", max_size, max_size);
|
||||
|
||||
last_width = fbo_rect->img_width;
|
||||
last_height = fbo_rect->img_height;
|
||||
last_max_width = fbo_rect->max_img_width;
|
||||
last_max_height = fbo_rect->max_img_height;
|
||||
}
|
||||
}
|
||||
|
||||
static void gl_create_fbo_texture(gl_t *gl, unsigned i, GLuint texture)
|
||||
{
|
||||
unsigned mip_level;
|
||||
bool fp_fbo;
|
||||
GLenum min_filter, mag_filter, wrap_enum;
|
||||
video_shader_ctx_filter_t filter_type;
|
||||
video_shader_ctx_wrap_t wrap = {0};
|
||||
bool mipmapped = false;
|
||||
bool smooth = false;
|
||||
settings_t *settings = config_get_ptr();
|
||||
GLuint base_filt = settings->video.smooth ? GL_LINEAR : GL_NEAREST;
|
||||
GLuint base_mip_filt = settings->video.smooth ?
|
||||
GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
mip_level = i + 2;
|
||||
mipmapped = video_shader_driver_mipmap_input(&mip_level);
|
||||
min_filter = mipmapped ? base_mip_filt : base_filt;
|
||||
filter_type.index = i + 2;
|
||||
filter_type.smooth = &smooth;
|
||||
|
||||
if (video_shader_driver_filter_type(&filter_type))
|
||||
{
|
||||
min_filter = mipmapped ? (smooth ?
|
||||
GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST)
|
||||
: (smooth ? GL_LINEAR : GL_NEAREST);
|
||||
}
|
||||
|
||||
mag_filter = min_filter_to_mag(min_filter);
|
||||
wrap.idx = i + 2;
|
||||
|
||||
video_shader_driver_wrap_type(&wrap);
|
||||
|
||||
wrap_enum = gl_wrap_type_to_enum(wrap.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);
|
||||
|
||||
fp_fbo = gl->fbo_scale[i].fp_fbo;
|
||||
|
||||
if (fp_fbo)
|
||||
{
|
||||
if (!gl->has_fp_fbo)
|
||||
RARCH_ERR("[GL]: Floating-point FBO was requested, but is not supported. Falling back to UNORM. Result may band/clip/etc.!\n");
|
||||
}
|
||||
|
||||
#ifndef HAVE_OPENGLES2
|
||||
if (fp_fbo && gl->has_fp_fbo)
|
||||
{
|
||||
RARCH_LOG("[GL]: FBO pass #%d is floating-point.\n", i);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height,
|
||||
0, GL_RGBA, GL_FLOAT, NULL);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef HAVE_OPENGLES
|
||||
bool srgb_fbo = gl->fbo_scale[i].srgb_fbo;
|
||||
|
||||
if (!fp_fbo && srgb_fbo)
|
||||
{
|
||||
if (!gl->has_srgb_fbo)
|
||||
RARCH_ERR("[GL]: sRGB FBO was requested, but it is not supported. Falling back to UNORM. Result may have banding!\n");
|
||||
}
|
||||
|
||||
if (settings->video.force_srgb_disable)
|
||||
srgb_fbo = false;
|
||||
|
||||
if (srgb_fbo && gl->has_srgb_fbo)
|
||||
{
|
||||
RARCH_LOG("[GL]: FBO pass #%d is sRGB.\n", i);
|
||||
#ifdef HAVE_OPENGLES2
|
||||
/* EXT defines are same as core GLES3 defines,
|
||||
* but GLES3 variant requires different arguments. */
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_SRGB_ALPHA_EXT,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0,
|
||||
gl->has_srgb_fbo_gles3 ? GL_RGBA : GL_SRGB_ALPHA_EXT,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
#else
|
||||
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
|
||||
#endif
|
||||
{
|
||||
#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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gl_create_fbo_textures(gl_t *gl)
|
||||
{
|
||||
int i;
|
||||
glGenTextures(gl->fbo_pass, gl->fbo_texture);
|
||||
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
gl_create_fbo_texture(gl, i, gl->fbo_texture[i]);
|
||||
|
||||
if (gl->fbo_feedback_enable)
|
||||
{
|
||||
glGenTextures(1, &gl->fbo_feedback_texture);
|
||||
gl_create_fbo_texture(gl,
|
||||
gl->fbo_feedback_pass, gl->fbo_feedback_texture);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
static bool gl_create_fbo_targets(gl_t *gl)
|
||||
{
|
||||
int i;
|
||||
GLenum status;
|
||||
|
||||
if (!gl)
|
||||
return false;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glGenFramebuffers(gl->fbo_pass, gl->fbo);
|
||||
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo[i]);
|
||||
glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER,
|
||||
RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->fbo_texture[i], 0);
|
||||
|
||||
status = glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER);
|
||||
if (status != RARCH_GL_FRAMEBUFFER_COMPLETE)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (gl->fbo_feedback_texture)
|
||||
{
|
||||
glGenFramebuffers(1, &gl->fbo_feedback);
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo_feedback);
|
||||
glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER,
|
||||
RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
gl->fbo_feedback_texture, 0);
|
||||
|
||||
status = glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER);
|
||||
if (status != RARCH_GL_FRAMEBUFFER_COMPLETE)
|
||||
goto error;
|
||||
|
||||
/* Make sure the feedback textures are cleared
|
||||
* so we don't feedback noise. */
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
glDeleteFramebuffers(gl->fbo_pass, gl->fbo);
|
||||
if (gl->fbo_feedback)
|
||||
glDeleteFramebuffers(1, &gl->fbo_feedback);
|
||||
RARCH_ERR("Failed to set up frame buffer objects. Multi-pass shading will not work.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gl_deinit_fbo(gl_t *gl)
|
||||
void gl_deinit_fbo(gl_t *gl)
|
||||
{
|
||||
if (!gl->fbo_inited)
|
||||
return;
|
||||
@ -1028,113 +786,7 @@ static void gl_deinit_fbo(gl_t *gl)
|
||||
gl->fbo_feedback = 0;
|
||||
}
|
||||
|
||||
/* Set up render to texture. */
|
||||
|
||||
static void gl_init_fbo(gl_t *gl, unsigned fbo_width, unsigned fbo_height)
|
||||
{
|
||||
int i;
|
||||
unsigned width, height;
|
||||
video_shader_ctx_scale_t scaler;
|
||||
video_shader_ctx_info_t shader_info;
|
||||
struct gfx_fbo_scale scale, scale_last;
|
||||
|
||||
if (!video_shader_driver_info(&shader_info))
|
||||
return;
|
||||
|
||||
if (!gl || shader_info.num == 0)
|
||||
return;
|
||||
|
||||
video_driver_get_size(&width, &height);
|
||||
|
||||
scaler.idx = 1;
|
||||
scaler.scale = &scale;
|
||||
|
||||
video_shader_driver_scale(&scaler);
|
||||
|
||||
scaler.idx = shader_info.num;
|
||||
scaler.scale = &scale_last;
|
||||
|
||||
video_shader_driver_scale(&scaler);
|
||||
|
||||
/* we always want FBO to be at least initialized on startup for consoles */
|
||||
if (shader_info.num == 1 && !scale.valid)
|
||||
return;
|
||||
|
||||
if (!gl_check_capability(GL_CAPS_FBO))
|
||||
{
|
||||
RARCH_ERR("Failed to locate FBO functions. Won't be able to use render-to-texture.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gl->fbo_pass = shader_info.num - 1;
|
||||
if (scale_last.valid)
|
||||
gl->fbo_pass++;
|
||||
|
||||
if (!scale.valid)
|
||||
{
|
||||
scale.scale_x = 1.0f;
|
||||
scale.scale_y = 1.0f;
|
||||
scale.type_x = scale.type_y = RARCH_SCALE_INPUT;
|
||||
scale.valid = true;
|
||||
}
|
||||
|
||||
gl->fbo_scale[0] = scale;
|
||||
|
||||
for (i = 1; i < gl->fbo_pass; i++)
|
||||
{
|
||||
scaler.idx = i + 1;
|
||||
scaler.scale = &gl->fbo_scale[i];
|
||||
|
||||
video_shader_driver_scale(&scaler);
|
||||
|
||||
if (!gl->fbo_scale[i].valid)
|
||||
{
|
||||
gl->fbo_scale[i].scale_x = gl->fbo_scale[i].scale_y = 1.0f;
|
||||
gl->fbo_scale[i].type_x = gl->fbo_scale[i].type_y =
|
||||
RARCH_SCALE_INPUT;
|
||||
gl->fbo_scale[i].valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
gl_recompute_pass_sizes(gl, fbo_width, fbo_height, width, height);
|
||||
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
gl->fbo_rect[i].width = next_pow2(gl->fbo_rect[i].img_width);
|
||||
gl->fbo_rect[i].height = next_pow2(gl->fbo_rect[i].img_height);
|
||||
RARCH_LOG("[GL]: Creating FBO %d @ %ux%u\n", i,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height);
|
||||
}
|
||||
|
||||
gl->fbo_feedback_enable = video_shader_driver_get_feedback_pass(
|
||||
&gl->fbo_feedback_pass);
|
||||
|
||||
if (gl->fbo_feedback_enable && gl->fbo_feedback_pass
|
||||
< (unsigned)gl->fbo_pass)
|
||||
{
|
||||
RARCH_LOG("[GL]: Creating feedback FBO %d @ %ux%u\n", i,
|
||||
gl->fbo_rect[gl->fbo_feedback_pass].width,
|
||||
gl->fbo_rect[gl->fbo_feedback_pass].height);
|
||||
}
|
||||
else if (gl->fbo_feedback_enable)
|
||||
{
|
||||
RARCH_WARN("[GL]: Tried to create feedback FBO of pass #%u, but there are only %d FBO passes. Will use input texture as feedback texture.\n",
|
||||
gl->fbo_feedback_pass, gl->fbo_pass);
|
||||
gl->fbo_feedback_enable = false;
|
||||
}
|
||||
|
||||
gl_create_fbo_textures(gl);
|
||||
if (!gl_create_fbo_targets(gl))
|
||||
{
|
||||
glDeleteTextures(gl->fbo_pass, gl->fbo_texture);
|
||||
RARCH_ERR("Failed to create FBO targets. Will continue without FBO.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gl->fbo_inited = true;
|
||||
}
|
||||
|
||||
static void gl_deinit_hw_render(gl_t *gl)
|
||||
void gl_deinit_hw_render(gl_t *gl)
|
||||
{
|
||||
if (!gl)
|
||||
return;
|
||||
@ -1246,118 +898,6 @@ static bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
|
||||
context_bind_hw_render(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static INLINE void gl_start_frame_fbo(gl_t *gl)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo[0]);
|
||||
|
||||
gl_set_viewport(gl, gl->fbo_rect[0].img_width,
|
||||
gl->fbo_rect[0].img_height, true, false);
|
||||
|
||||
/* Need to preserve the "flipped" state when in FBO
|
||||
* as well to have consistent texture coordinates.
|
||||
*
|
||||
* We will "flip" it in place on last pass. */
|
||||
gl->coords.vertex = vertexes;
|
||||
|
||||
#if defined(GL_FRAMEBUFFER_SRGB) && !defined(HAVE_OPENGLES)
|
||||
if (gl->has_srgb_fbo)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool gl_recreate_fbo(
|
||||
struct video_fbo_rect *fbo_rect,
|
||||
GLuint fbo,
|
||||
GLuint texture
|
||||
)
|
||||
{
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, fbo);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, RARCH_GL_INTERNAL_FORMAT32,
|
||||
fbo_rect->width,
|
||||
fbo_rect->height,
|
||||
0, RARCH_GL_TEXTURE_TYPE32,
|
||||
RARCH_GL_FORMAT32, NULL);
|
||||
|
||||
glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER,
|
||||
RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
texture, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER) != RARCH_GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
RARCH_WARN("Failed to reinitialize FBO texture.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gl_check_fbo_dimension(gl_t *gl, unsigned i,
|
||||
GLuint fbo, GLuint texture, bool update_feedback)
|
||||
{
|
||||
unsigned img_width, img_height, max, pow2_size;
|
||||
bool check_dimensions = false;
|
||||
struct video_fbo_rect *fbo_rect = &gl->fbo_rect[i];
|
||||
|
||||
if (!fbo_rect)
|
||||
return;
|
||||
|
||||
check_dimensions =
|
||||
(fbo_rect->max_img_width > fbo_rect->width) ||
|
||||
(fbo_rect->max_img_height > fbo_rect->height);
|
||||
|
||||
if (!check_dimensions)
|
||||
return;
|
||||
|
||||
/* Check proactively since we might suddently
|
||||
* get sizes of tex_w width or tex_h height. */
|
||||
img_width = fbo_rect->max_img_width;
|
||||
img_height = fbo_rect->max_img_height;
|
||||
max = img_width > img_height ? img_width : img_height;
|
||||
pow2_size = next_pow2(max);
|
||||
|
||||
fbo_rect->width = fbo_rect->height = pow2_size;
|
||||
|
||||
gl_recreate_fbo(fbo_rect, fbo, texture);
|
||||
|
||||
/* Update feedback texture in-place so we avoid having to
|
||||
* juggle two different fbo_rect structs since they get updated here. */
|
||||
if (update_feedback)
|
||||
{
|
||||
if (gl_recreate_fbo(fbo_rect, gl->fbo_feedback,
|
||||
gl->fbo_feedback_texture))
|
||||
{
|
||||
/* Make sure the feedback textures are cleared
|
||||
* so we don't feedback noise. */
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
RARCH_LOG("[GL]: Recreating FBO texture #%d: %ux%u\n",
|
||||
i, fbo_rect->width, fbo_rect->height);
|
||||
}
|
||||
|
||||
/* On resize, we might have to recreate our FBOs
|
||||
* due to "Viewport" scale, and set a new viewport. */
|
||||
|
||||
static void gl_check_fbo_dimensions(gl_t *gl)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Check if we have to recreate our FBO textures. */
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
bool update_feedback = gl->fbo_feedback_enable
|
||||
&& (unsigned)i == gl->fbo_feedback_pass;
|
||||
gl_check_fbo_dimension(gl, i, gl->fbo[i],
|
||||
gl->fbo_texture[i], update_feedback);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gl_set_rotation(void *data, unsigned rotation)
|
||||
@ -1990,9 +1530,9 @@ static bool gl_frame(void *data, const void *frame,
|
||||
/* Render to texture in first pass. */
|
||||
if (gl->fbo_inited)
|
||||
{
|
||||
gl_recompute_pass_sizes(gl, frame_width, frame_height,
|
||||
gl_renderchain_recompute_pass_sizes(gl, frame_width, frame_height,
|
||||
gl->vp_out_width, gl->vp_out_height);
|
||||
gl_start_frame_fbo(gl);
|
||||
gl_renderchain_start_render(gl);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -2014,7 +1554,7 @@ static bool gl_frame(void *data, const void *frame,
|
||||
|
||||
/* Go back to what we're supposed to do,
|
||||
* render to FBO #0. */
|
||||
gl_start_frame_fbo(gl);
|
||||
gl_renderchain_start_render(gl);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@ -2318,8 +1858,7 @@ static void gl_free(void *data)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
gl_deinit_fbo(gl);
|
||||
gl_deinit_hw_render(gl);
|
||||
gl_renderchain_free(gl);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OPENGLES
|
||||
@ -2940,7 +2479,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
gl_init_textures_data(gl);
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
|
||||
gl_renderchain_init(gl, gl->tex_w, gl->tex_h);
|
||||
|
||||
if (gl->hw_render_use &&
|
||||
!gl_init_hw_render(gl, gl->tex_w, gl->tex_h))
|
||||
@ -3170,7 +2709,7 @@ static bool gl_set_shader(void *data,
|
||||
}
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
|
||||
gl_renderchain_init(gl, gl->tex_w, gl->tex_h);
|
||||
#endif
|
||||
|
||||
/* Apparently need to set viewport for passes when we aren't using FBOs. */
|
||||
|
@ -73,9 +73,27 @@ void gl_renderchain_render(gl_t *gl,
|
||||
const struct video_tex_info *tex_info,
|
||||
const struct video_tex_info *feedback_info);
|
||||
|
||||
void gl_renderchain_init(gl_t *gl, unsigned fbo_width, unsigned fbo_height);
|
||||
|
||||
void gl_set_viewport(void *data, unsigned viewport_width,
|
||||
unsigned viewport_height, bool force_full, bool allow_rotate);
|
||||
|
||||
void gl_deinit_hw_render(gl_t *gl);
|
||||
|
||||
void gl_deinit_fbo(gl_t *gl);
|
||||
|
||||
GLenum min_filter_to_mag(GLenum type);
|
||||
|
||||
void gl_renderchain_recompute_pass_sizes(gl_t *gl,
|
||||
unsigned width, unsigned height,
|
||||
unsigned vp_width, unsigned vp_height);
|
||||
|
||||
void gl_renderchain_start_render(gl_t *gl);
|
||||
|
||||
void gl_check_fbo_dimensions(gl_t *gl);
|
||||
|
||||
void gl_renderchain_free(gl_t *gl);
|
||||
|
||||
bool gl_check_capability(enum gl_capability_enum enum_idx);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
@ -88,6 +88,16 @@
|
||||
coords[5] = yamt; \
|
||||
coords[7] = yamt
|
||||
|
||||
/* Used when rendering to an FBO.
|
||||
* Texture coords have to be aligned
|
||||
* with vertex coordinates. */
|
||||
static const GLfloat vertexes[] = {
|
||||
0, 0,
|
||||
1, 0,
|
||||
0, 1,
|
||||
1, 1
|
||||
};
|
||||
|
||||
#ifdef IOS
|
||||
/* There is no default frame buffer on iOS. */
|
||||
void cocoagl_bind_game_view_fbo(void);
|
||||
@ -96,6 +106,7 @@ void cocoagl_bind_game_view_fbo(void);
|
||||
#define gl_bind_backbuffer() glBindFramebuffer(RARCH_GL_FRAMEBUFFER, 0)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
void gl_renderchain_convert_geometry(gl_t *gl,
|
||||
struct video_fbo_rect *fbo_rect,
|
||||
struct gfx_fbo_scale *fbo_scale,
|
||||
@ -201,7 +212,98 @@ bool gl_renderchain_add_lut(const struct video_shader *shader,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
|
||||
static bool gl_recreate_fbo(
|
||||
struct video_fbo_rect *fbo_rect,
|
||||
GLuint fbo,
|
||||
GLuint texture
|
||||
)
|
||||
{
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, fbo);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, RARCH_GL_INTERNAL_FORMAT32,
|
||||
fbo_rect->width,
|
||||
fbo_rect->height,
|
||||
0, RARCH_GL_TEXTURE_TYPE32,
|
||||
RARCH_GL_FORMAT32, NULL);
|
||||
|
||||
glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER,
|
||||
RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
texture, 0);
|
||||
|
||||
if (glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER) != RARCH_GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
RARCH_WARN("Failed to reinitialize FBO texture.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void gl_check_fbo_dimension(gl_t *gl, unsigned i,
|
||||
GLuint fbo, GLuint texture, bool update_feedback)
|
||||
{
|
||||
unsigned img_width, img_height, max, pow2_size;
|
||||
bool check_dimensions = false;
|
||||
struct video_fbo_rect *fbo_rect = &gl->fbo_rect[i];
|
||||
|
||||
if (!fbo_rect)
|
||||
return;
|
||||
|
||||
check_dimensions =
|
||||
(fbo_rect->max_img_width > fbo_rect->width) ||
|
||||
(fbo_rect->max_img_height > fbo_rect->height);
|
||||
|
||||
if (!check_dimensions)
|
||||
return;
|
||||
|
||||
/* Check proactively since we might suddently
|
||||
* get sizes of tex_w width or tex_h height. */
|
||||
img_width = fbo_rect->max_img_width;
|
||||
img_height = fbo_rect->max_img_height;
|
||||
max = img_width > img_height ? img_width : img_height;
|
||||
pow2_size = next_pow2(max);
|
||||
|
||||
fbo_rect->width = fbo_rect->height = pow2_size;
|
||||
|
||||
gl_recreate_fbo(fbo_rect, fbo, texture);
|
||||
|
||||
/* Update feedback texture in-place so we avoid having to
|
||||
* juggle two different fbo_rect structs since they get updated here. */
|
||||
if (update_feedback)
|
||||
{
|
||||
if (gl_recreate_fbo(fbo_rect, gl->fbo_feedback,
|
||||
gl->fbo_feedback_texture))
|
||||
{
|
||||
/* Make sure the feedback textures are cleared
|
||||
* so we don't feedback noise. */
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
RARCH_LOG("[GL]: Recreating FBO texture #%d: %ux%u\n",
|
||||
i, fbo_rect->width, fbo_rect->height);
|
||||
}
|
||||
|
||||
/* On resize, we might have to recreate our FBOs
|
||||
* due to "Viewport" scale, and set a new viewport. */
|
||||
|
||||
void gl_check_fbo_dimensions(gl_t *gl)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Check if we have to recreate our FBO textures. */
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
bool update_feedback = gl->fbo_feedback_enable
|
||||
&& (unsigned)i == gl->fbo_feedback_pass;
|
||||
gl_check_fbo_dimension(gl, i, gl->fbo[i],
|
||||
gl->fbo_texture[i], update_feedback);
|
||||
}
|
||||
}
|
||||
void gl_renderchain_render(gl_t *gl,
|
||||
uint64_t frame_count,
|
||||
const struct video_tex_info *tex_info,
|
||||
@ -378,4 +480,379 @@ void gl_renderchain_render(gl_t *gl,
|
||||
|
||||
gl->coords.tex_coord = gl->tex_info.coord;
|
||||
}
|
||||
|
||||
void gl_renderchain_free(gl_t *gl)
|
||||
{
|
||||
gl_deinit_fbo(gl);
|
||||
gl_deinit_hw_render(gl);
|
||||
}
|
||||
|
||||
static bool gl_create_fbo_targets(gl_t *gl)
|
||||
{
|
||||
int i;
|
||||
GLenum status;
|
||||
|
||||
if (!gl)
|
||||
return false;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glGenFramebuffers(gl->fbo_pass, gl->fbo);
|
||||
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo[i]);
|
||||
glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER,
|
||||
RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->fbo_texture[i], 0);
|
||||
|
||||
status = glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER);
|
||||
if (status != RARCH_GL_FRAMEBUFFER_COMPLETE)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (gl->fbo_feedback_texture)
|
||||
{
|
||||
glGenFramebuffers(1, &gl->fbo_feedback);
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo_feedback);
|
||||
glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER,
|
||||
RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
gl->fbo_feedback_texture, 0);
|
||||
|
||||
status = glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER);
|
||||
if (status != RARCH_GL_FRAMEBUFFER_COMPLETE)
|
||||
goto error;
|
||||
|
||||
/* Make sure the feedback textures are cleared
|
||||
* so we don't feedback noise. */
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
glDeleteFramebuffers(gl->fbo_pass, gl->fbo);
|
||||
if (gl->fbo_feedback)
|
||||
glDeleteFramebuffers(1, &gl->fbo_feedback);
|
||||
RARCH_ERR("Failed to set up frame buffer objects. Multi-pass shading will not work.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gl_create_fbo_texture(gl_t *gl, unsigned i, GLuint texture)
|
||||
{
|
||||
unsigned mip_level;
|
||||
bool fp_fbo;
|
||||
GLenum min_filter, mag_filter, wrap_enum;
|
||||
video_shader_ctx_filter_t filter_type;
|
||||
video_shader_ctx_wrap_t wrap = {0};
|
||||
bool mipmapped = false;
|
||||
bool smooth = false;
|
||||
settings_t *settings = config_get_ptr();
|
||||
GLuint base_filt = settings->video.smooth ? GL_LINEAR : GL_NEAREST;
|
||||
GLuint base_mip_filt = settings->video.smooth ?
|
||||
GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
|
||||
mip_level = i + 2;
|
||||
mipmapped = video_shader_driver_mipmap_input(&mip_level);
|
||||
min_filter = mipmapped ? base_mip_filt : base_filt;
|
||||
filter_type.index = i + 2;
|
||||
filter_type.smooth = &smooth;
|
||||
|
||||
if (video_shader_driver_filter_type(&filter_type))
|
||||
{
|
||||
min_filter = mipmapped ? (smooth ?
|
||||
GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_NEAREST)
|
||||
: (smooth ? GL_LINEAR : GL_NEAREST);
|
||||
}
|
||||
|
||||
mag_filter = min_filter_to_mag(min_filter);
|
||||
wrap.idx = i + 2;
|
||||
|
||||
video_shader_driver_wrap_type(&wrap);
|
||||
|
||||
wrap_enum = gl_wrap_type_to_enum(wrap.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);
|
||||
|
||||
fp_fbo = gl->fbo_scale[i].fp_fbo;
|
||||
|
||||
if (fp_fbo)
|
||||
{
|
||||
if (!gl->has_fp_fbo)
|
||||
RARCH_ERR("[GL]: Floating-point FBO was requested, but is not supported. Falling back to UNORM. Result may band/clip/etc.!\n");
|
||||
}
|
||||
|
||||
#ifndef HAVE_OPENGLES2
|
||||
if (fp_fbo && gl->has_fp_fbo)
|
||||
{
|
||||
RARCH_LOG("[GL]: FBO pass #%d is floating-point.\n", i);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height,
|
||||
0, GL_RGBA, GL_FLOAT, NULL);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef HAVE_OPENGLES
|
||||
bool srgb_fbo = gl->fbo_scale[i].srgb_fbo;
|
||||
|
||||
if (!fp_fbo && srgb_fbo)
|
||||
{
|
||||
if (!gl->has_srgb_fbo)
|
||||
RARCH_ERR("[GL]: sRGB FBO was requested, but it is not supported. Falling back to UNORM. Result may have banding!\n");
|
||||
}
|
||||
|
||||
if (settings->video.force_srgb_disable)
|
||||
srgb_fbo = false;
|
||||
|
||||
if (srgb_fbo && gl->has_srgb_fbo)
|
||||
{
|
||||
RARCH_LOG("[GL]: FBO pass #%d is sRGB.\n", i);
|
||||
#ifdef HAVE_OPENGLES2
|
||||
/* EXT defines are same as core GLES3 defines,
|
||||
* but GLES3 variant requires different arguments. */
|
||||
glTexImage2D(GL_TEXTURE_2D,
|
||||
0, GL_SRGB_ALPHA_EXT,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height, 0,
|
||||
gl->has_srgb_fbo_gles3 ? GL_RGBA : GL_SRGB_ALPHA_EXT,
|
||||
GL_UNSIGNED_BYTE, NULL);
|
||||
#else
|
||||
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
|
||||
#endif
|
||||
{
|
||||
#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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gl_create_fbo_textures(gl_t *gl)
|
||||
{
|
||||
int i;
|
||||
glGenTextures(gl->fbo_pass, gl->fbo_texture);
|
||||
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
gl_create_fbo_texture(gl, i, gl->fbo_texture[i]);
|
||||
|
||||
if (gl->fbo_feedback_enable)
|
||||
{
|
||||
glGenTextures(1, &gl->fbo_feedback_texture);
|
||||
gl_create_fbo_texture(gl,
|
||||
gl->fbo_feedback_pass, gl->fbo_feedback_texture);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
/* Compute FBO geometry.
|
||||
* When width/height changes or window sizes change,
|
||||
* we have to recalculate geometry of our FBO. */
|
||||
|
||||
void gl_renderchain_recompute_pass_sizes(gl_t *gl,
|
||||
unsigned width, unsigned height,
|
||||
unsigned vp_width, unsigned vp_height)
|
||||
{
|
||||
int i;
|
||||
bool size_modified = false;
|
||||
GLint max_size = 0;
|
||||
unsigned last_width = width;
|
||||
unsigned last_height = height;
|
||||
unsigned last_max_width = gl->tex_w;
|
||||
unsigned last_max_height = gl->tex_h;
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
|
||||
|
||||
/* Calculate viewports for FBOs. */
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
struct video_fbo_rect *fbo_rect = &gl->fbo_rect[i];
|
||||
struct gfx_fbo_scale *fbo_scale = &gl->fbo_scale[i];
|
||||
|
||||
gl_renderchain_convert_geometry(gl, fbo_rect, fbo_scale,
|
||||
last_width, last_max_width,
|
||||
last_height, last_max_height,
|
||||
vp_width, vp_height
|
||||
);
|
||||
|
||||
|
||||
if (fbo_rect->img_width > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->img_width = max_size;
|
||||
}
|
||||
|
||||
if (fbo_rect->img_height > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->img_height = max_size;
|
||||
}
|
||||
|
||||
if (fbo_rect->max_img_width > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->max_img_width = max_size;
|
||||
}
|
||||
|
||||
if (fbo_rect->max_img_height > (unsigned)max_size)
|
||||
{
|
||||
size_modified = true;
|
||||
fbo_rect->max_img_height = max_size;
|
||||
}
|
||||
|
||||
if (size_modified)
|
||||
RARCH_WARN("FBO textures exceeded maximum size of GPU (%dx%d). Resizing to fit.\n", max_size, max_size);
|
||||
|
||||
last_width = fbo_rect->img_width;
|
||||
last_height = fbo_rect->img_height;
|
||||
last_max_width = fbo_rect->max_img_width;
|
||||
last_max_height = fbo_rect->max_img_height;
|
||||
}
|
||||
}
|
||||
|
||||
void gl_renderchain_start_render(gl_t *gl)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo[0]);
|
||||
|
||||
gl_set_viewport(gl, gl->fbo_rect[0].img_width,
|
||||
gl->fbo_rect[0].img_height, true, false);
|
||||
|
||||
/* Need to preserve the "flipped" state when in FBO
|
||||
* as well to have consistent texture coordinates.
|
||||
*
|
||||
* We will "flip" it in place on last pass. */
|
||||
gl->coords.vertex = vertexes;
|
||||
|
||||
#if defined(GL_FRAMEBUFFER_SRGB) && !defined(HAVE_OPENGLES)
|
||||
if (gl->has_srgb_fbo)
|
||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Set up render to texture. */
|
||||
void gl_renderchain_init(gl_t *gl, unsigned fbo_width, unsigned fbo_height)
|
||||
{
|
||||
int i;
|
||||
unsigned width, height;
|
||||
video_shader_ctx_scale_t scaler;
|
||||
video_shader_ctx_info_t shader_info;
|
||||
struct gfx_fbo_scale scale, scale_last;
|
||||
|
||||
if (!video_shader_driver_info(&shader_info))
|
||||
return;
|
||||
|
||||
if (!gl || shader_info.num == 0)
|
||||
return;
|
||||
|
||||
video_driver_get_size(&width, &height);
|
||||
|
||||
scaler.idx = 1;
|
||||
scaler.scale = &scale;
|
||||
|
||||
video_shader_driver_scale(&scaler);
|
||||
|
||||
scaler.idx = shader_info.num;
|
||||
scaler.scale = &scale_last;
|
||||
|
||||
video_shader_driver_scale(&scaler);
|
||||
|
||||
/* we always want FBO to be at least initialized on startup for consoles */
|
||||
if (shader_info.num == 1 && !scale.valid)
|
||||
return;
|
||||
|
||||
if (!gl_check_capability(GL_CAPS_FBO))
|
||||
{
|
||||
RARCH_ERR("Failed to locate FBO functions. Won't be able to use render-to-texture.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gl->fbo_pass = shader_info.num - 1;
|
||||
if (scale_last.valid)
|
||||
gl->fbo_pass++;
|
||||
|
||||
if (!scale.valid)
|
||||
{
|
||||
scale.scale_x = 1.0f;
|
||||
scale.scale_y = 1.0f;
|
||||
scale.type_x = scale.type_y = RARCH_SCALE_INPUT;
|
||||
scale.valid = true;
|
||||
}
|
||||
|
||||
gl->fbo_scale[0] = scale;
|
||||
|
||||
for (i = 1; i < gl->fbo_pass; i++)
|
||||
{
|
||||
scaler.idx = i + 1;
|
||||
scaler.scale = &gl->fbo_scale[i];
|
||||
|
||||
video_shader_driver_scale(&scaler);
|
||||
|
||||
if (!gl->fbo_scale[i].valid)
|
||||
{
|
||||
gl->fbo_scale[i].scale_x = gl->fbo_scale[i].scale_y = 1.0f;
|
||||
gl->fbo_scale[i].type_x = gl->fbo_scale[i].type_y =
|
||||
RARCH_SCALE_INPUT;
|
||||
gl->fbo_scale[i].valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
gl_renderchain_recompute_pass_sizes(gl,
|
||||
fbo_width, fbo_height, width, height);
|
||||
|
||||
for (i = 0; i < gl->fbo_pass; i++)
|
||||
{
|
||||
gl->fbo_rect[i].width = next_pow2(gl->fbo_rect[i].img_width);
|
||||
gl->fbo_rect[i].height = next_pow2(gl->fbo_rect[i].img_height);
|
||||
RARCH_LOG("[GL]: Creating FBO %d @ %ux%u\n", i,
|
||||
gl->fbo_rect[i].width, gl->fbo_rect[i].height);
|
||||
}
|
||||
|
||||
gl->fbo_feedback_enable = video_shader_driver_get_feedback_pass(
|
||||
&gl->fbo_feedback_pass);
|
||||
|
||||
if (gl->fbo_feedback_enable && gl->fbo_feedback_pass
|
||||
< (unsigned)gl->fbo_pass)
|
||||
{
|
||||
RARCH_LOG("[GL]: Creating feedback FBO %d @ %ux%u\n", i,
|
||||
gl->fbo_rect[gl->fbo_feedback_pass].width,
|
||||
gl->fbo_rect[gl->fbo_feedback_pass].height);
|
||||
}
|
||||
else if (gl->fbo_feedback_enable)
|
||||
{
|
||||
RARCH_WARN("[GL]: Tried to create feedback FBO of pass #%u, but there are only %d FBO passes. Will use input texture as feedback texture.\n",
|
||||
gl->fbo_feedback_pass, gl->fbo_pass);
|
||||
gl->fbo_feedback_enable = false;
|
||||
}
|
||||
|
||||
gl_create_fbo_textures(gl);
|
||||
if (!gl_create_fbo_targets(gl))
|
||||
{
|
||||
glDeleteTextures(gl->fbo_pass, gl->fbo_texture);
|
||||
RARCH_ERR("Failed to create FBO targets. Will continue without FBO.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
gl->fbo_inited = true;
|
||||
}
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user