diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index c54df889e5..a3fcb62f42 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -72,6 +72,7 @@ #include "Core/Config.h" #include "Core/CoreTiming.h" +#include "gfx/gl_debug_log.h" #include "profiler/profiler.h" #include "GPU/Math3D.h" @@ -256,6 +257,7 @@ static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) { // TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) { + CHECK_GL_ERROR_IF_DEBUG(); VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off); VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off); VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff); @@ -263,6 +265,7 @@ static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off); VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff); VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff); + CHECK_GL_ERROR_IF_DEBUG(); } void DrawEngineGLES::SetupVertexDecoder(u32 vertType) { @@ -643,14 +646,14 @@ void DrawEngineGLES::FreeVertexArray(VertexArrayInfo *vai) { void DrawEngineGLES::DoFlush() { PROFILE_THIS_SCOPE("flush"); + CHECK_GL_ERROR_IF_DEBUG(); + gpuStats.numFlushes++; gpuStats.numTrackedVertexArrays = (int)vai_.size(); - // This is not done on every drawcall, we should collect vertex data - // until critical state changes. That's when we draw (flush). - GEPrimitiveType prim = prevPrim_; ApplyDrawState(prim); + CHECK_GL_ERROR_IF_DEBUG(); ShaderID vsid; Shader *vshader = shaderManager_->ApplyVertexShader(prim, lastVType_, &vsid); @@ -1003,6 +1006,7 @@ rotateVBO: #ifndef MOBILE_DEVICE host->GPUNotifyDraw(); #endif + CHECK_GL_ERROR_IF_DEBUG(); } void DrawEngineGLES::Resized() { @@ -1212,4 +1216,5 @@ void DrawEngineGLES::TessellationDataTransferGLES::SendDataToShader(const float } } glActiveTexture(GL_TEXTURE0); + CHECK_GL_ERROR_IF_DEBUG(); } diff --git a/GPU/GLES/FragmentTestCacheGLES.cpp b/GPU/GLES/FragmentTestCacheGLES.cpp index b2c7881e2a..e49f336e13 100644 --- a/GPU/GLES/FragmentTestCacheGLES.cpp +++ b/GPU/GLES/FragmentTestCacheGLES.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "gfx/gl_debug_log.h" #include "Core/Config.h" #include "GPU/GLES/FragmentTestCacheGLES.h" #include "GPU/GPUState.h" @@ -55,10 +56,12 @@ void FragmentTestCacheGLES::BindTestTexture(GLenum unit) { // Already bound, hurray. return; } + CHECK_GL_ERROR_IF_DEBUG(); glActiveTexture(unit); glBindTexture(GL_TEXTURE_2D, tex); // Always return to the default. glActiveTexture(GL_TEXTURE0); + CHECK_GL_ERROR_IF_DEBUG(); lastTexture_ = tex; return; } diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp index 3cc97c5e32..f037fd2a94 100644 --- a/GPU/GLES/FramebufferManagerGLES.cpp +++ b/GPU/GLES/FramebufferManagerGLES.cpp @@ -20,6 +20,7 @@ #include "profiler/profiler.h" #include "gfx/gl_common.h" +#include "gfx/gl_debug_log.h" #include "gfx_es2/glsl_program.h" #include "thin3d/thin3d.h" @@ -347,6 +348,7 @@ void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferForma } } glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, useConvBuf ? convBuf_ : srcPixels); + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::SetViewport2D(int x, int y, int w, int h) { @@ -670,6 +672,7 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) draw_->BindFramebufferAsRenderTarget(nvfb->fbo); ClearBuffer(); } + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, int dstY, VirtualFramebuffer *src, int srcX, int srcY, int w, int h, int bpp) { @@ -721,6 +724,7 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, const bool yOverlap = src == dst && srcY2 > dstY1 && srcY1 < dstY2; if (sameSize && sameDepth && srcInsideBounds && dstInsideBounds && !(xOverlap && yOverlap)) { draw_->CopyFramebufferImage(src->fbo, 0, srcX1, srcY1, 0, dst->fbo, 0, dstX1, dstY1, 0, dstX2 - dstX1, dstY2 - dstY1, 1, Draw::FB_COLOR_BIT); + CHECK_GL_ERROR_IF_DEBUG(); return; } } @@ -767,6 +771,7 @@ void FramebufferManagerGLES::BlitFramebuffer(VirtualFramebuffer *dst, int dstX, } glstate.scissorTest.restore(); + CHECK_GL_ERROR_IF_DEBUG(); } // TODO: SSE/NEON @@ -887,6 +892,7 @@ static void LogReadPixelsError(GLenum error) { #endif static void SafeGLReadPixels(GLint x, GLint y, GLsizei w, GLsizei h, GLenum fmt, GLenum type, void *pixels) { + CHECK_GL_ERROR_IF_DEBUG(); if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) { // Some drivers seem to require we specify this. See #8254. glPixelStorei(GL_PACK_ROW_LENGTH, w); @@ -900,9 +906,11 @@ static void SafeGLReadPixels(GLint x, GLint y, GLsizei w, GLsizei h, GLenum fmt, if (!gl_extensions.IsGLES || gl_extensions.GLES3) { glPixelStorei(GL_PACK_ROW_LENGTH, 0); } + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { + CHECK_GL_ERROR_IF_DEBUG(); const int MAX_PBO = 2; GLubyte *packed = 0; bool unbind = false; @@ -1050,6 +1058,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) { if (unbind) { glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); } + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) { @@ -1097,6 +1106,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x if (UseBGRA8888()) { glfmt = GL_BGRA_EXT; } + CHECK_GL_ERROR_IF_DEBUG(); SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed); @@ -1116,6 +1126,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x GLenum attachments[3] = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT }; glInvalidateFramebuffer(target, 3, attachments); } + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) { @@ -1157,9 +1168,11 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int depth[i] = (int)scaled; } } + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::EndFrame() { + CHECK_GL_ERROR_IF_DEBUG(); if (resized_) { // TODO: Only do this if the new size actually changed the renderwidth/height. DestroyAllFBOs(false); @@ -1230,6 +1243,7 @@ void FramebufferManagerGLES::EndFrame() { } draw_->BindBackbufferAsRenderTarget(); } + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::DeviceLost() { @@ -1258,6 +1272,7 @@ std::vector FramebufferManagerGLES::GetFramebufferList() { } void FramebufferManagerGLES::DestroyAllFBOs(bool forceDelete) { + CHECK_GL_ERROR_IF_DEBUG(); draw_->BindBackbufferAsRenderTarget(); currentRenderVfb_ = 0; displayFramebuf_ = 0; @@ -1284,6 +1299,7 @@ void FramebufferManagerGLES::DestroyAllFBOs(bool forceDelete) { draw_->BindBackbufferAsRenderTarget(); DisableState(); + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::FlushBeforeCopy() { @@ -1295,6 +1311,7 @@ void FramebufferManagerGLES::FlushBeforeCopy() { // do something more focused here. SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); drawEngine_->Flush(); + CHECK_GL_ERROR_IF_DEBUG(); } void FramebufferManagerGLES::Resized() { @@ -1343,6 +1360,7 @@ bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBuf // We may have blitted to a temp FBO. RebindFramebuffer(); + CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1354,6 +1372,7 @@ bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) { buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true); glPixelStorei(GL_PACK_ALIGNMENT, 1); SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData()); + CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1384,7 +1403,7 @@ bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z glReadBuffer(GL_COLOR_ATTACHMENT0); glPixelStorei(GL_PACK_ALIGNMENT, 4); SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData()); - + CHECK_GL_ERROR_IF_DEBUG(); return true; } @@ -1409,7 +1428,7 @@ bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPU glReadBuffer(GL_COLOR_ATTACHMENT0); glPixelStorei(GL_PACK_ALIGNMENT, 2); SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData()); - + CHECK_GL_ERROR_IF_DEBUG(); return true; #else return false; diff --git a/GPU/GLES/ShaderManagerGLES.cpp b/GPU/GLES/ShaderManagerGLES.cpp index fb9e82d6e8..28da7033c2 100644 --- a/GPU/GLES/ShaderManagerGLES.cpp +++ b/GPU/GLES/ShaderManagerGLES.cpp @@ -24,6 +24,7 @@ #include "base/logging.h" #include "base/timeutil.h" +#include "gfx/gl_debug_log.h" #include "i18n/i18n.h" #include "math/math_util.h" #include "math/lin/matrix4x4.h" @@ -80,6 +81,7 @@ Shader::Shader(const char *code, uint32_t glShaderType, bool useHWTransform) } else { DEBUG_LOG(G3D, "Compiled shader:\n%s\n", (const char *)code); } + CHECK_GL_ERROR_IF_DEBUG(); } Shader::~Shader() { @@ -313,6 +315,7 @@ LinkedShader::LinkedShader(ShaderID VSID, Shader *vs, ShaderID FSID, Shader *fs, // The rest, use the "dirty" mechanism. dirtyUniforms = DIRTY_ALL_UNIFORMS; + CHECK_GL_ERROR_IF_DEBUG(); } LinkedShader::~LinkedShader() { @@ -435,6 +438,8 @@ void LinkedShader::stop() { } void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { + CHECK_GL_ERROR_IF_DEBUG(); + u64 dirty = dirtyUniforms & availableUniforms; dirtyUniforms = 0; if (!dirty) @@ -743,6 +748,7 @@ void LinkedShader::UpdateUniforms(u32 vertType, const ShaderID &vsid) { if (dirty & DIRTY_SPLINETYPEV) glUniform1i(u_spline_type_v, gstate_c.spline_type_v); } + CHECK_GL_ERROR_IF_DEBUG(); } ShaderManagerGLES::ShaderManagerGLES() diff --git a/GPU/GLES/StateMappingGLES.cpp b/GPU/GLES/StateMappingGLES.cpp index 927fe9a6ce..4c3b8e2c11 100644 --- a/GPU/GLES/StateMappingGLES.cpp +++ b/GPU/GLES/StateMappingGLES.cpp @@ -22,6 +22,7 @@ #include "StateMappingGLES.h" #include "profiler/profiler.h" +#include "gfx/gl_debug_log.h" #include "GPU/Math3D.h" #include "GPU/GPUState.h" @@ -344,6 +345,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) { if (vpAndScissor.dirtyDepth) { gstate_c.Dirty(DIRTY_DEPTHRANGE); } + CHECK_GL_ERROR_IF_DEBUG(); } void DrawEngineGLES::ApplyDrawStateLate() { @@ -351,9 +353,11 @@ void DrawEngineGLES::ApplyDrawStateLate() { // TODO: Set the nearest/linear here (since we correctly know if alpha/color tests are needed)? if (!gstate.isModeClear()) { if (fboTexNeedBind_) { + CHECK_GL_ERROR_IF_DEBUG(); // Note that this is positions, not UVs, that we need the copy from. framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY); framebufferManager_->RebindFramebuffer(); + CHECK_GL_ERROR_IF_DEBUG(); glActiveTexture(GL_TEXTURE1); // If we are rendering at a higher resolution, linear is probably best for the dest color. @@ -362,15 +366,19 @@ void DrawEngineGLES::ApplyDrawStateLate() { glActiveTexture(GL_TEXTURE0); fboTexBound_ = true; fboTexNeedBind_ = false; + CHECK_GL_ERROR_IF_DEBUG(); } + CHECK_GL_ERROR_IF_DEBUG(); // Apply the texture after the FBO tex, since it might unbind the texture. // TODO: Could use a separate texture unit to be safer? textureCache_->ApplyTexture(); + CHECK_GL_ERROR_IF_DEBUG(); // Apply last, once we know the alpha params of the texture. if (gstate.isAlphaTestEnabled() || gstate.isColorTestEnabled()) { fragmentTestCache_->BindTestTexture(GL_TEXTURE2); } + CHECK_GL_ERROR_IF_DEBUG(); } } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 17c7775e8d..16808215e4 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -19,6 +19,7 @@ #include #include "ext/xxhash.h" +#include "gfx/gl_debug_log.h" #include "i18n/i18n.h" #include "math/math_util.h" #include "profiler/profiler.h" @@ -121,6 +122,7 @@ static const GLuint MagFiltGL[2] = { // This should not have to be done per texture! OpenGL is silly yo void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { + CHECK_GL_ERROR_IF_DEBUG(); int minFilt; int magFilt; bool sClamp; @@ -172,6 +174,7 @@ void TextureCacheGLES::UpdateSamplingParams(TexCacheEntry &entry, bool force) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tClamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); entry.tClamp = tClamp; } + CHECK_GL_ERROR_IF_DEBUG(); } void TextureCacheGLES::SetFramebufferSamplingParams(u16 bufferWidth, u16 bufferHeight) { @@ -325,11 +328,14 @@ bool SetDebugTexture() { #endif void TextureCacheGLES::BindTexture(TexCacheEntry *entry) { + CHECK_GL_ERROR_IF_DEBUG(); if (entry->textureName != lastBoundTexture) { glBindTexture(GL_TEXTURE_2D, entry->textureName); lastBoundTexture = entry->textureName; } + CHECK_GL_ERROR_IF_DEBUG(); UpdateSamplingParams(*entry, false); + CHECK_GL_ERROR_IF_DEBUG(); } void TextureCacheGLES::Unbind() { @@ -514,6 +520,8 @@ void TextureCacheGLES::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFram framebufferManagerGL_->RebindFramebuffer(); SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); + CHECK_GL_ERROR_IF_DEBUG(); + lastBoundTexture = INVALID_TEX; } @@ -675,7 +683,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag LoadTextureLevel(*entry, replaced, level, replaceImages, scaleFactor, dstFmt); } else LoadTextureLevel(*entry, replaced, 0, replaceImages, scaleFactor, dstFmt); - + // Mipmapping only enable when texture scaling disable if (maxLevel > 0 && scaleFactor == 1) { if (gstate_c.Supports(GPU_SUPPORTS_TEXTURE_LOD_CONTROL)) { @@ -721,6 +729,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); + CHECK_GL_ERROR_IF_DEBUG(); } u32 TextureCacheGLES::AllocTextureName() { @@ -806,6 +815,8 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r bool useBGRA; u32 *pixelData; + CHECK_GL_ERROR_IF_DEBUG(); + // TODO: only do this once u32 texByteAlign = 1; @@ -871,6 +882,8 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r glPixelStorei(GL_UNPACK_ALIGNMENT, texByteAlign); + CHECK_GL_ERROR_IF_DEBUG(); + GLuint components = dstFmt == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA; GLuint components2 = components; @@ -888,6 +901,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r else glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData); if (!lowMemoryMode_) { + // TODO: We really, really should avoid calling glGetError. GLenum err = glGetError(); if (err == GL_OUT_OF_MEMORY) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); @@ -911,7 +925,9 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r case GL_INVALID_VALUE: str = "invalid_value"; break; } // We checked the err anyway, might as well log if there is one. - WARN_LOG(G3D, "Got an error in texture upload: %08x (%s) (components=%04x components2=%04x dstFmt=%04x w=%d h=%d level=%d)", err, str, components, components2, dstFmt, w, h, level); + WARN_LOG(G3D, "Got an error in texture upload: %08x (%s) (components=%s components2=%s dstFmt=%s w=%d h=%d level=%d)", + err, str, GLEnumToString(components).c_str(), GLEnumToString(components2).c_str(), GLEnumToString(dstFmt).c_str(), + w, h, level); } } } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index d4a5d8b7de..7cfb27282a 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -86,7 +86,7 @@ VkSampler SamplerCache::GetOrCreateSampler(const SamplerCacheKey &key) { VkSamplerCreateInfo samp = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; samp.addressModeU = key.sClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT; samp.addressModeV = key.tClamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT; - samp.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samp.addressModeW = samp.addressModeU; // irrelevant, but Mali recommends that all clamp modes are the same if possible. samp.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; samp.compareOp = VK_COMPARE_OP_ALWAYS; samp.flags = 0; diff --git a/ext/native/gfx/gl_debug_log.cpp b/ext/native/gfx/gl_debug_log.cpp index e895fd81e6..4f313f62f8 100644 --- a/ext/native/gfx/gl_debug_log.cpp +++ b/ext/native/gfx/gl_debug_log.cpp @@ -1,54 +1,40 @@ -#include "gfx/gl_common.h" -#include "base/logging.h" +#include -void glCheckzor(const char *file, int line) { - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - ELOG("GL error on line %i in %s: %i (%04x)", line, file, (int)err, (int)err); +#include "base/logging.h" +#include "gfx/gl_common.h" +#include "gfx/gl_debug_log.h" + +// This we can expand as needed. +std::string GLEnumToString(uint16_t value) { + char str[64]; + switch (value) { + case GL_UNSIGNED_SHORT_4_4_4_4: return "GL_UNSIGNED_SHORT_4_4_4_4"; + case GL_UNSIGNED_SHORT_5_5_5_1: return "GL_UNSIGNED_SHORT_5_5_5_1"; + case GL_UNSIGNED_SHORT_5_6_5: return "GL_UNSIGNED_SHORT_5_6_5"; + case GL_RGBA: return "GL_RGBA"; + case GL_RGB: return "GL_RGB"; +#if !defined(USING_GLES2) + case GL_BGRA: return "GL_BGRA"; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: return "GL_UNSIGNED_SHORT_4_4_4_4_REV"; + case GL_UNSIGNED_SHORT_5_6_5_REV: return "GL_UNSIGNED_SHORT_5_6_5_REV"; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: return "GL_UNSIGNED_SHORT_1_5_5_5_REV"; + case GL_UNSIGNED_INT_8_8_8_8_REV: return "GL_UNSIGNED_INT_8_8_8_8_REV"; +#endif + case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + case GL_PACK_ALIGNMENT: return "GL_PACK_ALIGNMENT"; + case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + default: { + snprintf(str, sizeof(str), "(unk:%04x)", value); + return str; + } } } -#if !defined(USING_GLES2) -#if 0 -void log_callback(GLenum source, GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - GLvoid* userParam) { - const char *src = "unknown"; - switch (source) { - case GL_DEBUG_SOURCE_API_GL_ARB: - src = "GL"; - break; - case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: - src = "GLSL"; - break; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: - src = "X"; - break; - default: - break; - } - switch (type) { - case GL_DEBUG_TYPE_ERROR_ARB: - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: - ELOG("%s: %s", src, message); - break; - default: - ILOG("%s: %s", src, message); - break; - } -} -#endif -#endif - -void gl_log_enable() { -#if !defined(USING_GLES2) -#if 0 - glEnable(DEBUG_OUTPUT_SYNCHRONOUS_ARB); // TODO: Look into disabling, for more perf - glDebugMessageCallback(&log_callback, 0); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE); -#endif -#endif +void CheckGLError(const char *file, int line) { + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + ELOG("GL error %s on %s:%d", GLEnumToString(err).c_str(), file, line); + } } diff --git a/ext/native/gfx/gl_debug_log.h b/ext/native/gfx/gl_debug_log.h index 411f55668d..ae22ab1700 100644 --- a/ext/native/gfx/gl_debug_log.h +++ b/ext/native/gfx/gl_debug_log.h @@ -1,25 +1,22 @@ #pragma once +#include +#include + // Utility to be able to liberally sprinkle GL error checks around your code // and easily disable them all in release builds - just undefine DEBUG_OPENGL. - -void gl_log_enable(); - -#if !defined(USING_GLES2) -//#define DEBUG_OPENGL -#endif +// #define DEBUG_OPENGL #if defined(DEBUG_OPENGL) -void glCheckzor(const char *file, int line); -#define GL_CHECK() glCheckzor(__FILE__, __LINE__) +void CheckGLError(const char *file, int line); +#define CHECK_GL_ERROR_IF_DEBUG() CheckGLError(__FILE__, __LINE__) #else -#define GL_CHECK() +#define CHECK_GL_ERROR_IF_DEBUG() #endif - - +std::string GLEnumToString(uint16_t value); diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 08834cb995..5cb640eee1 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -9,6 +9,7 @@ #include "math/lin/matrix4x4.h" #include "thin3d/thin3d.h" #include "gfx/gl_common.h" +#include "gfx/gl_debug_log.h" #include "gfx/GLStateCache.h" #include "gfx_es2/gpu_features.h" #include "gfx/gl_lost_manager.h" @@ -754,6 +755,7 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int return; } + CHECK_GL_ERROR_IF_DEBUG(); switch (target_) { case GL_TEXTURE_2D: glTexImage2D(GL_TEXTURE_2D, level, internalFormat, width_, height_, 0, format, type, data); @@ -762,11 +764,7 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int ELOG("Thin3D GL: Targets other than GL_TEXTURE_2D not yet supported"); break; } - - GLenum err2 = glGetError(); - if (err2) { - ELOG("Thin3D GL: Error loading texture: %08x", err2); - } + CHECK_GL_ERROR_IF_DEBUG(); } Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) { @@ -1336,6 +1334,7 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { } // If GLES2, we have basic FBO support and can just proceed. #endif + CHECK_GL_ERROR_IF_DEBUG(); OpenGLFramebuffer *fbo = new OpenGLFramebuffer(); fbo->width = desc.width; @@ -1444,6 +1443,7 @@ Framebuffer *OpenGLContext::CreateFramebuffer(const FramebufferDesc &desc) { // 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; @@ -1512,16 +1512,20 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo) { fbo_bind_fb_target(false, fb->handle); // Always restore viewport after render target binding glstate.viewport.restore(); + CHECK_GL_ERROR_IF_DEBUG(); } void OpenGLContext::BindBackbufferAsRenderTarget() { + CHECK_GL_ERROR_IF_DEBUG(); fbo_unbind(); + CHECK_GL_ERROR_IF_DEBUG(); } // For GL_EXT_FRAMEBUFFER_BLIT and similar. void OpenGLContext::BindFramebufferForRead(Framebuffer *fbo) { OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; fbo_bind_fb_target(true, fb->handle); + CHECK_GL_ERROR_IF_DEBUG(); } 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) { @@ -1562,6 +1566,7 @@ void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int s width, height, depth); } #endif + CHECK_GL_ERROR_IF_DEBUG(); } bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *fbdst, int dstX1, int dstY1, int dstX2, int dstY2, int channels, FBBlitFilter linearFilter) { @@ -1578,10 +1583,12 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in BindFramebufferForRead(src); if (gl_extensions.GLES3 || gl_extensions.ARB_framebuffer_object) { glBlitFramebuffer(srcX1, srcY1, srcX2, srcY2, dstX1, dstY1, dstX2, dstY2, bits, 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, bits, linearFilter == FB_BLIT_LINEAR ? GL_LINEAR : GL_NEAREST); + CHECK_GL_ERROR_IF_DEBUG(); #endif // defined(USING_GLES2) && defined(__ANDROID__) return true; } else { @@ -1590,8 +1597,13 @@ bool OpenGLContext::BlitFramebuffer(Framebuffer *fbsrc, int srcX1, int srcY1, in } uintptr_t OpenGLContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) { - // Unimplemented - return 0; + OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo; + switch (channelBits) { + case FB_COLOR_BIT: return (uintptr_t)fb->color_texture; + case FB_DEPTH_BIT: return (uintptr_t)(fb->z_buffer ? fb->z_buffer : fb->z_stencil_buffer); + default: + return 0; + } } void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int color) { @@ -1612,6 +1624,7 @@ void OpenGLContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBCh } OpenGLFramebuffer::~OpenGLFramebuffer() { + CHECK_GL_ERROR_IF_DEBUG(); if (gl_extensions.ARB_framebuffer_object || gl_extensions.IsGLES) { glBindFramebuffer(GL_FRAMEBUFFER, handle); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);