Blender V4.3
mtl_uniform_buffer.mm
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_global.hh"
10
11#include "BLI_string.h"
12
13#include "gpu_backend.hh"
15
16#include "mtl_backend.hh"
17#include "mtl_context.hh"
18#include "mtl_debug.hh"
19#include "mtl_storage_buffer.hh"
20#include "mtl_uniform_buffer.hh"
21
22namespace blender::gpu {
23
24MTLUniformBuf::MTLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) {}
25
27{
28 if (metal_buffer_ != nullptr) {
29 metal_buffer_->free();
30 metal_buffer_ = nullptr;
31 }
32 has_data_ = false;
33
34 /* Ensure UBO is not bound to active CTX.
35 * UBO bindings are reset upon Context-switch so we do not need
36 * to check deactivated context's. */
38 if (ctx) {
39 for (int i = 0; i < MTL_MAX_BUFFER_BINDINGS; i++) {
41 if (slot.bound && slot.ubo == this) {
42 slot.bound = false;
43 slot.ubo = nullptr;
44 }
45 }
46 }
47
48 if (ssbo_wrapper_) {
49 delete ssbo_wrapper_;
50 ssbo_wrapper_ = nullptr;
51 }
52}
53
54void MTLUniformBuf::update(const void *data)
55{
56 BLI_assert(this);
58
59 /* Free existing allocation.
60 * The previous UBO resource will be tracked by the memory manager,
61 * in case dependent GPU work is still executing. */
62 if (metal_buffer_ != nullptr) {
63 metal_buffer_->free();
64 metal_buffer_ = nullptr;
65 }
66
67 /* Allocate MTL buffer */
69 BLI_assert(ctx);
70 BLI_assert(ctx->device);
72
73 if (data != nullptr) {
75 size_in_bytes_, true, data);
76 has_data_ = true;
77
78#ifndef NDEBUG
79 metal_buffer_->set_label([NSString stringWithFormat:@"Uniform Buffer %s", name_]);
80#endif
81 BLI_assert(metal_buffer_ != nullptr);
82 BLI_assert(metal_buffer_->get_metal_buffer() != nil);
83 }
84 else {
85 /* If data is not yet present, no buffer will be allocated and MTLContext will use an empty
86 * null buffer, containing zeroes, if the UBO is bound. */
87 metal_buffer_ = nullptr;
88 has_data_ = false;
89 }
90}
91
93{
94 /* TODO(fclem): Avoid another allocation and just do the clear on the GPU if possible. */
95 void *clear_data = calloc(1, size_in_bytes_);
96 this->update(clear_data);
97 free(clear_data);
98}
99
101{
102 if (slot < 0) {
103 MTL_LOG_WARNING("Failed to bind UBO %p. uniform location %d invalid.", this, slot);
104 return;
105 }
106
108
109 /* Bind current UBO to active context. */
111 BLI_assert(ctx);
112
113 MTLUniformBufferBinding &ctx_ubo_bind_slot = ctx->pipeline_state.ubo_bindings[slot];
114 ctx_ubo_bind_slot.ubo = this;
115 ctx_ubo_bind_slot.bound = true;
116
117 bind_slot_ = slot;
118 bound_ctx_ = ctx;
119
120 /* Check if we have any deferred data to upload. */
121 if (data_ != nullptr) {
122 this->update(data_);
124 }
125
126 /* Ensure there is at least an empty dummy buffer. */
127 if (metal_buffer_ == nullptr) {
128 this->update(nullptr);
129 }
130}
131
133{
134 if (slot < 0) {
135 MTL_LOG_WARNING("Failed to bind UBO %p as SSBO. uniform location %d invalid.", this, slot);
136 return;
137 }
138
139 /* We need to ensure data is actually allocated if using as an SSBO, as resource may be written
140 * to. */
141 if (metal_buffer_ == nullptr) {
142 /* Check if we have any deferred data to upload. */
143 if (data_ != nullptr) {
144 this->update(data_);
146 }
147 else {
148 this->clear_to_zero();
149 }
150 }
151
152 /* Create MTLStorageBuffer to wrap this resource and use conventional binding. */
153 if (ssbo_wrapper_ == nullptr) {
154 ssbo_wrapper_ = new MTLStorageBuf(this, size_in_bytes_);
155 }
156
157 ssbo_wrapper_->bind(slot);
158}
159
161{
162 /* Unbind in debug mode to validate missing binds.
163 * Otherwise, only perform a full unbind upon destruction
164 * to ensure no lingering references. */
165#ifndef NDEBUG
166 if (true) {
167#else
168 if (G.debug & G_DEBUG_GPU) {
169#endif
170 if (bound_ctx_ != nullptr && bind_slot_ > -1) {
171 MTLUniformBufferBinding &ctx_ubo_bind_slot =
172 bound_ctx_->pipeline_state.ubo_bindings[bind_slot_];
173 if (ctx_ubo_bind_slot.bound && ctx_ubo_bind_slot.ubo == this) {
174 ctx_ubo_bind_slot.bound = false;
175 ctx_ubo_bind_slot.ubo = nullptr;
176 }
177 }
178 }
179
180 /* Reset bind index. */
181 bind_slot_ = -1;
182 bound_ctx_ = nullptr;
183}
184
186{
187 BLI_assert(this);
188 if (metal_buffer_ != nullptr && has_data_) {
189 metal_buffer_->debug_ensure_used();
190 return metal_buffer_->get_metal_buffer();
191 }
192 return nil;
193}
194
196{
197 BLI_assert(this);
198 return size_in_bytes_;
199}
200
201} // namespace blender::gpu
@ G_DEBUG_GPU
#define BLI_assert(a)
Definition BLI_assert.h:50
void BLI_kdtree_nd_ free(KDTree *tree)
#define UNUSED_VARS_NDEBUG(...)
#define MEM_SAFE_FREE(v)
gpu::MTLBuffer * allocate_with_data(uint64_t size, bool cpu_visible, const void *data=nullptr)
void set_label(NSString *str)
id< MTLBuffer > get_metal_buffer() const
static MTLContext * get()
MTLContextGlobalShaderPipelineState pipeline_state
static MTLBufferPool * get_global_memory_manager()
void bind(int slot) override
MTLUniformBuf(size_t size, const char *name)
void bind(int slot) override
void bind_as_ssbo(int slot) override
void update(const void *data) override
#define G(x, y, z)
#define MTL_MAX_BUFFER_BINDINGS
#define MTL_LOG_WARNING(info,...)
Definition mtl_debug.hh:44
MTLUniformBufferBinding ubo_bindings[MTL_MAX_BUFFER_BINDINGS]