core: rename mbgl directories to mln (#4511)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Bart Louwers
2026-08-22 20:50:14 +02:00
committed by GitHub
parent 06fcf13d42
commit 2a8ebc4906
1924 changed files with 10489 additions and 10489 deletions

View File

@@ -0,0 +1,69 @@
#pragma once
#include <cstdint>
#include <cstdlib>
#include <memory>
namespace mln {
namespace gfx {
class Backend {
public:
/// @brief The active graphics API/backend type.
enum class Type : uint8_t {
OpenGL, ///< The OpenGL API backend
Metal, ///< The Metal API backend
Vulkan, ///< The Vulkan API backend
WebGPU, ///< The WebGPU API backend
TYPE_MAX, ///< Not a valid backend type, used to determine the number
///< of available backends (ie for array allocation).
};
#if MLN_RENDER_BACKEND_WEBGPU
static constexpr Type DefaultType = Type::WebGPU;
#elif MLN_RENDER_BACKEND_METAL
static constexpr Type DefaultType = Type::Metal;
#elif MLN_RENDER_BACKEND_VULKAN
static constexpr Type DefaultType = Type::Vulkan;
#else // assume MLN_RENDER_BACKEND_OPENGL
static constexpr Type DefaultType = Type::OpenGL;
#endif
static void SetType(const Type value) {
if (Value(value) != value) {
abort(); // SetType must be called prior to any GetType calls.
}
}
static Type GetType() { return Value(DefaultType); }
static bool getEnableGPUExpressionEval() { return enableGPUExpressionEval; }
static void setEnableGPUExpressionEval(bool value) { enableGPUExpressionEval = value; }
template <typename T, typename... Args>
static std::unique_ptr<T> Create(Args... args) {
#if MLN_RENDER_BACKEND_WEBGPU
return Create<Type::WebGPU, T, Args...>(std::forward<Args>(args)...);
#elif MLN_RENDER_BACKEND_METAL
return Create<Type::Metal, T, Args...>(std::forward<Args>(args)...);
#elif MLN_RENDER_BACKEND_VULKAN
return Create<Type::Vulkan, T, Args...>(std::forward<Args>(args)...);
#else // assume MLN_RENDER_BACKEND_OPENGL
return Create<Type::OpenGL, T, Args...>(std::forward<Args>(args)...);
#endif
}
private:
template <Type, typename T, typename... Args>
static std::unique_ptr<T> Create(Args...);
static Type Value(Type value) {
static const Type type = value;
return type;
}
static bool enableGPUExpressionEval;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,38 @@
#pragma once
namespace mln {
namespace gfx {
class RendererBackend;
class BackendScope {
public:
/// There are two types of scopes: Creating an "Implicit" scope tells MapLibre
/// Native that the supporting windowing system has already activated the
/// RenderBackend and that no further actions are required. Creating an "Explicit"
/// scope (the default) actually enables the RenderBackend, and disables it when
/// the BackendScope is destroyed.
enum class ScopeType : bool {
Implicit,
Explicit,
};
BackendScope(RendererBackend&, ScopeType = ScopeType::Explicit);
~BackendScope();
// Returns true when there is currently a BackendScope active in this thread.
static bool exists();
private:
void activate();
void deactivate();
BackendScope* priorScope;
BackendScope* nextScope;
RendererBackend& backend;
const ScopeType scopeType;
bool activated = false;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,89 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <mln/util/variant.hpp>
#include <mln/util/color.hpp>
#include <mln/util/hash.hpp>
namespace mln {
namespace gfx {
class ColorMode {
public:
template <ColorBlendEquationType E>
struct ConstantBlend {
static constexpr ColorBlendEquationType equation = E;
static constexpr ColorBlendFactorType srcFactor = ColorBlendFactorType::One;
static constexpr ColorBlendFactorType dstFactor = ColorBlendFactorType::One;
};
template <ColorBlendEquationType E>
struct LinearBlend {
static constexpr ColorBlendEquationType equation = E;
ColorBlendFactorType srcFactor;
ColorBlendFactorType dstFactor;
};
struct Replace {
static constexpr ColorBlendEquationType equation = ColorBlendEquationType::Add;
static constexpr ColorBlendFactorType srcFactor = ColorBlendFactorType::One;
static constexpr ColorBlendFactorType dstFactor = ColorBlendFactorType::Zero;
};
using Add = LinearBlend<ColorBlendEquationType::Add>;
using Subtract = LinearBlend<ColorBlendEquationType::Subtract>;
using ReverseSubtract = LinearBlend<ColorBlendEquationType::ReverseSubtract>;
using BlendFunction = variant<Replace, Add, Subtract, ReverseSubtract>;
BlendFunction blendFunction;
Color blendColor;
struct Mask {
bool r;
bool g;
bool b;
bool a;
};
Mask mask;
static ColorMode disabled() {
return {.blendFunction = Replace{}, .blendColor = {}, .mask = {.r = false, .g = false, .b = false, .a = false}};
}
static ColorMode unblended() {
return {.blendFunction = Replace{}, .blendColor = {}, .mask = {.r = true, .g = true, .b = true, .a = true}};
}
static ColorMode alphaBlended() {
return {.blendFunction = Add{ColorBlendFactorType::One, ColorBlendFactorType::OneMinusSrcAlpha},
.blendColor = {},
.mask = {.r = true, .g = true, .b = true, .a = true}};
}
static ColorMode additive() {
return {.blendFunction = Add{ColorBlendFactorType::One, ColorBlendFactorType::One},
.blendColor = {},
.mask = {.r = true, .g = true, .b = true, .a = true}};
}
std::size_t hash() const {
return mln::util::hash(blendFunction.which(),
blendColor.r,
blendColor.g,
blendColor.b,
blendColor.a,
mask.r,
mask.g,
mask.b,
mask.a);
}
};
constexpr bool operator!=(const ColorMode::Mask& a, const ColorMode::Mask& b) {
return a.r != b.r || a.g != b.g || a.b != b.b || a.a != b.a;
}
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,39 @@
#pragma once
#include <mln/gfx/debug_group.hpp>
#include <memory>
#include <string_view>
namespace mln {
namespace gfx {
class RenderPassDescriptor;
class RenderPass;
class Renderable;
class UploadPass;
class CommandEncoder {
protected:
explicit CommandEncoder() = default;
friend class DebugGroup<CommandEncoder>;
virtual void pushDebugGroup(const char* name) = 0;
virtual void popDebugGroup() = 0;
public:
virtual ~CommandEncoder() = default;
CommandEncoder(const CommandEncoder&) = delete;
CommandEncoder& operator=(const CommandEncoder&) = delete;
DebugGroup<CommandEncoder> createDebugGroup(const char* name) { return {*this, name}; }
// NOLINTNEXTLINE(bugprone-suspicious-stringview-data-usage)
DebugGroup<CommandEncoder> createDebugGroup(std::string_view name) { return createDebugGroup(name.data()); }
virtual std::unique_ptr<UploadPass> createUploadPass(const char* name, Renderable&) = 0;
virtual std::unique_ptr<RenderPass> createRenderPass(const char* name, const RenderPassDescriptor&) = 0;
virtual void present(Renderable&) = 0;
};
} // namespace gfx
} // namespace mln

195
include/mln/gfx/context.hpp Normal file
View File

@@ -0,0 +1,195 @@
#pragma once
#include <mln/actor/scheduler.hpp>
#include <mln/gfx/backend.hpp>
#include <mln/gfx/command_encoder.hpp>
#include <mln/gfx/context_observer.hpp>
#include <mln/gfx/draw_scope.hpp>
#include <mln/gfx/renderbuffer.hpp>
#include <mln/gfx/rendering_stats.hpp>
#include <mln/gfx/types.hpp>
#include <mln/gfx/uniform_buffer.hpp>
#include <memory>
#include <string>
#include <mutex>
#include <shared_mutex>
namespace mln {
class PaintParameters;
class ProgramParameters;
class TileLayerGroup;
class LayerGroup;
class RenderTarget;
using TileLayerGroupPtr = std::shared_ptr<TileLayerGroup>;
using LayerGroupPtr = std::shared_ptr<LayerGroup>;
using RenderTargetPtr = std::shared_ptr<RenderTarget>;
namespace gfx {
class DepthMode;
class ColorMode;
class OffscreenTexture;
class ShaderRegistry;
class Drawable;
class DrawableBuilder;
class DynamicTexture;
class ShaderProgramBase;
class Texture2D;
class VertexAttributeArray;
using DrawablePtr = std::shared_ptr<Drawable>;
using DynamicTexturePtr = std::shared_ptr<DynamicTexture>;
using ShaderProgramBasePtr = std::shared_ptr<ShaderProgramBase>;
using Texture2DPtr = std::shared_ptr<Texture2D>;
using UniformBufferPtr = std::shared_ptr<UniformBuffer>;
using UniqueDrawableBuilder = std::unique_ptr<DrawableBuilder>;
using VertexAttributeArrayPtr = std::shared_ptr<VertexAttributeArray>;
namespace {
ContextObserver nullObserver;
}
class Context {
protected:
Context(uint32_t maximumVertexBindingCount_)
: maximumVertexBindingCount(maximumVertexBindingCount_),
observer(&nullObserver) {}
public:
static constexpr const uint32_t minimumRequiredVertexBindingCount = 8;
const uint32_t maximumVertexBindingCount;
Context(Context&&) = delete;
Context(const Context&) = delete;
Context& operator=(Context&& other) = delete;
Context& operator=(const Context& other) = delete;
virtual ~Context() = default;
virtual void setObserver(ContextObserver* observer_) { observer = observer_ ? observer_ : &nullObserver; }
virtual void beginFrame() = 0;
virtual void endFrame() = 0;
/// Called at the end of a frame.
virtual void performCleanup() = 0;
/// Called when the app receives a memory warning and before it goes to the background.
virtual void reduceMemoryUsage() = 0;
virtual std::unique_ptr<OffscreenTexture> createOffscreenTexture(Size, TextureChannelDataType) = 0;
template <RenderbufferPixelType pixelType>
Renderbuffer<pixelType> createRenderbuffer(const Size size) {
return {size, createRenderbufferResource(pixelType, size)};
}
DrawScope createDrawScope() { return DrawScope{createDrawScopeResource()}; }
virtual std::unique_ptr<CommandEncoder> createCommandEncoder() = 0;
gfx::RenderingStats& renderingStats() { return stats; }
const gfx::RenderingStats& renderingStats() const { return stats; }
void threadSafeAccessRenderingStats(const std::function<void(RenderingStats&)>& function) {
std::scoped_lock lock(renderingStatsMutex);
function(stats);
}
RenderingStats threadSafeCopyRenderingStats() {
std::shared_lock lock(renderingStatsMutex);
return stats;
}
#ifndef NDEBUG
virtual void visualizeStencilBuffer() = 0;
virtual void visualizeDepthBuffer(float depthRangeSize) = 0;
#endif
virtual void clearStencilBuffer(int32_t) = 0;
/// Sets dirty state
virtual void setDirtyState() = 0;
/// Create a new vertex attribute array
virtual gfx::VertexAttributeArrayPtr createVertexAttributeArray() const = 0;
/// Create a new drawable builder
virtual UniqueDrawableBuilder createDrawableBuilder(std::string name) = 0;
/// Create a new uniform buffer
/// @param data The data to copy, may be `nullptr`
/// @param size The size of the buffer
/// @param persistent Performance hint, optimize for few or many uses
virtual UniformBufferPtr createUniformBuffer(const void* data,
std::size_t size,
bool persistent = false,
bool ssbo = false) = 0;
virtual UniqueUniformBufferArray createLayerUniformBufferArray() = 0;
/// Get the generic shader with the specified name
virtual gfx::ShaderProgramBasePtr getGenericShader(gfx::ShaderRegistry&, const std::string& name) = 0;
/// Create a tile layer group implementation
virtual TileLayerGroupPtr createTileLayerGroup(int32_t layerIndex,
std::size_t initialCapacity,
std::string name) = 0;
/// Create a layer group implementation
virtual LayerGroupPtr createLayerGroup(int32_t layerIndex, std::size_t initialCapacity, std::string name) = 0;
/// Create a texture
virtual Texture2DPtr createTexture2D() = 0;
/// Create a dynamic texture
virtual DynamicTexturePtr createDynamicTexture(Size size, TexturePixelType pixelType) = 0;
/// Create a render target
virtual RenderTargetPtr createRenderTarget(const Size size, const TextureChannelDataType type) = 0;
/// Resets the context state to defaults
virtual void resetState(gfx::DepthMode depthMode, gfx::ColorMode colorMode) = 0;
/// Update the uniform buffer with the provided data if it already exists, otherwise create it.
/// @return True if the buffer was created, false if it was updated
virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&,
const void* data,
std::size_t size,
bool persistent = false) = 0;
/// `emplaceOrUpdateUniformBuffer` with type inference
template <typename T>
bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& ptr, const T* data, bool persistent = false)
requires(!std::is_pointer_v<T>)
{
return emplaceOrUpdateUniformBuffer(ptr, data, sizeof(T), persistent);
}
/// Get the global uniform buffers
virtual const gfx::UniformBufferArray& getGlobalUniformBuffers() const = 0;
/// Get the mutable global uniform buffer array
virtual gfx::UniformBufferArray& mutableGlobalUniformBuffers() = 0;
/// Bind the global uniform buffers
virtual void bindGlobalUniformBuffers(gfx::RenderPass&) const noexcept = 0;
/// Unbind the global uniform buffers
virtual void unbindGlobalUniformBuffers(gfx::RenderPass&) const noexcept = 0;
protected:
virtual std::unique_ptr<RenderbufferResource> createRenderbufferResource(RenderbufferPixelType, Size) = 0;
virtual std::unique_ptr<DrawScopeResource> createDrawScopeResource() = 0;
std::shared_mutex renderingStatsMutex;
gfx::RenderingStats stats;
ContextObserver* observer;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,20 @@
#pragma once
#include <mln/gfx/backend.hpp>
#include <mln/shaders/shader_source.hpp>
#include <string>
namespace mln {
namespace gfx {
class ContextObserver {
public:
virtual void onPreCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {}
virtual void onPostCompileShader(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {}
virtual void onShaderCompileFailed(shaders::BuiltIn, gfx::Backend::Type, const std::string&) {}
virtual void onRenderError(std::exception_ptr) {}
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,30 @@
#pragma once
namespace mln {
namespace gfx {
template <typename T>
class DebugGroup {
public:
DebugGroup(T& scope_, const char* name)
: scope(&scope_) {
scope->pushDebugGroup(name);
}
DebugGroup(DebugGroup&& rhs) noexcept
: scope(rhs.scope) {
rhs.scope = nullptr;
}
~DebugGroup() {
if (scope) {
scope->popDebugGroup();
}
}
private:
T* scope;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,27 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <mln/util/range.hpp>
namespace mln {
namespace gfx {
class DepthMode {
public:
DepthFunctionType func;
DepthMaskType mask;
#if MLN_RENDER_BACKEND_OPENGL
Range<float> range;
#endif
#if MLN_RENDER_BACKEND_OPENGL
static DepthMode disabled() { return DepthMode{DepthFunctionType::Always, DepthMaskType::ReadOnly, {0.0, 1.0}}; }
#else
static DepthMode disabled() {
return DepthMode{.func = DepthFunctionType::Always, .mask = DepthMaskType::ReadOnly};
}
#endif
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,94 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <cassert>
#include <cstddef>
#include <type_traits>
namespace mln {
namespace gfx {
class DrawMode {
protected:
DrawMode(DrawModeType type_, float size_)
: type(type_),
size(size_) {}
public:
const DrawModeType type;
const float size;
};
template <class>
struct BufferGroupSizeOf;
template <class>
struct PrimitiveTypeOf;
class Points : public DrawMode {
public:
explicit Points(float pointSize_)
: DrawMode(DrawModeType::Points, pointSize_) {
assert(size > 0);
}
};
template <>
struct BufferGroupSizeOf<Points> : std::integral_constant<std::size_t, 1> {};
template <>
struct PrimitiveTypeOf<Points> : std::integral_constant<PrimitiveType, PrimitiveType::Point> {};
class Lines : public DrawMode {
public:
explicit Lines(float lineWidth_)
: DrawMode(DrawModeType::Lines, lineWidth_) {
assert(size > 0);
}
};
template <>
struct BufferGroupSizeOf<Lines> : std::integral_constant<std::size_t, 2> {};
template <>
struct PrimitiveTypeOf<Lines> : std::integral_constant<PrimitiveType, PrimitiveType::Line> {};
// LineStrip is a form of "Line" rendering, but the element buffer
// cannot be grouped into logical elements beyond a single Point.
class LineStrip : public DrawMode {
public:
explicit LineStrip(float lineWidth_)
: DrawMode(DrawModeType::LineStrip, lineWidth_) {
assert(size > 0);
}
};
template <>
struct BufferGroupSizeOf<LineStrip> : std::integral_constant<std::size_t, 1> {};
template <>
struct PrimitiveTypeOf<LineStrip> : std::integral_constant<PrimitiveType, PrimitiveType::Line> {};
class Triangles : public DrawMode {
public:
explicit Triangles()
: DrawMode(DrawModeType::Triangles, 0) {}
};
template <>
struct BufferGroupSizeOf<Triangles> : std::integral_constant<std::size_t, 3> {};
template <>
struct PrimitiveTypeOf<Triangles> : std::integral_constant<PrimitiveType, PrimitiveType::Triangle> {};
// TriangleStrip is a form of "Triangle" rendering, but the element buffer
// cannot be grouped into logical elements beyond a single Point.
class TriangleStrip : public DrawMode {
public:
explicit TriangleStrip()
: DrawMode(DrawModeType::TriangleStrip, 0) {}
};
template <>
struct BufferGroupSizeOf<TriangleStrip> : std::integral_constant<std::size_t, 1> {};
template <>
struct PrimitiveTypeOf<TriangleStrip> : std::integral_constant<PrimitiveType, PrimitiveType::Triangle> {};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,33 @@
#pragma once
#include <memory>
#include <cassert>
namespace mln {
namespace gfx {
class DrawScopeResource {
protected:
DrawScopeResource() = default;
public:
virtual ~DrawScopeResource() = default;
};
class DrawScope {
public:
explicit DrawScope(std::unique_ptr<DrawScopeResource> resource_)
: resource(std::move(resource_)) {}
template <typename T = DrawScopeResource>
T& getResource() const {
assert(resource);
return static_cast<T&>(*resource);
}
protected:
std::unique_ptr<DrawScopeResource> resource;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,343 @@
#pragma once
#include <mln/gfx/drawable_data.hpp>
#include <mln/gfx/texture2d.hpp>
#include <mln/gfx/uniform_buffer.hpp>
#include <mln/tile/tile_id.hpp>
#include <mln/util/color.hpp>
#include <mln/util/containers.hpp>
#include <mln/util/identity.hpp>
#include <mln/util/monotonic_timer.hpp>
#include <mln/util/traits.hpp>
#include <mln/util/immutable.hpp>
#include <cstdint>
#include <cstddef>
#include <memory>
#include <optional>
#include <vector>
namespace mln {
class Bucket;
class Color;
class LayerTweaker;
class PaintParameters;
class PaintPropertyBindersBase;
enum class RenderPass : uint8_t;
class RenderTile;
class SegmentBase;
using LayerTweakerPtr = std::shared_ptr<LayerTweaker>;
using RenderTiles = std::shared_ptr<const std::vector<std::reference_wrapper<const RenderTile>>>;
namespace gfx {
class ColorMode;
class CullFaceMode;
enum class DepthMaskType : bool;
class DrawableTweaker;
class DrawMode;
class IndexVectorBase;
class ShaderProgramBase;
class VertexAttributeArray;
using DrawPriority = int64_t;
using DrawableTweakerPtr = std::shared_ptr<DrawableTweaker>;
using IndexVectorBasePtr = std::shared_ptr<IndexVectorBase>;
using ShaderProgramBasePtr = std::shared_ptr<ShaderProgramBase>;
using Texture2DPtr = std::shared_ptr<Texture2D>;
using VertexAttributeArrayPtr = std::shared_ptr<VertexAttributeArray>;
class Drawable {
public:
/// @brief Array of textures to bind
using Textures = std::array<gfx::Texture2DPtr, shaders::maxTextureCountPerShader>;
protected:
Drawable(std::string name);
public:
/// Drawable constructor
virtual ~Drawable();
struct DrawSegment;
using UniqueDrawSegment = std::unique_ptr<DrawSegment>;
/// Get drawable's ID
const util::SimpleIdentity& getID() const { return uniqueID; }
/// Draw the drawable
virtual void draw(PaintParameters&) const = 0;
/// Drawable name is used for debugging and troubleshooting
const std::string& getName() const { return name; }
/// Set drawable name
void setName(std::string value) { name = std::move(value); }
/// Which shader to use when rendering this drawable
const gfx::ShaderProgramBasePtr& getShader() const { return shader; }
/// Set the shader to be used
virtual void setShader(gfx::ShaderProgramBasePtr value) { shader = std::move(value); }
/// The pass on which we'll be rendered
mln::RenderPass getRenderPass() const { return renderPass; }
/// Sets the render passes
void setRenderPass(mln::RenderPass value) { renderPass = value; }
/// Test whether to draw this drawable in a given render pass.
bool hasRenderPass(const mln::RenderPass value) const {
return (mln::underlying_type(renderPass) & mln::underlying_type(value)) != 0;
}
/// Test whether to draw this drawable in a given render pass.
/// If multiple render pass bits are set, all must be present.
bool hasAllRenderPasses(const mln::RenderPass value) const {
const auto underlying_value = mln::underlying_type(value);
return (mln::underlying_type(renderPass) & underlying_value) == underlying_value;
}
/// Width for lines
int32_t getLineWidth() const { return lineWidth; }
/// Set line width
virtual void setLineWidth(int32_t value) { lineWidth = value; }
/// @brief Get the texture at the given internal ID.
const gfx::Texture2DPtr& getTexture(size_t id) const;
/// @brief Set the collection of textures bound to this drawable
/// @param textures_ A Textures collection to set
virtual void setTextures(const Textures& textures_) noexcept { textures = textures_; }
virtual void setTextures(Textures&& textures_) noexcept { textures = std::move(textures_); }
/// @brief Attach the given texture to this drawable at the given internal ID.
/// @param texture Texture2D instance
/// @param id Internal ID of the texture.
virtual void setTexture(gfx::Texture2DPtr texture, size_t id);
/// Whether the drawble should be drawn
bool getEnabled() const { return enabled; }
/// Sets whether the drawble should be drawn
void setEnabled(bool value) { enabled = value; }
/// Whether to render to the color target
bool getEnableColor() const { return enableColor; }
/// Set whether to render to the color target
virtual void setEnableColor(bool value) { enableColor = value; }
/// Whether to do stenciling (based on the Tile ID or 3D)
bool getEnableStencil() const { return enableStencil; }
/// Set stencil usage
virtual void setEnableStencil(bool value) { enableStencil = value; }
/// not used for anything yet
DrawPriority getDrawPriority() const { return drawPriority; }
void setDrawPriority(DrawPriority value) { drawPriority = value; }
/// Whether to enable depth testing
bool getEnableDepth() const { return enableDepth; }
virtual void setEnableDepth(bool value) { enableDepth = value; }
/// Determines depth range within the layer for 2D drawables
int32_t getSubLayerIndex() const { return subLayerIndex; }
/// Set sub-layer index
virtual void setSubLayerIndex(int32_t value) { subLayerIndex = value; }
/// Depth writability for 2D drawables
DepthMaskType getDepthType() const { return depthType; }
/// Set depth type
virtual void setDepthType(DepthMaskType value) { depthType = value; }
/// Uses 3D depth mode
bool getIs3D() const { return is3D; }
/// Set 3D mode
void setIs3D(bool value) { is3D = value; }
/// True if this is a custom drawable
bool getIsCustom() const { return isCustom; }
/// Sets custom status for this drawable
void setIsCustom(bool value) { isCustom = value; }
/// Get the ID of the tile that this drawable represents, if any
const std::optional<OverscaledTileID>& getTileID() const { return tileID; }
/// Set the ID of the tile that this drawable represents
void setTileID(const OverscaledTileID& value) { tileID = value; }
/// Get cull face mode
const gfx::CullFaceMode& getCullFaceMode() const;
/// Set cull face mode
virtual void setCullFaceMode(const gfx::CullFaceMode&);
/// Get color mode
const gfx::ColorMode& getColorMode() const;
/// Set color mode
virtual void setColorMode(const gfx::ColorMode&);
/// Get the vertex attributes that override default values in the shader program
const gfx::VertexAttributeArrayPtr& getVertexAttributes() const noexcept { return vertexAttributes; }
/// Set vertex attribute array
void setVertexAttributes(gfx::VertexAttributeArrayPtr value) noexcept {
vertexAttributes = std::move(value);
// The attribute bindings need to be rebuilt, we can't rely on the update
// time check as these new values may not have been modified recently.
attributeUpdateTime.reset();
}
/// Update vertices, indices, and segments
virtual void updateVertexAttributes(gfx::VertexAttributeArrayPtr,
std::size_t vertexCount,
gfx::DrawMode,
gfx::IndexVectorBasePtr,
const SegmentBase* segments,
std::size_t segmentCount) = 0;
/// Get the instance attributes
const gfx::VertexAttributeArrayPtr& getInstanceAttributes() const noexcept { return instanceAttributes; }
/// Set instance attribute array
void setInstanceAttributes(gfx::VertexAttributeArrayPtr value) noexcept { instanceAttributes = std::move(value); }
/// Provide raw data for vertices. Incompatible with adding primitives
virtual void setVertices(std::vector<uint8_t>&&, std::size_t, AttributeDataType) = 0;
/// Provide raw indexes and segments
void setIndexData(std::vector<std::uint16_t> indexes, std::vector<UniqueDrawSegment>);
/// Set shared indexes and segments
virtual void setIndexData(gfx::IndexVectorBasePtr, std::vector<UniqueDrawSegment>) = 0;
/// Get the tweakers attached to this drawable
const std::vector<DrawableTweakerPtr>& getTweakers() const { return tweakers; }
/// Add a tweaker to this drawable
void addTweaker(DrawableTweakerPtr value) { tweakers.emplace_back(std::move(value)); }
/// Set the entire tweaker collection
void setTweakers(std::vector<DrawableTweakerPtr> value) { tweakers = std::move(value); }
/// Clear the tweaker collection
void clearTweakers() { tweakers.clear(); }
/// Get the uniform buffers attached to this drawable
virtual const gfx::UniformBufferArray& getUniformBuffers() const = 0;
/// Get the mutable uniform buffer array
virtual gfx::UniformBufferArray& mutableUniformBuffers() = 0;
/// Get drawable data
const UniqueDrawableData& getData() const { return drawableData; }
/// Set drawable data
void setData(UniqueDrawableData&& value) { drawableData = std::move(value); }
/// Set drawable user-defined type
void setType(std::size_t type_) { type = type_; }
/// Get drawable user-defined type
size_t getType() const { return type; }
void setUBOIndex(uint32_t uboIndex_) { uboIndex = uboIndex_; }
uint32_t getUBOIndex() const { return uboIndex; }
/// Associate the drawable with a layer tweaker. This is used to manage the lifetime of the tweaker.
void setLayerTweaker(LayerTweakerPtr tweaker) { layerTweaker = std::move(tweaker); }
const LayerTweakerPtr& getLayerTweaker() const { return layerTweaker; }
/// Get origin point
const std::optional<mln::Point<double>>& getOrigin() const { return origin; }
/// Set origin point
void setOrigin(std::optional<Point<double>> p) { origin = std::move(p); }
/// Get the property binders used for property updates
PaintPropertyBindersBase* getBinders();
const PaintPropertyBindersBase* getBinders() const;
/// Set the property binders used for property updates
void setBinders(std::shared_ptr<Bucket>, PaintPropertyBindersBase*);
const RenderTile* getRenderTile() const;
const std::shared_ptr<Bucket>& getBucket() const;
void setRenderTile(Immutable<std::vector<RenderTile>>, const RenderTile*);
const std::chrono::duration<double> createTime = util::MonotonicTimer::now();
std::optional<std::chrono::duration<double>> getAttributeUpdateTime() const { return attributeUpdateTime; }
protected:
bool enabled = true;
bool enableColor = true;
bool enableStencil = false;
bool enableDepth = true;
bool is3D = false;
bool isCustom = false;
std::string name;
const util::SimpleIdentity uniqueID;
gfx::ShaderProgramBasePtr shader;
mln::RenderPass renderPass;
std::optional<OverscaledTileID> tileID;
DrawPriority drawPriority = 0;
int32_t lineWidth = 1;
int32_t subLayerIndex = 0;
DepthMaskType depthType; // = DepthMaskType::ReadOnly;
UniqueDrawableData drawableData{};
gfx::VertexAttributeArrayPtr vertexAttributes;
gfx::VertexAttributeArrayPtr instanceAttributes;
std::optional<std::chrono::duration<double>> attributeUpdateTime;
struct Impl;
std::unique_ptr<Impl> impl;
Textures textures;
std::vector<DrawableTweakerPtr> tweakers;
LayerTweakerPtr layerTweaker;
std::size_t type = 0;
std::optional<mln::Point<double>> origin;
uint32_t uboIndex = 0;
};
using DrawablePtr = std::shared_ptr<Drawable>;
using UniqueDrawable = std::unique_ptr<Drawable>;
/// Comparator for sorting drawable pointers primarily by draw priority
struct DrawableLessByPriority {
DrawableLessByPriority(bool descending = false)
: desc(descending) {}
bool operator()(const Drawable& left, const Drawable& right) const {
const auto& a = desc ? right : left;
const auto& b = desc ? left : right;
if (a.getDrawPriority() != b.getDrawPriority()) {
return a.getDrawPriority() < b.getDrawPriority();
}
return a.getID() < b.getID();
}
bool operator()(const Drawable* left, const Drawable* right) const { return operator()(*left, *right); }
bool operator()(const UniqueDrawable& left, const UniqueDrawable& right) const { return operator()(*left, *right); }
bool operator()(const DrawablePtr& left, const DrawablePtr& right) const {
const auto& a = desc ? right : left;
const auto& b = desc ? left : right;
// nulls are less than non-nulls
return (a && b) ? operator()(*a, *b) : (!a && b);
}
private:
bool desc;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,64 @@
#pragma once
#include <mln/gfx/drawable_tweaker.hpp>
#include <mln/style/types.hpp>
#include <mln/renderer/layers/render_symbol_layer.hpp>
#include <memory>
#include <string>
namespace mln {
class TileAtlasTextures;
using TileAtlasTexturesPtr = std::shared_ptr<TileAtlasTextures>;
class PaintParameters;
namespace gfx {
class Drawable;
/**
Tweaker that applies the latest values from a retained `TileAtlasTexturesPtr`
*/
class DrawableAtlasesTweaker : public gfx::DrawableTweaker {
public:
DrawableAtlasesTweaker(TileAtlasTexturesPtr atlases_,
const std::optional<size_t> iconTextureId_,
const std::optional<size_t> glyphTextureId_,
bool isText_,
const bool sdfIcons_,
const style::AlignmentType rotationAlignment_,
const bool iconScaled_,
const bool textSizeIsZoomConstant_)
: atlases(std::move(atlases_)),
iconTextureId(iconTextureId_),
glyphTextureId(glyphTextureId_),
isText(isText_),
sdfIcons(sdfIcons_),
rotationAlignment(rotationAlignment_),
iconScaled(iconScaled_),
textSizeIsZoomConstant(textSizeIsZoomConstant_) {
assert(iconTextureId_ != glyphTextureId_);
}
~DrawableAtlasesTweaker() override = default;
void init(Drawable&) override;
void execute(Drawable&, PaintParameters&) override;
protected:
void setupTextures(Drawable&, const bool);
TileAtlasTexturesPtr atlases;
std::optional<size_t> iconTextureId;
std::optional<size_t> glyphTextureId;
bool isText;
const bool sdfIcons;
const style::AlignmentType rotationAlignment;
const bool iconScaled;
const bool textSizeIsZoomConstant;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,258 @@
#pragma once
#include <mln/gfx/drawable.hpp>
#include <mln/gfx/types.hpp>
#include <mln/tile/geometry_tile_data.hpp>
#include <mln/style/types.hpp>
#include <mln/gfx/polyline_generator.hpp>
#include <array>
#include <memory>
#include <vector>
namespace mln {
class SegmentBase;
namespace gfx {
class DrawableTweaker;
class IndexVectorBase;
class ShaderProgramBase;
class VertexAttributeArray;
using DrawableTweakerPtr = std::shared_ptr<DrawableTweaker>;
using IndexVectorBasePtr = std::shared_ptr<IndexVectorBase>;
using ShaderProgramBasePtr = std::shared_ptr<ShaderProgramBase>;
using Texture2DPtr = std::shared_ptr<Texture2D>;
/**
Base class for drawable builders, which construct Drawables from primitives
*/
class DrawableBuilder {
protected:
DrawableBuilder(std::string name);
public:
/// Destructor
virtual ~DrawableBuilder();
/// Get the drawable we're currently working on, if any
UniqueDrawable& getCurrentDrawable(bool createIfNone);
/// Close the current drawable, using a new one for any further work
void flush(gfx::Context& context);
/// Get all the completed drawables
const std::vector<UniqueDrawable>& getDrawables() const { return drawables; }
/// Get all the completed drawables and release ownership
std::vector<UniqueDrawable> clearDrawables() {
std::vector<UniqueDrawable> v = std::move(drawables);
drawables = std::vector<UniqueDrawable>{};
return v;
}
/// Get the ID of the drawable we're currently working on, if any
util::SimpleIdentity getDrawableId();
/// Whether the drawble should be drawn
bool getEnabled() const { return enabled; }
/// Set whether the drawable should be enabled
void setEnabled(bool value) { enabled = value; }
/// The pass on which we'll be rendered
mln::RenderPass getRenderPass() const { return renderPass; }
/// Set the pass on which the drawable will render
void setRenderPass(mln::RenderPass value) { renderPass = value; }
/// The draw priority assigned to generated drawables
DrawPriority getDrawPriority() const;
/// Set drawable's draw priority
void setDrawPriority(DrawPriority);
/// Determines depth range within the layer for 2D drawables
int32_t getSubLayerIndex() const { return subLayerIndex; }
void setSubLayerIndex(int32_t value) { subLayerIndex = value; }
/// Whether to enable depth testing
bool getEnableDepth() { return enableDepth; }
void setEnableDepth(bool value) { enableDepth = value; }
/// Depth writability for 2D drawables
DepthMaskType getDepthType() const { return depthType; }
/// Set depth type
void setDepthType(DepthMaskType value) { depthType = value; }
/// Uses 3D depth mode
bool getIs3D() const { return is3D; }
/// Set 3D mode
void setIs3D(bool value) { is3D = value; }
/// Set the draw priority on all drawables including those already generated
void resetDrawPriority(DrawPriority);
/// Width for lines
float getLineWidth() const { return lineWidth; }
/// Set line width
void setLineWidth(float value) { lineWidth = value; }
/// Whether to render to the color target
bool getEnableColor() const { return enableColor; }
/// Set whether to render to the color target
void setEnableColor(bool value) { enableColor = value; }
/// Whether to do stenciling (based on the Tile ID or 3D)
bool getEnableStencil() const { return enableStencil; }
/// Set stencil usage
void setEnableStencil(bool value) { enableStencil = value; }
/// Get color mode
const gfx::ColorMode& getColorMode() const;
/// Set color mode
void setColorMode(const gfx::ColorMode& value);
/// Get cull face mode
const gfx::CullFaceMode& getCullFaceMode() const;
/// Set cull face mode
void setCullFaceMode(const gfx::CullFaceMode& value);
/// Which shader to use when rendering emitted drawables
const gfx::ShaderProgramBasePtr& getShader() const { return shader; }
/// Set the shader to be used
void setShader(gfx::ShaderProgramBasePtr value) { shader = std::move(value); }
/// Set the name given to new drawables
void setDrawableName(std::string value) { drawableName = std::move(value); }
/// The attribute id for vertex/position attribute
void setVertexAttrId(const size_t id) { vertexAttrId = id; }
/// @brief Get the texture at the given internal ID.
const gfx::Texture2DPtr& getTexture(size_t id) const;
/// @brief Attach the given texture at the given internal ID.
/// @param texture Texture2D instance
/// @param id Internal ID of the texture.
void setTexture(const gfx::Texture2DPtr&, size_t id);
/// Add a tweaker to emitted drawable
void addTweaker(DrawableTweakerPtr value) { tweakers.emplace_back(std::move(value)); }
/// Clear the tweaker collection
void clearTweakers() { tweakers.clear(); }
/// Get the vertex attributes that override default values in the shader program
VertexAttributeArrayPtr& getVertexAttributes() { return vertexAttrs; }
const VertexAttributeArrayPtr& getVertexAttributes() const { return vertexAttrs; }
/// A set of attribute values to be added for each vertex
void setVertexAttributes(VertexAttributeArrayPtr attrs) { vertexAttrs = std::move(attrs); }
/// Get the instance attributes that override default values in the shader program
VertexAttributeArrayPtr& getInstanceAttributes() { return instanceAttrs; }
const VertexAttributeArrayPtr& getInstanceAttributes() const { return instanceAttrs; }
/// A set of attribute values to be added for each instance
void setInstanceAttributes(VertexAttributeArrayPtr attrs) { instanceAttrs = std::move(attrs); }
/// Add some vertex elements, returns the index of the first one added
std::size_t addVertices(const std::vector<std::array<int16_t, 2>>& vertices,
std::size_t vertexOffset,
std::size_t vertexLength);
/// Provide raw data for vertices. Incompatible with adding primitives
void setRawVertices(std::vector<uint8_t>&&, std::size_t, AttributeDataType);
/// Set indexes and segments
void setSegments(gfx::DrawMode, std::vector<uint16_t> indexes, const SegmentBase*, std::size_t segmentCount);
/// Set shared indices and segments
void setSegments(gfx::DrawMode, gfx::IndexVectorBasePtr, const SegmentBase*, std::size_t segmentCount);
/// Create a segment wrapper
virtual std::unique_ptr<Drawable::DrawSegment> createSegment(gfx::DrawMode, SegmentBase&&) = 0;
/// Add a triangle
void addTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2);
/// Add another triangle based on the previous two points
void appendTriangle(int16_t x0, int16_t y0);
/// Add a rectangle consisting of two triangles
void addQuad(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
/// Add lines based on existing vertices
void addLines(const std::vector<uint16_t>& indexes,
std::size_t indexOffset,
std::size_t indexLength,
std::size_t baseIndex = 0);
/// Add triangles based on existing vertices
void addTriangles(const std::vector<uint16_t>& indexes,
std::size_t indexOffset,
std::size_t indexLength,
std::size_t baseIndex = 0);
/// Add a polyline. If the last point equals the first it will be closed, otherwise open
void addPolyline(const GeometryCoordinates& coordinates, const gfx::PolylineGeneratorOptions&);
/// Add a polyline in Tile coordinates, using wide vectors.
void addWideVectorPolylineLocal(const GeometryCoordinates& coordinates, const gfx::PolylineGeneratorOptions&);
/// Add a polyline in geographic coordinates, using wide vectors.
void addWideVectorPolylineGlobal(const LineString<double>& coordinates, const gfx::PolylineGeneratorOptions&);
/// return the current vertex count
std::size_t curVertexCount() const;
bool empty() const { return !(drawables.size() || curVertexCount()); }
protected:
/// Create an instance of the appropriate drawable type
virtual UniqueDrawable createDrawable() const = 0;
/// Setup the SDK-specific aspects after all the values are present
virtual void init() = 0;
protected:
std::string name;
std::string drawableName;
std::size_t vertexAttrId;
mln::RenderPass renderPass;
bool enabled = true;
bool enableColor = true;
bool enableStencil = false;
bool enableDepth = true;
bool is3D = false;
float lineWidth = 1.0f;
DrawPriority drawPriority = 0;
int32_t subLayerIndex = 0;
DepthMaskType depthType = DepthMaskType::ReadOnly;
gfx::ShaderProgramBasePtr shader;
UniqueDrawable currentDrawable;
std::vector<UniqueDrawable> drawables;
gfx::Drawable::Textures textures;
std::vector<DrawableTweakerPtr> tweakers;
gfx::VertexAttributeArrayPtr vertexAttrs;
gfx::VertexAttributeArrayPtr instanceAttrs;
std::optional<mln::Point<double>> origin;
class Impl;
std::unique_ptr<Impl> impl;
};
using UniqueDrawableBuilder = std::unique_ptr<DrawableBuilder>;
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,35 @@
#pragma once
#include <mln/gfx/drawable_tweaker.hpp>
#include <mln/style/layers/custom_layer.hpp>
#include <memory>
#include <string>
namespace mln {
class PaintParameters;
namespace gfx {
class Drawable;
/**
Tweaker that enables drawing a CustomLayer via their CustomLayerHost
*/
class DrawableCustomLayerHostTweaker : public gfx::DrawableTweaker {
public:
DrawableCustomLayerHostTweaker(std::shared_ptr<style::CustomLayerHost> host_)
: host(host_) {}
~DrawableCustomLayerHostTweaker() override = default;
void init(Drawable&) override {};
void execute(Drawable&, PaintParameters&) override;
protected:
std::shared_ptr<style::CustomLayerHost> host;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,17 @@
#pragma once
#include <memory>
namespace mln {
namespace gfx {
class DrawableData {
public:
virtual ~DrawableData() = default;
};
using UniqueDrawableData = std::unique_ptr<DrawableData>;
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,28 @@
#pragma once
#include <mln/gfx/draw_mode.hpp>
#include <mln/gfx/drawable.hpp>
#include <mln/shaders/segment.hpp>
namespace mln {
namespace gfx {
struct Drawable::DrawSegment {
DrawSegment(gfx::DrawMode mode_, SegmentBase&& segment_)
: mode(mode_),
segment(std::move(segment_)) {}
virtual ~DrawSegment() = default;
const gfx::DrawMode& getMode() const { return mode; }
SegmentBase& getSegment() { return segment; }
const SegmentBase& getSegment() const { return segment; }
protected:
gfx::DrawMode mode;
SegmentBase segment;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,33 @@
#pragma once
#include <memory>
namespace mln {
class PaintParameters;
namespace gfx {
class Drawable;
/**
Base class for drawable tweakers, which manipulate drawables per frame
*/
class DrawableTweaker {
protected:
DrawableTweaker() = default;
public:
virtual ~DrawableTweaker() = default;
/// Called as the drawable gets added to the layer
virtual void init(Drawable&) = 0;
/// Called just before rendering
virtual void execute(Drawable&, PaintParameters&) = 0;
};
using DrawableTweakerPtr = std::shared_ptr<DrawableTweaker>;
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,78 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <mln/util/image.hpp>
#include <mln/util/rect.hpp>
#include <mapbox/shelf-pack.hpp>
#include <optional>
#include <mutex>
namespace mln {
namespace gfx {
class Context;
class Texture2D;
class UploadPass;
using Texture2DPtr = std::shared_ptr<Texture2D>;
class TextureHandle {
public:
TextureHandle(const mapbox::Bin& bin)
: id(bin.id),
rectangle(bin.x, bin.y, bin.w, bin.h),
needsUpload(bin.refcount() == 1) {};
~TextureHandle() = default;
int32_t getId() const { return id; }
const Rect<uint16_t>& getRectangle() const { return rectangle; }
bool isUploadNeeded() const { return needsUpload; }
bool operator==(const TextureHandle& other) const { return (id == other.id); }
struct Hasher {
size_t operator()(const TextureHandle& texHandle) const { return texHandle.id; }
};
private:
int32_t id = 0;
Rect<uint16_t> rectangle;
bool needsUpload = false;
friend class DynamicTexture;
};
class DynamicTexture {
public:
DynamicTexture(Context& context, Size size, TexturePixelType pixelType);
virtual ~DynamicTexture() = default;
const Texture2DPtr& getTexture() const;
TexturePixelType getPixelFormat() const;
bool isEmpty() const;
std::optional<TextureHandle> reserveSize(const Size& size, int32_t uniqueId);
template <typename Image>
std::optional<TextureHandle> addImage(const Image& image, int32_t uniqueId = -1) {
return addImage(image.data ? image.data.get() : nullptr, image.size, uniqueId);
}
std::optional<TextureHandle> addImage(const uint8_t* pixelData, const Size& imageSize, int32_t uniqueId = -1);
virtual void uploadImage(const uint8_t* pixelData, TextureHandle& texHandle);
virtual void uploadDeferredImages(UploadPass&) {};
virtual bool removeTexture(const TextureHandle& texHandle);
protected:
DynamicTexture(Size size, TexturePixelType pixelType);
mapbox::ShelfPack shelfPack;
Texture2DPtr texture;
TexturePixelType pixelFormat;
int numTextures = 0;
std::mutex mutex;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,50 @@
#pragma once
#include <mln/gfx/dynamic_texture.hpp>
#include <mln/text/glyph.hpp>
#include <mln/style/image_impl.hpp>
namespace mln {
namespace gfx {
using DynamicTexturePtr = std::shared_ptr<gfx::DynamicTexture>;
class GlyphAtlas {
public:
GlyphPositions glyphPositions;
std::vector<TextureHandle> textureHandles;
DynamicTexturePtr dynamicTexture;
};
class ImageAtlas {
public:
ImagePositions iconPositions;
ImagePositions patternPositions;
std::vector<TextureHandle> textureHandles;
DynamicTexturePtr dynamicTexture;
};
class DynamicTextureAtlas {
public:
DynamicTextureAtlas(Context& context_)
: context(context_) {}
~DynamicTextureAtlas() = default;
GlyphAtlas uploadGlyphs(const GlyphMap& glyphs);
ImageAtlas uploadIconsAndPatterns(const ImageMap& icons,
const ImageMap& patterns,
const ImageVersionMap& versionMap);
void removeTextures(const std::vector<TextureHandle>& textureHandles, const DynamicTexturePtr& dynamicTexture);
void removeUnusedDynamicTextures();
private:
Context& context;
std::vector<DynamicTexturePtr> dynamicTextures;
std::unordered_map<TexturePixelType, DynamicTexturePtr> dummyDynamicTexture;
std::mutex mutex;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,48 @@
#pragma once
#include <mln/util/geometry.hpp>
#include <mln/tile/geometry_tile_data.hpp>
#include <mln/gfx/vertex_vector.hpp>
#include <mln/gfx/index_vector.hpp>
#include <mln/renderer/buckets/fill_bucket.hpp>
#include <mln/renderer/buckets/line_bucket.hpp>
namespace mln {
namespace gfx {
/// Generate fill buffers, without outline
void generateFillBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<Triangles>& fillIndexes,
SegmentVector& fillSegments);
/// Generate fill and outline buffers, with the outline composed of line primitives.
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& vertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector& fillSegments,
gfx::IndexVector<gfx::Lines>& lineIndexes,
SegmentVector& lineSegments);
/// Generate fill and outline buffers, where the outlines are built with triangle primitives
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector& fillSegments,
gfx::VertexVector<LineLayoutVertex>& lineVertices,
gfx::IndexVector<gfx::Triangles>& lineIndexes,
SegmentVector& lineSegments);
/// Generate fill and outline buffers, where the outlines are built both with triangle primitives AND with simple lines
void generateFillAndOutineBuffers(const GeometryCollection& geometry,
gfx::VertexVector<FillLayoutVertex>& fillVertices,
gfx::IndexVector<gfx::Triangles>& fillIndexes,
SegmentVector& fillSegments,
gfx::VertexVector<LineLayoutVertex>& lineVertices,
gfx::IndexVector<gfx::Triangles>& lineIndexes,
SegmentVector& lineSegments,
gfx::IndexVector<gfx::Lines>& basicLineIndexes,
SegmentVector& basicLineSegments);
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,68 @@
#pragma once
#include <cstdint>
namespace mln {
namespace gfx {
/// Graphic primitive type enumeration
enum class PrimitiveType : uint8_t {
Point, ///< point primitive
Line, ///< line primitive
Triangle, ///< triangle primitive
};
/// Drawing mode type
enum class DrawModeType : uint8_t {
Points, ///< points
Lines, ///< line segments
LineLoop, ///< line loop, a.k.a. closed polyline
LineStrip, ///< line strip, a.k.a. open polyline
Triangles, ///< independent triangles
TriangleStrip, ///< triangles sharing vertices
TriangleFan, ///< fan of triangles, with a common central point
};
/// Attribute data types
enum class AttributeDataType : uint8_t {
Byte, ///< one signed byte (8 bit)
Byte2, ///< pack of 2 signed bytes
Byte3, ///< pack of 3 signed bytes
Byte4, ///< pack 4 signed bytes
UByte, ///< one unsigned byte (8 bit)
UByte2, ///< pack of 2 unsigned bytes
UByte3, ///< pack of 3 unsigned bytes
UByte4, ///< pack of 4 unsigned bytes
Short, ///< signed 16 bit integer value
Short2, ///< pack of 2 signed 16 bit integers
Short3, ///< pack of 3 signed 16 bit integers
Short4, ///< pack of 4 signed 16 bit integers
UShort, ///< unsigned 16 bit integer value
UShort2, ///< pack of 2 unsigned 16 bit integers
UShort3, ///< pack of 3 unsigned 16 bit integers
UShort4, ///< pack of 4 unsigned 16 bit integers
UShort8, ///< pack of 6 unsigned 16 bit integers
Int, ///< signed 32 bit integer value
Int2, ///< pack of 2 signed 32 bit integers
Int3, ///< pack of 3 signed 32 bit integers
Int4, ///< pack of 4 signed 32 bit integers
UInt, ///< unsigned 32 bit integer value
UInt2, ///< pack of 2 unsigned 32 bit integers
UInt3, ///< pack of 3 unsigned 32 bit integers
UInt4, ///< pack of 4 unsigned 32 bit integers
Float, ///< 32 bit floating point value
Float2, ///< pack of 2 floating point values
Float3, ///< pack of 3 floating point values
Float4, ///< pack of 4 floating point values
Invalid = 255,
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,104 @@
#pragma once
#include <mln/util/color.hpp>
#include <mln/util/variant.hpp>
#include <optional>
namespace mln {
namespace style {
namespace expression {
class Expression;
class Interpolate;
class Step;
} // namespace expression
using ZoomCurvePtr = variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*>;
} // namespace style
namespace gfx {
enum class GPUInterpType : std::uint16_t {
Step,
Linear,
Exponential,
Bezier
};
enum class GPUOutputType : std::uint16_t {
Float,
Color,
};
enum class GPUOptions : std::uint16_t {
None = 0,
IntegerZoom = 1 << 0,
Transitioning = 1 << 1,
};
class GPUExpression;
using UniqueGPUExpression = std::unique_ptr<GPUExpression>;
class alignas(16) GPUExpression {
public:
static constexpr std::size_t maxStops = 16;
GPUExpression() = delete;
GPUExpression(GPUOutputType type, uint16_t count)
: outputType(type),
stopCount(count) {}
GPUExpression(GPUExpression&&) = default;
GPUExpression(const GPUExpression&) = default;
GPUExpression(const GPUExpression* ptr)
: GPUExpression(ptr ? *ptr : empty) {}
GPUExpression& operator=(GPUExpression&&) = delete;
GPUExpression& operator=(const GPUExpression&) = delete;
/* 0 */ const GPUOutputType outputType;
/* 2 */ const std::uint16_t stopCount;
/* 4 */ GPUOptions options;
/* 6 */ GPUInterpType interpolation;
/* 8 */ union InterpOptions {
struct Exponential {
float base;
} exponential;
struct Bezier {
float x1;
float y1;
float x2;
float y2;
} bezier;
} interpOptions;
/* 24 */ float inputs[maxStops];
/* 24 + (4 * maxStops) = 88 */ union Stops {
float floats[maxStops];
float colors[2 * maxStops];
} stops;
static UniqueGPUExpression create(GPUOutputType, std::uint16_t stopCount);
static UniqueGPUExpression create(const style::expression::Expression&, const style::ZoomCurvePtr&, bool intZoom);
float evaluateFloat(const float zoom) const;
Color evaluateColor(const float zoom) const;
template <typename T>
auto evaluate(const float zoom) const;
Color getColor(std::size_t index) const;
static const GPUExpression empty;
};
static_assert(sizeof(GPUExpression) == 32 + (4 + 8) * GPUExpression::maxStops);
static_assert(sizeof(GPUExpression) % 16 == 0);
template <>
inline auto GPUExpression::evaluate<Color>(const float zoom) const {
return evaluateColor(zoom);
}
template <>
inline auto GPUExpression::evaluate<float>(const float zoom) const {
return evaluateFloat(zoom);
}
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,103 @@
#pragma once
#include <mln/util/geometry.hpp>
#include <mln/tile/geometry_tile_data.hpp>
#include <mln/style/types.hpp>
#include <mln/gfx/vertex_vector.hpp>
#include <mln/gfx/index_vector.hpp>
#include <mln/shaders/segment.hpp>
#include <mln/util/math.hpp>
#include <cstdint>
#include <cstddef>
#include <vector>
#include <functional>
#include <optional>
namespace mln {
namespace gfx {
class PolylineGeneratorDistances {
public:
PolylineGeneratorDistances(double clipStart_, double clipEnd_, double total_)
: clipStart(clipStart_),
clipEnd(clipEnd_),
total(total_) {}
// Scale line distance from tile units to [0, 2^15).
double scaleToMaxLineDistance(double tileDistance) const;
private:
double clipStart;
double clipEnd;
double total;
};
struct PolylineGeneratorOptions {
FeatureType type{FeatureType::LineString};
style::LineJoinType joinType{style::LineJoinType::Miter};
float miterLimit{2.f};
style::LineCapType beginCap{style::LineCapType::Butt};
style::LineCapType endCap{style::LineCapType::Butt};
float roundLimit{1.f};
uint32_t overscaling{1};
std::optional<PolylineGeneratorDistances> clipDistances;
};
template <class PolylineLayoutVertex, class PolylineSegment>
class PolylineGenerator {
public:
using Vertices = gfx::VertexVector<PolylineLayoutVertex>;
using Segments = std::vector<PolylineSegment>;
using LayoutVertexFunc = std::function<PolylineLayoutVertex(
Point<int16_t> p, Point<double> e, bool round, bool up, int8_t dir, int32_t linesofar /*= 0*/)>;
using CreateSegmentFunc = std::function<PolylineSegment(std::size_t vertexOffset, std::size_t indexOffset)>;
using GetSegmentFunc = std::function<mln::SegmentBase&(PolylineSegment& segment)>;
using Indexes = gfx::IndexVector<gfx::Triangles>;
public:
PolylineGenerator(Vertices& polylineVertices,
LayoutVertexFunc layoutVertexFunc,
Segments& polylineSegments,
CreateSegmentFunc createSegmentFunc,
GetSegmentFunc getSegmentFunc,
Indexes& polylineIndexes);
~PolylineGenerator() = default;
void generate(const GeometryCoordinates& coordinates, const PolylineGeneratorOptions& options);
private:
struct TriangleElement;
void addCurrentVertex(const GeometryCoordinate& currentCoordinate,
double& distance,
const Point<double>& normal,
double endLeft,
double endRight,
bool round,
std::size_t startVertex,
std::vector<TriangleElement>& triangleStore,
std::optional<PolylineGeneratorDistances> lineDistances);
void addPieSliceVertex(const GeometryCoordinate& currentVertex,
double distance,
const Point<double>& extrude,
bool lineTurnsLeft,
std::size_t startVertex,
std::vector<TriangleElement>& triangleStore,
std::optional<PolylineGeneratorDistances> lineDistances);
private:
Vertices& vertices;
LayoutVertexFunc layoutVertex;
Segments& segments;
CreateSegmentFunc createSegment;
GetSegmentFunc getSegment;
Indexes& indexes;
std::ptrdiff_t e1;
std::ptrdiff_t e2;
std::ptrdiff_t e3;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,59 @@
#pragma once
#include <mln/util/size.hpp>
#include <memory>
#include <cassert>
namespace mln {
namespace gfx {
class RenderableResource {
protected:
explicit RenderableResource() = default;
public:
virtual ~RenderableResource() = default;
RenderableResource(const RenderableResource&) = delete;
RenderableResource& operator=(const RenderableResource&) = delete;
virtual void bind() = 0;
};
class Renderable {
public:
enum class SwapBehaviour {
NoFlush,
Flush
};
protected:
Renderable(const Size size_, std::unique_ptr<RenderableResource> resource_)
: size(size_),
resource(std::move(resource_)) {}
virtual ~Renderable() = default;
public:
Size getSize() const { return size; }
template <typename T>
T& getResource() const {
assert(resource);
return static_cast<T&>(*resource);
}
bool hasResource() const { return resource != nullptr; }
void setResource(std::unique_ptr<RenderableResource> resource_) { resource = std::move(resource_); }
virtual void wait() {}
bool operator!=(const Renderable& other) const { return resource.get() != other.resource.get(); }
protected:
Size size;
std::unique_ptr<RenderableResource> resource;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,46 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <mln/util/size.hpp>
#include <memory>
#include <cassert>
namespace mln {
namespace gfx {
class RenderbufferResource {
protected:
RenderbufferResource() = default;
public:
virtual ~RenderbufferResource() = default;
};
template <RenderbufferPixelType renderbufferType>
class Renderbuffer final {
public:
Renderbuffer(const Size size_, std::unique_ptr<RenderbufferResource> resource_)
: size(size_),
resource(std::move(resource_)) {}
void setShouldClear(bool clear) { dirty = clear; }
bool needsClearing() const { return dirty; }
const Size& getSize() const { return size; }
template <typename T = RenderbufferResource>
T& getResource() const {
assert(resource);
return static_cast<T&>(*resource);
}
protected:
Size size;
bool dirty = false;
std::unique_ptr<RenderbufferResource> resource;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,88 @@
#pragma once
#include <mln/actor/scheduler.hpp>
#include <mln/util/util.hpp>
#include <memory>
#include <mutex>
namespace mln {
class ProgramParameters;
class Map;
namespace gfx {
class BackendScope;
class Context;
class Renderable;
class ShaderRegistry;
// We can make some optimizations if we know that the drawing context is not shared with other code.
enum class ContextMode : bool {
Unique,
Shared,
};
class RendererBackend {
protected:
explicit RendererBackend(ContextMode);
RendererBackend(ContextMode, const TaggedScheduler&);
public:
virtual ~RendererBackend();
RendererBackend(const RendererBackend&) = delete;
RendererBackend& operator=(const RendererBackend&) = delete;
// Return the background thread pool assigned to this backend
TaggedScheduler& getThreadPool() noexcept { return threadPool; }
/// Returns the device's context.
Context& getContext();
template <typename T>
T& getContext() {
return static_cast<T&>(getContext());
}
bool contextIsShared() const { return contextMode == ContextMode::Shared; }
/// Returns a reference to the default surface that should be rendered on.
virtual Renderable& getDefaultRenderable() = 0;
/// One-time shader initialization
virtual void initShaders(gfx::ShaderRegistry&, const ProgramParameters&) = 0;
const mln::util::SimpleIdentity uniqueID;
protected:
virtual std::unique_ptr<Context> createContext() = 0;
/// Called when the backend's GL context needs to be made active or
/// inactive. These are called, as a matched pair, exclusively through
/// BackendScope, in two situations:
///
/// 1. When releasing GL resources during Renderer destruction
/// (Including calling CustomLayerHost::deinitialize during
/// RenderCustomLayer destruction)
/// 2. When rendering through Renderer::render()
/// (Including calling CustomLayerHost::initialize for newly added
/// custom layers and
/// CustomLayerHost::deinitialize on layer removal)
virtual void activate() = 0;
virtual void deactivate() = 0;
protected:
std::unique_ptr<Context> context;
const ContextMode contextMode;
std::once_flag initialized;
TaggedScheduler threadPool;
friend class BackendScope;
};
constexpr bool operator==(const RendererBackend& a, const RendererBackend& b) {
return &a == &b;
}
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,133 @@
#pragma once
#include <cstddef>
#include <string>
#include <memory>
#include <mln/util/color.hpp>
namespace mln {
namespace style {
class Style;
class SymbolLayer;
} // namespace style
namespace gfx {
struct RenderingStats {
bool isZero() const;
/// Frame CPU encoding time (seconds)
double encodingTime = 0.0;
/// Frame CPU rendering time (seconds)
double renderingTime = 0.0;
/// Number of frames rendered
int numFrames = 0;
/// Number of draw calls (`glDrawElements`, `drawIndexedPrimitives`, etc.) executed during the most recent frame
int numDrawCalls = 0;
/// Total number of draw calls executed during all the frames
int totalDrawCalls = 0;
/// Total number of textures created
int numCreatedTextures = 0;
/// Net textures
int numActiveTextures = 0;
/// Net texture bindings
int numTextureBindings = 0;
/// Number of times a texture was updated
int numTextureUpdates = 0;
/// Number of bytes used in texture updates
std::size_t textureUpdateBytes = 0;
/// Number of buffers created
std::size_t totalBuffers = 0;
/// Number of SDK-specific buffers created
std::size_t totalBufferObjs = 0;
/// Number of times a buffer is updated
std::size_t bufferUpdates = 0;
/// Number of times an SDK-specific buffer is updated
std::size_t bufferObjUpdates = 0;
/// Sum of update sizes
std::size_t bufferUpdateBytes = 0;
/// Number of active buffers
int numBuffers = 0;
/// Number of active offscreen frame buffers
int numFrameBuffers = 0;
/// Number of active index buffers
int numIndexBuffers = 0;
/// Sum of index buffers update sizes
std::size_t indexUpdateBytes = 0;
/// Number of active vertex buffers
int numVertexBuffers = 0;
/// Sum of vertex buffers update sizes
std::size_t vertexUpdateBytes = 0;
/// Number of active uniform buffers
int numUniformBuffers = 0;
/// Number of times a uniform buffer is updated
int numUniformUpdates = 0;
/// Sum of uniform buffers update sizes
std::size_t uniformUpdateBytes = 0;
/// Total texture memory
int memTextures = 0;
/// Total buffer memory
int memBuffers = 0;
/// Total index buffer memory
int memIndexBuffers = 0;
/// Total vertex buffer memory
int memVertexBuffers = 0;
/// Total uniform buffer memory
int memUniformBuffers = 0;
/// Number of stencil buffer clears
int stencilClears = 0;
/// Number of stencil buffer updates
int stencilUpdates = 0;
RenderingStats& operator+=(const RenderingStats&);
#ifndef NDEBUG
std::string toString(std::string_view separator) const;
#endif
};
class RenderingStatsView final {
public:
struct Options {
float updateInterval = 0.25f;
bool verbose = false;
Color textColor = Color::red();
float textSize = 4.0f;
};
RenderingStatsView() = default;
RenderingStatsView(const Options& options_)
: options(options_) {}
~RenderingStatsView() = default;
void create(style::Style& style);
void destroy(style::Style& style);
mln::style::SymbolLayer* getLayer(style::Style& style);
void update(style::Style& style, const gfx::RenderingStats& stats);
protected:
const std::string layerID = "rendering-stats";
const std::string sourceID = layerID + "-source";
Options options;
double lastUpdate = 0.0;
uint32_t frameCount = 0;
double encodingTime = 0.0;
double renderingTime = 0.0;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,21 @@
#pragma once
#include <cstdint>
namespace mln {
namespace gfx {
class ScissorRect {
public:
int32_t x;
int32_t y;
uint32_t width;
uint32_t height;
bool operator==(const ScissorRect& other) const = default;
bool operator!=(const ScissorRect& other) const = default;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,48 @@
#pragma once
#include <memory>
#include <string_view>
#include <type_traits>
namespace mln {
namespace gfx {
class Shader;
using ShaderPtr = std::shared_ptr<Shader>;
// Assert that a type is a valid shader for downcasting.
// A valid shader must:
// * Inherit gfx::Shader
// * Have properly called DECLARE_SHADER_TYPEINFO in the class body
// * Be a final class
template <typename T>
inline constexpr bool is_shader_v = std::is_base_of_v<gfx::Shader, T> &&
std::is_same_v<std::remove_cv_t<decltype(T::Name)>, std::string_view> &&
std::is_final_v<T>;
/// @brief A shader is used as the base class for all programs across any supported
/// backend API. Shaders are registered with a `gfx::ShaderRegistry` instance.
class Shader {
public:
virtual ~Shader() noexcept = default;
/// @brief Get the type name of this shader
/// @return Shader type name
virtual const std::string_view typeName() const noexcept = 0;
/// @brief Downcast to a type
/// @tparam T Derived type
/// @return Type or nullptr if type info was not a match
template <typename T>
T* to() noexcept
requires(is_shader_v<T>)
{
if (typeName() != T::Name) {
return nullptr;
}
return static_cast<T*>(this);
}
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,184 @@
#pragma once
#include <mln/gfx/shader.hpp>
#include <mln/util/containers.hpp>
#include <mln/util/hash.hpp>
#include <iomanip>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <sstream>
#include <string>
namespace mln {
using StringIDSetsPair = std::pair<unordered_set<std::string_view>, unordered_set<size_t>>;
namespace gfx {
class Context;
/// @brief A ShaderGroup contains a collection of gfx::Shader.
/// Using the group, shaders may be dynamically registered or replaced
/// at runtime.
class ShaderGroup {
public:
ShaderGroup() = default;
ShaderGroup(const ShaderGroup&) = delete;
ShaderGroup(ShaderGroup&&) noexcept = delete;
ShaderGroup& operator=(const ShaderGroup&) = delete;
ShaderGroup& operator=(ShaderGroup&&) noexcept = delete;
virtual ~ShaderGroup() = default;
/// @brief Checks if a shader exists in the group for the given name.
/// @param shaderName Name of shader
/// @return If a shader is found, true
[[nodiscard]] virtual bool isShader(const std::string& shaderName) const noexcept;
/// @brief Get a shader from the group by name.
/// @param shaderName Name of shader
/// @return A `gfx::Shader` or `nullptr` if no shader is found with the
/// given name
[[nodiscard]] virtual const std::shared_ptr<gfx::Shader> getShader(const std::string& shaderName) const noexcept;
/// @brief Replace a matching shader in the group with the provided
/// instance. Shader type-names must match.
/// @param shader A `gfx::Shader`. The ShaderGroup will take ownership.
/// @return True if a match was found and the shader was replaced, false
/// otherwise.
[[nodiscard]] virtual bool replaceShader(std::shared_ptr<Shader>&& shader) noexcept;
/// @brief Replace a matching shader in the group with the provided
/// instance. Shader type-names must match.
/// This variant replaces by explicit name.
/// @param shader A `gfx::Shader`. The ShaderGroup will take ownership.
/// @param shaderName Unique name to register the shader under.
/// @return True if a match was found and the shader was replaced, false
/// otherwise.
[[nodiscard]] virtual bool replaceShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept;
/// @brief Register a new shader with the group. If a shader is present
/// in the group with a conflicting name, registration will fail.
/// @param shader A `gfx::Shader` to register. The ShaderGroup will
/// take ownership.
/// @return True if the shader was registered, false if another shader is
/// already present with a conflicting name.
[[nodiscard]] virtual bool registerShader(std::shared_ptr<Shader>&& shader) noexcept;
/// @brief Register a new shader with the group. If a shader is present
/// in the group with a conflicting name, registration will fail.
/// This variant registers using an explicit name.
/// @param shader A `gfx::Shader` to register. The ShaderGroup will
/// take ownership.
/// @param shaderName Unique name to register the shader under.
/// @return True if the shader was registered, false if another shader is
/// already present with a conflicting name.
[[nodiscard]] virtual bool registerShader(std::shared_ptr<Shader>&& shader, const std::string& shaderName) noexcept;
/// @brief Shorthand helper to quickly get a derived type from the group.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param shaderName The group name to look up
/// @return T or nullptr if not found in the group
template <typename T>
std::shared_ptr<T> get(const std::string& shaderName) noexcept
requires(is_shader_v<T>)
{
auto shader = getShader(shaderName);
if (!shader || shader->typeName() != T::Name) {
return nullptr;
}
return std::static_pointer_cast<T>(shader);
}
/// @brief Shorthand helper to quickly get a derived type from the group.
/// This variant looks up shaders only by type name.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @return T or nullptr if not found in the group
template <typename T>
std::shared_ptr<T> get() noexcept
requires(is_shader_v<T>)
{
auto shader = getShader(std::string(T::Name));
if (!shader || shader->typeName() != T::Name) {
return nullptr;
}
return std::static_pointer_cast<T>(shader);
}
/// @brief Ensure the destination 'to' is populated with the requested
/// shader. If already non-null, does nothing.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param to Location to store the shader
/// @param shaderName The group name to look up
/// @return True if 'to' has a valid program object, false otherwise.
template <typename T>
bool populate(std::shared_ptr<T>& to, const std::string& shaderName) noexcept
requires(is_shader_v<T>)
{
if (to) {
return true;
}
auto shader = getShader(shaderName);
if (!shader || shader->typeName() != T::Name) {
return false;
}
to = std::static_pointer_cast<T>(shader);
return true;
}
/// @brief Ensure the destination 'to' is populated with the requested
/// shader. If already non-null, does nothing. This variant looks up
/// shaders only by type name.
/// @tparam T Derived type, inheriting `gfx::Shader`
/// @param to Location to store the shader
/// @return True if 'to' has a valid program object, false otherwise.
template <typename T>
bool populate(std::shared_ptr<T>& to) noexcept
requires(is_shader_v<T>)
{
if (to) {
return true;
}
auto shader = getShader(std::string(T::Name));
if (!shader || shader->typeName() != T::Name) {
return false;
}
to = std::static_pointer_cast<T>(shader);
return true;
}
/// @brief Get a shader from the group by its set of data driven properties as uniforms.
/// if no shader is found, create and register the shader.
/// @param propertiesAsUniforms Set of data driven properties as uniforms.
/// @param firstAttribName Name of the first attribute
/// @return A `gfx::ShaderPtr`
virtual gfx::ShaderPtr getOrCreateShader(gfx::Context&,
[[maybe_unused]] const StringIDSetsPair& propertiesAsUniforms,
[[maybe_unused]] std::string_view firstAttribName = "a_pos") {
return {};
}
protected:
using PropertyHashType = std::uint64_t;
std::string getShaderName(const std::string_view& name, const PropertyHashType key) {
return (std::ostringstream() << name << '#' << std::hex << key).str();
}
/// Generate a map key for the specified combination of properties
PropertyHashType propertyHash(const StringIDSetsPair& propertiesAsUniforms) {
const auto beg = propertiesAsUniforms.second.cbegin();
const auto end = propertiesAsUniforms.second.cend();
return util::order_independent_hash<decltype(beg), PropertyHashType>(beg, end);
}
private:
mln::unordered_map<std::string, std::shared_ptr<gfx::Shader>> programs;
mutable std::shared_mutex programLock;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,69 @@
#pragma once
#include <mln/gfx/shader_group.hpp>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
namespace mln {
namespace gfx {
using ShaderGroupPtr = std::shared_ptr<gfx::ShaderGroup>;
/// @brief A ShaderRegistry contains a collection of gfx::ShaderGroup instances.
/// Using the registry, shader groups may be dynamically registered or replaced
/// at runtime.
class ShaderRegistry {
public:
explicit ShaderRegistry();
ShaderRegistry(const ShaderRegistry&) = delete;
ShaderRegistry(ShaderRegistry&&) noexcept = delete;
ShaderRegistry& operator=(const ShaderRegistry&) = delete;
ShaderRegistry& operator=(ShaderRegistry&&) noexcept = delete;
virtual ~ShaderRegistry() = default;
/// @brief Get the legacy shader group.
/// @return A `gfx::ShaderGroup`
[[nodiscard]] ShaderGroup& getLegacyGroup() noexcept;
/// @brief Checks if a shader group exists in the registry for the given name.
/// @param shaderGroupName Name of shader group
/// @return If a shader group is found, true
[[nodiscard]] virtual bool isShaderGroup(const std::string& shaderGroupName) const noexcept;
/// @brief Get a shader group from the registry by name.
/// @param shaderGroupName Name of shader group
/// @return A `gfx::ShaderGroup` or `nullptr` if no shader group is found with the
/// given name
[[nodiscard]] virtual const ShaderGroupPtr getShaderGroup(const std::string& shaderGroupName) const noexcept;
/// @brief Replace a matching shader group in the registry with the provided
/// name.
/// @param shaderGroup A `gfx::ShaderGroup`. The ShaderRegistry will take ownership.
/// @param shaderGroupName Unique name to register the shader group under.
/// @return True if a match was found and the shader group was replaced, false
/// otherwise.
[[nodiscard]] virtual bool replaceShader(ShaderGroupPtr&& shaderGroup, const std::string& shaderName) noexcept;
/// @brief Register a new shader group with the registry for the given name.
/// If a shader group is present in the registry with a conflicting name,
/// registration will fail.
/// @param shaderGroup A `gfx::ShaderGroup` to register. The ShaderRegistry will
/// take ownership.
/// @param shaderGroupName Unique name to register the shader group under.
/// @return True if the shader group was registered, false if another shader group is
/// already present with a conflicting name.
[[nodiscard]] virtual bool registerShaderGroup(ShaderGroupPtr&& shaderGroup,
const std::string& shaderGroupName) noexcept;
private:
gfx::ShaderGroup legacyGroup;
std::unordered_map<std::string, ShaderGroupPtr> shaderGroups;
mutable std::shared_mutex shaderGroupLock;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,53 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <mln/util/variant.hpp>
namespace mln {
namespace gfx {
class StencilMode {
public:
template <StencilFunctionType F>
struct SimpleTest {
static constexpr StencilFunctionType func = F;
static constexpr uint32_t mask = 0;
};
template <StencilFunctionType F>
struct MaskedTest {
static constexpr StencilFunctionType func = F;
uint32_t mask;
};
using Never = SimpleTest<StencilFunctionType::Never>;
using Less = MaskedTest<StencilFunctionType::Less>;
using Equal = MaskedTest<StencilFunctionType::Equal>;
using LessEqual = MaskedTest<StencilFunctionType::LessEqual>;
using Greater = MaskedTest<StencilFunctionType::Greater>;
using NotEqual = MaskedTest<StencilFunctionType::NotEqual>;
using GreaterEqual = MaskedTest<StencilFunctionType::GreaterEqual>;
using Always = SimpleTest<StencilFunctionType::Always>;
using Test = variant<Never, Less, Equal, LessEqual, Greater, NotEqual, GreaterEqual, Always>;
Test test;
int32_t ref;
uint32_t mask;
StencilOpType fail;
StencilOpType depthFail;
StencilOpType pass;
static StencilMode disabled() {
return StencilMode{.test = Always(),
.ref = 0,
.mask = 0,
.fail = StencilOpType::Keep,
.depthFail = StencilOpType::Keep,
.pass = StencilOpType::Keep};
}
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,128 @@
#pragma once
#include <mln/gfx/types.hpp>
#include <mln/util/image.hpp>
#include <cstddef>
#include <vector>
namespace mln {
class Size;
namespace gfx {
class Context;
class UploadPass;
class TextureResource;
constexpr int32_t MaxActiveTextureUnits = 8;
class Texture2D {
public:
struct SamplerState {
/// Minification and magnification filter within a single mip level
TextureFilterType filter{TextureFilterType::Nearest};
/// Wrapping behavior along U coordinate
TextureWrapType wrapU{TextureWrapType::Clamp};
/// Wrapping behavior along V coordinate
TextureWrapType wrapV{TextureWrapType::Clamp};
uint8_t maxAnisotropy{1};
bool mipmapped{false};
};
public:
virtual ~Texture2D() = default;
/// @brief Set the sampler configuration to be used for shader access of this
/// texture.
/// @param samplerState Sampler state configuration
/// @return this
virtual Texture2D& setSamplerConfiguration(const SamplerState& samplerState) noexcept = 0;
/// @brief Set the texture pixel and data format
/// @param pixelFormat Pixel format to use
/// @param channelType Data type of each color channel
/// @return this
virtual Texture2D& setFormat(TexturePixelType pixelFormat, TextureChannelDataType channelType) noexcept = 0;
/// @brief Set the size of the texture
/// @param size_ Size of the texture
/// @return this
virtual Texture2D& setSize(Size size_) noexcept = 0;
/// @brief Sets the internal image
/// @param image_ Image data to transfer
virtual Texture2D& setImage(std::shared_ptr<PremultipliedImage> image_) noexcept = 0;
/// @brief Get the pixel format of the texture
/// @return Pixel format of the texture
virtual TexturePixelType getFormat() const noexcept = 0;
/// @brief Get the size of the texture
/// @return Size of the texture
virtual Size getSize() const noexcept = 0;
/// @brief Determine the size of the buffer backing this texture
/// as configured, in bytes.
/// @return Size in bytes
virtual size_t getDataSize() const noexcept = 0;
/// @brief Returns the size, in bytes, of a pixel in the texture.
/// @return Size of a pixel, in bytes
virtual size_t getPixelStride() const noexcept = 0;
/// @brief Returns the number of color channels in the texture.
/// @return Channel count
virtual size_t numChannels() const noexcept = 0;
/// @brief Create the texture with default initialized memory.
/// @note Be sure to configure a valid size and format.
virtual void create() = 0;
/// @brief Upload image data to the texture resource
/// @param pixelData Image data to transfer
virtual void upload(const void* pixelData, const Size& size_) = 0;
/// @brief Upload image data to the texture resource
/// @tparam Image Image object type
/// @param img Image to transfer
template <typename Image>
void upload(const Image& img) {
setFormat(Image::channels == 1 ? gfx::TexturePixelType::Alpha : gfx::TexturePixelType::RGBA,
gfx::TextureChannelDataType::UnsignedByte);
upload(img.data ? img.data.get() : nullptr, img.size);
}
/// @brief Upload a subregion of the texture resource
/// @param pixelData image pixel data pointer
/// @param size image dimensions
/// @param xOffset destination x coordinate
/// @param yOffset destination y coordinate
virtual void uploadSubRegion(const void* pixelData, const Size& size, uint16_t xOffset, uint16_t yOffset) = 0;
/// @brief Upload a subregion of the texture resource
/// @tparam Image Image object type
/// @param img Image to upload
/// @param xOffset Destination x coordinate
/// @param yOffset Destination y coordinate
template <typename Image>
void uploadSubRegion(const Image& img, uint16_t xOffset, uint16_t yOffset) noexcept {
assert(Image::channels == numChannels());
assert(Image::channels == getPixelStride());
assert(img.size.width + xOffset <= getSize().width);
assert(img.size.height + yOffset <= getSize().height);
uploadSubRegion(img.data ? img.data.get() : nullptr, img.size, xOffset, yOffset);
}
/// @brief Upload staged image data if present and required.
/// @see needsUpload
virtual void upload() = 0;
/// @brief Check whether the texture needs upload
/// @return bool
virtual bool needsUpload() const noexcept = 0;
};
} // namespace gfx
} // namespace mln

145
include/mln/gfx/types.hpp Normal file
View File

@@ -0,0 +1,145 @@
#pragma once
#include <mln/gfx/gfx_types.hpp>
#include <cstdint>
namespace mln {
namespace gfx {
/// Color blending equation type
enum class ColorBlendEquationType : uint8_t {
Add, ///< The source and destination colors are added to each other. O = sS + dD. The The s and d are blending
///< parameters that are multiplied into each of S and D before the addition.
Subtract, ///< Subtracts the destination from the source. O = sS - dD. The source and dest are multiplied by
///< blending parameters.
ReverseSubtract, ///< Subtracts the source from the destination. O = dD - sS. The source and dest are multiplied by
///< blending parameters.
};
/// Color blending factor type
enum class ColorBlendFactorType : uint8_t {
Zero, ///< Factor is (0, 0, 0, 0)
One, ///< Factor is (1, 1, 1, 1)
SrcColor, ///< Factor is (Rs, Gs, Bs, As) where S is the source color
OneMinusSrcColor, ///< Factor is (1, 1, 1, 1) - (Rs, Gs, Bs, As) where S is the source color
SrcAlpha, ///< Factor is (As, As, As, As) where S is the source color
OneMinusSrcAlpha, ///< Factor is (1, 1, 1, 1) - (As, As, As, As) where S is the source color
DstAlpha, ///< Factor is (Ad, Ad, Ad, Ad) where D is the destination color
OneMinusDstAlpha, ///< Factor is (1, 1, 1, 1) - (Ad, Ad, Ad, Ad) where D is the destination color
DstColor, ///< Factor is (Rd, Gd, Bd, Ad) where D is the destination color
OneMinusDstColor, ///< Factor is (1, 1, 1, 1) - (Rd, Gd, Bd, Ad) where D is the destination color
SrcAlphaSaturate, ///< Factor is (f, f, f, 1) where f = min(As, 1 - Ad) and S is the source color
ConstantColor, ///< Factor is (Rc, Gc, Bc, Ac) where C is the constant color
OneMinusConstantColor, ///< Factor is (1, 1, 1, 1) - (Rc, Gc, Bc, Ac) where C is the constant color
ConstantAlpha, ///< Factor is (Ac, Ac, Ac, Ac) where C is the constant color
OneMinusConstantAlpha, ///< Factor is (1, 1, 1, 1) - (Ac, Ac, Ac, Ac) where C is the constant color
};
/// Depth function type
enum class DepthFunctionType : uint8_t {
Never, ///< The depth test never passes.
Less, ///< The depth test passes if the incoming value is less than the stored value.
Equal, ///< The depth test passes if the incoming value is equal to the stored value.
LessEqual, ///< The depth test passes if the incoming value is less than or equal to the stored value.
Greater, ///< The depth test passes if the incoming value is greater than the stored value.
NotEqual, ///< The depth test passes if the incoming value is not equal to the stored value.
GreaterEqual, ///< The depth test passes if the incoming value is greater than or equal to the stored value.
Always, ///< The depth test always passes.
};
/// Depth buffer masking type
enum class DepthMaskType : bool {
ReadOnly = false, ///< The depth buffer is not written to
ReadWrite = true, ///< The depth buffer is is updated by the new fragments' depth
};
/// Stencil function type
enum class StencilFunctionType : uint8_t {
Never, ///< Always fails.
Less, ///< Passes if ( ref & mask ) < ( stencil & mask ).
Equal, ///< Passes if ( ref & mask ) == ( stencil & mask ).
LessEqual, ///< Passes if ( ref & mask ) <= ( stencil & mask ).
Greater, ///< Passes if ( ref & mask ) > ( stencil & mask ).
NotEqual, ///< Passes if ( ref & mask ) != ( stencil & mask ).
GreaterEqual, ///< Passes if ( ref & mask ) >= ( stencil & mask ).
Always, ///< Always passes.
};
/// Stencil operation type
enum class StencilOpType : uint8_t {
Zero, ///< Sets the stencil buffer value to 0.
Keep, ///< Keeps the current value.
Replace, ///< Replace the stencil buffer value
Increment, ///< Increments the current stencil buffer value. Clamps to the maximum representable unsigned value.
Decrement, ///< Decrements the current stencil buffer value. Clamps to 0.
Invert, ///< Bitwise inverts the current stencil buffer value.
IncrementWrap, ///< Increments the current stencil buffer value. Wraps stencil buffer value to zero when
///< incrementing the maximum representable unsigned value.
DecrementWrap, ///< Decrements the current stencil buffer value. Wraps stencil buffer value to the maximum
///< representable unsigned value when decrementing a stencil buffer value of zero.
};
/// Cull face side
enum CullFaceSideType : uint8_t {
Front, ///< Front facing facets are culled.
Back, ///< Back facing facets are culled.
FrontAndBack, ///< Front and back facing facets are culled.
};
/// Define the orientation of front facing polygons
enum CullFaceWindingType : uint8_t {
Clockwise, ///< Front face is clockwise.
CounterClockwise, ///< Front face is counter clockwise.
};
/// Buffer usage type
enum class BufferUsageType : uint8_t {
StreamDraw, ///< The data store contents will be modified once and used at most a few times.
StaticDraw, ///< The data store contents will be modified once and used many times.
DynamicDraw, ///< The data store contents will be modified repeatedly and used many times.
};
/// Texture pixel type
enum class TexturePixelType : uint8_t {
RGBA, ///< Red, Green, Blue and Alpha
Alpha, ///< Alpha only
Stencil, ///< Stencil
Depth, ///< Depth component
Luminance, ///< Luminance
};
/// Texture channel data type
enum class TextureChannelDataType : uint8_t {
UnsignedByte, ///< 8 bit unsigned byte
HalfFloat, ///< 16 bit "half-float"
Float, ///< 32 bit float
};
/// Texture mip map type
enum class TextureMipMapType : bool {
No, ///< does not use mipmapping
Yes ///< uses mipmapping
};
/// Texture filter type
enum class TextureFilterType : bool {
Nearest, ///< Nearest pixel
Linear, ///< Bilinear interpolation
};
/// Texture wrap mode
enum class TextureWrapType : bool {
Clamp, ///< Clamp to edge
Repeat, ///< Repeat
};
/// Render buffer pixel type
enum class RenderbufferPixelType : uint8_t {
RGBA, ///< Red, Green, Blue and Alpha
Depth, ///< Depth component
DepthStencil, ///< Depth and stencil components
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,93 @@
#pragma once
#include <mln/shaders/shader_defines.hpp>
#include <memory>
#include <vector>
#include <array>
namespace mln {
namespace gfx {
class Context;
class UniformBuffer;
class UniformBufferArray;
class RenderPass;
using UniformBufferPtr = std::shared_ptr<UniformBuffer>;
using UniqueUniformBuffer = std::unique_ptr<UniformBuffer>;
using UniqueUniformBufferArray = std::unique_ptr<UniformBufferArray>;
class UniformBuffer {
// Can only be created by platform specific implementations
protected:
UniformBuffer(std::size_t size_)
: size(size_) {}
UniformBuffer(const UniformBuffer& other)
: size(other.size) {}
UniformBuffer(UniformBuffer&& other)
: size(other.size) {}
public:
virtual ~UniformBuffer() = default;
virtual void update(const void* data, std::size_t dataSize) = 0;
std::size_t getSize() const { return size; }
UniformBuffer& operator=(const UniformBuffer&) = delete;
protected:
UniformBuffer& operator=(UniformBuffer&& other) {
size = other.size;
return *this;
}
protected:
std::size_t size;
};
/// Stores a collection of uniform buffers by id
class UniformBufferArray {
public:
UniformBufferArray() = default;
UniformBufferArray(UniformBufferArray&&);
// Would need to use the virtual assignment operator
UniformBufferArray(const UniformBufferArray&) = delete;
virtual ~UniformBufferArray() = default;
/// Number of maximum allocated elements
std::size_t allocatedSize() const { return uniformBufferVector.size(); }
/// Get an uniform buffer element.
/// Returns a pointer to the element on success, or null if the uniform buffer doesn't exists.
const std::shared_ptr<UniformBuffer>& get(const size_t id) const;
/// Set a new uniform buffer element or replace the existing one.
virtual const std::shared_ptr<UniformBuffer>& set(const size_t id, std::shared_ptr<UniformBuffer> uniformBuffer);
/// Create and add a new buffer or update an existing one
void createOrUpdate(const size_t id, const std::vector<uint8_t>& data, gfx::Context&, bool persistent = false);
virtual void createOrUpdate(
const size_t id, const void* data, std::size_t size, gfx::Context&, bool persistent = false);
template <typename T>
void createOrUpdate(const size_t id, const T* data, gfx::Context& context, bool persistent = false)
requires(!std::is_pointer_v<T>)
{
createOrUpdate(id, data, sizeof(T), context, persistent);
}
virtual void bind(gfx::RenderPass& renderPass) = 0;
UniformBufferArray& operator=(UniformBufferArray&&);
UniformBufferArray& operator=(const UniformBufferArray&);
protected:
virtual std::unique_ptr<UniformBuffer> copy(const UniformBuffer&) = 0;
protected:
std::array<UniformBufferPtr, shaders::maxUBOCountPerShader> uniformBufferVector;
static std::shared_ptr<UniformBuffer> nullref;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,489 @@
#pragma once
#include <mln/gfx/gfx_types.hpp>
#include <mln/renderer/paint_property_binder.hpp>
#include <mln/util/containers.hpp>
#include <algorithm>
#include <array>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <variant>
#include <vector>
namespace mln {
/// A 2 by 2 matrix
using mat2 = std::array<double, 2 * 2>;
/// A 3 by 3 matrix
using mat3 = std::array<double, 3 * 3>;
/// A 4 by 4 matrix
using mat4 = std::array<double, 4 * 4>;
namespace gfx {
class ShaderProgramBase;
class VertexAttribute;
class VertexAttributeArray;
class VertexVectorBase;
using UniqueVertexAttribute = std::unique_ptr<VertexAttribute>;
using StringIDSetsPair = std::pair<unordered_set<std::string_view>, unordered_set<size_t>>;
class VertexAttribute {
public:
/// type for vector of 2 floats
using float2 = std::array<float, 2>;
/// type for vector of 3 floats
using float3 = std::array<float, 3>;
/// type for vector of 3 floats
using float4 = std::array<float, 4>;
/// type for 2 by 2 matrix of floats
using matf2 = std::array<float, 2 * 2>;
/// type for 3 by 3 matrix of floats
using matf3 = std::array<float, 3 * 3>;
/// type for 4 by 4 matrix of floats
using matf4 = std::array<float, 4 * 4>;
/// type for vector of 2 integers
using int2 = std::array<std::int32_t, 2>;
/// type for vector of 3 integers
using int3 = std::array<std::int32_t, 3>;
/// type for vector of 4 integers
using int4 = std::array<std::int32_t, 4>;
/// type for vector of 8 unsigned short integers
using ushort8 = std::array<std::uint16_t, 8>;
using ElementType =
std::variant<std::int32_t, ushort8, int2, int3, int4, float, float2, float3, float4, matf3, matf4>;
/// @brief Constructor for vertex attribute
/// @param index_ the index of the vertex attribute
/// @param dataType_ the vertex attribute data type
/// @param count_ element count
/// @param stride_ the vertex attribute stride
VertexAttribute(int index_, AttributeDataType dataType_, std::size_t count_, std::size_t stride_)
: index(index_),
stride(stride_),
dataType(dataType_),
items(count_) {}
VertexAttribute(int index_, AttributeDataType dataType_, std::size_t count_)
: index(index_),
stride(getStrideOf(dataType_)),
dataType(dataType_),
items(count_) {}
VertexAttribute(const VertexAttribute& other)
: index(other.index),
stride(other.stride),
dataType(other.dataType),
items(other.items),
sharedRawData(other.sharedRawData),
sharedType(other.sharedType),
sharedOffset(other.sharedOffset),
sharedVertexOffset(other.sharedVertexOffset),
sharedStride(other.sharedStride) {}
VertexAttribute(VertexAttribute&& other)
: index(other.index),
stride(other.stride),
dataType(other.dataType),
items(std::move(other.items)),
sharedRawData(std::move(other.sharedRawData)),
sharedType(other.sharedType),
sharedOffset(other.sharedOffset),
sharedVertexOffset(other.sharedVertexOffset),
sharedStride(other.sharedStride) {}
public:
virtual ~VertexAttribute() = default;
static std::size_t getStrideOf(gfx::AttributeDataType);
/// @brief Get the index of the vertex attribute
int getIndex() const { return index; }
/// @brief Set the index of the vertex attribute
void setIndex(int value) { index = value; }
/// @brief Get the stride of the vertex attribute
virtual std::size_t getStride() const { return stride; }
void setStride(std::size_t value) { stride = value; }
/// @brief Get the count of vertex attribute items
std::size_t getCount() const;
/// @brief Get the data type of the vertex attribute
AttributeDataType getDataType() const { return dataType; }
/// @brief Get a vertex attribute item
/// @param index index of the item
/// @return ElementType reference to the item
const ElementType& get(std::size_t i) const { return items[i]; }
/// @brief Reserves space for a number of items
/// @param count Number of items to reserve memory for
void reserve(std::size_t count) { items.reserve(count); }
/// @brief Set the value of an item
/// @tparam T item data type
/// @param i index of the item
/// @param value value to set
/// @return ElementType reference to item
template <typename T>
const ElementType& set(std::size_t i, T value) {
if (items.size() < i + 1) {
items.resize(std::max(items.size(), i + 1));
setDirty(); // need to rebuild the raw data next time
}
if (!isDirty()) {
if (std::holds_alternative<T>(items[i])) {
// TODO: epsilon for floats?
if (std::get<T>(items[i]) != value) {
setDirty();
}
} else {
// different types
setDirty();
}
}
return items[i] = value;
}
/// @brief Set the value of an item
/// @param i index of the item
/// @param value value to set
/// @return ElementType reference to item
const ElementType& setVariant(std::size_t i, const ElementType& value) {
if (items.size() < i + 1) {
items.resize(std::max(items.size(), i + 1));
}
setDirty();
return items[i] = value;
}
/// @brief Clear the items
void clear() {
if (!items.empty()) {
setDirty();
}
items.clear();
}
/// @brief Set dirty state
void setDirty(bool value = true) { dirty = value; }
bool isModifiedAfter(std::chrono::duration<double> time) const {
if (sharedRawData) {
return sharedRawData->isModifiedAfter(time);
}
return dirty;
}
template <std::size_t I = 0, typename... Tp>
inline void set(std::size_t, std::tuple<Tp...>, std::size_t)
requires(I == sizeof...(Tp))
{}
/// Set item value
template <std::size_t I = 0, typename... Tp>
inline void set(std::size_t i, std::tuple<Tp...> tuple, std::size_t tupleIndex)
requires(I < sizeof...(Tp))
{
if (tupleIndex == 0) {
set(i, std::get<I>(tuple).a1);
} else {
set<I + 1, Tp...>(i, tuple, tupleIndex - 1);
}
}
/// Get the vertex attribute's raw data
std::vector<std::uint8_t>& getRawData() { return rawData; }
const std::vector<std::uint8_t>& getRawData() const { return rawData; }
void setRawData(std::vector<std::uint8_t> value) { rawData = std::move(value); }
/// Get the vertex attribute's shared raw data
const std::shared_ptr<VertexVectorBase>& getSharedRawData() const { return sharedRawData; }
/// Get the vertex attribute's shared data type
AttributeDataType getSharedType() const { return sharedType; }
/// Get the vertex attribute's shared data offset
uint32_t getSharedOffset() const { return sharedOffset; }
/// Get the vertex attribute's shared vertex offset
uint32_t getSharedVertexOffset() const { return sharedVertexOffset; }
/// Get the vertex attribute's shared data stride
uint32_t getSharedStride() const { return sharedStride; }
/// @brief Set the vertex attribute's shared data
/// @param data shared pointer to vertex vector
/// @param offset Offset into the source vertex layout structure
/// @param vertexOffset Offset into the source vertex array
/// @param stride Stride of the source vertex structure
/// @param type Type of the attribute
void setSharedRawData(std::shared_ptr<VertexVectorBase> data,
uint32_t offset,
uint32_t vertexOffset,
uint32_t stride_,
AttributeDataType type) {
sharedRawData = std::move(data);
sharedType = type;
sharedOffset = offset;
sharedVertexOffset = vertexOffset;
sharedStride = stride_;
}
/// @brief Clears the shared data
void resetSharedRawData() { sharedRawData.reset(); }
const gfx::UniqueVertexBufferResource& getBuffer() const { return buffer; }
void setBuffer(gfx::UniqueVertexBufferResource&& value) { buffer = std::move(value); }
VertexAttribute& operator=(const VertexAttribute&) = delete;
protected:
VertexAttribute& operator=(VertexAttribute&& other) {
index = other.index;
dataType = other.dataType;
items = std::move(other.items);
return *this;
}
protected:
friend class VertexAttributeArray;
bool isDirty() const { return dirty; }
int index;
std::size_t stride;
/// indicates that a value has changed and any cached result should be discarded
mutable bool dirty = true;
AttributeDataType dataType;
std::vector<ElementType> items;
// Cache of attribute data
mutable std::vector<std::uint8_t> rawData;
std::shared_ptr<VertexVectorBase> sharedRawData;
AttributeDataType sharedType = AttributeDataType::Invalid;
uint32_t sharedOffset = 0;
uint32_t sharedVertexOffset = 0;
uint32_t sharedStride = 0;
gfx::UniqueVertexBufferResource buffer;
};
/// Stores a collection of vertex attributes by name
class VertexAttributeArray {
public:
using AttributeVector = std::array<std::unique_ptr<VertexAttribute>, shaders::maxVertexAttributeCountPerShader>;
VertexAttributeArray() = default;
VertexAttributeArray(VertexAttributeArray&&);
VertexAttributeArray(const VertexAttributeArray&) = delete; // Would need to use the virtual assignment operator
virtual ~VertexAttributeArray() = default;
/// Number of maximum allocated elements
std::size_t allocatedSize() const { return attrs.size(); }
/// Sum of element strides, and the total size of a vertex in the buffer
std::size_t getTotalSize() const;
/// Get the smallest count value of the attribute elements
std::size_t getMinCount() const;
/// Get a attribute element.
/// Returns a pointer to the element on success, or null if the attribute doesn't exists.
const std::unique_ptr<VertexAttribute>& get(const size_t id) const;
/// Set a new attribute element or replace the existing one.
/// Returns a pointer to the new element on success, or null if the attribute already exists.
/// The result is valid only until the next non-const method call on this class.
/// @param index index to match, or -1 for any
/// @param type type to match, or `Invalid` for any
/// @param count Number of items, zero for shared data
const std::unique_ptr<VertexAttribute>& set(const size_t id,
int index = -1,
AttributeDataType type = AttributeDataType::Invalid,
std::size_t count = 0);
/// Indicates whether any values have changed
bool isModifiedAfter(std::chrono::duration<double> time) const {
return std::ranges::any_of(attrs, [&](const auto& attr) { return attr && attr->isModifiedAfter(time); });
}
/// Clear the collection
void clear();
/// Do something with each attribute
template <typename Func /* void(VertexAttribute&) */>
void visitAttributes(Func f) const {
std::for_each(attrs.begin(), attrs.end(), [&](const auto& attr) {
if (attr) {
f(*attr);
}
});
}
/// Call the provided delegate with each value, providing the override if one exists.
/// @param foundDelegate Called for each attribute in `default`, with the corresponding override, if present
/// @param missingDelegate Called for any overrides with no corresponding default
template <typename ResolveFunc /* void(const size_t, VertexAttribute&, const std::unique_ptr<VertexAttribute>&) */,
typename MissingFunc /* void(const size_t, const std::unique_ptr<VertexAttribute>&) */>
void resolve(const VertexAttributeArray& overrides, ResolveFunc foundDelegate, MissingFunc missingDelegate) const {
for (size_t id = 0; id < attrs.size(); id++) {
const auto& override = overrides.get(id);
if (const auto& attr = attrs[id]) {
foundDelegate(id, *attr, override);
} else if (override) {
missingDelegate(id, override);
}
}
}
VertexAttributeArray& operator=(VertexAttributeArray&&);
VertexAttributeArray& operator=(const VertexAttributeArray&) = delete;
/// Specialized DataDrivenPaintProperty reader
/// @param binders Property binders for the target shader
/// @param evaluated Evaluated properties
/// @param propertiesAsUniforms [out] A set of string identities for the properties which will be constant, not
/// attributes.
/// @details The property name IDs refer to the "a\_" prefixed values to match the shader definitions.
template <typename... DataDrivenPaintProperty, typename Binders, typename Evaluated>
void readDataDrivenPaintProperties(const Binders& binders,
const Evaluated& evaluated,
StringIDSetsPair* propertiesAsUniforms,
const size_t firstDataDrivenAttrId) {
// Read each property in the type pack
if (propertiesAsUniforms) {
propertiesAsUniforms->first.reserve(sizeof...(DataDrivenPaintProperty));
propertiesAsUniforms->second.reserve(sizeof...(DataDrivenPaintProperty));
}
size_t dataDrivenAttrId = firstDataDrivenAttrId;
(readDataDrivenPaintProperty<DataDrivenPaintProperty>(binders.template get<DataDrivenPaintProperty>(),
isConstant<DataDrivenPaintProperty>(evaluated),
propertiesAsUniforms,
dataDrivenAttrId),
...);
}
/// Specialized DataDrivenPaintProperty reader
/// @param binders Property binders for the target shader
/// @param evaluated Evaluated properties
/// @param propertiesAsUniforms [out] A set of string identities for the properties which will be constant, not
/// attributes.
/// @details The property name IDs refer to the "a\_" prefixed values to match the shader definitions.
template <typename... DataDrivenPaintProperty, typename Binders, typename Evaluated>
void readDataDrivenPaintProperties(const Binders& binders,
const Evaluated& evaluated,
StringIDSetsPair& propertiesAsUniforms,
const size_t firstDataDrivenAttrId) {
// Read each property in the type pack
propertiesAsUniforms.first.reserve(sizeof...(DataDrivenPaintProperty));
propertiesAsUniforms.second.reserve(sizeof...(DataDrivenPaintProperty));
size_t dataDrivenAttrId = firstDataDrivenAttrId;
(readDataDrivenPaintProperty<DataDrivenPaintProperty>(binders.template get<DataDrivenPaintProperty>(),
isConstant<DataDrivenPaintProperty>(evaluated),
&propertiesAsUniforms,
dataDrivenAttrId),
...);
}
protected:
template <typename DataDrivenPaintProperty, typename Evaluated>
static bool isConstant(const Evaluated& evaluated) noexcept {
using PropType = decltype(evaluated.template get<DataDrivenPaintProperty>());
using MethodType = decltype(&std::remove_reference_t<PropType>::isConstant);
static_assert(std::is_nothrow_invocable_v<MethodType, PropType>, "isConstant() must be noexcept");
return evaluated.template get<DataDrivenPaintProperty>().isConstant();
}
/// Place one property from a type pack into an attribute in this collection, replacing if it already exists.
template <typename DataDrivenPaintProperty, typename Binder>
void readDataDrivenPaintProperty(const Binder& binder,
const bool isConstant,
StringIDSetsPair* propertiesAsUniforms,
size_t& dataDrivenAttrId) {
if (!binder) {
return;
}
// Consider each attribute name in the attribute (e.g., pattern_from, pattern_to)
for (std::size_t attrIndex = 0; attrIndex < DataDrivenPaintProperty::AttributeNames.size(); ++attrIndex) {
const auto& attributeName = DataDrivenPaintProperty::AttributeNames[attrIndex];
// Apply the property, or add it to the uniforms collection if it's constant.
if (!isConstant && binder->getVertexCount() > 0) {
using Attribute = typename DataDrivenPaintProperty::Attribute;
if (const auto& attr = set(dataDrivenAttrId)) {
applyPaintProperty<Attribute>(attrIndex, attr, binder);
}
} else if (propertiesAsUniforms) {
propertiesAsUniforms->first.emplace(attributeName);
propertiesAsUniforms->second.emplace(dataDrivenAttrId);
}
dataDrivenAttrId++;
}
}
/// Copy or share the attribute data from a paint property
template <typename TAttribute, typename TBinder>
static void applyPaintProperty(const std::size_t attrIndex, const UniqueVertexAttribute& attrib, TBinder& binder) {
using Type = typename TAttribute::Type; // ::mln::gfx::AttributeType<type_, n_>
using InterpType = ZoomInterpolatedAttributeType<Type>;
if (!attrib) {
assert(!"Failed to add attribute");
return;
}
if (binder->interleavedVertexBuffer) {
const auto& interleavedSharedVector = binder->interleavedVertexBuffer->sharedVertexVector;
const auto rawSize = static_cast<uint32_t>(binder->interleavedVertexBuffer->stride);
const bool isInterpolated = binder->isInterpolated();
const auto dataType = isInterpolated ? InterpType::DataType : Type::DataType;
assert(interleavedSharedVector->getRawCount() / binder->interleavedVertexBuffer->stride ==
binder->getVertexCount());
attrib->setSharedRawData(
std::move(interleavedSharedVector), static_cast<uint32_t>(binder->vertexOffset), 0, rawSize, dataType);
} else {
const auto vertexCount = binder->getVertexCount();
for (std::size_t i = 0; i < vertexCount; ++i) {
attrib->set(i, binder->getVertexValue(i), attrIndex);
}
}
}
virtual UniqueVertexAttribute create(int index, AttributeDataType dataType, std::size_t count) const {
return std::make_unique<VertexAttribute>(index, dataType, count);
}
virtual UniqueVertexAttribute copy(const gfx::VertexAttribute& attr) const {
return std::make_unique<VertexAttribute>(attr);
}
protected:
AttributeVector attrs;
static const std::unique_ptr<VertexAttribute> nullref;
static const std::string attributePrefix;
};
} // namespace gfx
} // namespace mln

View File

@@ -0,0 +1,42 @@
#pragma once
#include <memory>
#include <cassert>
namespace mln {
namespace gfx {
class VertexBufferResource {
protected:
VertexBufferResource() noexcept = default;
public:
virtual ~VertexBufferResource() noexcept = default;
};
using UniqueVertexBufferResource = std::unique_ptr<VertexBufferResource>;
// This class has a template argument that we use to specify the vertex type. It
// is not used by the implementation, but serves type checking purposes during
// build time.
template <class>
class VertexBuffer {
public:
VertexBuffer(const std::size_t elements_, UniqueVertexBufferResource&& resource_)
: elements(elements_),
resource(std::move(resource_)) {}
std::size_t elements;
template <typename T = VertexBufferResource>
T& getResource() const {
assert(resource);
return static_cast<T&>(*resource);
}
protected:
UniqueVertexBufferResource resource;
};
} // namespace gfx
} // namespace mln