#define SDL_MAIN_USE_CALLBACKS #include #include #include #include #include #include class PixelApp { public: static constexpr int WIDTH = 160; static constexpr int HEIGHT = 160; static constexpr int SIZE = WIDTH * HEIGHT; static constexpr int ZOOM = 4; PixelApp() = default; ~PixelApp() { cleanup(); } bool init() { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GPU)) return false; window.reset(SDL_CreateWindow("pixels (GPU)", WIDTH * ZOOM, HEIGHT * ZOOM, SDL_WINDOW_RESIZABLE)); if (!window) return false; device.reset(SDL_CreateGPUDevice(SDL_GPU_SHADERFORMAT_SPIRV)); if (!device) return false; if (!SDL_ClaimWindowForGPUDevice(device.get(), window.get())) return false; // Crear textura GPU donde escribiremos los píxeles SDL_GPUTextureCreateInfo info{}; info.type = SDL_GPU_TEXTURETYPE_2D; info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; info.width = WIDTH; info.height = HEIGHT; info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLED | SDL_GPU_TEXTUREUSAGE_COPY_SRC | SDL_GPU_TEXTUREUSAGE_COPY_DST; texture.reset(SDL_CreateGPUTexture(device.get(), &info)); if (!texture) return false; palette = { 0xFF000000, 0xFFFFFFFF }; surface.fill(0); return true; } void handleEvent(const SDL_Event& e, SDL_AppResult& result) { if (e.type == SDL_EVENT_QUIT || (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE)) { result = SDL_APP_SUCCESS; } } void update() { // --- 1. Generar efecto en CPU --- surface.fill(0); float time = SDL_GetTicks() / 1000.0f; int rad = 96; int dx = (WIDTH - rad * 2) / 2; int dy = (HEIGHT - rad * 2) / 2; for (int j = -rad; j <= rad; j += 3) { for (int i = -rad; i <= rad; i += 2) { float dist = std::sqrt(float(i * i + j * j)); float z = std::cos((dist / 40 - time) * M_PI * 2) * 6; int X = rad + i + dx; int Y = rad + j - z + dy; if (X >= 0 && X < WIDTH && Y >= 0 && Y < HEIGHT) surface[X + Y * WIDTH] = 1; } } // --- 2. Mapear textura GPU --- SDL_GPUTextureRegion region{}; region.texture = texture.get(); region.w = WIDTH; region.h = HEIGHT; void* mapped = nullptr; uint32_t pitch = 0; if (!SDL_MapGPUTexture(device.get(), ®ion, &mapped, &pitch)) return; // --- 3. Copiar píxeles CPU → GPU --- Uint32* dst = reinterpret_cast(mapped); uint32_t rowPixels = pitch / sizeof(Uint32); for (int y = 0; y < HEIGHT; ++y) { for (int x = 0; x < WIDTH; ++x) { dst[x + y * rowPixels] = palette[surface[x + y * WIDTH]]; } } SDL_UnmapGPUTexture(device.get(), ®ion); // --- 4. Renderizar textura en pantalla --- SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(device.get()); if (!cmd) return; SDL_GPUCopyPass* pass = SDL_BeginGPUCopyPass(cmd); SDL_GPUBlitInfo blit{}; blit.source.texture = texture.get(); blit.source.w = WIDTH; blit.source.h = HEIGHT; blit.destination.x = 0; blit.destination.y = 0; blit.destination.w = WIDTH * ZOOM; blit.destination.h = HEIGHT * ZOOM; SDL_BlitGPUTexture(pass, &blit); SDL_EndGPUCopyPass(pass); SDL_SubmitGPUCommandBuffer(cmd); } private: struct SDL_Deleter { void operator()(SDL_Window* p) const { SDL_DestroyWindow(p); } void operator()(SDL_GPUDevice* p) const { SDL_DestroyGPUDevice(p); } void operator()(SDL_GPUTexture* p) const { SDL_DestroyGPUTexture(p); } }; std::unique_ptr window; std::unique_ptr device; std::unique_ptr texture; std::array surface; std::array palette; void cleanup() { texture.reset(); device.reset(); window.reset(); SDL_Quit(); } }; static PixelApp app; SDL_AppResult SDL_AppInit(void**, int, char**) { return app.init() ? SDL_APP_CONTINUE : SDL_APP_FAILURE; } SDL_AppResult SDL_AppEvent(void*, SDL_Event* e) { SDL_AppResult result = SDL_APP_CONTINUE; app.handleEvent(*e, result); return result; } SDL_AppResult SDL_AppIterate(void*) { app.update(); return SDL_APP_CONTINUE; } void SDL_AppQuit(void*, SDL_AppResult) { // RAII limpia todo }