#pragma once #include #include #include // --------------------------------------------------------------------------- // GpuVertex — 8-float vertex layout sent to the GPU. // Position is in NDC (pre-transformed on CPU), UV in [0,1], color in [0,1]. // --------------------------------------------------------------------------- struct GpuVertex { float x, y; // NDC position (−1..1) float u, v; // Texture coords (0..1) float r, g, b, a; // RGBA color (0..1) }; // ============================================================================ // GpuSpriteBatch — Accumulates sprite quads, uploads them in one copy pass. // // Usage per frame: // batch.beginFrame(); // batch.addBackground(...); // Must be first (bg indices = [0..5]) // batch.addSprite(...) × N; // batch.uploadBatch(device, cmd); // Copy pass // // Then in render pass: bind buffers, draw bg with white tex, draw sprites. // ============================================================================ class GpuSpriteBatch { public: // Maximum sprites (background + UI overlay each count as one sprite) static constexpr int MAX_SPRITES = 200000; bool init(SDL_GPUDevice* device); void destroy(SDL_GPUDevice* device); void beginFrame(); // Add the full-screen background gradient quad. // top_* and bot_* are RGB in [0,1]. void addBackground(float screen_w, float screen_h, float top_r, float top_g, float top_b, float bot_r, float bot_g, float bot_b); // Add a sprite quad (pixel coordinates). // scale: uniform scale around the quad centre. void addSprite(float x, float y, float w, float h, float r, float g, float b, float a, float scale, float screen_w, float screen_h); // Add a full-screen overlay quad (e.g. UI surface, NDC −1..1). void addFullscreenOverlay(); // Upload CPU vectors to GPU buffers via a copy pass. // Returns false if the batch is empty. bool uploadBatch(SDL_GPUDevice* device, SDL_GPUCommandBuffer* cmd_buf); SDL_GPUBuffer* vertexBuffer() const { return vertex_buf_; } SDL_GPUBuffer* indexBuffer() const { return index_buf_; } int bgIndexCount() const { return bg_index_count_; } int overlayIndexOffset() const { return overlay_index_offset_; } int overlayIndexCount() const { return overlay_index_count_; } int spriteIndexOffset() const { return sprite_index_offset_; } int spriteIndexCount() const { return sprite_index_count_; } bool isEmpty() const { return vertices_.empty(); } private: void toNDC(float px, float py, float screen_w, float screen_h, float& ndx, float& ndy) const; void pushQuad(float ndx0, float ndy0, float ndx1, float ndy1, float u0, float v0, float u1, float v1, float r, float g, float b, float a); std::vector vertices_; std::vector indices_; SDL_GPUBuffer* vertex_buf_ = nullptr; SDL_GPUBuffer* index_buf_ = nullptr; SDL_GPUTransferBuffer* vertex_transfer_ = nullptr; SDL_GPUTransferBuffer* index_transfer_ = nullptr; int bg_index_count_ = 0; int sprite_index_offset_ = 0; int sprite_index_count_ = 0; int overlay_index_offset_ = 0; int overlay_index_count_ = 0; };