From ee336e53934c0f62a7f76686165a77e6f09260d5 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Sat, 23 Apr 2022 13:15:58 -0300 Subject: [PATCH] opengl: restructure code for computing format support. --- src/modules/graphics/opengl/Graphics.cpp | 153 +++++++++++------------ src/modules/graphics/opengl/Graphics.h | 6 +- 2 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/modules/graphics/opengl/Graphics.cpp b/src/modules/graphics/opengl/Graphics.cpp index d7d34fa2b..df1b911fc 100644 --- a/src/modules/graphics/opengl/Graphics.cpp +++ b/src/modules/graphics/opengl/Graphics.cpp @@ -112,7 +112,7 @@ Graphics::Graphics() , bufferMapMemory(nullptr) , bufferMapMemorySize(2 * 1024 * 1024) , defaultBuffers() - , supportedFormats() + , pixelFormatUsage() { gl = OpenGL(); @@ -1670,6 +1670,13 @@ void Graphics::initCapabilities() for (int i = 0; i < TEXTURE_MAX_ENUM; i++) capabilities.textureTypes[i] = gl.isTextureTypeSupported((TextureType) i); + + for (int i = 0; i < PIXELFORMAT_MAX_ENUM; i++) + { + auto format = (PixelFormat) i; + pixelFormatUsage[i][0] = computePixelFormatUsage(format, false); + pixelFormatUsage[i][1] = computePixelFormatUsage(format, true); + } } PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool readable) const @@ -1697,111 +1704,97 @@ PixelFormat Graphics::getSizedFormat(PixelFormat format, bool rendertarget, bool } } -bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) +uint32 Graphics::computePixelFormatUsage(PixelFormat format, bool readable) { - if (sRGB) - { - format = getSRGBPixelFormat(format); - sRGB = false; - } + uint32 usage = OpenGL::getPixelFormatUsageFlags(format); - bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0; - bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0; - bool computewrite = (usage & PIXELFORMATUSAGEFLAGS_COMPUTEWRITE) != 0; - - format = getSizedFormat(format, rendertarget, readable); - - OptionalBool &supported = supportedFormats[format][rendertarget ? 1 : 0][readable ? 1 : 0][computewrite ? 1 : 0][sRGB ? 1 : 0]; - - if (supported.hasValue) - return supported.value; - - uint32 supportedflags = OpenGL::getPixelFormatUsageFlags(format); - - if ((usage & supportedflags) != usage) - { - supported.set(false); - return supported.value; - } - - if (!rendertarget) - { - supported.set(true); - return supported.value; - } + if (readable && (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) == 0) + return 0; // Even though we might have the necessary OpenGL version or extension, // drivers are still allowed to throw FRAMEBUFFER_UNSUPPORTED when attaching // a texture to a FBO whose format the driver doesn't like. So we should // test with an actual FBO. - GLuint texture = 0; - GLuint renderbuffer = 0; - // Avoid the test for depth/stencil formats - not every GL version // guarantees support for depth/stencil-only render targets (which we would // need for the test below to work), and we already do some finagling in // convertPixelFormat to try to use the best-supported internal // depth/stencil format for a particular driver. - if (isPixelFormatDepthStencil(format)) + if ((usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0 && !isPixelFormatDepthStencil(format)) { - supported.set(true); - return true; - } + GLuint texture = 0; + GLuint renderbuffer = 0; + bool sRGB = isPixelFormatSRGB(format); - OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable, sRGB); + OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(format, !readable, sRGB); - GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); + GLuint current_fbo = gl.getFramebuffer(OpenGL::FRAMEBUFFER_ALL); - GLuint fbo = 0; - glGenFramebuffers(1, &fbo); - gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); + GLuint fbo = 0; + glGenFramebuffers(1, &fbo); + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, fbo); - // Make sure at least something is bound to a color attachment. I believe - // this is required on ES2 but I'm not positive. - if (isPixelFormatDepthStencil(format)) - gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0); - - if (readable) - { - glGenTextures(1, &texture); - gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false); - - SamplerState s; - s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST; - gl.setSamplerState(TEXTURE_2D, s); - - gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1); - } - else - { - glGenRenderbuffers(1, &renderbuffer); - glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); - glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1); - } - - for (GLenum attachment : fmt.framebufferAttachments) - { - if (attachment == GL_NONE) - continue; + // Make sure at least something is bound to a color attachment. I believe + // this is required on ES2 but I'm not positive. + if (isPixelFormatDepthStencil(format)) + gl.framebufferTexture(GL_COLOR_ATTACHMENT0, TEXTURE_2D, gl.getDefaultTexture(TEXTURE_2D, DATA_BASETYPE_FLOAT), 0, 0, 0); if (readable) - gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0); + { + glGenTextures(1, &texture); + gl.bindTextureToUnit(TEXTURE_2D, texture, 0, false); + + SamplerState s; + s.minFilter = s.magFilter = SamplerState::FILTER_NEAREST; + gl.setSamplerState(TEXTURE_2D, s); + + gl.rawTexStorage(TEXTURE_2D, 1, format, sRGB, 1, 1); + } else - glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer); + { + glGenRenderbuffers(1, &renderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); + glRenderbufferStorage(GL_RENDERBUFFER, fmt.internalformat, 1, 1); + } + + for (GLenum attachment : fmt.framebufferAttachments) + { + if (attachment == GL_NONE) + continue; + + if (readable) + gl.framebufferTexture(attachment, TEXTURE_2D, texture, 0, 0, 0); + else + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer); + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + usage &= ~PIXELFORMATUSAGEFLAGS_RENDERTARGET; + + gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); + gl.deleteFramebuffer(fbo); + + if (texture != 0) + gl.deleteTexture(texture); + + if (renderbuffer != 0) + glDeleteRenderbuffers(1, &renderbuffer); } - supported.set(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + return usage; +} - gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); - gl.deleteFramebuffer(fbo); +bool Graphics::isPixelFormatSupported(PixelFormat format, uint32 usage, bool sRGB) +{ + if (sRGB) + format = getSRGBPixelFormat(format); - if (texture != 0) - gl.deleteTexture(texture); + bool rendertarget = (usage & PIXELFORMATUSAGEFLAGS_RENDERTARGET) != 0; + bool readable = (usage & PIXELFORMATUSAGEFLAGS_SAMPLE) != 0; - if (renderbuffer != 0) - glDeleteRenderbuffers(1, &renderbuffer); + format = getSizedFormat(format, rendertarget, readable); - return supported.value; + return (usage & pixelFormatUsage[format][readable ? 1 : 0]) == usage; } } // opengl diff --git a/src/modules/graphics/opengl/Graphics.h b/src/modules/graphics/opengl/Graphics.h index fab31e637..f9bcb6523 100644 --- a/src/modules/graphics/opengl/Graphics.h +++ b/src/modules/graphics/opengl/Graphics.h @@ -155,6 +155,8 @@ private: void setDebug(bool enable); + uint32 computePixelFormatUsage(PixelFormat format, bool readable); + std::unordered_map framebufferObjects; bool windowHasStencil; GLuint mainVAO; @@ -170,8 +172,8 @@ private: // Only needed for buffer types that can be bound to shaders. StrongRef defaultBuffers[BUFFERUSAGE_MAX_ENUM]; - // [rendertarget][readable][computewrite][srgb] - OptionalBool supportedFormats[PIXELFORMAT_MAX_ENUM][2][2][2][2]; + // [non-readable, readable] + uint32 pixelFormatUsage[PIXELFORMAT_MAX_ENUM][2]; }; // Graphics