void gl_deinit_hw_render(gl_t *gl) { if (!gl) return; context_bind_hw_render(true); if (gl->hw_render_fbo_init) glDeleteFramebuffers(gl->textures, gl->hw_render_fbo); if (gl->hw_render_depth_init) glDeleteRenderbuffers(gl->textures, gl->hw_render_depth); gl->hw_render_fbo_init = false; context_bind_hw_render(false); }
static void gl2_renderchain_deinit_hw_render( gl_t *gl, void *chain_data) { gl2_renderchain_t *chain = (gl2_renderchain_t*)chain_data; if (!gl) return; context_bind_hw_render(gl, true); if (gl->hw_render_fbo_init) gl2_delete_fb(gl->textures, gl->hw_render_fbo); if (chain->hw_render_depth_init) gl2_delete_rb(gl->textures, chain->hw_render_depth); gl->hw_render_fbo_init = false; context_bind_hw_render(gl, false); }
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; }
static bool gl2_renderchain_read_viewport( void *data, void *chain_data, uint8_t *buffer, bool is_idle) { unsigned num_pixels = 0; gl_t *gl = (gl_t*)data; if (!gl) return false; context_bind_hw_render(false); num_pixels = gl->vp.width * gl->vp.height; #ifdef HAVE_GL_ASYNC_READBACK if (gl->pbo_readback_enable) { const uint8_t *ptr = NULL; /* Don't readback if we're in menu mode. * We haven't buffered up enough frames yet, come back later. */ if (!gl->pbo_readback_valid[gl->pbo_readback_index]) goto error; gl->pbo_readback_valid[gl->pbo_readback_index] = false; glBindBuffer(GL_PIXEL_PACK_BUFFER, gl->pbo_readback[gl->pbo_readback_index]); #ifdef HAVE_OPENGLES3 /* Slower path, but should work on all implementations at least. */ ptr = (const uint8_t*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, num_pixels * sizeof(uint32_t), GL_MAP_READ_BIT); if (ptr) { unsigned y; for (y = 0; y < gl->vp.height; y++) { video_frame_convert_rgba_to_bgr( (const void*)ptr, buffer, gl->vp.width); } } #else ptr = (const uint8_t*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); if (ptr) { struct scaler_ctx *ctx = &gl->pbo_readback_scaler; scaler_ctx_scale_direct(ctx, buffer, ptr); } #endif if (!ptr) { RARCH_ERR("[GL]: Failed to map pixel unpack buffer.\n"); goto error; } glUnmapBuffer(GL_PIXEL_PACK_BUFFER); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } else #endif { /* Use slow synchronous readbacks. Use this with plain screenshots as we don't really care about performance in this case. */ /* GLES2 only guarantees GL_RGBA/GL_UNSIGNED_BYTE * readbacks so do just that. * GLES2 also doesn't support reading back data * from front buffer, so render a cached frame * and have gl_frame() do the readback while it's * in the back buffer. * * Keep codepath similar for GLES and desktop GL. */ gl->readback_buffer_screenshot = malloc(num_pixels * sizeof(uint32_t)); if (!gl->readback_buffer_screenshot) goto error; if (!is_idle) video_driver_cached_frame(); video_frame_convert_rgba_to_bgr( (const void*)gl->readback_buffer_screenshot, buffer, num_pixels); free(gl->readback_buffer_screenshot); gl->readback_buffer_screenshot = NULL; } context_bind_hw_render(true); return true; error: context_bind_hw_render(true); return false; }