OverviewFilesCommitsIssuesDocsReleasesPeers

proc-nodes / src / gpu / gpu_buffer_manager.cpp

187 lines · cpp ref:
#include "gpu_buffer_manager.h"
#include <godot_cpp/variant/utility_functions.hpp>

namespace godot {

// --- Upload CPU -> GPU ---

RID GpuBufferManager::upload_vec3_buffer(const PackedVector3Array &p_data) {
	ERR_FAIL_NULL_V(rd, RID());

	int count = p_data.size();
	if (count == 0) {
		return RID();
	}

	// Pad vec3 (12 bytes) to vec4 (16 bytes) for GPU alignment
	uint32_t byte_size = count * 16;
	PackedByteArray bytes;
	bytes.resize(byte_size);

	uint8_t *dst = bytes.ptrw();
	const Vector3 *src = p_data.ptr();

	for (int i = 0; i < count; i++) {
		float v[4] = { src[i].x, src[i].y, src[i].z, 0.0f };
		memcpy(dst + i * 16, v, 16);
	}

	RID buffer = rd->storage_buffer_create(byte_size, bytes);
	allocated_buffers.push_back(buffer);
	return buffer;
}

RID GpuBufferManager::upload_float_buffer(const PackedFloat32Array &p_data) {
	ERR_FAIL_NULL_V(rd, RID());

	int count = p_data.size();
	if (count == 0) {
		return RID();
	}

	uint32_t byte_size = count * sizeof(float);
	PackedByteArray bytes;
	bytes.resize(byte_size);
	memcpy(bytes.ptrw(), p_data.ptr(), byte_size);

	RID buffer = rd->storage_buffer_create(byte_size, bytes);
	allocated_buffers.push_back(buffer);
	return buffer;
}

RID GpuBufferManager::upload_int_buffer(const PackedInt32Array &p_data) {
	ERR_FAIL_NULL_V(rd, RID());

	int count = p_data.size();
	if (count == 0) {
		return RID();
	}

	uint32_t byte_size = count * sizeof(int32_t);
	PackedByteArray bytes;
	bytes.resize(byte_size);
	memcpy(bytes.ptrw(), p_data.ptr(), byte_size);

	RID buffer = rd->storage_buffer_create(byte_size, bytes);
	allocated_buffers.push_back(buffer);
	return buffer;
}

RID GpuBufferManager::upload_raw_buffer(const PackedByteArray &p_data) {
	ERR_FAIL_NULL_V(rd, RID());

	if (p_data.size() == 0) {
		return RID();
	}

	RID buffer = rd->storage_buffer_create(p_data.size(), p_data);
	allocated_buffers.push_back(buffer);
	return buffer;
}

// --- Output buffer ---

RID GpuBufferManager::create_output_buffer(uint32_t p_size_bytes) {
	ERR_FAIL_NULL_V(rd, RID());
	ERR_FAIL_COND_V(p_size_bytes == 0, RID());

	RID buffer = rd->storage_buffer_create(p_size_bytes);
	allocated_buffers.push_back(buffer);
	return buffer;
}

// --- Download GPU -> CPU ---

PackedVector3Array GpuBufferManager::download_vec3_buffer(RID p_buffer, int p_count) {
	PackedVector3Array result;
	ERR_FAIL_NULL_V(rd, result);
	ERR_FAIL_COND_V(!p_buffer.is_valid(), result);
	ERR_FAIL_COND_V(p_count <= 0, result);

	PackedByteArray bytes = rd->buffer_get_data(p_buffer);

	uint32_t expected_size = p_count * 16; // vec4 padded
	if ((uint32_t)bytes.size() < expected_size) {
		UtilityFunctions::push_error("GpuBufferManager: buffer too small for ", p_count,
				" vec3 elements. Expected ", expected_size, " bytes, got ", bytes.size());
		return result;
	}

	result.resize(p_count);
	Vector3 *dst = result.ptrw();
	const uint8_t *src = bytes.ptr();

	for (int i = 0; i < p_count; i++) {
		float v[4];
		memcpy(v, src + i * 16, 16);
		dst[i] = Vector3(v[0], v[1], v[2]); // skip w padding
	}

	return result;
}

PackedFloat32Array GpuBufferManager::download_float_buffer(RID p_buffer, int p_count) {
	PackedFloat32Array result;
	ERR_FAIL_NULL_V(rd, result);
	ERR_FAIL_COND_V(!p_buffer.is_valid(), result);
	ERR_FAIL_COND_V(p_count <= 0, result);

	PackedByteArray bytes = rd->buffer_get_data(p_buffer);

	uint32_t expected_size = p_count * sizeof(float);
	if ((uint32_t)bytes.size() < expected_size) {
		UtilityFunctions::push_error("GpuBufferManager: buffer too small for ", p_count,
				" float elements. Expected ", expected_size, " bytes, got ", bytes.size());
		return result;
	}

	result.resize(p_count);
	memcpy(result.ptrw(), bytes.ptr(), expected_size);

	return result;
}

// --- Uniform buffer ---

RID GpuBufferManager::create_uniform_buffer(const PackedByteArray &p_data) {
	ERR_FAIL_NULL_V(rd, RID());

	if (p_data.size() == 0) {
		return RID();
	}

	RID buffer = rd->uniform_buffer_create(p_data.size(), p_data);
	allocated_buffers.push_back(buffer);
	return buffer;
}

// --- Cleanup ---

void GpuBufferManager::free_buffer(RID p_buffer) {
	if (rd == nullptr || !p_buffer.is_valid()) {
		return;
	}

	rd->free_rid(p_buffer);
	allocated_buffers.erase(p_buffer);
}

void GpuBufferManager::free_all() {
	if (rd == nullptr) {
		return;
	}

	for (int i = 0; i < allocated_buffers.size(); i++) {
		if (allocated_buffers[i].is_valid()) {
			rd->free_rid(allocated_buffers[i]);
		}
	}
	allocated_buffers.clear();
}

GpuBufferManager::~GpuBufferManager() {
	free_all();
}

} // namespace godot