Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
202 lines
4.9 KiB
Plaintext
202 lines
4.9 KiB
Plaintext
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
|
*
|
|
* 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_storage_buffer.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_BUFFER_BINDINGS; i++) {
|
|
MTLUniformBufferBinding &slot = ctx->pipeline_state.ubo_bindings[i];
|
|
if (slot.bound && slot.ubo == this) {
|
|
slot.bound = false;
|
|
slot.ubo = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ssbo_wrapper_) {
|
|
delete ssbo_wrapper_;
|
|
ssbo_wrapper_ = 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;
|
|
|
|
#ifndef NDEBUG
|
|
metal_buffer_->set_label([NSString stringWithFormat:@"Uniform Buffer %s", name_]);
|
|
#endif
|
|
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::clear_to_zero()
|
|
{
|
|
/* TODO(fclem): Avoid another allocation and just do the clear on the GPU if possible. */
|
|
void *clear_data = calloc(1, size_in_bytes_);
|
|
this->update(clear_data);
|
|
free(clear_data);
|
|
}
|
|
|
|
void MTLUniformBuf::bind(int slot)
|
|
{
|
|
if (slot < 0) {
|
|
MTL_LOG_WARNING("Failed to bind UBO %p. uniform location %d invalid.", this, slot);
|
|
return;
|
|
}
|
|
|
|
BLI_assert(slot < MTL_MAX_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 at least an empty dummy buffer. */
|
|
if (metal_buffer_ == nullptr) {
|
|
this->update(nullptr);
|
|
}
|
|
}
|
|
|
|
void MTLUniformBuf::bind_as_ssbo(int slot)
|
|
{
|
|
if (slot < 0) {
|
|
MTL_LOG_WARNING("Failed to bind UBO %p as SSBO. uniform location %d invalid.", this, slot);
|
|
return;
|
|
}
|
|
|
|
/* We need to ensure data is actually allocated if using as an SSBO, as resource may be written
|
|
* to. */
|
|
if (metal_buffer_ == nullptr) {
|
|
/* Check if we have any deferred data to upload. */
|
|
if (data_ != nullptr) {
|
|
this->update(data_);
|
|
MEM_SAFE_FREE(data_);
|
|
}
|
|
else {
|
|
this->clear_to_zero();
|
|
}
|
|
}
|
|
|
|
/* Create MTLStorageBuffer to wrap this resource and use conventional binding. */
|
|
if (ssbo_wrapper_ == nullptr) {
|
|
ssbo_wrapper_ = new MTLStorageBuf(this, size_in_bytes_);
|
|
}
|
|
|
|
ssbo_wrapper_->bind(slot);
|
|
}
|
|
|
|
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()
|
|
{
|
|
BLI_assert(this);
|
|
if (metal_buffer_ != nullptr && has_data_) {
|
|
metal_buffer_->debug_ensure_used();
|
|
return metal_buffer_->get_metal_buffer();
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
size_t MTLUniformBuf::get_size()
|
|
{
|
|
BLI_assert(this);
|
|
return size_in_bytes_;
|
|
}
|
|
|
|
} // namespace blender::gpu
|