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:
69
include/mln/gfx/backend.hpp
Normal file
69
include/mln/gfx/backend.hpp
Normal 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
|
||||
38
include/mln/gfx/backend_scope.hpp
Normal file
38
include/mln/gfx/backend_scope.hpp
Normal 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
|
||||
89
include/mln/gfx/color_mode.hpp
Normal file
89
include/mln/gfx/color_mode.hpp
Normal 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
|
||||
39
include/mln/gfx/command_encoder.hpp
Normal file
39
include/mln/gfx/command_encoder.hpp
Normal 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
195
include/mln/gfx/context.hpp
Normal 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
|
||||
20
include/mln/gfx/context_observer.hpp
Normal file
20
include/mln/gfx/context_observer.hpp
Normal 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
|
||||
30
include/mln/gfx/debug_group.hpp
Normal file
30
include/mln/gfx/debug_group.hpp
Normal 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
|
||||
27
include/mln/gfx/depth_mode.hpp
Normal file
27
include/mln/gfx/depth_mode.hpp
Normal 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
|
||||
94
include/mln/gfx/draw_mode.hpp
Normal file
94
include/mln/gfx/draw_mode.hpp
Normal 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
|
||||
33
include/mln/gfx/draw_scope.hpp
Normal file
33
include/mln/gfx/draw_scope.hpp
Normal 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
|
||||
343
include/mln/gfx/drawable.hpp
Normal file
343
include/mln/gfx/drawable.hpp
Normal 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
|
||||
64
include/mln/gfx/drawable_atlases_tweaker.hpp
Normal file
64
include/mln/gfx/drawable_atlases_tweaker.hpp
Normal 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
|
||||
258
include/mln/gfx/drawable_builder.hpp
Normal file
258
include/mln/gfx/drawable_builder.hpp
Normal 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
|
||||
35
include/mln/gfx/drawable_custom_layer_host_tweaker.hpp
Normal file
35
include/mln/gfx/drawable_custom_layer_host_tweaker.hpp
Normal 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
|
||||
17
include/mln/gfx/drawable_data.hpp
Normal file
17
include/mln/gfx/drawable_data.hpp
Normal 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
|
||||
28
include/mln/gfx/drawable_impl.hpp
Normal file
28
include/mln/gfx/drawable_impl.hpp
Normal 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
|
||||
33
include/mln/gfx/drawable_tweaker.hpp
Normal file
33
include/mln/gfx/drawable_tweaker.hpp
Normal 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
|
||||
78
include/mln/gfx/dynamic_texture.hpp
Normal file
78
include/mln/gfx/dynamic_texture.hpp
Normal 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
|
||||
50
include/mln/gfx/dynamic_texture_atlas.hpp
Normal file
50
include/mln/gfx/dynamic_texture_atlas.hpp
Normal 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
|
||||
48
include/mln/gfx/fill_generator.hpp
Normal file
48
include/mln/gfx/fill_generator.hpp
Normal 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
|
||||
68
include/mln/gfx/gfx_types.hpp
Normal file
68
include/mln/gfx/gfx_types.hpp
Normal 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
|
||||
104
include/mln/gfx/gpu_expression.hpp
Normal file
104
include/mln/gfx/gpu_expression.hpp
Normal 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
|
||||
103
include/mln/gfx/polyline_generator.hpp
Normal file
103
include/mln/gfx/polyline_generator.hpp
Normal 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
|
||||
59
include/mln/gfx/renderable.hpp
Normal file
59
include/mln/gfx/renderable.hpp
Normal 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
|
||||
46
include/mln/gfx/renderbuffer.hpp
Normal file
46
include/mln/gfx/renderbuffer.hpp
Normal 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
|
||||
88
include/mln/gfx/renderer_backend.hpp
Normal file
88
include/mln/gfx/renderer_backend.hpp
Normal 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
|
||||
133
include/mln/gfx/rendering_stats.hpp
Normal file
133
include/mln/gfx/rendering_stats.hpp
Normal 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
|
||||
21
include/mln/gfx/scissor_rect.hpp
Normal file
21
include/mln/gfx/scissor_rect.hpp
Normal 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
|
||||
48
include/mln/gfx/shader.hpp
Normal file
48
include/mln/gfx/shader.hpp
Normal 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
|
||||
184
include/mln/gfx/shader_group.hpp
Normal file
184
include/mln/gfx/shader_group.hpp
Normal 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
|
||||
69
include/mln/gfx/shader_registry.hpp
Normal file
69
include/mln/gfx/shader_registry.hpp
Normal 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
|
||||
53
include/mln/gfx/stencil_mode.hpp
Normal file
53
include/mln/gfx/stencil_mode.hpp
Normal 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
|
||||
128
include/mln/gfx/texture2d.hpp
Normal file
128
include/mln/gfx/texture2d.hpp
Normal 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
145
include/mln/gfx/types.hpp
Normal 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
|
||||
93
include/mln/gfx/uniform_buffer.hpp
Normal file
93
include/mln/gfx/uniform_buffer.hpp
Normal 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
|
||||
489
include/mln/gfx/vertex_attribute.hpp
Normal file
489
include/mln/gfx/vertex_attribute.hpp
Normal 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
|
||||
42
include/mln/gfx/vertex_buffer.hpp
Normal file
42
include/mln/gfx/vertex_buffer.hpp
Normal 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
|
||||
Reference in New Issue
Block a user