Corregidos ~2570 issues automáticamente con clang-tidy --fix-errors más ajustes manuales posteriores: - modernize: designated-initializers, trailing-return-type, use-auto, avoid-c-arrays (→ std::array<>), use-ranges, use-emplace, deprecated-headers, use-equals-default, pass-by-value, return-braced-init-list, use-default-member-init - readability: math-missing-parentheses, implicit-bool-conversion, braces-around-statements, isolate-declaration, use-std-min-max, identifier-naming, else-after-return, redundant-casting, convert-member-functions-to-static, make-member-function-const, static-accessed-through-instance - performance: avoid-endl, unnecessary-value-param, type-promotion, inefficient-vector-operation - dead code: XOR_KEY (orphan tras eliminar encryptData/decryptData), dead stores en engine.cpp y png_shape.cpp - NOLINT justificado en 10 funciones con alta complejidad cognitiva (initialize, render, main, processEvents, update×3, performDemoAction, randomizeOnDemoStart, renderDebugHUD, AppLogo::update) Compilación: gcc -Wall sin warnings. clang-tidy: 0 issues. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
237 lines
8.4 KiB
C++
237 lines
8.4 KiB
C++
#include "gpu_sprite_batch.hpp"
|
|
|
|
#include <SDL3/SDL_log.h>
|
|
|
|
#include <cstring> // memcpy
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Public interface
|
|
// ---------------------------------------------------------------------------
|
|
|
|
auto GpuSpriteBatch::init(SDL_GPUDevice* device, int max_sprites) -> bool {
|
|
max_sprites_ = max_sprites;
|
|
// Pre-allocate GPU buffers large enough for (max_sprites_ + 2) quads.
|
|
// The +2 reserves one slot for the background quad and one for the fullscreen overlay.
|
|
Uint32 max_verts = static_cast<Uint32>(max_sprites_ + 2) * 4;
|
|
Uint32 max_indices = static_cast<Uint32>(max_sprites_ + 2) * 6;
|
|
|
|
Uint32 vb_size = max_verts * sizeof(GpuVertex);
|
|
Uint32 ib_size = max_indices * sizeof(uint32_t);
|
|
|
|
// Vertex buffer
|
|
SDL_GPUBufferCreateInfo vb_info = {};
|
|
vb_info.usage = SDL_GPU_BUFFERUSAGE_VERTEX;
|
|
vb_info.size = vb_size;
|
|
vertex_buf_ = SDL_CreateGPUBuffer(device, &vb_info);
|
|
if (vertex_buf_ == nullptr) {
|
|
SDL_Log("GpuSpriteBatch: vertex buffer creation failed: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
// Index buffer
|
|
SDL_GPUBufferCreateInfo ib_info = {};
|
|
ib_info.usage = SDL_GPU_BUFFERUSAGE_INDEX;
|
|
ib_info.size = ib_size;
|
|
index_buf_ = SDL_CreateGPUBuffer(device, &ib_info);
|
|
if (index_buf_ == nullptr) {
|
|
SDL_Log("GpuSpriteBatch: index buffer creation failed: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
// Transfer buffers (reused every frame via cycle=true on upload)
|
|
SDL_GPUTransferBufferCreateInfo tb_info = {};
|
|
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
|
|
|
|
tb_info.size = vb_size;
|
|
vertex_transfer_ = SDL_CreateGPUTransferBuffer(device, &tb_info);
|
|
if (vertex_transfer_ == nullptr) {
|
|
SDL_Log("GpuSpriteBatch: vertex transfer buffer failed: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
tb_info.size = ib_size;
|
|
index_transfer_ = SDL_CreateGPUTransferBuffer(device, &tb_info);
|
|
if (index_transfer_ == nullptr) {
|
|
SDL_Log("GpuSpriteBatch: index transfer buffer failed: %s", SDL_GetError());
|
|
return false;
|
|
}
|
|
|
|
vertices_.reserve(static_cast<size_t>(max_sprites_ + 2) * 4);
|
|
indices_.reserve(static_cast<size_t>(max_sprites_ + 2) * 6);
|
|
return true;
|
|
}
|
|
|
|
void GpuSpriteBatch::destroy(SDL_GPUDevice* device) {
|
|
if (device == nullptr) {
|
|
return;
|
|
}
|
|
if (vertex_transfer_ != nullptr) {
|
|
SDL_ReleaseGPUTransferBuffer(device, vertex_transfer_);
|
|
vertex_transfer_ = nullptr;
|
|
}
|
|
if (index_transfer_ != nullptr) {
|
|
SDL_ReleaseGPUTransferBuffer(device, index_transfer_);
|
|
index_transfer_ = nullptr;
|
|
}
|
|
if (vertex_buf_ != nullptr) {
|
|
SDL_ReleaseGPUBuffer(device, vertex_buf_);
|
|
vertex_buf_ = nullptr;
|
|
}
|
|
if (index_buf_ != nullptr) {
|
|
SDL_ReleaseGPUBuffer(device, index_buf_);
|
|
index_buf_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void GpuSpriteBatch::beginFrame() {
|
|
vertices_.clear();
|
|
indices_.clear();
|
|
bg_index_count_ = 0;
|
|
sprite_index_offset_ = 0;
|
|
sprite_index_count_ = 0;
|
|
overlay_index_offset_ = 0;
|
|
overlay_index_count_ = 0;
|
|
}
|
|
|
|
void GpuSpriteBatch::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) {
|
|
// Background is the full screen quad, corners:
|
|
// TL(-1, 1) TR(1, 1) → top color
|
|
// BL(-1,-1) BR(1,-1) → bottom color
|
|
// We push it as 4 separate vertices (different colors per row).
|
|
auto vi = static_cast<uint32_t>(vertices_.size());
|
|
|
|
// Top-left
|
|
vertices_.push_back({-1.0f, 1.0f, 0.0f, 0.0f, top_r, top_g, top_b, 1.0f});
|
|
// Top-right
|
|
vertices_.push_back({1.0f, 1.0f, 1.0f, 0.0f, top_r, top_g, top_b, 1.0f});
|
|
// Bottom-right
|
|
vertices_.push_back({1.0f, -1.0f, 1.0f, 1.0f, bot_r, bot_g, bot_b, 1.0f});
|
|
// Bottom-left
|
|
vertices_.push_back({-1.0f, -1.0f, 0.0f, 1.0f, bot_r, bot_g, bot_b, 1.0f});
|
|
|
|
// Two triangles: TL-TR-BR, BR-BL-TL
|
|
indices_.push_back(vi + 0);
|
|
indices_.push_back(vi + 1);
|
|
indices_.push_back(vi + 2);
|
|
indices_.push_back(vi + 2);
|
|
indices_.push_back(vi + 3);
|
|
indices_.push_back(vi + 0);
|
|
|
|
bg_index_count_ = 6;
|
|
sprite_index_offset_ = 6;
|
|
|
|
(void)screen_w;
|
|
(void)screen_h; // unused — bg always covers full NDC
|
|
}
|
|
|
|
void GpuSpriteBatch::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) {
|
|
// Apply scale around the sprite centre
|
|
float scaled_w = w * scale;
|
|
float scaled_h = h * scale;
|
|
float offset_x = (w - scaled_w) * 0.5f;
|
|
float offset_y = (h - scaled_h) * 0.5f;
|
|
|
|
float px0 = x + offset_x;
|
|
float py0 = y + offset_y;
|
|
float px1 = px0 + scaled_w;
|
|
float py1 = py0 + scaled_h;
|
|
|
|
float ndx0;
|
|
float ndy0;
|
|
float ndx1;
|
|
float ndy1;
|
|
toNDC(px0, py0, screen_w, screen_h, ndx0, ndy0);
|
|
toNDC(px1, py1, screen_w, screen_h, ndx1, ndy1);
|
|
|
|
pushQuad(ndx0, ndy0, ndx1, ndy1, 0.0f, 0.0f, 1.0f, 1.0f, r, g, b, a);
|
|
sprite_index_count_ += 6;
|
|
}
|
|
|
|
void GpuSpriteBatch::addFullscreenOverlay() {
|
|
// El overlay es un slot reservado fuera del espacio de max_sprites_, igual que el background.
|
|
// Escribe directamente sin pasar por el guard de pushQuad().
|
|
overlay_index_offset_ = static_cast<int>(indices_.size());
|
|
auto vi = static_cast<uint32_t>(vertices_.size());
|
|
vertices_.push_back({-1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f});
|
|
vertices_.push_back({1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f});
|
|
vertices_.push_back({1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
|
|
vertices_.push_back({-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f});
|
|
indices_.push_back(vi + 0);
|
|
indices_.push_back(vi + 1);
|
|
indices_.push_back(vi + 2);
|
|
indices_.push_back(vi + 2);
|
|
indices_.push_back(vi + 3);
|
|
indices_.push_back(vi + 0);
|
|
overlay_index_count_ = 6;
|
|
}
|
|
|
|
auto GpuSpriteBatch::uploadBatch(SDL_GPUDevice* device, SDL_GPUCommandBuffer* cmd_buf) -> bool {
|
|
if (vertices_.empty()) {
|
|
return false;
|
|
}
|
|
|
|
auto vb_size = static_cast<Uint32>(vertices_.size() * sizeof(GpuVertex));
|
|
auto ib_size = static_cast<Uint32>(indices_.size() * sizeof(uint32_t));
|
|
|
|
// Map → write → unmap transfer buffers
|
|
void* vp = SDL_MapGPUTransferBuffer(device, vertex_transfer_, true /* cycle */);
|
|
if (vp == nullptr) {
|
|
SDL_Log("GpuSpriteBatch: vertex map failed");
|
|
return false;
|
|
}
|
|
memcpy(vp, vertices_.data(), vb_size);
|
|
SDL_UnmapGPUTransferBuffer(device, vertex_transfer_);
|
|
|
|
void* ip = SDL_MapGPUTransferBuffer(device, index_transfer_, true /* cycle */);
|
|
if (ip == nullptr) {
|
|
SDL_Log("GpuSpriteBatch: index map failed");
|
|
return false;
|
|
}
|
|
memcpy(ip, indices_.data(), ib_size);
|
|
SDL_UnmapGPUTransferBuffer(device, index_transfer_);
|
|
|
|
// Upload via copy pass
|
|
SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd_buf);
|
|
|
|
SDL_GPUTransferBufferLocation v_src = {vertex_transfer_, 0};
|
|
SDL_GPUBufferRegion v_dst = {vertex_buf_, 0, vb_size};
|
|
SDL_UploadToGPUBuffer(copy, &v_src, &v_dst, true /* cycle */);
|
|
|
|
SDL_GPUTransferBufferLocation i_src = {index_transfer_, 0};
|
|
SDL_GPUBufferRegion i_dst = {index_buf_, 0, ib_size};
|
|
SDL_UploadToGPUBuffer(copy, &i_src, &i_dst, true /* cycle */);
|
|
|
|
SDL_EndGPUCopyPass(copy);
|
|
return true;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Private helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void GpuSpriteBatch::toNDC(float px, float py, float screen_w, float screen_h, float& ndx, float& ndy) {
|
|
ndx = (px / screen_w) * 2.0f - 1.0f;
|
|
ndy = 1.0f - (py / screen_h) * 2.0f;
|
|
}
|
|
|
|
void GpuSpriteBatch::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) {
|
|
// +1 reserva el slot del background que ya entró sin pasar por este guard.
|
|
if (vertices_.size() + 4 > static_cast<size_t>(max_sprites_ + 1) * 4) {
|
|
return;
|
|
}
|
|
auto vi = static_cast<uint32_t>(vertices_.size());
|
|
|
|
// TL, TR, BR, BL
|
|
vertices_.push_back({ndx0, ndy0, u0, v0, r, g, b, a});
|
|
vertices_.push_back({ndx1, ndy0, u1, v0, r, g, b, a});
|
|
vertices_.push_back({ndx1, ndy1, u1, v1, r, g, b, a});
|
|
vertices_.push_back({ndx0, ndy1, u0, v1, r, g, b, a});
|
|
|
|
indices_.push_back(vi + 0);
|
|
indices_.push_back(vi + 1);
|
|
indices_.push_back(vi + 2);
|
|
indices_.push_back(vi + 2);
|
|
indices_.push_back(vi + 3);
|
|
indices_.push_back(vi + 0);
|
|
}
|