From 062608ad7822ff690d706ddac1bc01b6b4beb4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 13 Dec 2017 01:05:33 +0100 Subject: [PATCH] Start moving the framebuffer stuff over to the render manager --- GPU/GLES/TextureCacheGLES.cpp | 51 +--- android/jni/app-android.cpp | 7 +- ext/native/thin3d/GLQueueRunner.cpp | 359 +++++++++++++++++++++- ext/native/thin3d/GLQueueRunner.h | 18 ++ ext/native/thin3d/GLRenderManager.cpp | 18 +- ext/native/thin3d/GLRenderManager.h | 46 ++- ext/native/thin3d/thin3d_gl.cpp | 419 +------------------------- 7 files changed, 443 insertions(+), 475 deletions(-) diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 9747dad92f..ddbc29b4bf 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -121,35 +121,34 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { GetSamplingParams(minFilt, magFilt, sClamp, tClamp, lodBias, maxLevel, entry.addr, mode); if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) { + float minLod = 0.0f; + float maxLod = 0.0f; if (maxLevel != 0) { // TODO: What about a swap of autoMip mode? if (force || entry.lodBias != lodBias) { if (mode == GE_TEXLEVEL_MODE_AUTO) { -#ifndef USING_GLES2 - // Sigh, LOD_BIAS is not even in ES 3.0.. but we could do it in the shader via texture()... - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, lodBias); -#endif - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel); + minLod = 0.0f; + maxLod = (float)maxLevel; } else if (mode == GE_TEXLEVEL_MODE_CONST) { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, std::max(0.0f, std::min((float)maxLevel, lodBias))); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, std::max(0.0f, std::min((float)maxLevel, lodBias))); + minLod = std::max(0.0f, std::min((float)maxLevel, lodBias)); + maxLod = std::max(0.0f, std::min((float)maxLevel, lodBias)); } else { // mode == GE_TEXLEVEL_MODE_SLOPE) { // It's incorrect to use the slope as a bias. Instead it should be passed // into the shader directly as an explicit lod level, with the bias on top. For now, we just kill the // lodBias in this mode, working around #9772. #ifndef USING_GLES2 - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0f); + lodBias = 0.0f; #endif - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, (float)maxLevel); + minLod = 0.0f; + maxLod = (float)maxLevel; } entry.lodBias = lodBias; } } else { - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0.0f); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0.0f); + minLod = 0.0f; + maxLod = 0.0f; } + render_->SetTextureLod(minLod, maxLod, lodBias); } if (force || entry.minFilt != minFilt || entry.magFilt != magFilt || entry.sClamp != sClamp || entry.tClamp != tClamp) { @@ -636,29 +635,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag // glBindTexture(GL_TEXTURE_2D, entry->textureName); lastBoundTexture = entry->textureName; - - // Disabled this due to issue #6075: https://github.com/hrydgard/ppsspp/issues/6075 - // This breaks Dangan Ronpa 2 with mipmapping enabled. Why? No idea, it shouldn't. - // glTexStorage2D probably has few benefits for us anyway. - if (false && gl_extensions.GLES3 && maxLevel > 0) { - // glTexStorage2D requires the use of sized formats. - GLenum actualFmt = replaced.Valid() ? ToGLESFormat(replaced.Format(0)) : dstFmt; - GLenum storageFmt = GL_RGBA8; - switch (actualFmt) { - case GL_UNSIGNED_BYTE: storageFmt = GL_RGBA8; break; - case GL_UNSIGNED_SHORT_5_6_5: storageFmt = GL_RGB565; break; - case GL_UNSIGNED_SHORT_4_4_4_4: storageFmt = GL_RGBA4; break; - case GL_UNSIGNED_SHORT_5_5_5_1: storageFmt = GL_RGB5_A1; break; - default: - ERROR_LOG(G3D, "Unknown dstfmt %i", (int)actualFmt); - break; - } - // TODO: This may cause bugs, since it hard-sets the texture w/h, and we might try to reuse it later with a different size. - glTexStorage2D(GL_TEXTURE_2D, maxLevel + 1, storageFmt, w * scaleFactor, h * scaleFactor); - // Make sure we don't use glTexImage2D after glTexStorage2D. - replaceImages = true; - } - + // GLES2 doesn't have support for a "Max lod" which is critical as PSP games often // don't specify mips all the way down. As a result, we either need to manually generate // the bottom few levels or rely on OpenGL's autogen mipmaps instead, which might not @@ -703,8 +680,6 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag texMaxLevel = 0; } - // TODO: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, texMaxLevel); - if (maxLevel == 0) { entry->status |= TexCacheEntry::STATUS_BAD_MIPS; } else { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 05306c22be..48fe18013c 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -486,12 +486,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, time_update(); } else { ELOG("BAD: Ended up in nativeRender even though app has quit.%s", ""); - // Shouldn't really get here. Let's draw magenta. - // TODO: Should we have GL here? - glDepthMask(GL_TRUE); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearColor(1.0, 0.0, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + // Shouldn't really get here. } std::lock_guard guard(frameCommandLock); diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 0f738f2ded..7c8f61d3c5 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -8,6 +8,12 @@ #define TEXCACHE_NAME_CACHE_SIZE 16 +// Workaround for Retroarch. Simply declare +// extern GLuint g_defaultFBO; +// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as +// appropriate. +GLuint g_defaultFBO = 0; + void GLQueueRunner::CreateDeviceObjects() { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyLevel_); glGenVertexArrays(1, &globalVAO_); @@ -156,7 +162,7 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } case GLRInitStepType::CREATE_FRAMEBUFFER: { - // TODO + InitCreateFramebuffer(step); break; } case GLRInitStepType::TEXTURE_SUBDATA: @@ -180,6 +186,114 @@ void GLQueueRunner::RunInitSteps(const std::vector &steps) { } } +void GLQueueRunner::InitCreateFramebuffer(const GLRInitStep &step) { + GLRFramebuffer *fbo = step.create_framebuffer.framebuffer; + +#ifndef USING_GLES2 + if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) { + fbo_ext_create(step); + } else if (!gl_extensions.ARB_framebuffer_object) { + return; + } + // If GLES2, we have basic FBO support and can just proceed. +#endif + CHECK_GL_ERROR_IF_DEBUG(); + + // Color texture is same everywhere + glGenFramebuffers(1, &fbo->handle); + glGenTextures(1, &fbo->color_texture); + + // Create the surfaces. + glBindTexture(GL_TEXTURE_2D, fbo->color_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (gl_extensions.IsGLES) { + if (gl_extensions.OES_packed_depth_stencil) { + ILOG("Creating %i x %i FBO using DEPTH24_STENCIL8", fbo->width, fbo->height); + // Standard method + fbo->stencil_buffer = 0; + fbo->z_buffer = 0; + // 24-bit Z, 8-bit stencil combined + glGenRenderbuffers(1, &fbo->z_stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbo->width, fbo->height); + + // Bind it all together + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + } else { + ILOG("Creating %i x %i FBO using separate stencil", fbo->width, fbo->height); + // TEGRA + fbo->z_stencil_buffer = 0; + // 16/24-bit Z, separate 8-bit stencil + glGenRenderbuffers(1, &fbo->z_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer); + // Don't forget to make sure fbo_standard_z_depth() matches. + glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, fbo->width, fbo->height); + + // 8-bit stencil buffer + glGenRenderbuffers(1, &fbo->stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbo->width, fbo->height); + + // Bind it all together + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer); + } + } else { + fbo->stencil_buffer = 0; + fbo->z_buffer = 0; + // 24-bit Z, 8-bit stencil + glGenRenderbuffers(1, &fbo->z_stencil_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbo->width, fbo->height); + + // Bind it all together + glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); + } + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + switch (status) { + case GL_FRAMEBUFFER_COMPLETE: + // ILOG("Framebuffer verified complete."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED: + ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); + break; + default: + FLOG("Other framebuffer error: %i", status); + break; + } + + // Unbind state we don't need + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + CHECK_GL_ERROR_IF_DEBUG(); + + currentDrawHandle_ = fbo->handle; + currentReadHandle_ = fbo->handle; +} + void GLQueueRunner::RunSteps(const std::vector &steps) { for (int i = 0; i < steps.size(); i++) { const GLRStep &step = *steps[i]; @@ -213,6 +327,25 @@ void GLQueueRunner::LogSteps(const std::vector &steps) { void GLQueueRunner::PerformBlit(const GLRStep &step) { + /* + // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing + // in ES 2.0 that actually separate them anyway of course, so doesn't matter. + fbo_bind_fb_target(false, dst->handle); + fbo_bind_fb_target(true, src->handle); + if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { + glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); + #if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. + return true; + } else if (gl_extensions.NV_framebuffer_blit) { + glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); + #endif // defined(USING_GLES2) && defined(__ANDROID__) + return true; + } else { + return false; + }*/ + } void GLQueueRunner::PerformRenderPass(const GLRStep &step) { @@ -236,6 +369,9 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { int attrMask = 0; + // TODO: We can implement state-filtering locally in this function, to mimic the old gl state tracker, + // to avoid redundant calls. Might be worth it? + auto &commands = step.commands; for (const auto &c : commands) { switch (c.cmd) { @@ -445,6 +581,13 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, c.textureSampler.anisotropy); } break; + case GLRRenderCommand::TEXTURELOD: + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, c.textureLod.minLod); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, c.textureLod.maxLod); +#ifndef USING_GLES2 + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, c.textureLod.lodBias); +#endif + break; case GLRRenderCommand::RASTER: if (c.raster.cullEnable) { glEnable(GL_CULL_FACE); @@ -498,13 +641,16 @@ void GLQueueRunner::PerformCopy(const GLRStep &step) { switch (step.copy.aspectMask) { case GL_COLOR_BUFFER_BIT: - srcTex = src->color.texture; - dstTex = dst->color.texture; + srcTex = src->color_texture; + dstTex = dst->color_texture; break; case GL_DEPTH_BUFFER_BIT: + _assert_msg_(G3D, false, "Depth copies not yet supported - soon"); target = GL_RENDERBUFFER; + /* srcTex = src->depth.texture; dstTex = src->depth.texture; + */ break; } #if defined(USING_GLES2) @@ -546,6 +692,59 @@ void GLQueueRunner::PerformBindFramebufferAsRenderTarget(const GLRStep &pass) { curFBWidth_ = targetWidth_; curFBHeight_ = targetHeight_; } + +#if 0 + + CHECK_GL_ERROR_IF_DEBUG(); + curFB_ = (OpenGLFramebuffer *)fbo; + if (fbo) { + OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; + // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing + // in ES 2.0 that actually separate them anyway of course, so doesn't matter. + fbo_bind_fb_target(false, fb->handle); + // Always restore viewport after render target binding. Works around driver bugs. + glstate.viewport.restore(); + } else { + fbo_unbind(); + } + int clearFlags = 0; + if (rp.color == RPAction::CLEAR) { + float fc[4]{}; + if (rp.clearColor) { + Uint8x4ToFloat4(fc, rp.clearColor); + } + glClearColor(fc[0], fc[1], fc[2], fc[3]); + clearFlags |= GL_COLOR_BUFFER_BIT; + glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + if (rp.depth == RPAction::CLEAR) { +#ifdef USING_GLES2 + glClearDepthf(rp.clearDepth); +#else + glClearDepth(rp.clearDepth); +#endif + glClearStencil(rp.clearStencil); + clearFlags |= GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; + glstate.depthWrite.force(GL_TRUE); + glstate.stencilFunc.force(GL_ALWAYS, 0, 0); + glstate.stencilMask.force(0xFF); + } + if (clearFlags) { + glstate.scissorTest.force(false); + glClear(clearFlags); + glstate.scissorTest.restore(); + } + if (rp.color == RPAction::CLEAR) { + glstate.colorMask.restore(); + } + if (rp.depth == RPAction::CLEAR) { + glstate.depthWrite.restore(); + glstate.stencilFunc.restore(); + glstate.stencilMask.restore(); + } + CHECK_GL_ERROR_IF_DEBUG(); +#endif + } void GLQueueRunner::CopyReadbackBuffer(int width, int height, Draw::DataFormat srcFormat, Draw::DataFormat destFormat, int pixelStride, uint8_t *pixels) { @@ -562,3 +761,157 @@ GLuint GLQueueRunner::AllocTextureName() { return name; } + +// On PC, we always use GL_DEPTH24_STENCIL8. +// On Android, we try to use what's available. + +#ifndef USING_GLES2 +void GLQueueRunner::fbo_ext_create(const GLRInitStep &step) { + GLRFramebuffer *fbo = step.create_framebuffer.framebuffer; + + // Color texture is same everywhere + glGenFramebuffersEXT(1, &fbo->handle); + glGenTextures(1, &fbo->color_texture); + + // Create the surfaces. + glBindTexture(GL_TEXTURE_2D, fbo->color_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + fbo->stencil_buffer = 0; + fbo->z_buffer = 0; + // 24-bit Z, 8-bit stencil + glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height); + //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); + + // Bind it all together + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + switch (status) { + case GL_FRAMEBUFFER_COMPLETE_EXT: + // ILOG("Framebuffer verified complete."); + break; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); + break; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); + break; + default: + FLOG("Other framebuffer error: %i", status); + break; + } + // Unbind state we don't need + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + currentDrawHandle_ = fbo->handle; + currentReadHandle_ = fbo->handle; +} +#endif + +GLenum GLQueueRunner::fbo_get_fb_target(bool read, GLuint **cached) { + bool supportsBlit = gl_extensions.ARB_framebuffer_object; + if (gl_extensions.IsGLES) { + supportsBlit = (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit); + } + + // Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV. + if (supportsBlit) { + if (read) { + *cached = ¤tReadHandle_; + return GL_READ_FRAMEBUFFER; + } else { + *cached = ¤tDrawHandle_; + return GL_DRAW_FRAMEBUFFER; + } + } else { + *cached = ¤tDrawHandle_; + return GL_FRAMEBUFFER; + } +} + +void GLQueueRunner::fbo_bind_fb_target(bool read, GLuint name) { + GLuint *cached; + GLenum target = fbo_get_fb_target(read, &cached); + if (*cached != name) { + if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { + glBindFramebuffer(target, name); + } else { +#ifndef USING_GLES2 + glBindFramebufferEXT(target, name); +#endif + } + *cached = name; + } +} + +void GLQueueRunner::fbo_unbind() { +#ifndef USING_GLES2 + if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { + glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); + } else if (gl_extensions.EXT_framebuffer_object) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); + } +#else + glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); +#endif + +#ifdef IOS + bindDefaultFBO(); +#endif + + currentDrawHandle_ = 0; + currentReadHandle_ = 0; +} + +GLRFramebuffer::~GLRFramebuffer() { + if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { + if (handle) { + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); + glDeleteFramebuffers(1, &handle); + } + if (z_stencil_buffer) + glDeleteRenderbuffers(1, &z_stencil_buffer); + if (z_buffer) + glDeleteRenderbuffers(1, &z_buffer); + if (stencil_buffer) + glDeleteRenderbuffers(1, &stencil_buffer); + } else if (gl_extensions.EXT_framebuffer_object) { +#ifndef USING_GLES2 + if (handle) { + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handle); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); + glDeleteFramebuffersEXT(1, &handle); + } + if (z_stencil_buffer) + glDeleteRenderbuffers(1, &z_stencil_buffer); + if (z_buffer) + glDeleteRenderbuffers(1, &z_buffer); + if (stencil_buffer) + glDeleteRenderbuffers(1, &stencil_buffer); +#endif + } + + glDeleteTextures(1, &color_texture); +} \ No newline at end of file diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h index f26d995d5e..e01a80075a 100644 --- a/ext/native/thin3d/GLQueueRunner.h +++ b/ext/native/thin3d/GLQueueRunner.h @@ -36,6 +36,7 @@ enum class GLRRenderCommand : uint8_t { UNIFORM4F, UNIFORMMATRIX, TEXTURESAMPLER, + TEXTURELOD, VIEWPORT, SCISSOR, RASTER, @@ -143,6 +144,11 @@ struct GLRRenderData { GLenum minFilter; // also includes mip. GL... float anisotropy; } textureSampler; + struct { + float minLod; + float maxLod; + float lodBias; + } textureLod; struct { GLRViewport vp; } viewport; @@ -307,6 +313,8 @@ public: targetHeight_ = height; } private: + void InitCreateFramebuffer(const GLRInitStep &step); + void PerformBindFramebufferAsRenderTarget(const GLRStep &pass); void PerformRenderPass(const GLRStep &pass); void PerformCopy(const GLRStep &pass); @@ -322,6 +330,12 @@ private: void ResizeReadbackBuffer(size_t requiredSize); + void fbo_ext_create(const GLRInitStep &step); + void fbo_bind_fb_target(bool read, GLuint name); + GLenum fbo_get_fb_target(bool read, GLuint **cached); + void fbo_unbind(); + + GLuint globalVAO_; GLint curFramebuffer_ = 0; @@ -337,6 +351,10 @@ private: float maxAnisotropyLevel_; + // Framebuffer state? + GLuint currentDrawHandle_ = 0; + GLuint currentReadHandle_ = 0; + GLuint AllocTextureName(); // Texture name cache. Ripped straight from TextureCacheGLES. std::vector nameCache_; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index 167359eaeb..e00f11f7b3 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -1,6 +1,7 @@ #include #include "GLRenderManager.h" +#include "gfx_es2/gpu_features.h" #include "thread/threadutil.h" #include "base/logging.h" @@ -10,10 +11,6 @@ #define VLOG(...) #endif -void GLCreateImage(GLRImage &img, int width, int height, GLuint format, bool color) { - -} - void GLDeleter::Perform() { for (auto shader : shaders) { delete shader; @@ -21,7 +18,18 @@ void GLDeleter::Perform() { for (auto program : programs) { delete program; } - // .. + for (auto buffer : buffers) { + delete buffer; + } + for (auto texture : textures) { + delete texture; + } + for (auto inputLayout : inputLayouts) { + delete inputLayout; + } + for (auto framebuffer : framebuffers) { + delete framebuffer; + } } GLRenderManager::GLRenderManager() { diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index b014302f52..34bc3d238b 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -11,13 +11,6 @@ #include "Common/Log.h" #include "GLQueueRunner.h" -struct GLRImage { - GLuint texture; - GLuint format; -}; - -void GLCreateImage(GLRImage &img, int width, int height, GLint format, bool color); - class GLRInputLayout; class GLRFramebuffer { @@ -26,18 +19,21 @@ public: : width(_width), height(_height), z_stencil_(z_stencil) { } - ~GLRFramebuffer() { - glDeleteTextures(1, &color.texture); - glDeleteRenderbuffers(1, &depth.texture); - } + ~GLRFramebuffer(); int numShadows = 1; // TODO: Support this. + GLuint handle = 0; + GLuint color_texture = 0; + GLuint z_stencil_buffer = 0; // Either this is set, or the two below. + GLuint z_buffer = 0; + GLuint stencil_buffer = 0; + + int width; + int height; + GLuint colorDepth; + GLuint framebuf = 0; - GLRImage color{}; - GLRImage depth{}; - int width = 0; - int height = 0; bool z_stencil_; }; @@ -144,6 +140,13 @@ public: buffers = std::move(other.buffers); textures = std::move(other.textures); inputLayouts = std::move(other.inputLayouts); + framebuffers = std::move(other.framebuffers); + other.shaders.clear(); + other.programs.clear(); + other.buffers.clear(); + other.textures.clear(); + other.inputLayouts.clear(); + other.framebuffers.clear(); } std::vector shaders; @@ -151,6 +154,7 @@ public: std::vector buffers; std::vector textures; std::vector inputLayouts; + std::vector framebuffers; }; class GLRInputLayout { @@ -272,6 +276,9 @@ public: void DeleteInputLayout(GLRInputLayout *inputLayout) { deleter_.inputLayouts.push_back(inputLayout); } + void DeleteFramebuffer(GLRFramebuffer *framebuffer) { + deleter_.framebuffers.push_back(framebuffer); + } void BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRenderPassAction color, GLRRenderPassAction depth, uint32_t clearColor, float clearDepth, uint8_t clearStencil); void BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit, int attachment); @@ -519,6 +526,15 @@ public: curRenderStep_->commands.push_back(data); } + void SetTextureLod(float minLod, float maxLod, float lodBias) { + _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); + GLRRenderData data{ GLRRenderCommand::TEXTURELOD}; + data.textureLod.minLod = minLod; + data.textureLod.maxLod = maxLod; + data.textureLod.lodBias = lodBias; + curRenderStep_->commands.push_back(data); + } + void Clear(uint32_t clearColor, float clearZ, int clearStencil, int clearMask) { _dbg_assert_(G3D, curRenderStep_ && curRenderStep_->stepType == GLRStepType::RENDER); GLRRenderData data{ GLRRenderCommand::CLEAR }; diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index cc0fb5299c..ee6b366516 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -21,12 +21,6 @@ extern void bindDefaultFBO(); // #define DEBUG_READ_PIXELS 1 -// Workaround for Retroarch. Simply declare -// extern GLuint g_defaultFBO; -// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as -// appropriate. -GLuint g_defaultFBO = 0; - namespace Draw { static const unsigned short compToGL[] = { @@ -483,10 +477,6 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {} private: - OpenGLFramebuffer *fbo_ext_create(const FramebufferDesc &desc); - void fbo_bind_fb_target(bool read, GLuint name); - GLenum fbo_get_fb_target(bool read, GLuint **cached); - void fbo_unbind(); void ApplySamplers(); GLRenderManager renderManager_; @@ -504,10 +494,6 @@ private: int curIBufferOffset_ = 0; OpenGLFramebuffer *curFB_; - // Framebuffer state - GLuint currentDrawHandle_ = 0; - GLuint currentReadHandle_ = 0; - // Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences. // But might as well have the structure ready, and can't hurt to rotate buffers. struct FrameData { @@ -652,16 +638,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : } mipLevels_ = desc.generateMips ? desc.mipLevels : level; - /* -#ifdef USING_GLES2 - if (gl_extensions.GLES3) { - glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1); - } -#else - glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1); -#endif -*/ - if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) { ILOG("Generating mipmaps"); AutoGenMipmaps(); @@ -687,19 +663,14 @@ void OpenGLTexture::AutoGenMipmaps() { class OpenGLFramebuffer : public Framebuffer { public: - OpenGLFramebuffer() {} - ~OpenGLFramebuffer(); + OpenGLFramebuffer(GLRenderManager *render) : render_(render) {} + ~OpenGLFramebuffer() { + render_->DeleteFramebuffer(framebuffer); + } + GLRenderManager *render_; GLRFramebuffer *framebuffer; - GLuint handle = 0; - GLuint color_texture = 0; - GLuint z_stencil_buffer = 0; // Either this is set, or the two below. - GLuint z_buffer = 0; - GLuint stencil_buffer = 0; - - int width; - int height; FBColorDepth colorDepth; }; @@ -852,6 +823,7 @@ static void LogReadPixelsError(GLenum error) { bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src; + /* fbo_bind_fb_target(true, fb ? fb->handle : 0); // Reads from the "bound for read" framebuffer. @@ -883,6 +855,7 @@ bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBit glPixelStorei(GL_PACK_ROW_LENGTH, 0); } CHECK_GL_ERROR_IF_DEBUG(); + */ return true; } @@ -1245,335 +1218,20 @@ void OpenGLInputLayout::Compile(const InputLayoutDesc &desc) { inputLayout_ = render_->CreateInputLayout(entries); } -// On PC, we always use GL_DEPTH24_STENCIL8. -// On Android, we try to use what's available. - -#ifndef USING_GLES2 -OpenGLFramebuffer *OpenGLContext::fbo_ext_create(const FramebufferDesc &desc) { - OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); - fbo->width = desc.width; - fbo->height = desc.height; - fbo->colorDepth = desc.colorDepth; - - // Color texture is same everywhere - glGenFramebuffersEXT(1, &fbo->handle); - glGenTextures(1, &fbo->color_texture); - - // Create the surfaces. - glBindTexture(GL_TEXTURE_2D, fbo->color_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // TODO: We could opt to only create 16-bit render targets on slow devices. For later. - switch (fbo->colorDepth) { - case FBO_8888: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - break; - case FBO_4444: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL); - break; - case FBO_5551: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); - break; - case FBO_565: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fbo->width, fbo->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); - break; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - fbo->stencil_buffer = 0; - fbo->z_buffer = 0; - // 24-bit Z, 8-bit stencil - glGenRenderbuffersEXT(1, &fbo->z_stencil_buffer); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_STENCIL_EXT, fbo->width, fbo->height); - //glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8, width, height); - - // Bind it all together - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->handle); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->z_stencil_buffer); - - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - switch (status) { - case GL_FRAMEBUFFER_COMPLETE_EXT: - // ILOG("Framebuffer verified complete."); - break; - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); - break; - default: - FLOG("Other framebuffer error: %i", status); - break; - } - // Unbind state we don't need - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - glBindTexture(GL_TEXTURE_2D, 0); - - currentDrawHandle_ = fbo->handle; - currentReadHandle_ = fbo->handle; - return fbo; -} -#endif - Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { CheckGLExtensions(); - OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); - // fbo->framebuffer = renderManager_.CreateFramebuffer() - -#ifndef USING_GLES2 - if (!gl_extensions.ARB_framebuffer_object && gl_extensions.EXT_framebuffer_object) { - return fbo_ext_create(desc); - } else if (!gl_extensions.ARB_framebuffer_object) { - return nullptr; - } - // If GLES2, we have basic FBO support and can just proceed. -#endif - CHECK_GL_ERROR_IF_DEBUG(); - - fbo->width = desc.width; - fbo->height = desc.height; - fbo->colorDepth = desc.colorDepth; - - // Color texture is same everywhere - glGenFramebuffers(1, &fbo->handle); - glGenTextures(1, &fbo->color_texture); - - // Create the surfaces. - glBindTexture(GL_TEXTURE_2D, fbo->color_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // TODO: We could opt to only create 16-bit render targets on slow devices. For later. - switch (fbo->colorDepth) { - case FBO_8888: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - break; - case FBO_4444: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, NULL); - break; - case FBO_5551: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fbo->width, fbo->height, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, NULL); - break; - case FBO_565: - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, fbo->width, fbo->height, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL); - break; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - if (gl_extensions.IsGLES) { - if (gl_extensions.OES_packed_depth_stencil) { - ILOG("Creating %i x %i FBO using DEPTH24_STENCIL8", fbo->width, fbo->height); - // Standard method - fbo->stencil_buffer = 0; - fbo->z_buffer = 0; - // 24-bit Z, 8-bit stencil combined - glGenRenderbuffers(1, &fbo->z_stencil_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbo->width, fbo->height); - - // Bind it all together - glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - } else { - ILOG("Creating %i x %i FBO using separate stencil", fbo->width, fbo->height); - // TEGRA - fbo->z_stencil_buffer = 0; - // 16/24-bit Z, separate 8-bit stencil - glGenRenderbuffers(1, &fbo->z_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_buffer); - // Don't forget to make sure fbo_standard_z_depth() matches. - glRenderbufferStorage(GL_RENDERBUFFER, gl_extensions.OES_depth24 ? GL_DEPTH_COMPONENT24 : GL_DEPTH_COMPONENT16, fbo->width, fbo->height); - - // 8-bit stencil buffer - glGenRenderbuffers(1, &fbo->stencil_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->stencil_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, fbo->width, fbo->height); - - // Bind it all together - glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->stencil_buffer); - } - } else { - fbo->stencil_buffer = 0; - fbo->z_buffer = 0; - // 24-bit Z, 8-bit stencil - glGenRenderbuffers(1, &fbo->z_stencil_buffer); - glBindRenderbuffer(GL_RENDERBUFFER, fbo->z_stencil_buffer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbo->width, fbo->height); - - // Bind it all together - glBindFramebuffer(GL_FRAMEBUFFER, fbo->handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->color_texture, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fbo->z_stencil_buffer); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - switch (status) { - case GL_FRAMEBUFFER_COMPLETE: - // ILOG("Framebuffer verified complete."); - break; - case GL_FRAMEBUFFER_UNSUPPORTED: - ELOG("GL_FRAMEBUFFER_UNSUPPORTED"); - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - ELOG("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT "); - break; - default: - FLOG("Other framebuffer error: %i", status); - break; - } - - // Unbind state we don't need - glBindRenderbuffer(GL_RENDERBUFFER, 0); - glBindTexture(GL_TEXTURE_2D, 0); - CHECK_GL_ERROR_IF_DEBUG(); - - currentDrawHandle_ = fbo->handle; - currentReadHandle_ = fbo->handle; + OpenGLFramebuffer *fbo = new OpenGLFramebuffer(&renderManager_); + fbo->framebuffer = renderManager_.CreateFramebuffer(desc.width, desc.height, desc.z_stencil); return fbo; } -GLenum OpenGLContext::fbo_get_fb_target(bool read, GLuint **cached) { - bool supportsBlit = gl_extensions.ARB_framebuffer_object; - if (gl_extensions.IsGLES) { - supportsBlit = (gl_extensions.GLES3 || gl_extensions.NV_framebuffer_blit); - } - - // Note: GL_FRAMEBUFFER_EXT and GL_FRAMEBUFFER have the same value, same with _NV. - if (supportsBlit) { - if (read) { - *cached = ¤tReadHandle_; - return GL_READ_FRAMEBUFFER; - } else { - *cached = ¤tDrawHandle_; - return GL_DRAW_FRAMEBUFFER; - } - } else { - *cached = ¤tDrawHandle_; - return GL_FRAMEBUFFER; - } -} - -void OpenGLContext::fbo_bind_fb_target(bool read, GLuint name) { - GLuint *cached; - GLenum target = fbo_get_fb_target(read, &cached); - if (*cached != name) { - if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { - glBindFramebuffer(target, name); - } else { -#ifndef USING_GLES2 - glBindFramebufferEXT(target, name); -#endif - } - *cached = name; - } -} - -void OpenGLContext::fbo_unbind() { -#ifndef USING_GLES2 - if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { - glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); - } else if (gl_extensions.EXT_framebuffer_object) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); - } -#else - glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); -#endif - -#ifdef IOS - bindDefaultFBO(); -#endif - - currentDrawHandle_ = 0; - currentReadHandle_ = 0; -} - void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; GLRRenderPassAction color = (GLRRenderPassAction)rp.color; GLRRenderPassAction depth = (GLRRenderPassAction)rp.depth; renderManager_.BindFramebufferAsRenderTarget(fb ? fb->framebuffer : nullptr, color, depth, rp.clearColor, rp.clearDepth, rp.clearStencil); - -#if 0 - - CHECK_GL_ERROR_IF_DEBUG(); - curFB_ = (OpenGLFramebuffer *)fbo; - if (fbo) { - OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; - // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing - // in ES 2.0 that actually separate them anyway of course, so doesn't matter. - fbo_bind_fb_target(false, fb->handle); - // Always restore viewport after render target binding. Works around driver bugs. - glstate.viewport.restore(); - } else { - fbo_unbind(); - } - int clearFlags = 0; - if (rp.color == RPAction::CLEAR) { - float fc[4]{}; - if (rp.clearColor) { - Uint8x4ToFloat4(fc, rp.clearColor); - } - glClearColor(fc[0], fc[1], fc[2], fc[3]); - clearFlags |= GL_COLOR_BUFFER_BIT; - glstate.colorMask.force(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - if (rp.depth == RPAction::CLEAR) { -#ifdef USING_GLES2 - glClearDepthf(rp.clearDepth); -#else - glClearDepth(rp.clearDepth); -#endif - clearFlags |= GL_DEPTH_BUFFER_BIT; - glstate.depthWrite.force(GL_TRUE); - } - if (rp.stencil == RPAction::CLEAR) { - glClearStencil(rp.clearStencil); - clearFlags |= GL_STENCIL_BUFFER_BIT; - glstate.stencilFunc.force(GL_ALWAYS, 0, 0); - glstate.stencilMask.force(0xFF); - } - if (clearFlags) { - glstate.scissorTest.force(false); - glClear(clearFlags); - glstate.scissorTest.restore(); - } - if (rp.color == RPAction::CLEAR) { - glstate.colorMask.restore(); - } - if (rp.depth == RPAction::CLEAR) { - glstate.depthWrite.restore(); - } - if (rp.stencil == RPAction::CLEAR) { - glstate.stencilFunc.restore(); - glstate.stencilMask.restore(); - } - CHECK_GL_ERROR_IF_DEBUG(); -#endif } void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) { @@ -1604,24 +1262,6 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in aspect |= GL_STENCIL_BUFFER_BIT; renderManager_.BlitFramebuffer(src->framebuffer, GLRect2D{ srcX1, srcY1, srcX2 - srcX1, srcY2 - srcY1 }, dst->framebuffer, GLRect2D{ dstX1, dstY1, dstX2 - dstX1, dstY2 - dstY1 }, aspect, linearFilter == FB_BLIT_LINEAR); - /* - // Without FBO_ARB / GLES3, this will collide with bind_for_read, but there's nothing - // in ES 2.0 that actually separate them anyway of course, so doesn't matter. - fbo_bind_fb_target(false, dst->handle); - fbo_bind_fb_target(true, src->handle); - if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { - glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); - CHECK_GL_ERROR_IF_DEBUG(); -#if defined(USING_GLES2) && defined(__ANDROID__) // We only support this extension on Android, it's not even available on PC. - return true; - } else if (gl_extensions.NV_framebuffer_blit) { - glBlitFramebufferNV(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, aspect, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); - CHECK_GL_ERROR_IF_DEBUG(); -#endif // defined(USING_GLES2) && defined(__ANDROID__) - return true; - } else { - return false; - }*/ return true; } @@ -1632,48 +1272,11 @@ void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBCh renderManager_.BindFramebufferAsTexture(fb->framebuffer, binding, (int)channelBit, color); } -OpenGLFramebuffer::~OpenGLFramebuffer() { - CHECK_GL_ERROR_IF_DEBUG(); - if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { - if (handle) { - glBindFramebuffer(GL_FRAMEBUFFER, handle); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0); - glBindFramebuffer(GL_FRAMEBUFFER, g_defaultFBO); - glDeleteFramebuffers(1, &handle); - } - if (z_stencil_buffer) - glDeleteRenderbuffers(1, &z_stencil_buffer); - if (z_buffer) - glDeleteRenderbuffers(1, &z_buffer); - if (stencil_buffer) - glDeleteRenderbuffers(1, &stencil_buffer); - } else if (gl_extensions.EXT_framebuffer_object) { -#ifndef USING_GLES2 - if (handle) { - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handle); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER_EXT, 0); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, g_defaultFBO); - glDeleteFramebuffersEXT(1, &handle); - } - if (z_stencil_buffer) - glDeleteRenderbuffers(1, &z_stencil_buffer); - if (z_buffer) - glDeleteRenderbuffers(1, &z_buffer); - if (stencil_buffer) - glDeleteRenderbuffers(1, &stencil_buffer); -#endif - } - - glDeleteTextures(1, &color_texture); -} - void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; if (fb) { - *w = fb->width; - *h = fb->height; + *w = fb->framebuffer->width; + *h = fb->framebuffer->height; } else { *w = targetWidth_; *h = targetHeight_;