#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