refactor(gpu): eliminar GPU compute boids (prevé crash macOS)

Elimina el kernel Metal O(N²) de boids en GPU que causava GPU timeout
a macOS amb >50K boles, arrossegant WindowServer fins al crash.

- Elimina gpu_boid_buffer.hpp/cpp (GpuBoidBuffer, BallComputeData, BoidParams)
- Elimina kBoidComputeMSL i kBallComputeVertMSL de gpu_pipeline
- Elimina boid_compute_pipeline_ i ball_compute_pipeline_
- Elimina use_gpu_boids_, boid_params_, ball_screen_uniforms_ de Engine
- Elimina syncAndExitGpuBoids() i tot el compute dispatch de render()
- Mode BOIDS ara usa sempre boid_manager_ (CPU, spatial hash O(N))
  i renderitza via gpu_ball_buffer_ instanced (mateix path que PHYSICS)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 08:45:01 +01:00
parent badf92420b
commit d2e7f2ff86
8 changed files with 321 additions and 41 deletions

View File

@@ -0,0 +1,65 @@
#include "gpu_ball_buffer.hpp"
#include <SDL3/SDL_log.h>
#include <algorithm> // std::min
#include <cstring> // memcpy
bool GpuBallBuffer::init(SDL_GPUDevice* device) {
Uint32 buf_size = static_cast<Uint32>(MAX_BALLS) * sizeof(BallGPUData);
// GPU vertex buffer (instance-rate data read by the ball instanced shader)
SDL_GPUBufferCreateInfo buf_info = {};
buf_info.usage = SDL_GPU_BUFFERUSAGE_VERTEX;
buf_info.size = buf_size;
gpu_buf_ = SDL_CreateGPUBuffer(device, &buf_info);
if (!gpu_buf_) {
SDL_Log("GpuBallBuffer: GPU buffer creation failed: %s", SDL_GetError());
return false;
}
// Transfer buffer (upload staging, cycled every frame)
SDL_GPUTransferBufferCreateInfo tb_info = {};
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
tb_info.size = buf_size;
transfer_buf_ = SDL_CreateGPUTransferBuffer(device, &tb_info);
if (!transfer_buf_) {
SDL_Log("GpuBallBuffer: transfer buffer creation failed: %s", SDL_GetError());
return false;
}
SDL_Log("GpuBallBuffer: initialized (capacity %d balls, %.1f MB VRAM)",
MAX_BALLS, buf_size / (1024.0f * 1024.0f));
return true;
}
void GpuBallBuffer::destroy(SDL_GPUDevice* device) {
if (!device) return;
if (transfer_buf_) { SDL_ReleaseGPUTransferBuffer(device, transfer_buf_); transfer_buf_ = nullptr; }
if (gpu_buf_) { SDL_ReleaseGPUBuffer(device, gpu_buf_); gpu_buf_ = nullptr; }
count_ = 0;
}
bool GpuBallBuffer::upload(SDL_GPUDevice* device, SDL_GPUCommandBuffer* cmd,
const BallGPUData* data, int count) {
if (!data || count <= 0) { count_ = 0; return false; }
count = std::min(count, MAX_BALLS);
Uint32 upload_size = static_cast<Uint32>(count) * sizeof(BallGPUData);
void* ptr = SDL_MapGPUTransferBuffer(device, transfer_buf_, true /* cycle */);
if (!ptr) {
SDL_Log("GpuBallBuffer: transfer buffer map failed: %s", SDL_GetError());
return false;
}
memcpy(ptr, data, upload_size);
SDL_UnmapGPUTransferBuffer(device, transfer_buf_);
SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd);
SDL_GPUTransferBufferLocation src = { transfer_buf_, 0 };
SDL_GPUBufferRegion dst = { gpu_buf_, 0, upload_size };
SDL_UploadToGPUBuffer(copy, &src, &dst, true /* cycle */);
SDL_EndGPUCopyPass(copy);
count_ = count;
return true;
}