Files
vibe3_physics/source/gpu/gpu_texture.cpp
Sergio Valor 00a5875c92 feat(gpu): migrar a SDL3_GPU amb 2-pass rendering i post-processat
- Infraestructura GPU: GpuContext, GpuPipeline, GpuSpriteBatch, GpuTexture
- Engine::render() migrat a 2-pass: sprites → offscreen R8G8B8A8 → swapchain + vignette
- UI/text via software renderer (SDL3_ttf) + upload com a textura overlay GPU
- CMakeLists.txt actualitzat per incloure subsistema gpu/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:08:12 +01:00

209 lines
7.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "gpu_texture.hpp"
#include <SDL3/SDL_log.h>
#include <SDL3/SDL_pixels.h>
#include <cstring> // memcpy
#include <string>
// stb_image is compiled in texture.cpp (STB_IMAGE_IMPLEMENTATION defined there)
#include "external/stb_image.h"
#include "resource_manager.hpp"
// ---------------------------------------------------------------------------
// Public interface
// ---------------------------------------------------------------------------
bool GpuTexture::fromFile(SDL_GPUDevice* device, const std::string& file_path) {
unsigned char* resource_data = nullptr;
size_t resource_size = 0;
if (!ResourceManager::loadResource(file_path, resource_data, resource_size)) {
SDL_Log("GpuTexture: can't load resource '%s'", file_path.c_str());
return false;
}
int w = 0, h = 0, orig = 0;
unsigned char* pixels = stbi_load_from_memory(
resource_data, static_cast<int>(resource_size),
&w, &h, &orig, STBI_rgb_alpha);
delete[] resource_data;
if (!pixels) {
SDL_Log("GpuTexture: stbi decode failed for '%s': %s",
file_path.c_str(), stbi_failure_reason());
return false;
}
destroy(device);
bool ok = uploadPixels(device, pixels, w, h, SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
stbi_image_free(pixels);
if (ok) {
ok = createSampler(device, true /*nearest = pixel-perfect sprites*/);
}
return ok;
}
bool GpuTexture::fromSurface(SDL_GPUDevice* device, SDL_Surface* surface, bool nearest) {
if (!surface) return false;
// Ensure RGBA32 format
SDL_Surface* rgba = surface;
bool need_free = false;
if (surface->format != SDL_PIXELFORMAT_RGBA32) {
rgba = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
if (!rgba) {
SDL_Log("GpuTexture: SDL_ConvertSurface failed: %s", SDL_GetError());
return false;
}
need_free = true;
}
destroy(device);
bool ok = uploadPixels(device, rgba->pixels, rgba->w, rgba->h,
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
if (ok) ok = createSampler(device, nearest);
if (need_free) SDL_DestroySurface(rgba);
return ok;
}
bool GpuTexture::createRenderTarget(SDL_GPUDevice* device, int w, int h,
SDL_GPUTextureFormat format) {
destroy(device);
SDL_GPUTextureCreateInfo info = {};
info.type = SDL_GPU_TEXTURETYPE_2D;
info.format = format;
info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET
| SDL_GPU_TEXTUREUSAGE_SAMPLER;
info.width = static_cast<Uint32>(w);
info.height = static_cast<Uint32>(h);
info.layer_count_or_depth = 1;
info.num_levels = 1;
info.sample_count = SDL_GPU_SAMPLECOUNT_1;
texture_ = SDL_CreateGPUTexture(device, &info);
if (!texture_) {
SDL_Log("GpuTexture: createRenderTarget failed: %s", SDL_GetError());
return false;
}
width_ = w;
height_ = h;
// Render targets are sampled with linear filter (postfx reads them)
return createSampler(device, false);
}
bool GpuTexture::createWhite(SDL_GPUDevice* device) {
destroy(device);
// 1×1 white RGBA pixel
const Uint8 white[4] = {255, 255, 255, 255};
bool ok = uploadPixels(device, white, 1, 1,
SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM);
if (ok) ok = createSampler(device, true);
return ok;
}
void GpuTexture::destroy(SDL_GPUDevice* device) {
if (!device) return;
if (sampler_) { SDL_ReleaseGPUSampler(device, sampler_); sampler_ = nullptr; }
if (texture_) { SDL_ReleaseGPUTexture(device, texture_); texture_ = nullptr; }
width_ = height_ = 0;
}
// ---------------------------------------------------------------------------
// Private helpers
// ---------------------------------------------------------------------------
bool GpuTexture::uploadPixels(SDL_GPUDevice* device, const void* pixels,
int w, int h, SDL_GPUTextureFormat format) {
// Create GPU texture
SDL_GPUTextureCreateInfo tex_info = {};
tex_info.type = SDL_GPU_TEXTURETYPE_2D;
tex_info.format = format;
tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
tex_info.width = static_cast<Uint32>(w);
tex_info.height = static_cast<Uint32>(h);
tex_info.layer_count_or_depth = 1;
tex_info.num_levels = 1;
tex_info.sample_count = SDL_GPU_SAMPLECOUNT_1;
texture_ = SDL_CreateGPUTexture(device, &tex_info);
if (!texture_) {
SDL_Log("GpuTexture: SDL_CreateGPUTexture failed: %s", SDL_GetError());
return false;
}
// Create transfer buffer and upload pixels
Uint32 data_size = static_cast<Uint32>(w * h * 4); // RGBA = 4 bytes/pixel
SDL_GPUTransferBufferCreateInfo tb_info = {};
tb_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
tb_info.size = data_size;
SDL_GPUTransferBuffer* transfer = SDL_CreateGPUTransferBuffer(device, &tb_info);
if (!transfer) {
SDL_Log("GpuTexture: transfer buffer creation failed: %s", SDL_GetError());
SDL_ReleaseGPUTexture(device, texture_);
texture_ = nullptr;
return false;
}
void* mapped = SDL_MapGPUTransferBuffer(device, transfer, false);
if (!mapped) {
SDL_Log("GpuTexture: map failed: %s", SDL_GetError());
SDL_ReleaseGPUTransferBuffer(device, transfer);
SDL_ReleaseGPUTexture(device, texture_);
texture_ = nullptr;
return false;
}
memcpy(mapped, pixels, data_size);
SDL_UnmapGPUTransferBuffer(device, transfer);
// Upload via command buffer
SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device);
SDL_GPUCopyPass* copy = SDL_BeginGPUCopyPass(cmd);
SDL_GPUTextureTransferInfo src = {};
src.transfer_buffer = transfer;
src.offset = 0;
src.pixels_per_row = static_cast<Uint32>(w);
src.rows_per_layer = static_cast<Uint32>(h);
SDL_GPUTextureRegion dst = {};
dst.texture = texture_;
dst.mip_level = 0;
dst.layer = 0;
dst.x = dst.y = dst.z = 0;
dst.w = static_cast<Uint32>(w);
dst.h = static_cast<Uint32>(h);
dst.d = 1;
SDL_UploadToGPUTexture(copy, &src, &dst, false);
SDL_EndGPUCopyPass(copy);
SDL_SubmitGPUCommandBuffer(cmd);
SDL_ReleaseGPUTransferBuffer(device, transfer);
width_ = w;
height_ = h;
return true;
}
bool GpuTexture::createSampler(SDL_GPUDevice* device, bool nearest) {
SDL_GPUSamplerCreateInfo info = {};
info.min_filter = nearest ? SDL_GPU_FILTER_NEAREST : SDL_GPU_FILTER_LINEAR;
info.mag_filter = nearest ? SDL_GPU_FILTER_NEAREST : SDL_GPU_FILTER_LINEAR;
info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST;
info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE;
sampler_ = SDL_CreateGPUSampler(device, &info);
if (!sampler_) {
SDL_Log("GpuTexture: SDL_CreateGPUSampler failed: %s", SDL_GetError());
return false;
}
return true;
}