diff --git a/src/AbstractFramebuffer.cpp b/src/AbstractFramebuffer.cpp index cd0497be5..56cf6f77b 100644 --- a/src/AbstractFramebuffer.cpp +++ b/src/AbstractFramebuffer.cpp @@ -47,6 +47,51 @@ FramebufferTarget AbstractFramebuffer::readTarget = FramebufferTarget::ReadDraw; FramebufferTarget AbstractFramebuffer::drawTarget = FramebufferTarget::ReadDraw; #endif +Vector2i AbstractFramebuffer::maxViewportSize() { + Vector2i& value = Context::current()->state().framebuffer->maxViewportSize; + + /* Get the value, if not already cached */ + if(value == Vector2i()) + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, value.data()); + + return value; +} + +Int AbstractFramebuffer::maxDrawBuffers() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + return 0; + #endif + + GLint& value = Context::current()->state().framebuffer->maxDrawBuffers; + + /* Get the value, if not already cached */ + if(value == 0) { + #ifndef MAGNUM_TARGET_GLES2 + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &value); + #else + glGetIntegerv(GL_MAX_DRAW_BUFFERS_NV, &value); + #endif + } + + return value; +} + +#ifndef MAGNUM_TARGET_GLES +Int AbstractFramebuffer::maxDualSourceDrawBuffers() { + if(!Context::current()->isExtensionSupported()) + return 0; + + GLint& value = Context::current()->state().framebuffer->maxDualSourceDrawBuffers; + + /* Get the value, if not already cached */ + if(value == 0) + glGetIntegerv(GL_MAX_DUAL_SOURCE_DRAW_BUFFERS, &value); + + return value; +} +#endif + void AbstractFramebuffer::bind(FramebufferTarget target) { bindInternal(target); setViewportInternal(); diff --git a/src/AbstractFramebuffer.h b/src/AbstractFramebuffer.h index b00b908fa..8235c1e47 100644 --- a/src/AbstractFramebuffer.h +++ b/src/AbstractFramebuffer.h @@ -133,11 +133,13 @@ See DefaultFramebuffer and Framebuffer for more information. The engine tracks currently bound framebuffer and current viewport to avoid unnecessary calls to @fn_gl{BindFramebuffer} and @fn_gl{Viewport} when -switching framebuffers. +switching framebuffers. %Framebuffer limits and implementation-defined values +(such as @ref maxViewportSize()) are cached, so repeated queries don't result +in repeated @fn_gl{Get} calls. If @extension{ARB,robustness} is available, read() operations are protected from buffer overflow. -@todo @extension{ARB,viewport_array} +@todo @extension{ARB,viewport_array} (and `GL_MAX_VIEWPORTS`) */ class MAGNUM_EXPORT AbstractFramebuffer { friend class Context; @@ -148,6 +150,43 @@ class MAGNUM_EXPORT AbstractFramebuffer { AbstractFramebuffer& operator=(AbstractFramebuffer&&) = delete; public: + /** @todo `GL_IMPLEMENTATION_COLOR_READ_FORMAT`, `GL_IMPLEMENTATION_COLOR_READ_TYPE`, seems to be depending on currently bound FB (aargh). Also for consistency it might be good to rename ImageFormat and ImageType to `ColorFormat` and `ColorType` (@extension{ARB,ES2_compatibility}). */ + + /** + * @brief Max supported viewport size + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. + * @see @ref setViewport(), @fn_gl{Get} with @def_gl{MAX_VIEWPORT_DIMS} + */ + static Vector2i maxViewportSize(); + + /** + * @brief Max supported draw buffer count + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. If ES extension @extension{NV,draw_buffers} is not + * available, returns `0`. + * @see @ref DefaultFramebuffer::mapForDraw(), @ref Framebuffer::mapForDraw(), + * @fn_gl{Get} with @def_gl{MAX_DRAW_BUFFERS} + */ + static Int maxDrawBuffers(); + + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Max supported dual-source draw buffer count + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. If extension @extension{ARB,blend_func_extended} is + * not available, returns `0`. + * @see @ref DefaultFramebuffer::mapForDraw(), @ref Framebuffer::mapForDraw(), + * @fn_gl{Get} with @def_gl{MAX_DUAL_SOURCE_DRAW_BUFFERS} + * @requires_gl Multiple blending inputs are not available in + * OpenGL ES. + */ + static Int maxDualSourceDrawBuffers(); + #endif + /** * @brief Copy block of pixels * @param source Source framebuffer @@ -211,7 +250,7 @@ class MAGNUM_EXPORT AbstractFramebuffer { * Saves the viewport to be used at later time in bind(). If the * framebuffer is currently bound, updates the viewport to given * rectangle. - * @see @fn_gl{Viewport} + * @see @ref maxViewportSize(), @fn_gl{Viewport} */ AbstractFramebuffer& setViewport(const Rectanglei& rectangle); diff --git a/src/DefaultFramebuffer.h b/src/DefaultFramebuffer.h index a2cdb8e82..c7e7ee0fa 100644 --- a/src/DefaultFramebuffer.h +++ b/src/DefaultFramebuffer.h @@ -323,8 +323,9 @@ class MAGNUM_EXPORT DefaultFramebuffer: public AbstractFramebuffer { * If @extension{EXT,direct_state_access} is not available and the * framebuffer is not currently bound, it is bound before the * operation. - * @see mapForRead(), @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffers} or - * @fn_gl_extension{FramebufferDrawBuffers,EXT,direct_state_access} + * @see @ref maxDrawBuffers(), @ref maxDualSourceDrawBuffers(), + * @ref mapForRead(), @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffers} + * or @fn_gl_extension{FramebufferDrawBuffers,EXT,direct_state_access} * @requires_gles30 Draw attachments for default framebuffer are * available only in OpenGL ES 3.0. */ diff --git a/src/Framebuffer.cpp b/src/Framebuffer.cpp index 5458066fe..c332f2db8 100644 --- a/src/Framebuffer.cpp +++ b/src/Framebuffer.cpp @@ -52,6 +52,25 @@ const Framebuffer::BufferAttachment Framebuffer::BufferAttachment::DepthStencil const Framebuffer::InvalidationAttachment Framebuffer::InvalidationAttachment::Depth = Framebuffer::InvalidationAttachment(GL_DEPTH_ATTACHMENT); const Framebuffer::InvalidationAttachment Framebuffer::InvalidationAttachment::Stencil = Framebuffer::InvalidationAttachment(GL_STENCIL_ATTACHMENT); +Int Framebuffer::maxColorAttachments() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + return 0; + #endif + + GLint& value = Context::current()->state().framebuffer->maxColorAttachments; + + if(value == 0) { + #ifndef MAGNUM_TARGET_GLES2 + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &value); + #else + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_NV, &value); + #endif + } + + return value; +} + Framebuffer::Framebuffer(const Rectanglei& viewport) { _viewport = viewport; diff --git a/src/Framebuffer.h b/src/Framebuffer.h index e71265f50..0e7f70fb9 100644 --- a/src/Framebuffer.h +++ b/src/Framebuffer.h @@ -272,6 +272,18 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer { #endif }; + /** @todo `GL_MAX_FRAMEBUFFER_WIDTH` etc. when @extension{ARB,framebuffer_no_attachments} is done */ + + /** + * @brief Max supported color attachment count + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. If ES extension @extension{NV,fbo_color_attachments} + * is not available, returns `0`. + * @see @ref mapForDraw(), @fn_gl{Get} with @def_gl{MAX_COLOR_ATTACHMENTS} + */ + static Int maxColorAttachments(); + /** * @brief Constructor * @@ -318,7 +330,9 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer { * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. - * @see mapForRead(), @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffers} or + * @see @ref maxDrawBuffers(), @ref maxDualSourceDrawBuffers(), + * @ref maxColorAttachments(), @ref mapForRead(), + * @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffers} or * @fn_gl_extension{FramebufferDrawBuffers,EXT,direct_state_access} * @requires_gles30 %Extension @es_extension2{NV,draw_buffers,GL_NV_draw_buffers} */ @@ -335,7 +349,8 @@ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer { * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. - * @see mapForRead(), @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffer} or + * @see @ref maxColorAttachments(), @ref mapForRead(), + * @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffer} or * @fn_gl_extension{FramebufferDrawBuffer,EXT,direct_state_access}, * @fn_gl{DrawBuffers} in OpenGL ES 3.0 * @requires_gles30 %Extension @es_extension2{NV,draw_buffers,GL_NV_draw_buffers} diff --git a/src/ImageFormat.h b/src/ImageFormat.h index 9ab0818a9..7e88cc6a1 100644 --- a/src/ImageFormat.h +++ b/src/ImageFormat.h @@ -258,7 +258,7 @@ Note that some formats can be used only for framebuffer reading (using AbstractFramebuffer::read()) and some only for texture data (using Texture::setImage() and others). @see Image, ImageReference, BufferImage, Trade::ImageData - */ +*/ enum class ImageType: GLenum { /** Each component unsigned byte. */ UnsignedByte = GL_UNSIGNED_BYTE, diff --git a/src/Implementation/FramebufferState.h b/src/Implementation/FramebufferState.h index f8c2d5ba4..a6138581d 100644 --- a/src/Implementation/FramebufferState.h +++ b/src/Implementation/FramebufferState.h @@ -31,10 +31,19 @@ namespace Magnum { namespace Implementation { struct FramebufferState { - constexpr FramebufferState(): readBinding(0), drawBinding(0), renderbufferBinding(0) {} + constexpr FramebufferState(): readBinding(0), drawBinding(0), renderbufferBinding(0), maxDrawBuffers(0), maxColorAttachments(0), maxRenderbufferSize(0), maxSamples(0) + #ifndef MAGNUM_TARGET_GLES + , maxDualSourceDrawBuffers(0) + #endif + {} GLuint readBinding, drawBinding, renderbufferBinding; + GLint maxDrawBuffers, maxColorAttachments, maxRenderbufferSize, maxSamples; + #ifndef MAGNUM_TARGET_GLES + GLint maxDualSourceDrawBuffers; + #endif Rectanglei viewport; + Vector2i maxViewportSize; }; }} diff --git a/src/Platform/magnum-info.cpp b/src/Platform/magnum-info.cpp index 1d9c6a72c..8e6eca7d7 100644 --- a/src/Platform/magnum-info.cpp +++ b/src/Platform/magnum-info.cpp @@ -32,7 +32,9 @@ #include "AbstractShaderProgram.h" #include "Context.h" #include "Extensions.h" +#include "Framebuffer.h" #include "Mesh.h" +#include "Renderbuffer.h" #include "Shader.h" #ifndef CORRADE_TARGET_NACL #include "Platform/WindowlessGlxApplication.h" @@ -179,10 +181,15 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat #define _l(val) Debug() << " " << #val << (sizeof(#val) > 64 ? "\n" + std::string(68, ' ') : std::string(64 - sizeof(#val), ' ')) << val; Debug() << "Limits and implementation-defined values:"; + _l(AbstractFramebuffer::maxViewportSize()) + _l(AbstractFramebuffer::maxDrawBuffers()) + _l(Framebuffer::maxColorAttachments()) #ifndef MAGNUM_TARGET_GLES2 _l(Mesh::maxElementsIndices()) _l(Mesh::maxElementsVertices()) #endif + _l(Renderbuffer::maxSize()) + _l(Renderbuffer::maxSamples()) _l(Shader::maxVertexOutputComponents()) _l(Shader::maxFragmentInputComponents()) _l(Shader::maxTextureImageUnits(Shader::Type::Vertex)) @@ -208,6 +215,12 @@ MagnumInfo::MagnumInfo(const Arguments& arguments): Platform::WindowlessApplicat _l(AbstractShaderProgram::maxVertexAttributes()) #ifndef MAGNUM_TARGET_GLES + if(c->isExtensionSupported()) { + _h(ARB::blend_func_extended) + + _l(AbstractFramebuffer::maxDualSourceDrawBuffers()) + } + if(c->isExtensionSupported()) { _h(ARB::compute_shader) diff --git a/src/Renderbuffer.cpp b/src/Renderbuffer.cpp index 82562a66d..c78bcfc5b 100644 --- a/src/Renderbuffer.cpp +++ b/src/Renderbuffer.cpp @@ -40,6 +40,36 @@ Renderbuffer::StorageMultisampleImplementation Renderbuffer::storageMultisampleI nullptr; #endif +Int Renderbuffer::maxSize() { + GLint& value = Context::current()->state().framebuffer->maxRenderbufferSize; + + /* Get the value, if not already cached */ + if(value == 0) + glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &value); + + return value; +} + +Int Renderbuffer::maxSamples() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported() && !Context::current()->isExtensionSupported()) + return 0; + #endif + + GLint& value = Context::current()->state().framebuffer->maxSamples; + + /* Get the value, if not already cached */ + if(value == 0) { + #ifndef MAGNUM_TARGET_GLES2 + glGetIntegerv(GL_MAX_SAMPLES, &value); + #else + glGetIntegerv(GL_MAX_SAMPLES_NV, &value); + #endif + } + + return value; +} + Renderbuffer::~Renderbuffer() { /* If bound, remove itself from state */ GLuint& binding = Context::current()->state().framebuffer->renderbufferBinding; diff --git a/src/Renderbuffer.h b/src/Renderbuffer.h index 6e30d85d5..1e11819e4 100644 --- a/src/Renderbuffer.h +++ b/src/Renderbuffer.h @@ -43,10 +43,12 @@ for more information. @section Renderbuffer-performance-optimization Performance optimizations The engine tracks currently bound renderbuffer to avoid unnecessary calls to -@fn_gl{BindRenderbuffer} in setStorage(). +@fn_gl{BindRenderbuffer} in setStorage(). %Renderbuffer limits and +implementation-defined values (such as @ref maxSize()) are cached, so repeated +queries don't result in repeated @fn_gl{Get} calls. If extension @extension{EXT,direct_state_access} is available, function -setStorage() uses DSA to avoid unnecessary calls to @fn_gl{BindFramebuffer}. +setStorage() uses DSA to avoid unnecessary calls to @fn_gl{BindRenderbuffer}. See its documentation for more information. @requires_gl30 %Extension @extension{ARB,framebuffer_object} @@ -60,6 +62,27 @@ class MAGNUM_EXPORT Renderbuffer { Renderbuffer& operator=(Renderbuffer&&) = delete; public: + /** + * @brief Max supported renderbuffer size + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. + * @see @ref setStorage(), @ref setStorageMultisample(), @fn_gl{Get} + * with @def_gl{MAX_RENDERBUFFER_SIZE} + */ + static Int maxSize(); + + /** + * @brief Max supported sample count + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. If ES extension @es_extension{ANGLE,framebuffer_multisample} + * or @es_extension{NV,framebuffer_multisample} is not available, + * returns `0`. + * @see @ref setStorageMultisample(), @fn_gl{Get} with @def_gl{MAX_SAMPLES} + */ + static Int maxSamples(); + /** * @brief Constructor * @@ -87,8 +110,8 @@ class MAGNUM_EXPORT Renderbuffer { * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. - * @see @fn_gl{BindRenderbuffer}, @fn_gl{RenderbufferStorage} or - * @fn_gl_extension{NamedRenderbufferStorage,EXT,direct_state_access} + * @see @ref maxSize(), @fn_gl{BindRenderbuffer}, @fn_gl{RenderbufferStorage} + * or @fn_gl_extension{NamedRenderbufferStorage,EXT,direct_state_access} */ void setStorage(RenderbufferFormat internalFormat, const Vector2i& size) { (this->*storageImplementation)(internalFormat, size); @@ -103,8 +126,8 @@ class MAGNUM_EXPORT Renderbuffer { * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. - * @see @fn_gl{BindRenderbuffer}, @fn_gl{RenderbufferStorage} or - * @fn_gl_extension{NamedRenderbufferStorage,EXT,direct_state_access} + * @see @ref maxSize(), @ref maxSamples(), @fn_gl{BindRenderbuffer}, + * @fn_gl{RenderbufferStorage} or @fn_gl_extension{NamedRenderbufferStorage,EXT,direct_state_access} * @requires_gles30 %Extension @es_extension{ANGLE,framebuffer_multisample} * or @es_extension{NV,framebuffer_multisample} * @todo How about @es_extension{APPLE,framebuffer_multisample}?