/* This function should only be used without mipmaps and when data == NULL */ void gl_load_texture_image(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * data) { #ifndef HAVE_PSGL #ifdef HAVE_OPENGLES2 if (gl_check_capability(GL_CAPS_TEX_STORAGE_EXT) && internalFormat != GL_BGRA_EXT) { gl_size_format(&internalFormat); glTexStorage2DEXT(target, 1, internalFormat, width, height); } #else if (gl_check_capability(GL_CAPS_TEX_STORAGE) && internalFormat != GL_BGRA_EXT) { gl_size_format(&internalFormat); glTexStorage2D(target, 1, internalFormat, width, height); } #endif else #endif { #ifdef HAVE_OPENGLES if (gl_check_capability(GL_CAPS_GLES3_SUPPORTED)) #endif gl_size_format(&internalFormat); glTexImage2D(target, level, internalFormat, width, height, border, format, type, data); } }
static void gl2_renderchain_resolve_extensions(gl_t *gl, void *chain_data, const char *context_ident, const video_info_t *video) { gl2_renderchain_t *chain = (gl2_renderchain_t*)chain_data; settings_t *settings = config_get_ptr(); if (!chain) return; chain->has_srgb_fbo = false; chain->has_fp_fbo = gl_check_capability(GL_CAPS_FP_FBO); /* GLES3 has unpack_subimage and sRGB in core. */ chain->has_srgb_fbo_gles3 = gl_check_capability(GL_CAPS_SRGB_FBO_ES3); if (!settings->bools.video_force_srgb_disable) chain->has_srgb_fbo = gl_check_capability(GL_CAPS_SRGB_FBO); /* Use regular textures if we use HW render. */ chain->egl_images = !gl->hw_render_use && gl_check_capability(GL_CAPS_EGLIMAGE) && video_context_driver_init_image_buffer(video); }
bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height) { GLenum status; unsigned i; bool depth = false; bool stencil = false; GLint max_fbo_size = 0; GLint max_renderbuffer_size = 0; struct retro_hw_render_callback *hwr = video_driver_get_hw_context(); /* We can only share texture objects through contexts. * FBOs are "abstract" objects and are not shared. */ context_bind_hw_render(true); RARCH_LOG("[GL]: Initializing HW render (%u x %u).\n", width, height); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_fbo_size); glGetIntegerv(RARCH_GL_MAX_RENDERBUFFER_SIZE, &max_renderbuffer_size); RARCH_LOG("[GL]: Max texture size: %d px, renderbuffer size: %d px.\n", max_fbo_size, max_renderbuffer_size); if (!gl_check_capability(GL_CAPS_FBO)) return false; RARCH_LOG("[GL]: Supports FBO (render-to-texture).\n"); glBindTexture(GL_TEXTURE_2D, 0); glGenFramebuffers(gl->textures, gl->hw_render_fbo); depth = hwr->depth; stencil = hwr->stencil; #ifdef HAVE_OPENGLES if (!gl_check_capability(GL_CAPS_PACKED_DEPTH_STENCIL)) return false; RARCH_LOG("[GL]: Supports Packed depth stencil.\n"); #endif if (depth) { glGenRenderbuffers(gl->textures, gl->hw_render_depth); gl->hw_render_depth_init = true; } for (i = 0; i < gl->textures; i++) { glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->hw_render_fbo[i]); glFramebufferTexture2D(RARCH_GL_FRAMEBUFFER, RARCH_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->texture[i], 0); if (depth) { glBindRenderbuffer(RARCH_GL_RENDERBUFFER, gl->hw_render_depth[i]); glRenderbufferStorage(RARCH_GL_RENDERBUFFER, stencil ? RARCH_GL_DEPTH24_STENCIL8 : GL_DEPTH_COMPONENT16, width, height); glBindRenderbuffer(RARCH_GL_RENDERBUFFER, 0); if (stencil) { #if defined(HAVE_OPENGLES2) || defined(HAVE_OPENGLES1) || ((defined(__MACH__) && (defined(__ppc__) || defined(__ppc64__)))) /* GLES2 is a bit weird, as always. * There's no GL_DEPTH_STENCIL_ATTACHMENT like in desktop GL. */ glFramebufferRenderbuffer(RARCH_GL_FRAMEBUFFER, RARCH_GL_DEPTH_ATTACHMENT, RARCH_GL_RENDERBUFFER, gl->hw_render_depth[i]); glFramebufferRenderbuffer(RARCH_GL_FRAMEBUFFER, RARCH_GL_STENCIL_ATTACHMENT, RARCH_GL_RENDERBUFFER, gl->hw_render_depth[i]); #else /* We use ARB FBO extensions, no need to check. */ glFramebufferRenderbuffer(RARCH_GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, RARCH_GL_RENDERBUFFER, gl->hw_render_depth[i]); #endif } else { glFramebufferRenderbuffer(RARCH_GL_FRAMEBUFFER, RARCH_GL_DEPTH_ATTACHMENT, RARCH_GL_RENDERBUFFER, gl->hw_render_depth[i]); } } status = glCheckFramebufferStatus(RARCH_GL_FRAMEBUFFER); if (status != RARCH_GL_FRAMEBUFFER_COMPLETE) { RARCH_ERR("[GL]: Failed to create HW render FBO #%u, error: 0x%u.\n", i, (unsigned)status); return false; } } gl_bind_backbuffer(); gl->hw_render_fbo_init = true; context_bind_hw_render(false); return true; }
/* 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; }
void gl_renderchain_render(gl_t *gl, uint64_t frame_count, const struct video_tex_info *tex_info, const struct video_tex_info *feedback_info) { unsigned mip_level; video_shader_ctx_mvp_t mvp; video_shader_ctx_coords_t coords; video_shader_ctx_params_t params; video_shader_ctx_info_t shader_info; unsigned width, height; const struct video_fbo_rect *prev_rect; struct video_tex_info *fbo_info; struct video_tex_info fbo_tex_info[GFX_MAX_SHADERS]; int i; GLfloat xamt, yamt; unsigned fbo_tex_info_cnt = 0; GLfloat fbo_tex_coords[8] = {0.0f}; video_driver_get_size(&width, &height); /* Render the rest of our passes. */ gl->coords.tex_coord = fbo_tex_coords; /* Calculate viewports, texture coordinates etc, * and render all passes from FBOs, to another FBO. */ for (i = 1; i < gl->fbo_pass; i++) { video_shader_ctx_mvp_t mvp; video_shader_ctx_coords_t coords; video_shader_ctx_params_t params; const struct video_fbo_rect *rect = &gl->fbo_rect[i]; prev_rect = &gl->fbo_rect[i - 1]; fbo_info = &fbo_tex_info[i - 1]; xamt = (GLfloat)prev_rect->img_width / prev_rect->width; yamt = (GLfloat)prev_rect->img_height / prev_rect->height; set_texture_coords(fbo_tex_coords, xamt, yamt); fbo_info->tex = gl->fbo_texture[i - 1]; fbo_info->input_size[0] = prev_rect->img_width; fbo_info->input_size[1] = prev_rect->img_height; fbo_info->tex_size[0] = prev_rect->width; fbo_info->tex_size[1] = prev_rect->height; memcpy(fbo_info->coord, fbo_tex_coords, sizeof(fbo_tex_coords)); fbo_tex_info_cnt++; glBindFramebuffer(RARCH_GL_FRAMEBUFFER, gl->fbo[i]); shader_info.data = gl; shader_info.idx = i + 1; shader_info.set_active = true; video_shader_driver_use(&shader_info); glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[i - 1]); mip_level = i + 1; if (video_shader_driver_mipmap_input(&mip_level) && gl_check_capability(GL_CAPS_MIPMAP)) glGenerateMipmap(GL_TEXTURE_2D); glClear(GL_COLOR_BUFFER_BIT); /* Render to FBO with certain size. */ gl_set_viewport(gl, rect->img_width, rect->img_height, true, false); params.data = gl; params.width = prev_rect->img_width; params.height = prev_rect->img_height; params.tex_width = prev_rect->width; params.tex_height = prev_rect->height; params.out_width = gl->vp.width; params.out_height = gl->vp.height; params.frame_counter = (unsigned int)frame_count; params.info = tex_info; params.prev_info = gl->prev_info; params.feedback_info = feedback_info; params.fbo_info = fbo_tex_info; params.fbo_info_cnt = fbo_tex_info_cnt; video_shader_driver_set_parameters(¶ms); gl->coords.vertices = 4; coords.handle_data = NULL; coords.data = &gl->coords; video_shader_driver_set_coords(&coords); mvp.data = gl; mvp.matrix = &gl->mvp; video_shader_driver_set_mvp(&mvp); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } #if defined(GL_FRAMEBUFFER_SRGB) && !defined(HAVE_OPENGLES) 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]; xamt = (GLfloat)prev_rect->img_width / prev_rect->width; yamt = (GLfloat)prev_rect->img_height / prev_rect->height; set_texture_coords(fbo_tex_coords, xamt, yamt); /* Push final FBO to list. */ fbo_info = &fbo_tex_info[gl->fbo_pass - 1]; fbo_info->tex = gl->fbo_texture[gl->fbo_pass - 1]; fbo_info->input_size[0] = prev_rect->img_width; fbo_info->input_size[1] = prev_rect->img_height; fbo_info->tex_size[0] = prev_rect->width; fbo_info->tex_size[1] = prev_rect->height; memcpy(fbo_info->coord, fbo_tex_coords, sizeof(fbo_tex_coords)); fbo_tex_info_cnt++; /* Render our FBO texture to back buffer. */ gl_bind_backbuffer(); shader_info.data = gl; shader_info.idx = gl->fbo_pass + 1; shader_info.set_active = true; video_shader_driver_use(&shader_info); glBindTexture(GL_TEXTURE_2D, gl->fbo_texture[gl->fbo_pass - 1]); mip_level = gl->fbo_pass + 1; if (video_shader_driver_mipmap_input(&mip_level) && gl_check_capability(GL_CAPS_MIPMAP)) glGenerateMipmap(GL_TEXTURE_2D); glClear(GL_COLOR_BUFFER_BIT); gl_set_viewport(gl, width, height, false, true); params.data = gl; params.width = prev_rect->img_width; params.height = prev_rect->img_height; params.tex_width = prev_rect->width; params.tex_height = prev_rect->height; params.out_width = gl->vp.width; params.out_height = gl->vp.height; params.frame_counter = (unsigned int)frame_count; params.info = tex_info; params.prev_info = gl->prev_info; params.feedback_info = feedback_info; params.fbo_info = fbo_tex_info; params.fbo_info_cnt = fbo_tex_info_cnt; video_shader_driver_set_parameters(¶ms); gl->coords.vertex = gl->vertex_ptr; gl->coords.vertices = 4; coords.handle_data = NULL; coords.data = &gl->coords; video_shader_driver_set_coords(&coords); mvp.data = gl; mvp.matrix = &gl->mvp; video_shader_driver_set_mvp(&mvp); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); gl->coords.tex_coord = gl->tex_info.coord; }