Refactor bindings to allow for eliminating duplicates... (#1831)
Co-authored-by: Bart Louwers <bart@emeel.net>
This commit is contained in:
@@ -68,13 +68,18 @@ public:
|
||||
std::shared_ptr<UniformBuffer> uniformBuffer);
|
||||
|
||||
/// Create and add a new buffer or update an existing one
|
||||
void createOrUpdate(const StringIdentity id, const std::vector<uint8_t>& data, gfx::Context&);
|
||||
void createOrUpdate(const StringIdentity id, const void* data, std::size_t size, gfx::Context&);
|
||||
void createOrUpdate(const StringIdentity id,
|
||||
const std::vector<uint8_t>& data,
|
||||
gfx::Context&,
|
||||
bool persistent = false);
|
||||
void createOrUpdate(
|
||||
const StringIdentity id, const void* data, std::size_t size, gfx::Context&, bool persistent = false);
|
||||
template <typename T>
|
||||
std::enable_if_t<!std::is_pointer_v<T>> createOrUpdate(const StringIdentity id,
|
||||
const T* data,
|
||||
gfx::Context& context) {
|
||||
createOrUpdate(id, data, sizeof(T), context);
|
||||
gfx::Context& context,
|
||||
bool persistent = false) {
|
||||
createOrUpdate(id, data, sizeof(T), context, persistent);
|
||||
}
|
||||
|
||||
UniformBufferArray& operator=(UniformBufferArray&&);
|
||||
|
||||
@@ -14,37 +14,79 @@ class Context;
|
||||
|
||||
class BufferResource {
|
||||
public:
|
||||
BufferResource() = delete;
|
||||
BufferResource() noexcept = delete;
|
||||
/** @brief Create a new Metal buffer
|
||||
@param device The Metal device on which to create the buffer.
|
||||
@param raw Data to use for the contents of the new buffer. May be null.
|
||||
@param size The minimum size of the new buffer. Must be non-zero.
|
||||
@param usage A `MTL::ResourceOptions` value. Currently, only `ResourceStorageModeShared` is supported.
|
||||
@param isIndexBuffer True if the buffer will be used for indexes. The Metal API only accepts `MTLBuffer`
|
||||
objects for drawing indexed primitives, so this constrains how the buffer can be managed.
|
||||
@param persistent Performance hint, assume this buffer will be reused many times.
|
||||
*/
|
||||
BufferResource(Context& context_, const void* raw, std::size_t size, MTL::ResourceOptions usage);
|
||||
BufferResource(BufferResource&&);
|
||||
virtual ~BufferResource();
|
||||
BufferResource(Context& context_,
|
||||
const void* raw,
|
||||
std::size_t size,
|
||||
MTL::ResourceOptions usage,
|
||||
bool isIndexBuffer,
|
||||
bool persistent);
|
||||
BufferResource(BufferResource&&) noexcept;
|
||||
virtual ~BufferResource() noexcept;
|
||||
|
||||
BufferResource& operator=(BufferResource&&);
|
||||
BufferResource& operator=(BufferResource&&) noexcept;
|
||||
|
||||
BufferResource clone() const;
|
||||
|
||||
void update(const void* data, std::size_t size, std::size_t offset);
|
||||
void update(const void* data, std::size_t size, std::size_t offset) noexcept;
|
||||
|
||||
std::size_t getSizeInBytes() const { return buffer ? buffer->length() : 0; }
|
||||
void* contents() const { return buffer ? buffer->contents() : nullptr; }
|
||||
std::size_t getSizeInBytes() const noexcept { return size; }
|
||||
const void* contents() const noexcept { return buffer ? buffer->contents() : (raw.empty() ? nullptr : raw.data()); }
|
||||
|
||||
Context& getContext() const { return context; }
|
||||
const MTLBufferPtr& getMetalBuffer() const { return buffer; }
|
||||
Context& getContext() const noexcept { return context; }
|
||||
const MTLBufferPtr& getMetalBuffer() const noexcept { return buffer; }
|
||||
|
||||
operator bool() const { return buffer.operator bool(); }
|
||||
bool operator!() const { return !buffer.operator bool(); }
|
||||
bool isValid() const noexcept { return buffer || !raw.empty(); }
|
||||
operator bool() const noexcept { return isValid(); }
|
||||
bool operator!() const noexcept { return !isValid(); }
|
||||
|
||||
using VersionType = std::uint16_t;
|
||||
|
||||
/// Used to detect whether buffer contents have changed
|
||||
VersionType getVersion() const noexcept { return version; }
|
||||
|
||||
/// Indicates whether this buffer needs to be re-bound from a previous binding at the given version
|
||||
bool needReBind(VersionType version) const noexcept;
|
||||
|
||||
/// Bind this buffer to the specified vertex buffer index
|
||||
void bindVertex(const MTLRenderCommandEncoderPtr&,
|
||||
std::size_t offset,
|
||||
std::size_t index,
|
||||
std::size_t size) const noexcept;
|
||||
/// Bind this buffer to the specified fragment buffer index
|
||||
void bindFragment(const MTLRenderCommandEncoderPtr&,
|
||||
std::size_t offset,
|
||||
std::size_t index,
|
||||
std::size_t size) const noexcept;
|
||||
|
||||
/// Update the offset, when this buffer is alread bound to the specified index (unchecked).
|
||||
void updateVertexBindOffset(const MTLRenderCommandEncoderPtr&,
|
||||
std::size_t offset,
|
||||
std::size_t index,
|
||||
std::size_t size) const noexcept;
|
||||
void updateFragmentBindOffset(const MTLRenderCommandEncoderPtr&,
|
||||
std::size_t offset,
|
||||
std::size_t index,
|
||||
std::size_t size) const noexcept;
|
||||
|
||||
protected:
|
||||
Context& context;
|
||||
MTLBufferPtr buffer;
|
||||
std::vector<std::uint8_t> raw;
|
||||
NS::UInteger size;
|
||||
NS::UInteger usage;
|
||||
std::uint16_t version = 0;
|
||||
bool isIndexBuffer;
|
||||
bool persistent;
|
||||
};
|
||||
|
||||
} // namespace mtl
|
||||
|
||||
@@ -30,8 +30,10 @@ namespace mtl {
|
||||
class RenderPass;
|
||||
class RendererBackend;
|
||||
class ShaderProgram;
|
||||
class VertexBufferResource;
|
||||
|
||||
using UniqueShaderProgram = std::unique_ptr<ShaderProgram>;
|
||||
using UniqueVertexBufferResource = std::unique_ptr<VertexBufferResource>;
|
||||
|
||||
class Context final : public gfx::Context {
|
||||
public:
|
||||
@@ -44,7 +46,15 @@ public:
|
||||
|
||||
std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;
|
||||
|
||||
BufferResource createBuffer(const void* data, std::size_t size, gfx::BufferUsageType) const;
|
||||
/// Create a new buffer object
|
||||
/// @param data The raw data to copy, may be `nullptr`
|
||||
/// @param size The size of the buffer
|
||||
/// @param usage Not currently used
|
||||
/// @param isIndexBuffer True if the buffer will be used for indexes. The Metal API only accepts `MTLBuffer`
|
||||
/// objects for drawing indexed primitives, so this constrains how the buffer can be managed.
|
||||
/// @param persistent Performance hint, assume this buffer will be reused many times.
|
||||
BufferResource createBuffer(
|
||||
const void* data, std::size_t size, gfx::BufferUsageType usage, bool isIndexBuffer, bool persistent) const;
|
||||
|
||||
UniqueShaderProgram createProgram(std::string name,
|
||||
std::string_view source,
|
||||
@@ -64,7 +74,7 @@ public:
|
||||
void reduceMemoryUsage() override {}
|
||||
|
||||
gfx::UniqueDrawableBuilder createDrawableBuilder(std::string name) override;
|
||||
gfx::UniformBufferPtr createUniformBuffer(const void* data, std::size_t size) override;
|
||||
gfx::UniformBufferPtr createUniformBuffer(const void* data, std::size_t size, bool persistent) override;
|
||||
|
||||
gfx::ShaderProgramBasePtr getGenericShader(gfx::ShaderRegistry&, const std::string& name) override;
|
||||
|
||||
@@ -94,10 +104,6 @@ public:
|
||||
Size size) override;
|
||||
|
||||
std::unique_ptr<gfx::DrawScopeResource> createDrawScopeResource() override;
|
||||
/*
|
||||
UniqueFramebuffer createFramebuffer();
|
||||
std::unique_ptr<uint8_t[]> readFramebuffer(Size, gfx::TexturePixelType, bool flip);
|
||||
*/
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
void visualizeStencilBuffer() override;
|
||||
@@ -110,7 +116,10 @@ public:
|
||||
const gfx::StencilMode&,
|
||||
const gfx::Renderable&) const;
|
||||
|
||||
virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&, const void* data, std::size_t size);
|
||||
virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&,
|
||||
const void* data,
|
||||
std::size_t size,
|
||||
bool persistent);
|
||||
|
||||
/// Get a reusable buffer containing the standard fixed tile vertices (+/- `util::EXTENT`)
|
||||
const BufferResource& getTileVertexBuffer();
|
||||
@@ -119,7 +128,7 @@ public:
|
||||
const BufferResource& getTileIndexBuffer();
|
||||
|
||||
/// Get a buffer to be bound to unused vertex buffers
|
||||
const gfx::UniqueVertexBufferResource& getEmptyVertexBuffer();
|
||||
const UniqueVertexBufferResource& getEmptyVertexBuffer();
|
||||
|
||||
bool renderTileClippingMasks(gfx::RenderPass& renderPass,
|
||||
RenderStaticData& staticData,
|
||||
@@ -132,7 +141,7 @@ private:
|
||||
std::optional<BufferResource> tileVertexBuffer;
|
||||
std::optional<BufferResource> tileIndexBuffer;
|
||||
|
||||
gfx::UniqueVertexBufferResource emptyVertexBuffer;
|
||||
UniqueVertexBufferResource emptyVertexBuffer;
|
||||
|
||||
gfx::ShaderProgramBasePtr clipMaskShader;
|
||||
MTLDepthStencilStatePtr clipMaskDepthStencilState;
|
||||
|
||||
@@ -66,16 +66,16 @@ protected:
|
||||
// For testing only.
|
||||
Drawable(std::unique_ptr<Impl>);
|
||||
|
||||
void bindAttributes(const RenderPass& renderPass) const;
|
||||
void unbindAttributes(const RenderPass& renderPass) const;
|
||||
void bindAttributes(RenderPass&) const noexcept;
|
||||
void unbindAttributes(RenderPass&) const noexcept {}
|
||||
|
||||
void bindUniformBuffers(const RenderPass& renderPass) const;
|
||||
void unbindUniformBuffers(const RenderPass& renderPass) const;
|
||||
void bindUniformBuffers(RenderPass&) const noexcept;
|
||||
void unbindUniformBuffers(RenderPass&) const noexcept {}
|
||||
|
||||
void bindTextures(const RenderPass& renderPass) const;
|
||||
void unbindTextures(const RenderPass& renderPass) const;
|
||||
void bindTextures(RenderPass&) const noexcept;
|
||||
void unbindTextures(RenderPass&) const noexcept;
|
||||
|
||||
void uploadTextures() const;
|
||||
void uploadTextures(UploadPass&) const noexcept;
|
||||
|
||||
class Impl;
|
||||
const std::unique_ptr<Impl> impl;
|
||||
|
||||
@@ -8,17 +8,17 @@ namespace mtl {
|
||||
|
||||
class IndexBufferResource : public gfx::IndexBufferResource {
|
||||
public:
|
||||
IndexBufferResource() = default;
|
||||
IndexBufferResource(BufferResource&&);
|
||||
IndexBufferResource(IndexBufferResource&& other)
|
||||
IndexBufferResource() noexcept = default;
|
||||
IndexBufferResource(BufferResource&&) noexcept;
|
||||
IndexBufferResource(IndexBufferResource&& other) noexcept
|
||||
: buffer(std::move(other.buffer)) {}
|
||||
~IndexBufferResource() override;
|
||||
~IndexBufferResource() noexcept override;
|
||||
|
||||
std::size_t getSizeInBytes() const { return buffer.getSizeInBytes(); }
|
||||
void* contents() const { return buffer.contents(); }
|
||||
std::size_t getSizeInBytes() const noexcept { return buffer.getSizeInBytes(); }
|
||||
const void* contents() const noexcept { return buffer.contents(); }
|
||||
|
||||
BufferResource& get() { return buffer; }
|
||||
const BufferResource& get() const { return buffer; }
|
||||
BufferResource& get() noexcept { return buffer; }
|
||||
const BufferResource& get() const noexcept { return buffer; }
|
||||
|
||||
protected:
|
||||
BufferResource buffer;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace mbgl {
|
||||
namespace mtl {
|
||||
|
||||
class BufferResource;
|
||||
class CommandEncoder;
|
||||
class Context;
|
||||
|
||||
@@ -27,6 +28,10 @@ public:
|
||||
|
||||
void addDebugSignpost(const char* name) override;
|
||||
|
||||
void bindVertex(const BufferResource&, std::size_t offset, std::size_t index, std::size_t size = 0);
|
||||
|
||||
void bindFragment(const BufferResource&, std::size_t offset, std::size_t index, std::size_t size = 0);
|
||||
|
||||
private:
|
||||
void pushDebugGroup(const char* name) override;
|
||||
void popDebugGroup() override;
|
||||
@@ -36,6 +41,15 @@ private:
|
||||
mtl::CommandEncoder& commandEncoder;
|
||||
MTLRenderCommandEncoderPtr encoder;
|
||||
std::vector<gfx::DebugGroup<gfx::RenderPass>> debugGroups;
|
||||
|
||||
struct BindInfo {
|
||||
const BufferResource* buf = nullptr;
|
||||
NS::UInteger offset = 0;
|
||||
std::uint16_t version = 0;
|
||||
};
|
||||
static constexpr auto maxBinds = 32;
|
||||
std::array<std::optional<BindInfo>, maxBinds> vertexBinds;
|
||||
std::array<std::optional<BindInfo>, maxBinds> fragmentBinds;
|
||||
};
|
||||
|
||||
} // namespace mtl
|
||||
|
||||
@@ -44,12 +44,14 @@ public:
|
||||
|
||||
std::unique_ptr<gfx::VertexBufferResource> createVertexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
gfx::BufferUsageType) override;
|
||||
gfx::BufferUsageType,
|
||||
bool persistent) override;
|
||||
void updateVertexBufferResource(gfx::VertexBufferResource&, const void* data, std::size_t size) override;
|
||||
|
||||
std::unique_ptr<gfx::IndexBufferResource> createIndexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
gfx::BufferUsageType) override;
|
||||
gfx::BufferUsageType,
|
||||
bool persistent) override;
|
||||
void updateIndexBufferResource(gfx::IndexBufferResource&, const void* data, std::size_t size) override;
|
||||
|
||||
void updateResource(BufferResource&, const void* data, std::size_t size);
|
||||
|
||||
@@ -2,26 +2,30 @@
|
||||
|
||||
#include <mbgl/mtl/buffer_resource.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mbgl {
|
||||
namespace mtl {
|
||||
|
||||
class VertexBufferResource : public gfx::VertexBufferResource {
|
||||
public:
|
||||
VertexBufferResource() = default;
|
||||
VertexBufferResource(BufferResource&&);
|
||||
VertexBufferResource(VertexBufferResource&& other)
|
||||
VertexBufferResource() noexcept = default;
|
||||
VertexBufferResource(BufferResource&&) noexcept;
|
||||
VertexBufferResource(VertexBufferResource&& other) noexcept
|
||||
: buffer(std::move(other.buffer)) {}
|
||||
~VertexBufferResource() override;
|
||||
~VertexBufferResource() noexcept override;
|
||||
|
||||
std::size_t getSizeInBytes() const { return buffer.getSizeInBytes(); }
|
||||
void* contents() const { return buffer.contents(); }
|
||||
std::size_t getSizeInBytes() const noexcept { return buffer.getSizeInBytes(); }
|
||||
const void* contents() const noexcept { return buffer.contents(); }
|
||||
|
||||
BufferResource& get() { return buffer; }
|
||||
const BufferResource& get() const { return buffer; }
|
||||
BufferResource& get() noexcept { return buffer; }
|
||||
const BufferResource& get() const noexcept { return buffer; }
|
||||
|
||||
protected:
|
||||
BufferResource buffer;
|
||||
};
|
||||
|
||||
using UniqueVertexBufferResource = std::unique_ptr<VertexBufferResource>;
|
||||
|
||||
} // namespace mtl
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -123,7 +123,10 @@ public:
|
||||
virtual UniqueDrawableBuilder createDrawableBuilder(std::string name) = 0;
|
||||
|
||||
/// Create a new uniform buffer
|
||||
virtual UniformBufferPtr createUniformBuffer(const void* data, std::size_t size) = 0;
|
||||
/// @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) = 0;
|
||||
|
||||
/// Get the generic shader with the specified name
|
||||
virtual gfx::ShaderProgramBasePtr getGenericShader(gfx::ShaderRegistry&, const std::string& name) = 0;
|
||||
@@ -147,13 +150,17 @@ public:
|
||||
|
||||
/// 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) = 0;
|
||||
virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&,
|
||||
const void* data,
|
||||
std::size_t size,
|
||||
bool persistent = false) = 0;
|
||||
|
||||
/// `emplaceOrUpdateUniformBuffer` with type inference
|
||||
template <typename T>
|
||||
std::enable_if_t<!std::is_pointer_v<T>, bool> emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& ptr,
|
||||
const T* data) {
|
||||
return emplaceOrUpdateUniformBuffer(ptr, data, sizeof(T));
|
||||
const T* data,
|
||||
bool persistent = false) {
|
||||
return emplaceOrUpdateUniformBuffer(ptr, data, sizeof(T), persistent);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,10 +8,10 @@ namespace gfx {
|
||||
|
||||
class IndexBufferResource {
|
||||
protected:
|
||||
IndexBufferResource() = default;
|
||||
IndexBufferResource() noexcept = default;
|
||||
|
||||
public:
|
||||
virtual ~IndexBufferResource() = default;
|
||||
virtual ~IndexBufferResource() noexcept = default;
|
||||
};
|
||||
|
||||
class IndexBuffer {
|
||||
|
||||
@@ -37,18 +37,17 @@ const std::shared_ptr<UniformBuffer>& UniformBufferArray::addOrReplace(const Str
|
||||
|
||||
void UniformBufferArray::createOrUpdate(const StringIdentity id,
|
||||
const std::vector<uint8_t>& data,
|
||||
gfx::Context& context) {
|
||||
createOrUpdate(id, data.data(), data.size(), context);
|
||||
gfx::Context& context,
|
||||
bool persistent) {
|
||||
createOrUpdate(id, data.data(), data.size(), context, persistent);
|
||||
}
|
||||
|
||||
void UniformBufferArray::createOrUpdate(const StringIdentity id,
|
||||
const void* data,
|
||||
const std::size_t size,
|
||||
gfx::Context& context) {
|
||||
void UniformBufferArray::createOrUpdate(
|
||||
const StringIdentity id, const void* data, const std::size_t size, gfx::Context& context, bool persistent) {
|
||||
if (auto& ubo = get(id); ubo && ubo->getSize() == size) {
|
||||
ubo->update(data, size);
|
||||
} else {
|
||||
add(id, context.createUniformBuffer(data, size));
|
||||
add(id, context.createUniformBuffer(data, size, persistent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,9 @@ public:
|
||||
public:
|
||||
template <class Vertex>
|
||||
VertexBuffer<Vertex> createVertexBuffer(const VertexVector<Vertex>& v,
|
||||
const BufferUsageType usage = BufferUsageType::StaticDraw) {
|
||||
return {v.elements(), createVertexBufferResource(v.data(), v.bytes(), usage)};
|
||||
const BufferUsageType usage = BufferUsageType::StaticDraw,
|
||||
bool persistent = false) {
|
||||
return {v.elements(), createVertexBufferResource(v.data(), v.bytes(), usage, persistent)};
|
||||
}
|
||||
|
||||
template <class Vertex>
|
||||
@@ -66,8 +67,9 @@ public:
|
||||
|
||||
template <class DrawMode>
|
||||
IndexBuffer createIndexBuffer(IndexVector<DrawMode>&& v,
|
||||
const BufferUsageType usage = BufferUsageType::StaticDraw) {
|
||||
return {v.elements(), createIndexBufferResource(v.data(), v.bytes(), usage)};
|
||||
const BufferUsageType usage = BufferUsageType::StaticDraw,
|
||||
bool persistent = false) {
|
||||
return {v.elements(), createIndexBufferResource(v.data(), v.bytes(), usage, persistent)};
|
||||
}
|
||||
|
||||
template <class DrawMode>
|
||||
@@ -91,13 +93,15 @@ public:
|
||||
protected:
|
||||
virtual std::unique_ptr<VertexBufferResource> createVertexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
BufferUsageType) = 0;
|
||||
BufferUsageType,
|
||||
bool persistent = false) = 0;
|
||||
virtual void updateVertexBufferResource(VertexBufferResource&, const void* data, std::size_t size) = 0;
|
||||
|
||||
public:
|
||||
virtual std::unique_ptr<IndexBufferResource> createIndexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
BufferUsageType) = 0;
|
||||
BufferUsageType,
|
||||
bool persistent = false) = 0;
|
||||
virtual void updateIndexBufferResource(IndexBufferResource&, const void* data, std::size_t size) = 0;
|
||||
|
||||
public:
|
||||
|
||||
@@ -8,10 +8,10 @@ namespace gfx {
|
||||
|
||||
class VertexBufferResource {
|
||||
protected:
|
||||
VertexBufferResource() = default;
|
||||
VertexBufferResource() noexcept = default;
|
||||
|
||||
public:
|
||||
virtual ~VertexBufferResource() = default;
|
||||
virtual ~VertexBufferResource() noexcept = default;
|
||||
};
|
||||
|
||||
using UniqueVertexBufferResource = std::unique_ptr<VertexBufferResource>;
|
||||
|
||||
@@ -436,12 +436,15 @@ void Context::resetState(gfx::DepthMode depthMode, gfx::ColorMode colorMode) {
|
||||
setCullFaceMode(gfx::CullFaceMode::disabled());
|
||||
}
|
||||
|
||||
bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer, const void* data, std::size_t size) {
|
||||
bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer,
|
||||
const void* data,
|
||||
std::size_t size,
|
||||
bool persistent) {
|
||||
if (buffer) {
|
||||
buffer->update(data, size);
|
||||
return false;
|
||||
} else {
|
||||
buffer = createUniformBuffer(data, size);
|
||||
buffer = createUniformBuffer(data, size, persistent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -487,7 +490,7 @@ gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) {
|
||||
return std::make_unique<gl::DrawableGLBuilder>(std::move(name));
|
||||
}
|
||||
|
||||
gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size) {
|
||||
gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size, bool /*persistent*/) {
|
||||
return std::make_shared<gl::UniformBufferGL>(data, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
gfx::UniqueDrawableBuilder createDrawableBuilder(std::string name) override;
|
||||
gfx::UniformBufferPtr createUniformBuffer(const void* data, std::size_t size) override;
|
||||
gfx::UniformBufferPtr createUniformBuffer(const void* data, std::size_t size, bool persistent) override;
|
||||
|
||||
gfx::ShaderProgramBasePtr getGenericShader(gfx::ShaderRegistry&, const std::string& name) override;
|
||||
|
||||
@@ -119,7 +119,10 @@ public:
|
||||
|
||||
void resetState(gfx::DepthMode depthMode, gfx::ColorMode colorMode) override;
|
||||
|
||||
bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&, const void* data, std::size_t size) override;
|
||||
bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&,
|
||||
const void* data,
|
||||
std::size_t size,
|
||||
bool persistent) override;
|
||||
#endif
|
||||
|
||||
void setDirtyState() override;
|
||||
|
||||
@@ -29,7 +29,8 @@ UploadPass::UploadPass(gl::CommandEncoder& commandEncoder_, const char* name)
|
||||
|
||||
std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResource(const void* data,
|
||||
const std::size_t size,
|
||||
const gfx::BufferUsageType usage) {
|
||||
const gfx::BufferUsageType usage,
|
||||
bool /*persistent*/) {
|
||||
BufferID id = 0;
|
||||
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
|
||||
commandEncoder.context.renderingStats().numBuffers++;
|
||||
@@ -48,7 +49,8 @@ void UploadPass::updateVertexBufferResource(gfx::VertexBufferResource& resource,
|
||||
|
||||
std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
const gfx::BufferUsageType usage) {
|
||||
const gfx::BufferUsageType usage,
|
||||
bool /*persistent*/) {
|
||||
BufferID id = 0;
|
||||
MBGL_CHECK_ERROR(glGenBuffers(1, &id));
|
||||
commandEncoder.context.renderingStats().numBuffers++;
|
||||
@@ -161,7 +163,7 @@ const gfx::UniqueVertexBufferResource& UploadPass::getBuffer(const gfx::VertexVe
|
||||
// Otherwise, create a new one
|
||||
if (rawBufSize > 0) {
|
||||
auto buffer = std::make_unique<VertexBufferGL>();
|
||||
buffer->resource = createVertexBufferResource(rawBufPtr, rawBufSize, usage);
|
||||
buffer->resource = createVertexBufferResource(rawBufPtr, rawBufSize, usage, /*persistent=*/false);
|
||||
vec->setBuffer(std::move(buffer));
|
||||
vec->setDirty(false);
|
||||
return static_cast<VertexBufferGL*>(vec->getBuffer())->resource;
|
||||
@@ -283,7 +285,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
|
||||
assert(vertexStride * vertexCount <= allData.size());
|
||||
|
||||
if (!allData.empty()) {
|
||||
if (auto vertBuf = createVertexBufferResource(allData.data(), allData.size(), usage)) {
|
||||
if (auto vertBuf = createVertexBufferResource(allData.data(), allData.size(), usage, /*persistent=*/false)) {
|
||||
// Fill in the buffer in each binding that was generated without its own buffer
|
||||
std::for_each(bindings.begin(), bindings.end(), [&](auto& b) {
|
||||
if (b && !b->vertexBufferResource) {
|
||||
|
||||
@@ -35,11 +35,14 @@ public:
|
||||
|
||||
std::unique_ptr<gfx::VertexBufferResource> createVertexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
gfx::BufferUsageType) override;
|
||||
gfx::BufferUsageType,
|
||||
bool persistent) override;
|
||||
void updateVertexBufferResource(gfx::VertexBufferResource&, const void* data, std::size_t size) override;
|
||||
|
||||
std::unique_ptr<gfx::IndexBufferResource> createIndexBufferResource(const void* data,
|
||||
std::size_t size,
|
||||
gfx::BufferUsageType) override;
|
||||
gfx::BufferUsageType,
|
||||
bool persistent) override;
|
||||
void updateIndexBufferResource(gfx::IndexBufferResource&, const void* data, std::size_t size) override;
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
|
||||
@@ -4,62 +4,158 @@
|
||||
#include <mbgl/mtl/renderer_backend.hpp>
|
||||
|
||||
#include <Metal/MTLDevice.hpp>
|
||||
#include <Metal/MTLRenderCommandEncoder.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mbgl {
|
||||
namespace mtl {
|
||||
|
||||
BufferResource::BufferResource(Context& context_, const void* data, std::size_t size_, MTL::ResourceOptions usage_)
|
||||
namespace {
|
||||
// The `setVertexBytes:length:atIndex:` method is the best option for binding a very small
|
||||
// amount (less than 4 KB) of dynamic buffer data to a vertex function, as shown in Listing
|
||||
// 5-1. This method avoids the overhead of creating an intermediary MTLBuffer object.
|
||||
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/BufferBindings.html
|
||||
constexpr auto bufferThreshold = 4096;
|
||||
} // namespace
|
||||
|
||||
BufferResource::BufferResource(Context& context_,
|
||||
const void* data,
|
||||
std::size_t size_,
|
||||
MTL::ResourceOptions usage_,
|
||||
bool isIndexBuffer_,
|
||||
bool persistent_)
|
||||
: context(context_),
|
||||
size(static_cast<NS::UInteger>(size_)),
|
||||
usage(usage_) {
|
||||
auto& device = context.getBackend().getDevice();
|
||||
buffer = NS::TransferPtr((data && size) ? device->newBuffer(data, size, usage) : device->newBuffer(size, usage));
|
||||
if (buffer) {
|
||||
usage(usage_),
|
||||
isIndexBuffer(isIndexBuffer_),
|
||||
persistent(persistent_) {
|
||||
// If the buffer is small, not for indexes, and not explicitly persistent, skip the overhead of an `NSBuffer`
|
||||
if (size_ < bufferThreshold && !isIndexBuffer_ && !persistent) {
|
||||
if (data) {
|
||||
raw.assign(static_cast<const std::uint8_t*>(data), static_cast<const std::uint8_t*>(data) + size);
|
||||
} else {
|
||||
raw.resize(size);
|
||||
}
|
||||
assert(raw.size() == size);
|
||||
} else {
|
||||
auto& device = context.getBackend().getDevice();
|
||||
buffer = NS::TransferPtr((data && size) ? device->newBuffer(data, size, usage)
|
||||
: device->newBuffer(size, usage));
|
||||
}
|
||||
|
||||
if (isValid()) {
|
||||
context.renderingStats().numBuffers++;
|
||||
context.renderingStats().memBuffers += size;
|
||||
}
|
||||
}
|
||||
|
||||
BufferResource::BufferResource(BufferResource&& other)
|
||||
BufferResource::BufferResource(BufferResource&& other) noexcept
|
||||
: context(other.context),
|
||||
buffer(std::move(other.buffer)),
|
||||
raw(std::move(other.raw)),
|
||||
size(other.size),
|
||||
usage(other.usage) {}
|
||||
usage(other.usage),
|
||||
isIndexBuffer(other.isIndexBuffer),
|
||||
persistent(other.persistent) {}
|
||||
|
||||
BufferResource::~BufferResource() {
|
||||
if (buffer) {
|
||||
BufferResource::~BufferResource() noexcept {
|
||||
if (isValid()) {
|
||||
context.renderingStats().numBuffers--;
|
||||
context.renderingStats().memBuffers -= size;
|
||||
}
|
||||
}
|
||||
|
||||
BufferResource BufferResource::clone() const {
|
||||
return {context, buffer->contents(), size, usage};
|
||||
return {context, contents(), size, usage, isIndexBuffer, persistent};
|
||||
}
|
||||
|
||||
BufferResource& BufferResource::operator=(BufferResource&& other) {
|
||||
BufferResource& BufferResource::operator=(BufferResource&& other) noexcept {
|
||||
assert(&context == &other.context);
|
||||
if (buffer) {
|
||||
if (isValid()) {
|
||||
context.renderingStats().numBuffers--;
|
||||
context.renderingStats().memBuffers -= size;
|
||||
}
|
||||
buffer = std::move(other.buffer);
|
||||
raw = std::move(other.raw);
|
||||
size = other.size;
|
||||
usage = other.usage;
|
||||
isIndexBuffer = other.isIndexBuffer;
|
||||
persistent = other.persistent;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BufferResource::update(const void* data, std::size_t size, std::size_t offset) {
|
||||
assert(buffer && (data || size == 0));
|
||||
if (buffer && data && size > 0) {
|
||||
assert(buffer->contents());
|
||||
if (void* content = buffer->contents()) {
|
||||
const auto size_ = std::min(size, static_cast<std::size_t>(buffer->length()) - offset);
|
||||
assert(size == size_);
|
||||
std::memcpy(static_cast<uint8_t*>(content) + offset, data, size_);
|
||||
void BufferResource::update(const void* data, std::size_t updateSize, std::size_t offset) noexcept {
|
||||
assert(size >= 0 && updateSize + offset <= size);
|
||||
updateSize = std::min(updateSize, size - offset);
|
||||
|
||||
if (updateSize > 0) {
|
||||
if (buffer) {
|
||||
if (auto* content = static_cast<uint8_t*>(buffer->contents())) {
|
||||
std::memcpy(content + offset, data, updateSize);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(raw.data() + offset, data, updateSize);
|
||||
}
|
||||
version++;
|
||||
}
|
||||
}
|
||||
|
||||
bool BufferResource::needReBind(VersionType version_) const noexcept {
|
||||
// If we're using a raw buffer, an update means we have to re-bind.
|
||||
// For a MTLBuffer, the binding can be left alone.
|
||||
return (!buffer || version != version_);
|
||||
}
|
||||
|
||||
void BufferResource::bindVertex(const MTLRenderCommandEncoderPtr& encoder,
|
||||
const std::size_t offset,
|
||||
const std::size_t index,
|
||||
std::size_t size_) const noexcept {
|
||||
assert(offset + size_ <= size);
|
||||
if (const auto* mtlBuf = buffer.get()) {
|
||||
encoder->setVertexBuffer(mtlBuf, static_cast<NS::UInteger>(offset), static_cast<NS::UInteger>(index));
|
||||
} else if (!raw.empty()) {
|
||||
size_ = size_ ? std::min(size_, size - offset) : size - offset;
|
||||
encoder->setVertexBytes(raw.data() + offset, size_, index);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferResource::bindFragment(const MTLRenderCommandEncoderPtr& encoder,
|
||||
const std::size_t offset,
|
||||
const std::size_t index,
|
||||
std::size_t size_) const noexcept {
|
||||
assert(offset + size_ <= size);
|
||||
if (const auto* mtlBuf = buffer.get()) {
|
||||
encoder->setFragmentBuffer(mtlBuf, static_cast<NS::UInteger>(offset), static_cast<NS::UInteger>(index));
|
||||
} else if (!raw.empty()) {
|
||||
size_ = size_ ? std::min(size_, size - offset) : size - offset;
|
||||
encoder->setFragmentBytes(raw.data() + offset, size_, index);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferResource::updateVertexBindOffset(const MTLRenderCommandEncoderPtr& encoder,
|
||||
std::size_t offset,
|
||||
std::size_t index,
|
||||
std::size_t size_) const noexcept {
|
||||
// If we're using a MTLBuffer, just update the offset.
|
||||
// The documentation for `setVertexBufferOffset` indicates that it should work for buffers
|
||||
// assigned using `setVertexBytes` but, in practice, it produces a validation failure:
|
||||
// `Set Vertex Buffer Offset Validation index(1) must have an existing buffer.`
|
||||
if (const auto* mtlBuf = buffer.get()) {
|
||||
encoder->setVertexBufferOffset(offset, index);
|
||||
} else {
|
||||
bindVertex(encoder, offset, index, size_);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferResource::updateFragmentBindOffset(const MTLRenderCommandEncoderPtr& encoder,
|
||||
std::size_t offset,
|
||||
std::size_t index,
|
||||
std::size_t size_) const noexcept {
|
||||
if (const auto* mtlBuf = buffer.get()) {
|
||||
encoder->setFragmentBufferOffset(offset, index);
|
||||
} else {
|
||||
bindFragment(encoder, offset, index, size_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,9 @@ std::unique_ptr<gfx::CommandEncoder> Context::createCommandEncoder() {
|
||||
return std::make_unique<CommandEncoder>(*this);
|
||||
}
|
||||
|
||||
BufferResource Context::createBuffer(const void* data, std::size_t size, gfx::BufferUsageType) const {
|
||||
return {const_cast<Context&>(*this), data, size, MTL::ResourceStorageModeShared};
|
||||
BufferResource Context::createBuffer(
|
||||
const void* data, std::size_t size, gfx::BufferUsageType, bool isIndexBuffer, bool persistent) const {
|
||||
return {const_cast<Context&>(*this), data, size, MTL::ResourceStorageModeShared, isIndexBuffer, persistent};
|
||||
}
|
||||
|
||||
UniqueShaderProgram Context::createProgram(std::string name,
|
||||
@@ -170,8 +171,9 @@ gfx::UniqueDrawableBuilder Context::createDrawableBuilder(std::string name) {
|
||||
return std::make_unique<DrawableBuilder>(std::move(name));
|
||||
}
|
||||
|
||||
gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size) {
|
||||
return std::make_shared<UniformBuffer>(createBuffer(data, size, gfx::BufferUsageType::StaticDraw));
|
||||
gfx::UniformBufferPtr Context::createUniformBuffer(const void* data, std::size_t size, bool persistent) {
|
||||
return std::make_shared<UniformBuffer>(
|
||||
createBuffer(data, size, gfx::BufferUsageType::StaticDraw, /*isIndexBuffer=*/false, persistent));
|
||||
}
|
||||
|
||||
gfx::ShaderProgramBasePtr Context::getGenericShader(gfx::ShaderRegistry& shaders, const std::string& name) {
|
||||
@@ -198,12 +200,15 @@ RenderTargetPtr Context::createRenderTarget(const Size size, const gfx::TextureC
|
||||
|
||||
void Context::resetState(gfx::DepthMode depthMode, gfx::ColorMode colorMode) {}
|
||||
|
||||
bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer, const void* data, std::size_t size) {
|
||||
bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer,
|
||||
const void* data,
|
||||
std::size_t size,
|
||||
bool persistent) {
|
||||
if (buffer) {
|
||||
buffer->update(data, size);
|
||||
return false;
|
||||
} else {
|
||||
buffer = createUniformBuffer(data, size);
|
||||
buffer = createUniformBuffer(data, size, persistent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -212,7 +217,11 @@ const BufferResource& Context::getTileVertexBuffer() {
|
||||
if (!tileVertexBuffer) {
|
||||
const auto vertices = RenderStaticData::tileVertices();
|
||||
constexpr auto vertexSize = sizeof(decltype(vertices)::Vertex::a1);
|
||||
tileVertexBuffer.emplace(createBuffer(vertices.data(), vertices.bytes(), gfx::BufferUsageType::StaticDraw));
|
||||
tileVertexBuffer.emplace(createBuffer(vertices.data(),
|
||||
vertices.bytes(),
|
||||
gfx::BufferUsageType::StaticDraw,
|
||||
/*isIndexBuffer=*/true,
|
||||
/*persistent=*/true));
|
||||
}
|
||||
return *tileVertexBuffer;
|
||||
}
|
||||
@@ -220,12 +229,16 @@ const BufferResource& Context::getTileVertexBuffer() {
|
||||
const BufferResource& Context::getTileIndexBuffer() {
|
||||
if (!tileIndexBuffer) {
|
||||
const auto indexes = RenderStaticData::quadTriangleIndices();
|
||||
tileIndexBuffer.emplace(createBuffer(indexes.data(), indexes.bytes(), gfx::BufferUsageType::StaticDraw));
|
||||
tileIndexBuffer.emplace(createBuffer(indexes.data(),
|
||||
indexes.bytes(),
|
||||
gfx::BufferUsageType::StaticDraw,
|
||||
/*isIndexBuffer=*/true,
|
||||
/*persistent=*/true));
|
||||
}
|
||||
return *tileIndexBuffer;
|
||||
}
|
||||
|
||||
const gfx::UniqueVertexBufferResource& Context::getEmptyVertexBuffer() {
|
||||
const UniqueVertexBufferResource& Context::getEmptyVertexBuffer() {
|
||||
if (!emptyVertexBuffer) {
|
||||
// This buffer is bound to vertex attribtue indexes when the uniforms are used instead and
|
||||
// shaders are expected not to access the attribute values, but Metal requires a binding.
|
||||
@@ -234,7 +247,8 @@ const gfx::UniqueVertexBufferResource& Context::getEmptyVertexBuffer() {
|
||||
// descriptors, but that isn't currently being used.
|
||||
constexpr std::size_t size = 32;
|
||||
constexpr auto usage = gfx::BufferUsageType::StaticDraw;
|
||||
emptyVertexBuffer = std::make_unique<VertexBufferResource>(createBuffer(nullptr, size, usage));
|
||||
emptyVertexBuffer = std::make_unique<VertexBufferResource>(
|
||||
createBuffer(nullptr, size, usage, /*isIndexBuffer=*/false, /*persistent=*/false));
|
||||
}
|
||||
return emptyVertexBuffer;
|
||||
}
|
||||
@@ -272,7 +286,7 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass,
|
||||
}
|
||||
|
||||
const auto& mtlShader = static_cast<const mtl::ShaderProgram&>(*clipMaskShader);
|
||||
const auto& mtlRenderPass = static_cast<mtl::RenderPass&>(renderPass);
|
||||
auto& mtlRenderPass = static_cast<mtl::RenderPass&>(renderPass);
|
||||
const auto& encoder = mtlRenderPass.getMetalEncoder();
|
||||
const auto colorMode = gfx::ColorMode::disabled();
|
||||
|
||||
@@ -352,7 +366,11 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass,
|
||||
auto& uboBuffer = clipMaskUniformsBufferUsed ? tempBuffer : clipMaskUniformsBuffer;
|
||||
clipMaskUniformsBufferUsed = true;
|
||||
if (!uboBuffer || !*uboBuffer || uboBuffer->getSizeInBytes() < bufferSize) {
|
||||
uboBuffer = createBuffer(tileUBOs.data(), bufferSize, gfx::BufferUsageType::StaticDraw);
|
||||
uboBuffer = createBuffer(tileUBOs.data(),
|
||||
bufferSize,
|
||||
gfx::BufferUsageType::StaticDraw,
|
||||
/*isIndexBuffer=*/false,
|
||||
/*persistent=*/!clipMaskUniformsBufferUsed);
|
||||
if (!uboBuffer) {
|
||||
return false;
|
||||
}
|
||||
@@ -361,7 +379,8 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass,
|
||||
}
|
||||
|
||||
encoder->setCullMode(MTL::CullModeNone);
|
||||
encoder->setVertexBuffer(vertexRes.getMetalBuffer().get(), /*offset=*/0, ShaderClass::attributes[0].index);
|
||||
|
||||
mtlRenderPass.bindVertex(vertexRes, /*offset=*/0, ShaderClass::attributes[0].index);
|
||||
|
||||
// Instancing is disabled for now because the `[[stencil]]` attribute in the fragment shader output
|
||||
// that we need to apply a different stencil value for each tile causes a problem on some older (A8-A11)
|
||||
@@ -379,10 +398,10 @@ bool Context::renderTileClippingMasks(gfx::RenderPass& renderPass,
|
||||
/*baseVertex=*/0,
|
||||
/*baseInstance=*/0);
|
||||
#else
|
||||
const auto uboIndex = ShaderClass::uniforms[0].index;
|
||||
for (std::size_t ii = 0; ii < tileUBOs.size(); ++ii) {
|
||||
encoder->setStencilReferenceValue(tileUBOs[ii].stencil_ref);
|
||||
encoder->setVertexBuffer(
|
||||
uboBuffer->getMetalBuffer().get(), /*offset=*/ii * uboSize, ShaderClass::uniforms[0].index);
|
||||
mtlRenderPass.bindVertex(*uboBuffer, /*offset=*/ii * uboSize, uboIndex, /*size=*/uboSize);
|
||||
encoder->drawIndexedPrimitives(MTL::PrimitiveType::PrimitiveTypeTriangle,
|
||||
indexCount,
|
||||
MTL::IndexType::IndexTypeUInt16,
|
||||
|
||||
@@ -43,16 +43,6 @@ Drawable::Drawable(std::string name_)
|
||||
Drawable::~Drawable() {}
|
||||
|
||||
namespace {
|
||||
// Downcast a generic vertex buffer resource and extract the Metal buffer
|
||||
MTL::Buffer* getMetalBuffer(const gfx::VertexBufferResource* resource_) {
|
||||
if (const auto* resource = static_cast<const VertexBufferResource*>(resource_)) {
|
||||
if (const auto& bufferResource = resource->get()) {
|
||||
return bufferResource.getMetalBuffer().get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
std::size_t getBufferSize(const gfx::VertexBufferResource* resource_) {
|
||||
if (const auto* resource = static_cast<const VertexBufferResource*>(resource_)) {
|
||||
@@ -64,7 +54,7 @@ std::size_t getBufferSize(const gfx::VertexBufferResource* resource_) {
|
||||
}
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
MTL::PrimitiveType getPrimitiveType(const gfx::DrawModeType type) {
|
||||
MTL::PrimitiveType getPrimitiveType(const gfx::DrawModeType type) noexcept {
|
||||
switch (type) {
|
||||
default:
|
||||
assert(false);
|
||||
@@ -88,7 +78,7 @@ std::string debugLabel(const gfx::Drawable& drawable) {
|
||||
}
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
MTL::Buffer* getMetalBuffer(const gfx::IndexVectorBasePtr& indexes) {
|
||||
MTL::Buffer* getMetalBuffer(const gfx::IndexVectorBasePtr& indexes) noexcept {
|
||||
if (const auto* buf0 = indexes->getBuffer()) {
|
||||
if (const auto* buf1 = static_cast<const IndexBuffer*>(buf0)->buffer.get()) {
|
||||
const auto& buf2 = buf1->getResource<IndexBufferResource>().get();
|
||||
@@ -98,7 +88,7 @@ MTL::Buffer* getMetalBuffer(const gfx::IndexVectorBasePtr& indexes) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MTL::CullMode mapCullMode(const gfx::CullFaceSideType mode) {
|
||||
MTL::CullMode mapCullMode(const gfx::CullFaceSideType mode) noexcept {
|
||||
switch (mode) {
|
||||
case gfx::CullFaceSideType::Front:
|
||||
return MTL::CullModeFront;
|
||||
@@ -110,7 +100,7 @@ MTL::CullMode mapCullMode(const gfx::CullFaceSideType mode) {
|
||||
}
|
||||
}
|
||||
|
||||
MTL::Winding mapWindingMode(const gfx::CullFaceWindingType mode) {
|
||||
MTL::Winding mapWindingMode(const gfx::CullFaceWindingType mode) noexcept {
|
||||
switch (mode) {
|
||||
case gfx::CullFaceWindingType::Clockwise:
|
||||
return MTL::Winding::WindingClockwise;
|
||||
@@ -158,7 +148,7 @@ void Drawable::draw(PaintParameters& parameters) const {
|
||||
}
|
||||
|
||||
auto& context = static_cast<Context&>(parameters.context);
|
||||
const auto& renderPass = static_cast<RenderPass&>(*parameters.renderPass);
|
||||
auto& renderPass = static_cast<RenderPass&>(*parameters.renderPass);
|
||||
const auto& encoder = renderPass.getMetalEncoder();
|
||||
if (!encoder) {
|
||||
assert(false);
|
||||
@@ -284,16 +274,6 @@ void Drawable::draw(PaintParameters& parameters) const {
|
||||
|
||||
assert(mlSegment.indexOffset + mlSegment.indexLength <= indexBufferLength);
|
||||
assert(static_cast<std::size_t>(maxIndex) < mlSegment.vertexLength);
|
||||
|
||||
for (const auto& binding : impl->attributeBindings) {
|
||||
if (binding) {
|
||||
if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) {
|
||||
assert((maxIndex + mlSegment.vertexOffset) * binding->vertexStride <= buffer->length());
|
||||
} else if (impl->noBindingBuffer) {
|
||||
assert(binding->vertexStride <= impl->noBindingBuffer->length());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
encoder->drawIndexedPrimitives(primitiveType,
|
||||
@@ -364,103 +344,74 @@ void Drawable::setVertexAttrNameId(const StringIdentity value) {
|
||||
impl->idVertexAttrName = value;
|
||||
}
|
||||
|
||||
void Drawable::bindAttributes(const RenderPass& renderPass) const {
|
||||
void Drawable::bindAttributes(RenderPass& renderPass) const noexcept {
|
||||
const auto& encoder = renderPass.getMetalEncoder();
|
||||
|
||||
NS::UInteger attributeIndex = 0;
|
||||
for (const auto& binding : impl->attributeBindings) {
|
||||
if (const auto buffer = getMetalBuffer(binding ? binding->vertexBufferResource : nullptr)) {
|
||||
const auto* buffer = static_cast<const mtl::VertexBufferResource*>(binding ? binding->vertexBufferResource
|
||||
: nullptr);
|
||||
if (buffer && buffer->get()) {
|
||||
assert(binding->vertexStride * impl->vertexCount <= getBufferSize(binding->vertexBufferResource));
|
||||
encoder->setVertexBuffer(buffer, /*offset=*/0, attributeIndex);
|
||||
renderPass.bindVertex(buffer->get(), /*offset=*/0, attributeIndex);
|
||||
} else if (impl->noBindingBuffer) {
|
||||
encoder->setVertexBuffer(impl->noBindingBuffer, /*offset=*/0, attributeIndex);
|
||||
renderPass.bindVertex(impl->noBindingBuffer->get(), /*offset=*/0, attributeIndex);
|
||||
}
|
||||
attributeIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::unbindAttributes(const RenderPass& renderPass) const {
|
||||
const auto& encoder = renderPass.getMetalEncoder();
|
||||
|
||||
NS::UInteger attributeIndex = 0;
|
||||
for (const auto& binding : impl->attributeBindings) {
|
||||
encoder->setVertexBuffer(nullptr, /*offset=*/0, attributeIndex);
|
||||
attributeIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::bindUniformBuffers(const RenderPass& renderPass) const {
|
||||
void Drawable::bindUniformBuffers(RenderPass& renderPass) const noexcept {
|
||||
if (shader) {
|
||||
const auto& shaderMTL = static_cast<const ShaderProgram&>(*shader);
|
||||
const auto& encoder = renderPass.getMetalEncoder();
|
||||
for (const auto& element : shaderMTL.getUniformBlocks().getMap()) {
|
||||
const auto& uniformBuffer = getUniformBuffers().get(element.first);
|
||||
if (!uniformBuffer) {
|
||||
using namespace std::string_literals;
|
||||
const auto tileID = getTileID() ? util::toString(*getTileID()) : "<no tile>";
|
||||
const auto tileLabel = util::toString(getID()) + "/" + getName() + "/" + tileID;
|
||||
Log::Error(Event::General,
|
||||
"bindUniformBuffers: UBO "s + stringIndexer().get(element.first) + " not found on " +
|
||||
tileLabel + ". skipping.");
|
||||
assert(false);
|
||||
continue;
|
||||
}
|
||||
assert(uniformBuffer && "UBO missing, drawable skipped");
|
||||
if (uniformBuffer) {
|
||||
const auto& buffer = static_cast<UniformBuffer&>(*uniformBuffer.get());
|
||||
const auto& resource = buffer.getBufferResource();
|
||||
const auto& block = static_cast<const UniformBlock&>(*element.second);
|
||||
const auto index = block.getIndex();
|
||||
|
||||
const auto& buffer = static_cast<UniformBuffer&>(*uniformBuffer.get());
|
||||
const auto& bufferMTL = buffer.getBufferResource().getMetalBuffer().get();
|
||||
|
||||
const auto& block = static_cast<const UniformBlock&>(*element.second);
|
||||
const auto index = block.getIndex();
|
||||
|
||||
if (block.getBindVertex()) {
|
||||
encoder->setVertexBuffer(bufferMTL, /*offset=*/0, index);
|
||||
}
|
||||
if (block.getBindFragment()) {
|
||||
encoder->setFragmentBuffer(bufferMTL, /*offset=*/0, index);
|
||||
if (block.getBindVertex()) {
|
||||
renderPass.bindVertex(resource, /*offset=*/0, index);
|
||||
}
|
||||
if (block.getBindFragment()) {
|
||||
renderPass.bindFragment(resource, /*offset=*/0, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::unbindUniformBuffers(const RenderPass& renderPass) const {
|
||||
if (shader) {
|
||||
const auto& shaderMTL = static_cast<const ShaderProgram&>(*shader);
|
||||
const auto& encoder = renderPass.getMetalEncoder();
|
||||
for (const auto& element : shaderMTL.getUniformBlocks().getMap()) {
|
||||
const auto index = element.second->getIndex();
|
||||
encoder->setVertexBuffer(nullptr, 0, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::bindTextures(const RenderPass& renderPass) const {
|
||||
void Drawable::bindTextures(RenderPass& renderPass) const noexcept {
|
||||
for (const auto& pair : textures) {
|
||||
if (const auto& tex = pair.second) {
|
||||
const auto& location = pair.first;
|
||||
std::static_pointer_cast<mtl::Texture2D>(tex)->bind(renderPass, location);
|
||||
static_cast<mtl::Texture2D&>(*tex).bind(renderPass, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::unbindTextures(const RenderPass& renderPass) const {
|
||||
void Drawable::unbindTextures(RenderPass& renderPass) const noexcept {
|
||||
for (const auto& pair : textures) {
|
||||
if (const auto& tex = pair.second) {
|
||||
const auto& location = pair.first;
|
||||
std::static_pointer_cast<mtl::Texture2D>(tex)->unbind(renderPass, location);
|
||||
static_cast<mtl::Texture2D&>(*tex).unbind(renderPass, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Drawable::uploadTextures() const {
|
||||
void Drawable::uploadTextures(UploadPass&) const noexcept {
|
||||
for (const auto& pair : textures) {
|
||||
if (const auto& tex = pair.second) {
|
||||
std::static_pointer_cast<mtl::Texture2D>(tex)->upload();
|
||||
static_cast<mtl::Texture2D&>(*tex).upload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
MTL::VertexFormat mtlVertexTypeOf(gfx::AttributeDataType type) {
|
||||
MTL::VertexFormat mtlVertexTypeOf(gfx::AttributeDataType type) noexcept {
|
||||
switch (type) {
|
||||
case gfx::AttributeDataType::Byte:
|
||||
return MTL::VertexFormatChar;
|
||||
@@ -548,8 +499,8 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
|
||||
}
|
||||
|
||||
if (impl->indexes->getDirty()) {
|
||||
auto indexBufferResource{
|
||||
uploadPass.createIndexBufferResource(impl->indexes->data(), impl->indexes->bytes(), usage)};
|
||||
auto indexBufferResource{uploadPass.createIndexBufferResource(
|
||||
impl->indexes->data(), impl->indexes->bytes(), usage, /*persistent=*/false)};
|
||||
auto indexBuffer = std::make_unique<gfx::IndexBuffer>(impl->indexes->elements(),
|
||||
std::move(indexBufferResource));
|
||||
auto buffer = std::make_unique<IndexBuffer>(std::move(indexBuffer));
|
||||
@@ -581,7 +532,7 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
|
||||
[](const auto&, gfx::VertexAttribute& attrib) { attrib.setDirty(false); });
|
||||
|
||||
if (impl->attributeBindings != attributeBindings_) {
|
||||
impl->attributeBindings = attributeBindings_;
|
||||
impl->attributeBindings = std::move(attributeBindings_);
|
||||
|
||||
// Create a layout descriptor for each attribute
|
||||
auto vertDesc = NS::RetainPtr(MTL::VertexDescriptor::vertexDescriptor());
|
||||
@@ -595,7 +546,7 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
|
||||
|
||||
if (!binding->vertexBufferResource && !impl->noBindingBuffer) {
|
||||
if (const auto& buf = context.getEmptyVertexBuffer()) {
|
||||
impl->noBindingBuffer = getMetalBuffer(buf.get());
|
||||
impl->noBindingBuffer = buf.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -626,23 +577,9 @@ void Drawable::upload(gfx::UploadPass& uploadPass_) {
|
||||
textures.begin(), textures.end(), [](const auto& pair) { return pair.second && pair.second->needsUpload(); });
|
||||
|
||||
if (texturesNeedUpload) {
|
||||
uploadTextures();
|
||||
uploadTextures(uploadPass);
|
||||
}
|
||||
}
|
||||
/*
|
||||
gfx::ColorMode Drawable::makeColorMode(PaintParameters& parameters) const {
|
||||
return enableColor ? parameters.colorModeForRenderPass() : gfx::ColorMode::disabled();
|
||||
}
|
||||
|
||||
gfx::StencilMode Drawable::makeStencilMode(PaintParameters& parameters) const {
|
||||
if (enableStencil) {
|
||||
if (!is3D && tileID) {
|
||||
return parameters.stencilModeForClipping(tileID->toUnwrapped());
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
return gfx::StencilMode::disabled();
|
||||
}
|
||||
*/
|
||||
} // namespace mtl
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <mbgl/mtl/render_pass.hpp>
|
||||
#include <mbgl/mtl/uniform_buffer.hpp>
|
||||
#include <mbgl/mtl/upload_pass.hpp>
|
||||
#include <mbgl/mtl/vertex_buffer_resource.hpp>
|
||||
#include <mbgl/programs/segment.hpp>
|
||||
#include <mbgl/renderer/paint_parameters.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
@@ -55,7 +56,7 @@ public:
|
||||
// GLfloat pointSize = 0.0f;
|
||||
StringIdentity idVertexAttrName = stringIndexer().get("a_pos");
|
||||
|
||||
MTL::Buffer* noBindingBuffer = nullptr;
|
||||
VertexBufferResource* noBindingBuffer = nullptr;
|
||||
|
||||
gfx::AttributeBindingArray attributeBindings;
|
||||
|
||||
|
||||
@@ -5,18 +5,20 @@
|
||||
namespace mbgl {
|
||||
namespace mtl {
|
||||
|
||||
IndexBufferResource::IndexBufferResource(BufferResource&& ptr)
|
||||
IndexBufferResource::IndexBufferResource(BufferResource&& ptr) noexcept
|
||||
: buffer(std::move(ptr)) {
|
||||
if (buffer) {
|
||||
buffer.getContext().renderingStats().numIndexBuffers++;
|
||||
buffer.getContext().renderingStats().memIndexBuffers += buffer.getSizeInBytes();
|
||||
auto& stats = buffer.getContext().renderingStats();
|
||||
stats.numIndexBuffers++;
|
||||
stats.memIndexBuffers += buffer.getSizeInBytes();
|
||||
}
|
||||
}
|
||||
|
||||
IndexBufferResource::~IndexBufferResource() {
|
||||
IndexBufferResource::~IndexBufferResource() noexcept {
|
||||
if (buffer) {
|
||||
buffer.getContext().renderingStats().numIndexBuffers--;
|
||||
buffer.getContext().renderingStats().memIndexBuffers -= buffer.getSizeInBytes();
|
||||
auto& stats = buffer.getContext().renderingStats();
|
||||
stats.numIndexBuffers--;
|
||||
stats.memIndexBuffers -= buffer.getSizeInBytes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -93,5 +93,45 @@ void RenderPass::addDebugSignpost(const char* name) {
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::bindVertex(const BufferResource& buf, std::size_t offset, std::size_t index, std::size_t size) {
|
||||
assert(0 <= index && index < maxBinds);
|
||||
if (0 <= index && index < maxBinds) {
|
||||
if (auto& bind = vertexBinds[index]) {
|
||||
// Is this the same buffer already bound to this index?
|
||||
if (bind->buf == &buf && !buf.needReBind(bind->version)) {
|
||||
// Yes, but is the offset different?
|
||||
if (bind->offset != offset) {
|
||||
// Yes, update just the offset
|
||||
buf.updateVertexBindOffset(encoder, offset, index, size);
|
||||
bind->offset = offset;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
vertexBinds[index] = BindInfo{&buf, offset};
|
||||
}
|
||||
buf.bindVertex(encoder, offset, index, size);
|
||||
}
|
||||
|
||||
void RenderPass::bindFragment(const BufferResource& buf, std::size_t offset, std::size_t index, std::size_t size) {
|
||||
assert(0 <= index && index < maxBinds);
|
||||
if (0 <= index && index < maxBinds) {
|
||||
if (auto& bind = fragmentBinds[index]) {
|
||||
// Is this the same buffer already bound to this index?
|
||||
if (bind->buf == &buf && !buf.needReBind(bind->version)) {
|
||||
// Yes, but is the offset different?
|
||||
if (bind->offset != offset) {
|
||||
// Yes, update just the offset
|
||||
buf.updateFragmentBindOffset(encoder, offset, index, size);
|
||||
bind->offset = offset;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
fragmentBinds[index] = BindInfo{&buf, offset};
|
||||
}
|
||||
buf.bindFragment(encoder, offset, index, size);
|
||||
}
|
||||
|
||||
} // namespace mtl
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -201,11 +201,7 @@ void Texture2D::bind(const RenderPass& renderPass, int32_t location) noexcept {
|
||||
context.renderingStats().numTextureBindings++;
|
||||
}
|
||||
|
||||
void Texture2D::unbind(const RenderPass& renderPass, int32_t location) noexcept {
|
||||
const auto& encoder = renderPass.getMetalEncoder();
|
||||
encoder->setFragmentTexture(nullptr, location);
|
||||
encoder->setFragmentSamplerState(nullptr, location);
|
||||
|
||||
void Texture2D::unbind(const RenderPass&, int32_t /*location*/) noexcept {
|
||||
context.renderingStats().numTextureBindings--;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,8 +60,10 @@ void UploadPass::endEncoding() {
|
||||
|
||||
std::unique_ptr<gfx::VertexBufferResource> UploadPass::createVertexBufferResource(const void* data,
|
||||
const std::size_t size,
|
||||
const gfx::BufferUsageType usage) {
|
||||
return std::make_unique<VertexBufferResource>(commandEncoder.context.createBuffer(data, size, usage));
|
||||
const gfx::BufferUsageType usage,
|
||||
bool persistent) {
|
||||
return std::make_unique<VertexBufferResource>(
|
||||
commandEncoder.context.createBuffer(data, size, usage, /*isIndexBuffer=*/false, persistent));
|
||||
}
|
||||
|
||||
void UploadPass::updateVertexBufferResource(gfx::VertexBufferResource& resource, const void* data, std::size_t size) {
|
||||
@@ -70,8 +72,10 @@ void UploadPass::updateVertexBufferResource(gfx::VertexBufferResource& resource,
|
||||
|
||||
std::unique_ptr<gfx::IndexBufferResource> UploadPass::createIndexBufferResource(const void* data,
|
||||
const std::size_t size,
|
||||
const gfx::BufferUsageType usage) {
|
||||
return std::make_unique<IndexBufferResource>(commandEncoder.context.createBuffer(data, size, usage));
|
||||
const gfx::BufferUsageType usage,
|
||||
bool persistent) {
|
||||
return std::make_unique<IndexBufferResource>(
|
||||
commandEncoder.context.createBuffer(data, size, usage, /*isIndexBuffer=*/true, persistent));
|
||||
}
|
||||
|
||||
void UploadPass::updateIndexBufferResource(gfx::IndexBufferResource& resource, const void* data, std::size_t size) {
|
||||
@@ -137,7 +141,7 @@ const gfx::UniqueVertexBufferResource& UploadPass::getBuffer(const gfx::VertexVe
|
||||
// Otherwise, create a new one
|
||||
if (rawBufSize > 0) {
|
||||
auto buffer = std::make_unique<VertexBuffer>();
|
||||
buffer->resource = createVertexBufferResource(rawBufPtr, rawBufSize, usage);
|
||||
buffer->resource = createVertexBufferResource(rawBufPtr, rawBufSize, usage, /*persistent=*/false);
|
||||
vec->setBuffer(std::move(buffer));
|
||||
vec->setDirty(false);
|
||||
return static_cast<VertexBuffer*>(vec->getBuffer())->resource;
|
||||
@@ -223,7 +227,7 @@ gfx::AttributeBindingArray UploadPass::buildAttributeBindings(
|
||||
assert(vertexStride * vertexCount <= allData.size());
|
||||
|
||||
if (!allData.empty()) {
|
||||
if (auto vertBuf = createVertexBufferResource(allData.data(), allData.size(), usage)) {
|
||||
if (auto vertBuf = createVertexBufferResource(allData.data(), allData.size(), usage, /*persistent=*/false)) {
|
||||
// Fill in the buffer in each binding that was generated without its own buffer
|
||||
std::for_each(bindings.begin(), bindings.end(), [&](auto& b) {
|
||||
if (b && !b->vertexBufferResource) {
|
||||
|
||||
@@ -85,7 +85,8 @@ const gfx::UniqueVertexBufferResource& VertexAttribute::getBuffer(gfx::VertexAtt
|
||||
if (attrib.sharedRawData) {
|
||||
return uploadPass.getBuffer(attrib.sharedRawData, usage);
|
||||
} else if (!attrib.rawData.empty()) {
|
||||
auto buffer = uploadPass.createVertexBufferResource(attrib.rawData.data(), attrib.rawData.size(), usage);
|
||||
auto buffer = uploadPass.createVertexBufferResource(
|
||||
attrib.rawData.data(), attrib.rawData.size(), usage, false);
|
||||
attrib.setBuffer(std::move(buffer));
|
||||
attrib.setRawData({});
|
||||
} else {
|
||||
|
||||
@@ -5,18 +5,20 @@
|
||||
namespace mbgl {
|
||||
namespace mtl {
|
||||
|
||||
VertexBufferResource::VertexBufferResource(BufferResource&& ptr)
|
||||
VertexBufferResource::VertexBufferResource(BufferResource&& ptr) noexcept
|
||||
: buffer(std::move(ptr)) {
|
||||
if (buffer) {
|
||||
buffer.getContext().renderingStats().numVertexBuffers++;
|
||||
buffer.getContext().renderingStats().memVertexBuffers += buffer.getSizeInBytes();
|
||||
auto& stats = buffer.getContext().renderingStats();
|
||||
stats.numVertexBuffers++;
|
||||
stats.memVertexBuffers += buffer.getSizeInBytes();
|
||||
}
|
||||
}
|
||||
|
||||
VertexBufferResource::~VertexBufferResource() {
|
||||
VertexBufferResource::~VertexBufferResource() noexcept {
|
||||
if (buffer) {
|
||||
buffer.getContext().renderingStats().numVertexBuffers--;
|
||||
buffer.getContext().renderingStats().memVertexBuffers -= buffer.getSizeInBytes();
|
||||
auto& stats = buffer.getContext().renderingStats();
|
||||
stats.numVertexBuffers--;
|
||||
stats.memVertexBuffers -= buffer.getSizeInBytes();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +377,7 @@ void RenderHeatmapLayer::update(gfx::ShaderRegistry& shaders,
|
||||
/* .weight_t = */ std::get<0>(paintPropertyBinders.get<HeatmapWeight>()->interpolationFactor(zoom)),
|
||||
/* .radius_t = */ std::get<0>(paintPropertyBinders.get<HeatmapRadius>()->interpolationFactor(zoom)),
|
||||
/* .padding = */ {0}};
|
||||
interpolateBuffer = context.createUniformBuffer(&interpolateUBO, sizeof(interpolateUBO));
|
||||
interpolateBuffer = context.createUniformBuffer(&interpolateUBO, sizeof(interpolateUBO), false);
|
||||
}
|
||||
return interpolateBuffer;
|
||||
};
|
||||
|
||||
@@ -87,8 +87,10 @@ SegmentVector<HeatmapTextureAttributes> RenderStaticData::heatmapTextureSegments
|
||||
void RenderStaticData::upload(gfx::UploadPass& uploadPass) {
|
||||
if (!uploaded) {
|
||||
// these are still used by stencil buffer rendering
|
||||
tileVertexBuffer = uploadPass.createVertexBuffer(tileVertices());
|
||||
quadTriangleIndexBuffer = uploadPass.createIndexBuffer(quadTriangleIndices());
|
||||
tileVertexBuffer = uploadPass.createVertexBuffer(
|
||||
tileVertices(), gfx::BufferUsageType::StaticDraw, /*persistent=*/false);
|
||||
quadTriangleIndexBuffer = uploadPass.createIndexBuffer(
|
||||
quadTriangleIndices(), gfx::BufferUsageType::StaticDraw, /*persistent=*/false);
|
||||
#if MLN_LEGACY_RENDERER
|
||||
rasterVertexBuffer = uploadPass.createVertexBuffer(rasterVertices());
|
||||
heatmapTextureVertexBuffer = uploadPass.createVertexBuffer(heatmapTextureVertices());
|
||||
|
||||
Reference in New Issue
Block a user