diff --git a/src/AbstractFramebuffer.cpp b/src/AbstractFramebuffer.cpp index b82ba3fb3..061a69adb 100644 --- a/src/AbstractFramebuffer.cpp +++ b/src/AbstractFramebuffer.cpp @@ -34,6 +34,8 @@ namespace Magnum { +AbstractFramebuffer::ReadImplementation AbstractFramebuffer::readImplementation = &AbstractFramebuffer::readImplementationDefault; + AbstractFramebuffer::DrawBuffersImplementation AbstractFramebuffer::drawBuffersImplementation = &AbstractFramebuffer::drawBuffersImplementationDefault; AbstractFramebuffer::DrawBufferImplementation AbstractFramebuffer::drawBufferImplementation = &AbstractFramebuffer::drawBufferImplementationDefault; AbstractFramebuffer::ReadBufferImplementation AbstractFramebuffer::readBufferImplementation = &AbstractFramebuffer::readBufferImplementationDefault; @@ -128,8 +130,9 @@ void AbstractFramebuffer::clear(ClearMask mask) { void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, AbstractImage::Format format, AbstractImage::Type type, Image2D* image) { bindInternal(readTarget); - char* data = new char[AbstractImage::pixelSize(format, type)*size.product()]; - glReadPixels(offset.x(), offset.y(), size.x(), size.y(), static_cast(format), static_cast(type), data); + const std::size_t dataSize = AbstractImage::pixelSize(format, type)*size.product(); + char* const data = new char[dataSize]; + readImplementation(offset, size, format, type, dataSize, data); image->setData(size, format, type, data); } @@ -142,7 +145,8 @@ void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, Abs image->setData(size, format, type, nullptr, usage); image->buffer()->bind(Buffer::Target::PixelPack); - glReadPixels(offset.x(), offset.y(), size.x(), size.y(), static_cast(format), static_cast(type), nullptr); + /** @todo De-duplicate buffer size computation */ + readImplementation(offset, size, format, type, AbstractImage::pixelSize(format, type)*size.product(), nullptr); } #endif @@ -185,6 +189,26 @@ void AbstractFramebuffer::initializeContextBasedFunctionality(Context* context) drawBufferImplementation = &AbstractFramebuffer::drawBufferImplementationDSA; readBufferImplementation = &AbstractFramebuffer::readBufferImplementationDSA; } + #endif + + #ifndef MAGNUM_TARGET_GLES3 + #ifndef MAGNUM_TARGET_GLES + if(context->isExtensionSupported()) + #else + if(context->isExtensionSupported()) + #endif + { + #ifndef MAGNUM_TARGET_GLES + Debug() << "AbstractFramebuffer: using" << Extensions::GL::ARB::robustness::string() << "features"; + #else + //Debug() << "AbstractFramebuffer: using" << Extensions::GL::EXT::robustness::string() << "features"; + #endif + + /** @todo Enable when extension wrangler for ES is available */ + #ifndef MAGNUM_TARGET_GLES + readImplementation = &AbstractFramebuffer::readImplementationRobustness; + #endif + } #else static_cast(context); #endif @@ -243,4 +267,26 @@ void AbstractFramebuffer::readBufferImplementationDSA(GLenum buffer) { } #endif +void AbstractFramebuffer::readImplementationDefault(const Vector2i& offset, const Vector2i& size, const AbstractImage::Format format, const AbstractImage::Type type, const std::size_t, GLvoid* const data) { + glReadPixels(offset.x(), offset.y(), size.x(), size.y(), static_cast(format), static_cast(type), data); +} + +#ifndef MAGNUM_TARGET_GLES3 +void AbstractFramebuffer::readImplementationRobustness(const Vector2i& offset, const Vector2i& size, const AbstractImage::Format format, const AbstractImage::Type type, const std::size_t dataSize, GLvoid* const data) { + /** @todo Enable when extension wrangler for ES is available */ + #ifndef MAGNUM_TARGET_GLES + glReadnPixelsARB(offset.x(), offset.y(), size.x(), size.y(), static_cast(format), static_cast(type), dataSize, data); + #else + CORRADE_INTERNAL_ASSERT(false); + //glReadnPixelsEXT(offset.x(), offset.y(), size.x(), size.y(), static_cast(format), static_cast(type), data); + static_cast(offset); + static_cast(size); + static_cast(format); + static_cast(type); + static_cast(dataSize); + static_cast(data); + #endif +} +#endif + } diff --git a/src/AbstractFramebuffer.h b/src/AbstractFramebuffer.h index db85c3af9..78dea55cb 100644 --- a/src/AbstractFramebuffer.h +++ b/src/AbstractFramebuffer.h @@ -41,12 +41,14 @@ namespace Magnum { See DefaultFramebuffer and Framebuffer for more information. -@section AbstractFramebuffer-performance-optimization Performance optimizations +@section AbstractFramebuffer-performance-optimization Performance optimizations and security The engine tracks currently bound framebuffer and current viewport to avoid unnecessary calls to @fn_gl{BindFramebuffer} and @fn_gl{Viewport} when switching framebuffers. +If @extension{ARB,robustness} is available, read() operations are protected +from buffer overflow. @todo @extension{ARB,viewport_array} */ class MAGNUM_EXPORT AbstractFramebuffer { @@ -238,7 +240,10 @@ class MAGNUM_EXPORT AbstractFramebuffer { * @param type Data type of pixel data * @param image %Image where to put the data * - * @see @fn_gl{BindFramebuffer}, @fn_gl{ReadPixels} + * If @extension{ARB,robustness} is available, the operation is + * protected from buffer overflow. + * @see @fn_gl{BindFramebuffer}, @fn_gl{ReadPixels} or + * @fn_gl_extension{ReadnPixels,ARB,robustness} * @todo Read size, format & type from image? */ void read(const Vector2i& offset, const Vector2i& size, AbstractImage::Format format, AbstractImage::Type type, Image2D* image); @@ -253,7 +258,8 @@ class MAGNUM_EXPORT AbstractFramebuffer { * @param image %Buffer image where to put the data * @param usage %Buffer usage * - * @see @fn_gl{BindFramebuffer}, @fn_gl{ReadPixels} + * See read(const Vector2i&, const Vector2i&, Image2D*) for more + * information. * @requires_gles30 Pixel buffer objects are not available in OpenGL ES 2.0. * @todo Read size, format & type from image? */ @@ -304,6 +310,13 @@ class MAGNUM_EXPORT AbstractFramebuffer { #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL readBufferImplementationDSA(GLenum buffer); #endif + + typedef void(*ReadImplementation)(const Vector2i&, const Vector2i&, AbstractImage::Format, AbstractImage::Type, std::size_t, GLvoid*); + static void MAGNUM_LOCAL readImplementationDefault(const Vector2i& offset, const Vector2i& size, AbstractImage::Format format, AbstractImage::Type type, std::size_t dataSize, GLvoid* data); + #ifndef MAGNUM_TARGET_GLES3 + static void MAGNUM_LOCAL readImplementationRobustness(const Vector2i& offset, const Vector2i& size, AbstractImage::Format format, AbstractImage::Type type, std::size_t dataSize, GLvoid* data); + #endif + static ReadImplementation MAGNUM_LOCAL readImplementation; }; inline AbstractFramebuffer::~AbstractFramebuffer() {} diff --git a/src/Context.cpp b/src/Context.cpp index 9da905866..e6a63ec3e 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -80,6 +80,7 @@ const std::vector& Extension::extensions(Version version) { static const std::vector extensions{ _extension(GL,AMD,vertex_shader_layer), // done _extension(GL,AMD,shader_trinary_minmax), // done + _extension(GL,ARB,robustness), _extension(GL,EXT,texture_filter_anisotropic), // done _extension(GL,EXT,direct_state_access), _extension(GL,GREMEDY,string_marker)}; // done @@ -211,6 +212,7 @@ const std::vector& Extension::extensions(Version version) { _extension(GL,EXT,debug_marker), _extension(GL,EXT,separate_shader_objects), _extension(GL,EXT,sRGB), + _extension(GL,EXT,robustness), _extension(GL,NV,read_buffer_front), _extension(GL,NV,read_stencil), _extension(GL,NV,texture_border_clamp), // done diff --git a/src/Context.h b/src/Context.h index c0df5d267..25238edca 100644 --- a/src/Context.h +++ b/src/Context.h @@ -157,7 +157,18 @@ class MAGNUM_EXPORT Context { * @requires_gl43 %Extension @es_extension{KHR,debug} * @requires_es_extension %Extension @es_extension{KHR,debug} */ - Debug = GL_CONTEXT_FLAG_DEBUG_BIT + Debug = GL_CONTEXT_FLAG_DEBUG_BIT, + #endif + + #ifndef MAGNUM_TARGET_GLES + /** + * Context with robust buffer access + * @requires_extension %Extension @extension{EXT,robustness} + * @requires_es_extension %Extension @es_extension{EXT,robustness} + * @todo In ES available under glGetIntegerv(CONTEXT_ROBUST_ACCESS_EXT), + * how to make it compatible? + */ + Robustness = GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB #endif }; diff --git a/src/Extensions.h b/src/Extensions.h index de1d5c285..a137b7491 100644 --- a/src/Extensions.h +++ b/src/Extensions.h @@ -120,6 +120,7 @@ namespace GL { _extension(GL,ARB,shader_precision, GL400, GL410) // #98 _extension(GL,ARB,vertex_attrib_64bit, GL300, GL410) // #99 _extension(GL,ARB,viewport_array, GL210, GL410) // #100 + _extension(GL,ARB,robustness, GL210, None) // #105 _extension(GL,ARB,base_instance, GL210, GL420) // #107 _extension(GL,ARB,shading_language_420pack, GL300, GL420) // #108 _extension(GL,ARB,transform_feedback_instanced, GL210, GL420) // #109 @@ -204,6 +205,7 @@ namespace GL { _extension(GL,EXT,separate_shader_objects, GLES200, None) // #101 _extension(GL,EXT,texture_rg, GLES200, GLES300) // #103 _extension(GL,EXT,sRGB, GLES200, None) // #105 + _extension(GL,EXT,robustness, GLES200, None) // #105 _extension(GL,EXT,texture_storage, GLES200, GLES300) // #108 _extension(GL,EXT,map_buffer_range, GLES200, GLES300) // #121 } namespace NV { diff --git a/src/Implementation/RendererState.h b/src/Implementation/RendererState.h new file mode 100644 index 000000000..8d1eb2287 --- /dev/null +++ b/src/Implementation/RendererState.h @@ -0,0 +1,45 @@ +#ifndef Magnum_Implementation_RendererState_h +#define Magnum_Implementation_RendererState_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Renderer.h" + +namespace Magnum { namespace Implementation { + +struct RendererState { + inline constexpr RendererState() + #ifndef MAGNUM_TARGET_GLES3 + : resetNotificationStrategy() + #endif + {} + + #ifndef MAGNUM_TARGET_GLES3 + Renderer::ResetNotificationStrategy resetNotificationStrategy; + #endif +}; + +}} + +#endif diff --git a/src/Implementation/State.cpp b/src/Implementation/State.cpp index c4d00e811..a218a3f46 100644 --- a/src/Implementation/State.cpp +++ b/src/Implementation/State.cpp @@ -27,6 +27,7 @@ #include "BufferState.h" #include "FramebufferState.h" #include "MeshState.h" +#include "RendererState.h" #include "ShaderProgramState.h" #include "TextureState.h" @@ -36,12 +37,14 @@ State::State(): buffer(new BufferState), framebuffer(new FramebufferState), mesh(new MeshState), + renderer(new RendererState), shaderProgram(new ShaderProgramState), texture(new TextureState) {} State::~State() { delete texture; delete shaderProgram; + delete renderer; delete mesh; delete framebuffer; delete buffer; diff --git a/src/Implementation/State.h b/src/Implementation/State.h index 16b5b59c9..17a31f398 100644 --- a/src/Implementation/State.h +++ b/src/Implementation/State.h @@ -29,6 +29,7 @@ namespace Magnum { namespace Implementation { struct BufferState; struct FramebufferState; struct MeshState; +struct RendererState; struct ShaderProgramState; struct TextureState; @@ -39,6 +40,7 @@ struct State { BufferState* const buffer; FramebufferState* const framebuffer; MeshState* const mesh; + RendererState* const renderer; ShaderProgramState* const shaderProgram; TextureState* const texture; }; diff --git a/src/Platform/AbstractXApplication.h b/src/Platform/AbstractXApplication.h index 4913d03bb..649ca4d63 100644 --- a/src/Platform/AbstractXApplication.h +++ b/src/Platform/AbstractXApplication.h @@ -178,6 +178,7 @@ CORRADE_ENUMSET_OPERATORS(AbstractXApplication::Flags) Double-buffered OpenGL context. @see AbstractXApplication(), createContext() +@todo GLX_ARB_create_context_robustness/EGL_EXT_create_context_robustness */ class AbstractXApplication::Configuration { Configuration(const Configuration&) = delete; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index b74e66e60..990bc6619 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -28,6 +28,8 @@ #include "Math/Geometry/Rectangle.h" #include "Context.h" #include "Extensions.h" +#include "Implementation/State.h" +#include "Implementation/RendererState.h" namespace Magnum { @@ -36,6 +38,9 @@ Renderer::ClearDepthfImplementation Renderer::clearDepthfImplementation = &Rende #else Renderer::ClearDepthfImplementation Renderer::clearDepthfImplementation = &Renderer::clearDepthfImplementationES; #endif +#ifndef MAGNUM_TARGET_GLES3 +Renderer::GraphicsResetStatusImplementation Renderer::graphicsResetStatusImplementation = &Renderer::graphicsResetStatusImplementationDefault; +#endif void Renderer::setFeature(const Feature feature, const bool enabled) { enabled ? glEnable(GLenum(feature)) : glDisable(GLenum(feature)); @@ -153,6 +158,22 @@ void Renderer::setLogicOperation(const LogicOperation operation) { } #endif +#ifndef MAGNUM_TARGET_GLES3 +Renderer::ResetNotificationStrategy Renderer::resetNotificationStrategy() { + ResetNotificationStrategy& strategy = Context::current()->state()->renderer->resetNotificationStrategy; + + if(strategy == ResetNotificationStrategy()) { + #ifndef MAGNUM_TARGET_GLES + glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, reinterpret_cast(&strategy)); + #else + glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_EXT, reinterpret_cast(&strategy)); + #endif + } + + return strategy; +} +#endif + void Renderer::initializeContextBasedFunctionality(Context* context) { #ifndef MAGNUM_TARGET_GLES if(context->isExtensionSupported()) { @@ -160,6 +181,23 @@ void Renderer::initializeContextBasedFunctionality(Context* context) { clearDepthfImplementation = &Renderer::clearDepthfImplementationES; } + #endif + + #ifndef MAGNUM_TARGET_GLES3 + #ifndef MAGNUM_TARGET_GLES + if(context->isExtensionSupported()) + #else + if(context->isExtensionSupported()) + #endif + { + #ifndef MAGNUM_TARGET_GLES + Debug() << "Renderer: using" << Extensions::GL::ARB::robustness::string() << "features"; + #else + Debug() << "Renderer: using" << Extensions::GL::EXT::robustness::string() << "features"; + #endif + + graphicsResetStatusImplementation = &Renderer::graphicsResetStatusImplementationRobustness; + } #else static_cast(context); #endif @@ -175,4 +213,20 @@ void Renderer::clearDepthfImplementationES(const GLfloat depth) { glClearDepthf(depth); } +#ifndef MAGNUM_TARGET_GLES3 +Renderer::GraphicsResetStatus Renderer::graphicsResetStatusImplementationDefault() { + return GraphicsResetStatus::NoError; +} + +Renderer::GraphicsResetStatus Renderer::graphicsResetStatusImplementationRobustness() { + /** @todo Enable when extension wrangler for ES is available */ + #ifndef MAGNUM_TARGET_GLES + return GraphicsResetStatus(glGetGraphicsResetStatusARB()); + #else + //return GraphicsResetStatus(glGetGraphicsResetStatusEXT()); + CORRADE_INTERNAL_ASSERT(false); + #endif +} +#endif + } diff --git a/src/Renderer.h b/src/Renderer.h index 7798d6ede..af1716b5f 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -838,6 +838,95 @@ class MAGNUM_EXPORT Renderer { glFinish(); } + #ifndef MAGNUM_TARGET_GLES3 + /** + * @brief Graphics reset notification strategy + * + * @see resetNotificationStrategy() + * @requires_extension %Extension @extension{ARB,robustness} + * @requires_es_extension %Extension @es_extension{EXT,robustness} + */ + enum class ResetNotificationStrategy: GLint { + /** + * No reset notification, thus graphicsResetStatus() will always + * return @ref GraphicsResetStatus "GraphicsResetStatus::NoError". + * However this doesn't mean that the context cannot be lost. + */ + #ifndef MAGNUM_TARGET_GLES + NoResetNotification = GL_NO_RESET_NOTIFICATION_ARB, + #else + NoResetNotification = GL_NO_RESET_NOTIFICATION_EXT, + #endif + + /** + * Graphics reset will result in context loss, cause of the reset + * can be queried with graphicsResetStatus(). + */ + #ifndef MAGNUM_TARGET_GLES + LoseContextOnReset = GL_LOSE_CONTEXT_ON_RESET_ARB + #else + LoseContextOnReset = GL_LOSE_CONTEXT_ON_RESET_EXT + #endif + }; + + /** + * @brief Graphics reset notification strategy + * + * The result is cached, repeated queries don't result in repeated + * OpenGL calls. If OpenGL extension @extension{ARB,robustness} or ES + * extension @es_extension{EXT,robustness} is not available, this + * function always returns @ref ResetNotificationStrategy "ResetNotificationStrategy::NoResetNotification". + * @see graphicsResetStatus(), @fn_gl{Get} with @def_gl{RESET_NOTIFICATION_STRATEGY_ARB} + */ + static ResetNotificationStrategy resetNotificationStrategy(); + + /** + * @brief Graphics reset status + * + * @see resetNotificationStrategy(), graphicsResetStatus() + * @requires_extension %Extension @extension{ARB,robustness} + * @requires_es_extension %Extension @es_extension{EXT,robustness} + */ + enum class GraphicsResetStatus: GLenum { + /** No reset occured since last call. */ + NoError = GL_NO_ERROR, + + /** Reset attributable to the current context has been detected. */ + #ifndef MAGNUM_TARGET_GLES + GuiltyContextReset = GL_GUILTY_CONTEXT_RESET_ARB, + #else + GuiltyContextReset = GL_GUILTY_CONTEXT_RESET_EXT, + #endif + + /** Reset not attributable to the current context has been detected. */ + #ifndef MAGNUM_TARGET_GLES + InnocentContextReset = GL_INNOCENT_CONTEXT_RESET_ARB, + #else + InnocentContextReset = GL_INNOCENT_CONTEXT_RESET_EXT, + #endif + + /** Reset with unknown cause has been detected. */ + #ifndef MAGNUM_TARGET_GLES + UnknownContextReset = GL_UNKNOWN_CONTEXT_RESET_ARB + #else + UnknownContextReset = GL_UNKNOWN_CONTEXT_RESET_EXT + #endif + }; + + /** + * @brief Check graphics reset status + * + * Reset causes all context state to be lost. If OpenGL extension + * @extension{ARB,robustness} or ES extension @es_extension{EXT,robustness} + * is not available, this function always returns + * @ref GraphicsResetStatus "GraphicsResetStatus::NoError". + * @see resetNotificationStrategy(), @fn_gl_extension{GetGraphicsResetStatus,ARB,robustness} + */ + inline static GraphicsResetStatus graphicsResetStatus() { + return graphicsResetStatusImplementation(); + } + #endif + /*@}*/ private: @@ -849,6 +938,13 @@ class MAGNUM_EXPORT Renderer { #endif static void MAGNUM_LOCAL clearDepthfImplementationES(GLfloat depth); static ClearDepthfImplementation clearDepthfImplementation; + + #ifndef MAGNUM_TARGET_GLES3 + typedef GraphicsResetStatus(*GraphicsResetStatusImplementation)(); + static GraphicsResetStatus MAGNUM_LOCAL graphicsResetStatusImplementationDefault(); + static GraphicsResetStatus MAGNUM_LOCAL graphicsResetStatusImplementationRobustness(); + static GraphicsResetStatusImplementation graphicsResetStatusImplementation; + #endif }; }