Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/gpu/metal')
-rw-r--r--source/blender/gpu/metal/mtl_backend.hh5
-rw-r--r--source/blender/gpu/metal/mtl_backend.mm8
-rw-r--r--source/blender/gpu/metal/mtl_command_buffer.mm6
-rw-r--r--source/blender/gpu/metal/mtl_context.hh24
-rw-r--r--source/blender/gpu/metal/mtl_context.mm42
-rw-r--r--source/blender/gpu/metal/mtl_memory.hh18
-rw-r--r--source/blender/gpu/metal/mtl_memory.mm48
-rw-r--r--source/blender/gpu/metal/mtl_query.hh41
-rw-r--r--source/blender/gpu/metal/mtl_query.mm122
-rw-r--r--source/blender/gpu/metal/mtl_texture.hh2
-rw-r--r--source/blender/gpu/metal/mtl_uniform_buffer.hh50
-rw-r--r--source/blender/gpu/metal/mtl_uniform_buffer.mm162
12 files changed, 498 insertions, 30 deletions
diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh
index 7228a5f7596..3e09408e43e 100644
--- a/source/blender/gpu/metal/mtl_backend.hh
+++ b/source/blender/gpu/metal/mtl_backend.hh
@@ -40,6 +40,11 @@ class MTLBackend : public GPUBackend {
MTLBackend::platform_exit();
}
+ void delete_resources()
+ {
+ /* Delete any resources with context active. */
+ }
+
static bool metal_is_supported();
static MTLBackend *get()
{
diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm
index d2524cc16e3..3328855bbf8 100644
--- a/source/blender/gpu/metal/mtl_backend.mm
+++ b/source/blender/gpu/metal/mtl_backend.mm
@@ -10,6 +10,8 @@
#include "mtl_backend.hh"
#include "mtl_context.hh"
#include "mtl_framebuffer.hh"
+#include "mtl_uniform_buffer.hh"
+#include "mtl_query.hh"
#include "gpu_capabilities_private.hh"
#include "gpu_platform_private.hh"
@@ -64,8 +66,7 @@ IndexBuf *MTLBackend::indexbuf_alloc()
QueryPool *MTLBackend::querypool_alloc()
{
- /* TODO(Metal): Implement MTLQueryPool. */
- return nullptr;
+ return new MTLQueryPool();
};
Shader *MTLBackend::shader_alloc(const char *name)
@@ -81,8 +82,7 @@ Texture *MTLBackend::texture_alloc(const char *name)
UniformBuf *MTLBackend::uniformbuf_alloc(int size, const char *name)
{
- /* TODO(Metal): Implement MTLUniformBuf. */
- return nullptr;
+ return new MTLUniformBuf(size, name);
};
StorageBuf *MTLBackend::storagebuf_alloc(int size, GPUUsageType usage, const char *name)
diff --git a/source/blender/gpu/metal/mtl_command_buffer.mm b/source/blender/gpu/metal/mtl_command_buffer.mm
index 7b6066b3d86..9a9a2d55103 100644
--- a/source/blender/gpu/metal/mtl_command_buffer.mm
+++ b/source/blender/gpu/metal/mtl_command_buffer.mm
@@ -308,6 +308,12 @@ id<MTLRenderCommandEncoder> MTLCommandBufferManager::ensure_begin_render_command
active_pass_descriptor_ = active_frame_buffer_->bake_render_pass_descriptor(
is_rebind && (!active_frame_buffer_->get_pending_clear()));
+ /* Determine if there is a visibility buffer assigned to the context. */
+ gpu::MTLBuffer *visibility_buffer = context_.get_visibility_buffer();
+ this->active_pass_descriptor_.visibilityResultBuffer =
+ (visibility_buffer) ? visibility_buffer->get_metal_buffer() : nil;
+ context_.clear_visibility_dirty();
+
/* Ensure we have already cleaned up our previous render command encoder. */
BLI_assert(active_render_command_encoder_ == nil);
diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh
index 4b87b994a3d..0db87bf5da5 100644
--- a/source/blender/gpu/metal/mtl_context.hh
+++ b/source/blender/gpu/metal/mtl_context.hh
@@ -3,6 +3,9 @@
/** \file
* \ingroup gpu
*/
+
+#pragma once
+
#include "MEM_guardedalloc.h"
#include "gpu_context_private.hh"
@@ -588,6 +591,10 @@ class MTLContext : public Context {
bool is_inside_frame_ = false;
uint current_frame_index_;
+ /* Visibility buffer for MTLQuery results. */
+ gpu::MTLBuffer *visibility_buffer_ = nullptr;
+ bool visibility_is_dirty_ = false;
+
public:
/* Shaders and Pipeline state. */
MTLContextGlobalShaderPipelineState pipeline_state;
@@ -619,6 +626,11 @@ class MTLContext : public Context {
void memory_statistics_get(int *total_mem, int *free_mem) override;
+ static MTLContext *get()
+ {
+ return static_cast<MTLContext *>(Context::get());
+ }
+
void debug_group_begin(const char *name, int index) override;
void debug_group_end() override;
@@ -660,6 +672,18 @@ class MTLContext : public Context {
void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height);
void set_scissor_enabled(bool scissor_enabled);
+ /* Visibility buffer control. */
+ void set_visibility_buffer(gpu::MTLBuffer *buffer);
+ gpu::MTLBuffer *get_visibility_buffer() const;
+
+ /* Flag whether the visibility buffer for query results
+ * has changed. This requires a new RenderPass in order
+ * to update.*/
+ bool is_visibility_dirty() const;
+
+ /* Reset dirty flag state for visibility buffer. */
+ void clear_visibility_dirty();
+
/* Texture utilities. */
MTLContextTextureUtils &get_texture_utils()
{
diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm
index 2ab2e20158a..26cfe6632ef 100644
--- a/source/blender/gpu/metal/mtl_context.mm
+++ b/source/blender/gpu/metal/mtl_context.mm
@@ -188,7 +188,8 @@ id<MTLRenderCommandEncoder> MTLContext::ensure_begin_render_pass()
* framebuffer state has been modified (is_dirty). */
if (!this->main_command_buffer.is_inside_render_pass() ||
this->active_fb != this->main_command_buffer.get_active_framebuffer() ||
- this->main_command_buffer.get_active_framebuffer()->get_dirty()) {
+ this->main_command_buffer.get_active_framebuffer()->get_dirty() ||
+ this->is_visibility_dirty()) {
/* Validate bound framebuffer before beginning render pass. */
if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) {
@@ -372,6 +373,45 @@ void MTLContext::set_scissor_enabled(bool scissor_enabled)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Visibility buffer control for MTLQueryPool.
+ * \{ */
+
+void MTLContext::set_visibility_buffer(gpu::MTLBuffer *buffer)
+{
+ /* Flag visibility buffer as dirty if the buffer being used for visibility has changed --
+ * This is required by the render pass, and we will break the pass if the results destination
+ * buffer is modified. */
+ if (buffer) {
+ visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_;
+ visibility_buffer_ = buffer;
+ visibility_buffer_->debug_ensure_used();
+ }
+ else {
+ /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no
+ * longer needed. */
+ visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_;
+ visibility_buffer_ = nullptr;
+ }
+}
+
+gpu::MTLBuffer *MTLContext::get_visibility_buffer() const
+{
+ return visibility_buffer_;
+}
+
+void MTLContext::clear_visibility_dirty()
+{
+ visibility_is_dirty_ = false;
+}
+
+bool MTLContext::is_visibility_dirty() const
+{
+ return visibility_is_dirty_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Texture State Management
* \{ */
diff --git a/source/blender/gpu/metal/mtl_memory.hh b/source/blender/gpu/metal/mtl_memory.hh
index daa049e78af..dc5417dc11a 100644
--- a/source/blender/gpu/metal/mtl_memory.hh
+++ b/source/blender/gpu/metal/mtl_memory.hh
@@ -78,8 +78,10 @@
* Usage:
* MTLContext::get_global_memory_manager(); - static routine to fetch global memory manager.
*
- * gpu::MTLBuffer *allocate_buffer(size, is_cpu_visibile, bytes=nullptr)
- * gpu::MTLBuffer *allocate_buffer_aligned(size, alignment, is_cpu_visibile, bytes=nullptr)
+ * gpu::MTLBuffer *allocate(size, is_cpu_visibile)
+ * gpu::MTLBuffer *allocate_aligned(size, alignment, is_cpu_visibile)
+ * gpu::MTLBuffer *allocate_with_data(size, is_cpu_visibile, data_ptr)
+ * gpu::MTLBuffer *allocate_aligned_with_data(size, alignment, is_cpu_visibile, data_ptr)
*/
/* Debug memory statistics: Disabled by Macro rather than guarded for
@@ -389,11 +391,13 @@ class MTLBufferPool {
void init(id<MTLDevice> device);
~MTLBufferPool();
- gpu::MTLBuffer *allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes = nullptr);
- gpu::MTLBuffer *allocate_buffer_aligned(uint64_t size,
- uint alignment,
- bool cpu_visible,
- const void *bytes = nullptr);
+ gpu::MTLBuffer *allocate(uint64_t size, bool cpu_visible);
+ gpu::MTLBuffer *allocate_aligned(uint64_t size, uint alignment, bool cpu_visible);
+ gpu::MTLBuffer *allocate_with_data(uint64_t size, bool cpu_visible, const void *data = nullptr);
+ gpu::MTLBuffer *allocate_aligned_with_data(uint64_t size,
+ uint alignment,
+ bool cpu_visible,
+ const void *data = nullptr);
bool free_buffer(gpu::MTLBuffer *buffer);
/* Flush MTLSafeFreeList buffers, for completed lists in `completed_safelist_queue_`,
diff --git a/source/blender/gpu/metal/mtl_memory.mm b/source/blender/gpu/metal/mtl_memory.mm
index e5db32ed1b1..48e27dd2bb6 100644
--- a/source/blender/gpu/metal/mtl_memory.mm
+++ b/source/blender/gpu/metal/mtl_memory.mm
@@ -57,17 +57,23 @@ void MTLBufferPool::free()
buffer_pools_.clear();
}
-gpu::MTLBuffer *MTLBufferPool::allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes)
+gpu::MTLBuffer *MTLBufferPool::allocate(uint64_t size, bool cpu_visible)
{
/* Allocate buffer with default HW-compatible alignment of 256 bytes.
* See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */
- return this->allocate_buffer_aligned(size, 256, cpu_visible, bytes);
+ return this->allocate_aligned(size, 256, cpu_visible);
}
-gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size,
- uint alignment,
- bool cpu_visible,
- const void *bytes)
+gpu::MTLBuffer *MTLBufferPool::allocate_with_data(uint64_t size,
+ bool cpu_visible,
+ const void *data)
+{
+ /* Allocate buffer with default HW-compatible alignemnt of 256 bytes.
+ * See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */
+ return this->allocate_aligned_with_data(size, 256, cpu_visible, data);
+}
+
+gpu::MTLBuffer *MTLBufferPool::allocate_aligned(uint64_t size, uint alignment, bool cpu_visible)
{
/* Check not required. Main GPU module usage considered thread-safe. */
// BLI_assert(BLI_thread_is_main());
@@ -153,15 +159,6 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size,
/* Flag buffer as actively in-use. */
new_buffer->flag_in_use(true);
- /* Upload initial data if provided -- Size based on original size param, not aligned size*/
- if (bytes) {
- BLI_assert(!(options & MTLResourceStorageModePrivate));
- BLI_assert(size <= aligned_alloc_size);
- BLI_assert(size <= [new_buffer->get_metal_buffer() length]);
- memcpy(new_buffer->get_host_ptr(), bytes, size);
- new_buffer->flush_range(0, size);
- }
-
#if MTL_DEBUG_MEMORY_STATISTICS == 1
this->per_frame_allocation_count++;
#endif
@@ -169,6 +166,23 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size,
return new_buffer;
}
+gpu::MTLBuffer *MTLBufferPool::allocate_aligned_with_data(uint64_t size,
+ uint alignment,
+ bool cpu_visible,
+ const void *data)
+{
+ gpu::MTLBuffer *buf = this->allocate_aligned(size, 256, cpu_visible);
+
+ /* Upload initial data. */
+ BLI_assert(data != nullptr);
+ BLI_assert(!(buf->get_resource_options() & MTLResourceStorageModePrivate));
+ BLI_assert(size <= buf->get_size());
+ BLI_assert(size <= [buf->get_metal_buffer() length]);
+ memcpy(buf->get_host_ptr(), data, size);
+ buf->flush_range(0, size);
+ return buf;
+}
+
bool MTLBufferPool::free_buffer(gpu::MTLBuffer *buffer)
{
/* Ensure buffer is flagged as in-use. I.e. has not already been returned to memory pools. */
@@ -356,7 +370,7 @@ void MTLBufferPool::insert_buffer_into_pool(MTLResourceOptions options, gpu::MTL
#if MTL_DEBUG_MEMORY_STATISTICS == 1
/* Debug statistics. */
- allocations_in_pool_ += buffer->size;
+ allocations_in_pool_ += buffer->get_size();
buffers_in_pool_++;
#endif
}
@@ -413,7 +427,7 @@ void MTLSafeFreeList::decrement_reference()
{
lock_.lock();
BLI_assert(in_free_queue_ == false);
- int ref_count = reference_count_--;
+ int ref_count = --reference_count_;
if (ref_count == 0) {
MTLContext::get_global_memory_manager().push_completed_safe_list(this);
diff --git a/source/blender/gpu/metal/mtl_query.hh b/source/blender/gpu/metal/mtl_query.hh
new file mode 100644
index 00000000000..c1ec9a2a0f5
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_query.hh
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "BLI_vector.hh"
+
+#include "gpu_query.hh"
+#include "mtl_context.hh"
+
+namespace blender::gpu {
+
+class MTLQueryPool : public QueryPool {
+ private:
+ /** Number of queries that have been issued since last initialization.
+ * Should be equal to query_ids_.size(). */
+ uint32_t query_issued_;
+ /** Type of this query pool. */
+ GPUQueryType type_;
+ /** Can only be initialized once. */
+ bool initialized_ = false;
+ MTLVisibilityResultMode mtl_type_;
+ Vector<gpu::MTLBuffer *> buffer_;
+
+ void allocate_buffer();
+
+ public:
+ MTLQueryPool();
+ ~MTLQueryPool();
+
+ void init(GPUQueryType type) override;
+
+ void begin_query() override;
+ void end_query() override;
+
+ void get_occlusion_result(MutableSpan<uint32_t> r_values) override;
+};
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm
new file mode 100644
index 00000000000..33ae8f554c3
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_query.mm
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "mtl_query.hh"
+
+namespace blender::gpu {
+
+static const size_t VISIBILITY_COUNT_PER_BUFFER = 512;
+/* defined in the documentation but not queryable programmatically:
+ * https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc
+ */
+static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8;
+
+MTLQueryPool::MTLQueryPool()
+{
+ allocate_buffer();
+}
+MTLQueryPool::~MTLQueryPool()
+{
+ for (gpu::MTLBuffer *buf : buffer_) {
+ BLI_assert(buf);
+ buf->free();
+ }
+}
+
+void MTLQueryPool::allocate_buffer()
+{
+ /* Allocate Metal buffer for visibility results. */
+ size_t buffer_size_in_bytes = VISIBILITY_COUNT_PER_BUFFER * VISIBILITY_RESULT_SIZE_IN_BYTES;
+ gpu::MTLBuffer *buffer = MTLContext::get_global_memory_manager().allocate_buffer(
+ buffer_size_in_bytes, true);
+ BLI_assert(buffer);
+ buffer_.append(buffer);
+}
+
+static inline MTLVisibilityResultMode to_mtl_type(GPUQueryType type)
+{
+ if (type == GPU_QUERY_OCCLUSION) {
+ return MTLVisibilityResultModeBoolean;
+ }
+ BLI_assert(0);
+ return MTLVisibilityResultModeBoolean;
+}
+
+void MTLQueryPool::init(GPUQueryType type)
+{
+ BLI_assert(initialized_ == false);
+ initialized_ = true;
+ type_ = type;
+ mtl_type_ = to_mtl_type(type);
+ query_issued_ = 0;
+}
+
+void MTLQueryPool::begin_query()
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+
+ /* Ensure our allocated buffer pool has enough space for the current queries. */
+ int query_id = query_issued_;
+ int requested_buffer = query_id / VISIBILITY_COUNT_PER_BUFFER;
+ if (requested_buffer >= buffer_.size()) {
+ allocate_buffer();
+ }
+
+ BLI_assert(requested_buffer < buffer_.size());
+ gpu::MTLBuffer *buffer = buffer_[requested_buffer];
+
+ /* Ensure visibility buffer is set on the context. If visibility buffer changes,
+ * we need to begin a new render pass with an updated reference in the
+ * MTLRenderPassDescriptor. */
+ ctx->set_visibility_buffer(buffer);
+
+ ctx->ensure_begin_render_pass();
+ id<MTLRenderCommandEncoder> rec = ctx->main_command_buffer.get_active_render_command_encoder();
+ [rec setVisibilityResultMode:mtl_type_
+ offset:(query_id % VISIBILITY_COUNT_PER_BUFFER) *
+ VISIBILITY_RESULT_SIZE_IN_BYTES];
+ query_issued_ += 1;
+}
+
+void MTLQueryPool::end_query()
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+
+ id<MTLRenderCommandEncoder> rec = ctx->main_command_buffer.get_active_render_command_encoder();
+ [rec setVisibilityResultMode:MTLVisibilityResultModeDisabled offset:0];
+}
+
+void MTLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+
+ /* Create a blit encoder to synchronize the query buffer results between
+ * GPU and CPU when not using shared-memory. */
+ if ([ctx->device hasUnifiedMemory] == false) {
+ id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
+ BLI_assert(blit_encoder);
+ for (gpu::MTLBuffer *buf : buffer_) {
+ [blit_encoder synchronizeResource:buf->get_metal_buffer()];
+ }
+ BLI_assert(ctx->get_inside_frame());
+ }
+
+ /* Wait for GPU operatiosn to complete and for query buffer contents
+ * to be synchronised back to host memory. */
+ GPU_finish();
+
+ /* Iterate through all possible visibility buffers and copy results into provided
+ * container. */
+ for (const int i : IndexRange(query_issued_)) {
+ int requested_buffer = i / VISIBILITY_COUNT_PER_BUFFER;
+ const uint64_t *queries = static_cast<const uint64_t *>(
+ buffer_[requested_buffer]->get_host_ptr());
+ r_values[i] = static_cast<uint32_t>(queries[i % VISIBILITY_COUNT_PER_BUFFER]);
+ }
+ ctx->set_visibility_buffer(nullptr);
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh
index 9387d5af814..82a7a20a310 100644
--- a/source/blender/gpu/metal/mtl_texture.hh
+++ b/source/blender/gpu/metal/mtl_texture.hh
@@ -93,7 +93,7 @@ struct TextureReadRoutineSpecialisation {
* 0 = Not a Depth format,
* 1 = FLOAT DEPTH,
* 2 = 24Bit Integer Depth,
- * 4 = 32bit uinteger Depth. */
+ * 4 = 32bit Unsigned-Integer Depth. */
int depth_format_mode;
bool operator==(const TextureReadRoutineSpecialisation &other) const
diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.hh b/source/blender/gpu/metal/mtl_uniform_buffer.hh
new file mode 100644
index 00000000000..722d819cf96
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_uniform_buffer.hh
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+#include "gpu_uniform_buffer_private.hh"
+
+#include "mtl_context.hh"
+
+namespace blender::gpu {
+
+/**
+ * Implementation of Uniform Buffers using Metal.
+ **/
+class MTLUniformBuf : public UniformBuf {
+ private:
+ /* Allocation Handle. */
+ gpu::MTLBuffer *metal_buffer_ = nullptr;
+
+ /* Whether buffer has contents, if false, no GPU buffer will
+ * have yet been allocated. */
+ bool has_data_ = false;
+
+ /* Bindstate tracking. */
+ int bind_slot_ = -1;
+ MTLContext *bound_ctx_ = nullptr;
+
+ public:
+ MTLUniformBuf(size_t size, const char *name);
+ ~MTLUniformBuf();
+
+ void update(const void *data) override;
+ void bind(int slot) override;
+ void unbind() override;
+
+ id<MTLBuffer> get_metal_buffer(int *r_offset);
+ int get_size();
+ const char *get_name()
+ {
+ return name_;
+ }
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MTLUniformBuf");
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.mm b/source/blender/gpu/metal/mtl_uniform_buffer.mm
new file mode 100644
index 00000000000..3415c41f2cc
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_uniform_buffer.mm
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "BKE_global.h"
+
+#include "BLI_string.h"
+
+#include "gpu_backend.hh"
+#include "gpu_context_private.hh"
+
+#include "mtl_backend.hh"
+#include "mtl_context.hh"
+#include "mtl_debug.hh"
+#include "mtl_uniform_buffer.hh"
+
+namespace blender::gpu {
+
+MTLUniformBuf::MTLUniformBuf(size_t size, const char *name) : UniformBuf(size, name)
+{
+}
+
+MTLUniformBuf::~MTLUniformBuf()
+{
+ if (metal_buffer_ != nullptr) {
+ metal_buffer_->free();
+ metal_buffer_ = nullptr;
+ }
+ has_data_ = false;
+
+ /* Ensure UBO is not bound to active CTX.
+ * UBO bindings are reset upon Context-switch so we do not need
+ * to check deactivated context's. */
+ MTLContext *ctx = MTLContext::get();
+ if (ctx) {
+ for (int i = 0; i < MTL_MAX_UNIFORM_BUFFER_BINDINGS; i++) {
+ MTLUniformBufferBinding &slot = ctx->pipeline_state.ubo_bindings[i];
+ if (slot.bound && slot.ubo == this) {
+ slot.bound = false;
+ slot.ubo = nullptr;
+ }
+ }
+ }
+}
+
+void MTLUniformBuf::update(const void *data)
+{
+ BLI_assert(this);
+ BLI_assert(size_in_bytes_ > 0);
+
+ /* Free existing allocation.
+ * The previous UBO resource will be tracked by the memory manager,
+ * in case dependent GPU work is still executing. */
+ if (metal_buffer_ != nullptr) {
+ metal_buffer_->free();
+ metal_buffer_ = nullptr;
+ }
+
+ /* Allocate MTL buffer */
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ BLI_assert(ctx);
+ BLI_assert(ctx->device);
+ UNUSED_VARS_NDEBUG(ctx);
+
+ if (data != nullptr) {
+ metal_buffer_ = MTLContext::get_global_memory_manager().allocate_with_data(
+ size_in_bytes_, true, data);
+ has_data_ = true;
+
+ metal_buffer_->set_label(@"Uniform Buffer");
+ BLI_assert(metal_buffer_ != nullptr);
+ BLI_assert(metal_buffer_->get_metal_buffer() != nil);
+ }
+ else {
+ /* If data is not yet present, no buffer will be allocated and MTLContext will use an empty
+ * null buffer, containing zeroes, if the UBO is bound. */
+ metal_buffer_ = nullptr;
+ has_data_ = false;
+ }
+}
+
+void MTLUniformBuf::bind(int slot)
+{
+ if (slot < 0) {
+ MTL_LOG_WARNING("Failed to bind UBO %p. uniform location %d invalid.\n", this, slot);
+ return;
+ }
+
+ BLI_assert(slot < MTL_MAX_UNIFORM_BUFFER_BINDINGS);
+
+ /* Bind current UBO to active context. */
+ MTLContext *ctx = MTLContext::get();
+ BLI_assert(ctx);
+
+ MTLUniformBufferBinding &ctx_ubo_bind_slot = ctx->pipeline_state.ubo_bindings[slot];
+ ctx_ubo_bind_slot.ubo = this;
+ ctx_ubo_bind_slot.bound = true;
+
+ bind_slot_ = slot;
+ bound_ctx_ = ctx;
+
+ /* Check if we have any deferred data to upload. */
+ if (data_ != nullptr) {
+ this->update(data_);
+ MEM_SAFE_FREE(data_);
+ }
+
+ /* Ensure there is atleast an empty dummy buffer. */
+ if (metal_buffer_ == nullptr) {
+ this->update(nullptr);
+ }
+}
+
+void MTLUniformBuf::unbind()
+{
+ /* Unbind in debug mode to validate missing binds.
+ * Otherwise, only perform a full unbind upon destruction
+ * to ensure no lingering references. */
+#ifndef NDEBUG
+ if (true) {
+#else
+ if (G.debug & G_DEBUG_GPU) {
+#endif
+ if (bound_ctx_ != nullptr && bind_slot_ > -1) {
+ MTLUniformBufferBinding &ctx_ubo_bind_slot =
+ bound_ctx_->pipeline_state.ubo_bindings[bind_slot_];
+ if (ctx_ubo_bind_slot.bound && ctx_ubo_bind_slot.ubo == this) {
+ ctx_ubo_bind_slot.bound = false;
+ ctx_ubo_bind_slot.ubo = nullptr;
+ }
+ }
+ }
+
+ /* Reset bind index. */
+ bind_slot_ = -1;
+ bound_ctx_ = nullptr;
+}
+
+id<MTLBuffer> MTLUniformBuf::get_metal_buffer(int *r_offset)
+{
+ BLI_assert(this);
+ *r_offset = 0;
+ if (metal_buffer_ != nullptr && has_data_) {
+ *r_offset = 0;
+ metal_buffer_->debug_ensure_used();
+ return metal_buffer_->get_metal_buffer();
+ }
+ else {
+ *r_offset = 0;
+ return nil;
+ }
+}
+
+int MTLUniformBuf::get_size()
+{
+ BLI_assert(this);
+ return size_in_bytes_;
+}
+
+} // blender::gpu