#pragma once #include #include // --------------------------------------------------------------------------- // BallGPUData — 32-byte per-instance record stored in VRAM. // Positions and sizes pre-converted to NDC space on CPU so the vertex shader // needs no screen-dimension uniform. // cx, cy : NDC center (cx = (x + w/2)/sw*2-1, cy = 1-(y+h/2)/sh*2) // hw, hh : NDC half-size (hw = w/sw, hh = h/sh, both positive) // r,g,b,a: RGBA in [0,1] // --------------------------------------------------------------------------- struct BallGPUData { float cx, cy; // NDC center float hw, hh; // NDC half-size (positive) float r, g, b, a; // RGBA color [0,1] }; static_assert(sizeof(BallGPUData) == 32, "BallGPUData must be 32 bytes"); // ============================================================================ // GpuBallBuffer — owns the GPU vertex buffer used for instanced ball rendering. // // Usage per frame: // buffer.upload(device, cmd, data, count); // inside a copy pass // // Then in render pass: bind buffer, SDL_DrawGPUPrimitives(pass, 6, count, 0, 0) // ============================================================================ class GpuBallBuffer { public: static constexpr int MAX_BALLS = 500000; bool init(SDL_GPUDevice* device); void destroy(SDL_GPUDevice* device); // Upload ball array to GPU via an internal copy pass. // count is clamped to MAX_BALLS. Returns false on error or empty input. bool upload(SDL_GPUDevice* device, SDL_GPUCommandBuffer* cmd, const BallGPUData* data, int count); SDL_GPUBuffer* buffer() const { return gpu_buf_; } int count() const { return count_; } private: SDL_GPUBuffer* gpu_buf_ = nullptr; SDL_GPUTransferBuffer* transfer_buf_ = nullptr; int count_ = 0; };