mirror of https://github.com/mosra/magnum.git
Browse Source
This is a replacement for the existing AbstractRenderer and Renderer2D / Renderer3D classes, with no STL or other shittiness like excessive allocations, and with a much better feature set. Deprecation of the old APIs is going to happen next. Compared to the old implementation it doesn't make use of the complicated buffer mapping because -- unlike in 2012 -- such behavior was since deemed questionable as the driver (or whichever translation layer like ANGLE or Zink or Apple's GL-over-Metal) may still make a copy anyway and doing so prevents buffer orphaning. So it's right now just plain setData() / setSubData() calls. *If* it becomes some sort of a bottleneck (which I doubt), I may reconsider, or add something else like double buffering.pull/674/head
12 changed files with 1437 additions and 8 deletions
@ -0,0 +1,306 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022, 2023, 2024, 2025 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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 "RendererGL.h" |
||||
|
||||
#include <Corrade/Containers/EnumSet.hpp> |
||||
#include <Corrade/Containers/StringView.h> |
||||
|
||||
#include "Magnum/Shaders/GenericGL.h" /* no link-time dependency here */ |
||||
#include "Magnum/Text/AbstractGlyphCache.h" |
||||
#include "Magnum/Text/Implementation/rendererState.h" |
||||
|
||||
/* Somehow on GCC 4.8 to 7 the {} passed as a default argument for
|
||||
ArrayView<const FeatureRange> causes "error: elements of array 'const class |
||||
Magnum::Text::FeatureRange [0]' have incomplete type". GCC 9 is fine, no |
||||
idea about version 8, but including the definition for it as well to be |
||||
safe. Similar problem happens with MSVC STL, where the initializer_list is |
||||
implemented as a (begin, end) pair and size() is a difference of those two |
||||
pointers. Which needs to know the type size to calculate the actual element |
||||
count. */ |
||||
#if (defined(CORRADE_TARGET_GCC) && __GNUC__ <= 8) || defined(CORRADE_TARGET_DINKUMWARE) |
||||
#include "Magnum/Text/Feature.h" |
||||
#endif |
||||
|
||||
namespace Magnum { namespace Text { |
||||
|
||||
Debug& operator<<(Debug& debug, const RendererGLFlag value) { |
||||
debug << "Text::RendererGLFlag" << Debug::nospace; |
||||
|
||||
switch(value) { |
||||
/* LCOV_EXCL_START */ |
||||
#define _c(v) case RendererGLFlag::v: return debug << "::" #v; |
||||
_c(GlyphPositionsClusters) |
||||
#undef _c |
||||
/* LCOV_EXCL_STOP */ |
||||
} |
||||
|
||||
return debug << "(" << Debug::nospace << Debug::hex << UnsignedByte(value) << Debug::nospace << ")"; |
||||
} |
||||
|
||||
Debug& operator<<(Debug& debug, const RendererGLFlags value) { |
||||
return Containers::enumSetDebugOutput(debug, value, "Text::RendererGLFlags{}", { |
||||
RendererGLFlag::GlyphPositionsClusters |
||||
}); |
||||
} |
||||
|
||||
struct RendererGL::State: Renderer::State { |
||||
explicit State(const AbstractGlyphCache& glyphCache, RendererGLFlags flags); |
||||
|
||||
GL::Buffer indices{GL::Buffer::TargetHint::ElementArray}, |
||||
vertices{GL::Buffer::TargetHint::Array}; |
||||
GL::Mesh mesh; |
||||
|
||||
/* Because querying GL buffer size is not possible on all platforms and it
|
||||
may be slow, track the size here. It's used to know whether the buffer |
||||
should be reuploaded as a whole or can be partially updated, updated in |
||||
both reserve() and render(). */ |
||||
UnsignedInt bufferGlyphCapacity = 0; |
||||
}; |
||||
|
||||
RendererGL::State::State(const AbstractGlyphCache& glyphCache, RendererGLFlags flags): Renderer::State{glyphCache, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, RendererFlags{UnsignedByte(flags)}} { |
||||
#ifdef MAGNUM_TARGET_GLES2 |
||||
CORRADE_ASSERT(glyphCache.size().z() == 1, |
||||
"Text::RendererGL: array glyph caches are not supported in OpenGL ES 2.0 and WebGL 1 builds", ); |
||||
#endif |
||||
|
||||
/* As documented in RendererGL::setIndexType(), use of 8-bit indices is
|
||||
discouraged on contemporary GPUs */ |
||||
indexType = minIndexType = MeshIndexType::UnsignedShort; |
||||
|
||||
/* Set up the mesh with the initial index type and zero primitives to draw.
|
||||
The count gets updated on each renderer(), index buffer properties each |
||||
time the index type changes. */ |
||||
mesh.setIndexBuffer(indices, 0, indexType) |
||||
.setCount(0); |
||||
#ifndef MAGNUM_TARGET_GLES2 |
||||
if(glyphCache.size().z() != 1) { |
||||
mesh.addVertexBuffer(vertices, 0, |
||||
Shaders::GenericGL2D::Position{}, |
||||
Shaders::GenericGL2D::TextureArrayCoordinates{}); |
||||
} else |
||||
#endif |
||||
{ |
||||
mesh.addVertexBuffer(vertices, 0, |
||||
Shaders::GenericGL2D::Position{}, |
||||
Shaders::GenericGL2D::TextureCoordinates{}); |
||||
} |
||||
} |
||||
|
||||
RendererGL::RendererGL(const AbstractGlyphCache& glyphCache, RendererGLFlags flags): Renderer{Containers::pointer<State>(glyphCache, flags)} {} |
||||
|
||||
RendererGL::RendererGL(RendererGL&&) noexcept = default; |
||||
|
||||
RendererGL::~RendererGL() = default; |
||||
|
||||
RendererGL& RendererGL::operator=(RendererGL&&) noexcept = default; |
||||
|
||||
RendererGLFlags RendererGL::flags() const { |
||||
return RendererGLFlags{UnsignedByte(_state->flags)}; |
||||
} |
||||
|
||||
GL::Mesh& RendererGL::mesh() { |
||||
return static_cast<State&>(*_state).mesh; |
||||
} |
||||
|
||||
const GL::Mesh& RendererGL::mesh() const { |
||||
return static_cast<const State&>(*_state).mesh; |
||||
} |
||||
|
||||
RendererGL& RendererGL::setIndexType(MeshIndexType atLeast) { |
||||
State& state = static_cast<State&>(*_state); |
||||
|
||||
Renderer::setIndexType(atLeast); |
||||
|
||||
/* Upload indices anew if the type is different from before. In this case
|
||||
it's also most likely that the size is bigger than before, so do it as |
||||
a setData() call instead of having a specialized setSubData() code path |
||||
if the total size shrinks. |
||||
|
||||
Besides the type, the capacity should not change compared to when the |
||||
buffer was last updated in reserve() or render(). (Which only holds for |
||||
builtin allocators, but RendererGL so far allows only builtin allocators |
||||
so that's fine. It however does *not* hold for `state.indexData`, as |
||||
that can stay larger if the index type becomes smaller, so verifying |
||||
against `state.glyphPositions` instead.) */ |
||||
CORRADE_INTERNAL_ASSERT(state.bufferGlyphCapacity == state.glyphPositions.size()); |
||||
if(GL::meshIndexType(state.indexType) != state.mesh.indexType()) { |
||||
state.indices.setData(state.indexData); |
||||
state.mesh.setIndexBuffer(state.indices, 0, state.indexType); |
||||
} |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
RendererGL& RendererGL::clear() { |
||||
Renderer::clear(); |
||||
static_cast<State&>(*_state).mesh.setCount(0); |
||||
return *this; |
||||
} |
||||
|
||||
RendererGL& RendererGL::reset() { |
||||
Renderer::reset(); |
||||
static_cast<State&>(*_state).mesh.setCount(0); |
||||
return *this; |
||||
} |
||||
|
||||
RendererGL& RendererGL::reserve(const UnsignedInt glyphCapacity, const UnsignedInt runCapacity) { |
||||
State& state = static_cast<State&>(*_state); |
||||
|
||||
Renderer::reserve(glyphCapacity, runCapacity); |
||||
|
||||
/* Upload indices anew if the capacity is bigger than before */ |
||||
if(state.bufferGlyphCapacity < glyphCapacity) { |
||||
state.indices.setData(state.indexData); |
||||
/* Update the mesh index buffer reference if the type changed */ |
||||
if(GL::meshIndexType(state.indexType) != state.mesh.indexType()) |
||||
state.mesh.setIndexBuffer(state.indices, 0, state.indexType); |
||||
|
||||
/* If the capacity isn't bigger, the index type shouldn't have changed
|
||||
either and so no upload needs to be done. It can change only if the new |
||||
capacity is too larger to fit the type used, or in a setIndexType() |
||||
call, but there we handle the reupload directly. */ |
||||
} else CORRADE_INTERNAL_ASSERT(GL::meshIndexType(state.indexType) == state.mesh.indexType()); |
||||
|
||||
/* Resize the vertex buffer and reupload its contents if the capacity is
|
||||
bigger than before */ |
||||
if(state.bufferGlyphCapacity < glyphCapacity) { |
||||
const UnsignedInt glyphSize = 4*( |
||||
#ifndef MAGNUM_TARGET_GLES2 |
||||
state.glyphCache.size().z() != 1 ? |
||||
sizeof(Implementation::VertexArray) : |
||||
#endif |
||||
sizeof(Implementation::Vertex)); |
||||
|
||||
/* The assumption in this case is that the capacity is bigger than the
|
||||
actually rendered glyph count, otherwise we'd have it all resized |
||||
and uploaded in render() already. Thus we have to do a bigger |
||||
setData() allocation first and then upload just a portion with |
||||
setSubData(). */ |
||||
CORRADE_INTERNAL_ASSERT(glyphCapacity > state.glyphCount); |
||||
state.vertices |
||||
.setData({nullptr, glyphCapacity*glyphSize}) |
||||
.setSubData(0, state.vertexData.prefix(state.glyphCount*glyphSize)); |
||||
} |
||||
|
||||
/* Remember the currently used capacity if it grew. It can happen that
|
||||
reserve() is called with a smaller capacity, or with just runCapacity |
||||
being larger, so this shouldn't reset that and cause needless reupload |
||||
next time. */ |
||||
state.bufferGlyphCapacity = Math::max(state.bufferGlyphCapacity, glyphCapacity); |
||||
|
||||
return *this; |
||||
} |
||||
|
||||
Containers::Pair<Range2D, Range1Dui> RendererGL::render() { |
||||
State& state = static_cast<State&>(*_state); |
||||
|
||||
const Containers::Pair<Range2D, Range1Dui> out = Renderer::render(); |
||||
|
||||
/* Upload indices anew if the glyph count is bigger than before */ |
||||
if(state.bufferGlyphCapacity < state.glyphCount) { |
||||
state.indices.setData(state.indexData); |
||||
/* Update the mesh index buffer reference if the type changed */ |
||||
if(GL::meshIndexType(state.indexType) != state.mesh.indexType()) |
||||
state.mesh.setIndexBuffer(state.indices, 0, state.indexType); |
||||
|
||||
/* If the glyph count isn't bigger, the index type shouldn't have changed
|
||||
either. Same reasoning as in reserve() above. */ |
||||
} else CORRADE_INTERNAL_ASSERT(GL::meshIndexType(state.indexType) == state.mesh.indexType()); |
||||
|
||||
/* Upload vertices fully anew if the glyph count is bigger than before */ |
||||
const UnsignedInt glyphSize = 4*( |
||||
#ifndef MAGNUM_TARGET_GLES2 |
||||
state.glyphCache.size().z() != 1 ? |
||||
sizeof(Implementation::VertexArray) : |
||||
#endif |
||||
sizeof(Implementation::Vertex)); |
||||
if(state.bufferGlyphCapacity < state.glyphCount) { |
||||
/* Unlike in render(), it's just setData() alone, with the assumption
|
||||
that the render() caused the capacity to grow to fit exactly all |
||||
glyphs, and so we upload everything. (Which only holds for builtin |
||||
vertex allocators, but RendererGL so far allows only builtin |
||||
allocators so that's fine.) */ |
||||
CORRADE_INTERNAL_ASSERT( |
||||
state.vertexPositions.size() == state.glyphCount*4 && |
||||
state.vertexTextureCoordinates.size() == state.glyphCount*4); |
||||
state.vertices.setData(state.vertexData.prefix(state.glyphCount*glyphSize)); |
||||
|
||||
/* Otherwise upload just what was rendered new */ |
||||
} else { |
||||
const Range1Dui glyphRange = glyphsForRuns(out.second()); |
||||
state.vertices.setSubData(glyphRange.min()*glyphSize, state.vertexData.slice(glyphRange.min()*glyphSize, glyphRange.max()*glyphSize)); |
||||
} |
||||
|
||||
/* Remember the currently used capacity if it grew */ |
||||
state.bufferGlyphCapacity = Math::max(state.bufferGlyphCapacity, state.glyphCount); |
||||
|
||||
/* Set the mesh index count to exactly what was rendered in total */ |
||||
state.mesh.setCount(state.glyphCount*6); |
||||
|
||||
return out; |
||||
} |
||||
|
||||
RendererGL& RendererGL::add(AbstractShaper& shaper, const Float size, const Containers::StringView text, const UnsignedInt begin, const UnsignedInt end, const Containers::ArrayView<const FeatureRange> features) { |
||||
return static_cast<RendererGL&>(Renderer::add(shaper, size, text, begin, end, features)); |
||||
} |
||||
|
||||
RendererGL& RendererGL::add(AbstractShaper& shaper, const Float size, const Containers::StringView text, const UnsignedInt begin, const UnsignedInt end) { |
||||
return static_cast<RendererGL&>(Renderer::add(shaper, size, text, begin, end)); |
||||
} |
||||
|
||||
RendererGL& RendererGL::add(AbstractShaper& shaper, const Float size, const Containers::StringView text, const UnsignedInt begin, const UnsignedInt end, const std::initializer_list<FeatureRange> features) { |
||||
return static_cast<RendererGL&>(Renderer::add(shaper, size, text, begin, end, features)); |
||||
} |
||||
|
||||
RendererGL& RendererGL::add(AbstractShaper& shaper, const Float size, const Containers::StringView text, const Containers::ArrayView<const FeatureRange> features) { |
||||
return static_cast<RendererGL&>(Renderer::add(shaper, size, text, features)); |
||||
} |
||||
|
||||
RendererGL& RendererGL::add(AbstractShaper& shaper, const Float size, const Containers::StringView text) { |
||||
return static_cast<RendererGL&>(Renderer::add(shaper, size, text)); |
||||
} |
||||
|
||||
RendererGL& RendererGL::add(AbstractShaper& shaper, const Float size, const Containers::StringView text, const std::initializer_list<FeatureRange> features) { |
||||
return static_cast<RendererGL&>(Renderer::add(shaper, size, text, features)); |
||||
} |
||||
|
||||
Containers::Pair<Range2D, Range1Dui> RendererGL::render(AbstractShaper& shaper, const Float size, const Containers::StringView text, const Containers::ArrayView<const FeatureRange> features) { |
||||
/* Compared to Renderer::render() this calls our render() instead of
|
||||
Renderer::render() */ |
||||
add(shaper, size, text, features); |
||||
return render(); |
||||
} |
||||
|
||||
Containers::Pair<Range2D, Range1Dui> RendererGL::render(AbstractShaper& shaper, const Float size, const Containers::StringView text) { |
||||
return render(shaper, size, text, {}); |
||||
} |
||||
|
||||
Containers::Pair<Range2D, Range1Dui> RendererGL::render(AbstractShaper& shaper, const Float size, const Containers::StringView text, const std::initializer_list<FeatureRange> features) { |
||||
return render(shaper, size, text, Containers::arrayView(features)); |
||||
} |
||||
|
||||
}} |
||||
@ -0,0 +1,245 @@
|
||||
#ifndef Magnum_Text_RendererGL_h |
||||
#define Magnum_Text_RendererGL_h |
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022, 2023, 2024, 2025 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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. |
||||
*/ |
||||
|
||||
/** @file
|
||||
* @brief Class @ref Magnum::Text::RendererGL, enum @ref Magnum::Text::RendererGLFlag, enum set @ref Magnum::Text::RendererGLFlags |
||||
* @m_since_latest |
||||
*/ |
||||
|
||||
#include "Magnum/configure.h" |
||||
|
||||
#ifdef MAGNUM_TARGET_GL |
||||
#include "Magnum/Text/Renderer.h" |
||||
|
||||
namespace Magnum { namespace Text { |
||||
|
||||
/**
|
||||
@brief OpenGL text renderer flag |
||||
@m_since_latest |
||||
|
||||
A superset of @ref RendererFlag. |
||||
|
||||
@note This enum is available only if Magnum is compiled with |
||||
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features |
||||
for more information. |
||||
|
||||
@see @ref RendererGLFlags, @ref RendererGL |
||||
*/ |
||||
/* Currently is the same as RendererFlag, but is made to a dedicated type to
|
||||
not cause a breaking change once GL-specific flags are introduced, such as |
||||
buffer mapping */ |
||||
enum class RendererGLFlag: UnsignedByte { |
||||
/** @copydoc RendererFlag::GlyphPositionsClusters */ |
||||
GlyphPositionsClusters = UnsignedByte(RendererFlag::GlyphPositionsClusters) |
||||
}; |
||||
|
||||
/**
|
||||
@debugoperatorenum{RendererGLFlag} |
||||
@m_since_latest |
||||
|
||||
@note This function is available only if Magnum is compiled with |
||||
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features |
||||
for more information. |
||||
*/ |
||||
MAGNUM_TEXT_EXPORT Debug& operator<<(Debug& output, RendererGLFlag value); |
||||
|
||||
/**
|
||||
@brief OpenGL text renderer flags |
||||
@m_since_latest |
||||
|
||||
@note This enum set is available only if Magnum is compiled with |
||||
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features |
||||
for more information. |
||||
|
||||
@see @ref RendererGL |
||||
*/ |
||||
typedef Containers::EnumSet<RendererGLFlag> RendererGLFlags; |
||||
|
||||
CORRADE_ENUMSET_OPERATORS(RendererGLFlags) |
||||
|
||||
/**
|
||||
@debugoperatorenum{RendererGLFlags} |
||||
@m_since_latest |
||||
|
||||
@note This function is available only if Magnum is compiled with |
||||
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features |
||||
for more information. |
||||
*/ |
||||
MAGNUM_TEXT_EXPORT Debug& operator<<(Debug& output, RendererGLFlags value); |
||||
|
||||
/**
|
||||
@brief OpenGL text renderer |
||||
@m_since_latest |
||||
|
||||
@note This class is available only if Magnum is compiled with |
||||
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features |
||||
for more information. |
||||
*/ |
||||
class MAGNUM_TEXT_EXPORT RendererGL: public Renderer { |
||||
public: |
||||
/**
|
||||
* @brief Construct |
||||
* @param glyphCache Glyph cache to use |
||||
* @param flags Opt-in feature flags |
||||
* |
||||
* Unlike with the @ref Renderer base, the OpenGL implementation needs |
||||
* to have a complete control over memory layout and allocation and |
||||
* thus it isn't possible to supply custom allocators. If you want the |
||||
* control, use @ref Renderer with custom index and vertex allocators |
||||
* and fill a @ref GL::Mesh instance with the data manually. |
||||
*/ |
||||
explicit RendererGL(const AbstractGlyphCache& glyphCache, RendererGLFlags flags = {}); |
||||
|
||||
/**
|
||||
* @brief Construct without creating the internal state and the OpenGL objects |
||||
* |
||||
* The constructed instance is equivalent to moved-from state, i.e. no |
||||
* APIs can be safely called on the object. Useful in cases where you |
||||
* will overwrite the instance later anyway. Move another object over |
||||
* it to make it useful. |
||||
* |
||||
* This function can be safely used for constructing (and later |
||||
* destructing) objects even without any OpenGL context being active. |
||||
* However note that this is a low-level and a potentially dangerous |
||||
* API, see the documentation of @ref NoCreate for alternatives. |
||||
*/ |
||||
explicit RendererGL(NoCreateT) noexcept: Renderer{NoCreate} {} |
||||
|
||||
/** @brief Copying is not allowed */ |
||||
RendererGL(RendererGL&) = delete; |
||||
|
||||
/**
|
||||
* @brief Move constructor |
||||
* |
||||
* Performs a destructive move, i.e. the original object isn't usable |
||||
* afterwards anymore. |
||||
*/ |
||||
RendererGL(RendererGL&&) noexcept; |
||||
|
||||
~RendererGL(); |
||||
|
||||
/** @brief Copying is not allowed */ |
||||
RendererGL& operator=(RendererGL&) = delete; |
||||
|
||||
/** @brief Move assignment */ |
||||
RendererGL& operator=(RendererGL&&) noexcept; |
||||
|
||||
/** @brief Flags */ |
||||
RendererGLFlags flags() const; |
||||
|
||||
/** @brief Mesh containing the rendered index and vertex data */ |
||||
GL::Mesh& mesh(); |
||||
const GL::Mesh& mesh() const; /**< @overload */ |
||||
|
||||
/**
|
||||
* @brief Set index type |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Calls @ref Renderer::setIndexType() and updates @ref mesh() with the |
||||
* rendered index data, if different from before. Compared to |
||||
* @ref Renderer, the default index type is |
||||
* @ref MeshIndexType::UnsignedShort, not |
||||
* @ref MeshIndexType::UnsignedByte, as use of 8-bit indices is |
||||
* discouraged on contemporary GPUs. |
||||
*/ |
||||
RendererGL& setIndexType(MeshIndexType atLeast); |
||||
|
||||
/**
|
||||
* @brief Reserve capacity for given glyph count |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Calls @ref Renderer::reserve() and updates @ref mesh() with the |
||||
* rendered index data, if different from before. |
||||
*/ |
||||
RendererGL& reserve(UnsignedInt glyphCapacity, UnsignedInt runCapacity); |
||||
|
||||
/**
|
||||
* @brief Clear rendered glyphs, runs and vertices |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Calls @ref Renderer::clear() and additionally also sets @ref mesh() |
||||
* index count to @cpp 0 @ce. |
||||
*/ |
||||
RendererGL& clear(); |
||||
|
||||
/**
|
||||
* @brief Reset internal renderer state |
||||
* @return Reference to self (for method chaining) |
||||
* |
||||
* Calls @ref Renderer::reset(), and additionally also sets @ref mesh() |
||||
* index count to @cpp 0 @ce. |
||||
*/ |
||||
RendererGL& reset(); |
||||
|
||||
/**
|
||||
* @brief Wrap up rendering of all text added so far |
||||
* |
||||
* Calls @ref Renderer::render(), updates @ref mesh() with the newly |
||||
* rendered vertex data and potentially updates also the index data, if |
||||
* different from before. |
||||
*/ |
||||
Containers::Pair<Range2D, Range1Dui> render(); |
||||
|
||||
/* Overloads to remove a WTF factor from method chaining order, and to
|
||||
ensure our render() is called instead of Render::render() */ |
||||
#ifndef DOXYGEN_GENERATING_OUTPUT |
||||
RendererGL& setCursor(const Vector2& cursor) { |
||||
return static_cast<RendererGL&>(Renderer::setCursor(cursor)); |
||||
} |
||||
RendererGL& setAlignment(Alignment alignment) { |
||||
return static_cast<RendererGL&>(Renderer::setAlignment(alignment)); |
||||
} |
||||
RendererGL& setLineAdvance(Float advance) { |
||||
return static_cast<RendererGL&>(Renderer::setLineAdvance(advance)); |
||||
} |
||||
RendererGL& setLayoutDirection(LayoutDirection direction) { |
||||
return static_cast<RendererGL&>(Renderer::setLayoutDirection(direction)); |
||||
} |
||||
|
||||
RendererGL& add(AbstractShaper& shaper, Float size, Containers::StringView text, UnsignedInt begin, UnsignedInt end, Containers::ArrayView<const FeatureRange> features); |
||||
RendererGL& add(AbstractShaper& shaper, Float size, Containers::StringView text, UnsignedInt begin, UnsignedInt end); |
||||
RendererGL& add(AbstractShaper& shaper, Float size, Containers::StringView text, UnsignedInt begin, UnsignedInt end, std::initializer_list<FeatureRange> features); |
||||
RendererGL& add(AbstractShaper& shaper, Float size, Containers::StringView text, Containers::ArrayView<const FeatureRange> features); |
||||
RendererGL& add(AbstractShaper& shaper, Float size, Containers::StringView text); |
||||
RendererGL& add(AbstractShaper& shaper, Float size, Containers::StringView text, std::initializer_list<FeatureRange> features); |
||||
|
||||
Containers::Pair<Range2D, Range1Dui> render(AbstractShaper& shaper, Float size, Containers::StringView text, Containers::ArrayView<const FeatureRange> features); |
||||
Containers::Pair<Range2D, Range1Dui> render(AbstractShaper& shaper, Float size, Containers::StringView text); |
||||
Containers::Pair<Range2D, Range1Dui> render(AbstractShaper& shaper, Float size, Containers::StringView text, std::initializer_list<FeatureRange> features); |
||||
#endif |
||||
|
||||
private: |
||||
struct State; |
||||
}; |
||||
|
||||
}} |
||||
#else |
||||
#error this header is available only in the OpenGL build |
||||
#endif |
||||
|
||||
#endif |
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of Magnum. |
||||
|
||||
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||
2020, 2021, 2022, 2023, 2024, 2025 |
||||
Vladimír Vondruš <mosra@centrum.cz> |
||||
|
||||
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 <Corrade/Containers/String.h> |
||||
#include <Corrade/TestSuite/Tester.h> |
||||
|
||||
#include "Magnum/Text/RendererGL.h" |
||||
|
||||
namespace Magnum { namespace Text { namespace Test { namespace { |
||||
|
||||
struct RendererGL_Test: TestSuite::Tester { |
||||
explicit RendererGL_Test(); |
||||
|
||||
void debugFlag(); |
||||
void debugFlags(); |
||||
|
||||
void constructNoCreate(); |
||||
}; |
||||
|
||||
RendererGL_Test::RendererGL_Test() { |
||||
addTests({&RendererGL_Test::debugFlag, |
||||
&RendererGL_Test::debugFlags, |
||||
|
||||
&RendererGL_Test::constructNoCreate}); |
||||
} |
||||
|
||||
void RendererGL_Test::debugFlag() { |
||||
Containers::String out; |
||||
Debug{&out} << RendererGLFlag::GlyphPositionsClusters << RendererGLFlag(0xca); |
||||
CORRADE_COMPARE(out, "Text::RendererGLFlag::GlyphPositionsClusters Text::RendererGLFlag(0xca)\n"); |
||||
} |
||||
|
||||
void RendererGL_Test::debugFlags() { |
||||
Containers::String out; |
||||
Debug{&out} << (RendererGLFlag::GlyphPositionsClusters|RendererGLFlag(0xf0)) << RendererGLFlags{}; |
||||
CORRADE_COMPARE(out, "Text::RendererGLFlag::GlyphPositionsClusters|Text::RendererGLFlag(0xf0) Text::RendererGLFlags{}\n"); |
||||
} |
||||
|
||||
void RendererGL_Test::constructNoCreate() { |
||||
RendererGL renderer{NoCreate}; |
||||
|
||||
/* Shouldn't crash */ |
||||
CORRADE_VERIFY(true); |
||||
|
||||
/* Implicit construction is not allowed */ |
||||
CORRADE_VERIFY(!std::is_convertible<NoCreateT, RendererGL>::value); |
||||
} |
||||
|
||||
}}}} |
||||
|
||||
CORRADE_TEST_MAIN(Magnum::Text::Test::RendererGL_Test) |
||||
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue