CPU và GPU resource trong SDL2

SDL2 phân biệt resource theo hai trục quan trọng: resource nằm ở CPU RAM hay GPU VRAM, và với audio là load full vào RAM hay stream on-demand. Bốn loại resource phổ biến (SDL_Surface, SDL_Texture, Mix_Chunk, Mix_Music) là bốn vị trí trên hai trục đó. Chọn nhầm không gây crash nhưng gây leak RAM hoặc không thể overlap sound — bug “im lặng” khó phát hiện.

Surface và Texture: nơi pixel sống

SDL_Surface giữ pixel data ở CPU RAM. Mọi thao tác (fill rect, blit, alpha blend tay) đều do CPU làm — chậm nhưng kiểm soát từng pixel. SDL_Texture giữ pixel data ở GPU VRAM, hardware-accelerated qua renderer. Render cùng một image bằng texture nhanh hơn surface nhiều lần vì GPU có pipeline song song và memory bandwidth cao.

Quy tắc thực dụng: cái gì render lên screen thì phải là Texture; cái gì cần pixel manipulation thì phải là Surface; chuyển đổi qua SDL_CreateTextureFromSurface.

SDL_ttf minh hoạ điển hình. TTF_RenderText_Blended chỉ tạo Surface vì font rasterization (vẽ glyph từ vector outline thành bitmap) là CPU-bound — phải pass qua CPU memory. Dev có trách nhiệm upload sang Texture rồi free Surface:

SDL_Surface* surf = TTF_RenderText_Blended(font, "Hello", color);
SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);
SDL_FreeSurface(surf);  // bắt buộc, nếu không sẽ leak CPU RAM

Bỏ SDL_FreeSurface là một trong những lỗi rò rỉ phổ biến nhất khi học SDL — code vẫn chạy đúng nhưng leak ~20KB mỗi lần render text mới.

Mix_Chunk và Mix_Music: streaming hay preload

SDL2_mixer phân biệt hai loại audio:

Mix_Chunk (qua Mix_LoadWAV) Mix_Music (qua Mix_LoadMUS)
Memory Decode full vào RAM khi load Stream on-demand, decode dần
Channel Nhiều channel song song (default 8) Một music channel duy nhất
API play Mix_PlayChannel(-1, chunk, 0) Mix_PlayMusic(music, loops)
Phù hợp cho Sound effect ngắn, có thể overlap Background music dài

Hệ quả của “một music channel duy nhất”: gọi Mix_PlayMusic lần hai sẽ cắt music đang chơi. Nếu lỡ dùng Mix_LoadMUS cho sound effect (như tiếng ăn của snake), mỗi lần ăn sẽ cắt music nền — bug rất rõ. Ngược lại, dùng Mix_LoadWAV cho background music dài sẽ load toàn bộ file vào RAM (file 13MB → tốn 13MB RAM cứng) thay vì streaming.

Quy tắc: file ngắn, cần overlap → Chunk; file dài, chỉ chơi một bản tại một thời điểm → Music.

Mỗi loại cần API destroy riêng

Vì C không có destructor tự động, mỗi loại resource có hàm cleanup tương ứng:

SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
Mix_FreeChunk(chunk);
Mix_FreeMusic(music);
TTF_CloseFont(font);

Gọi nhầm sẽ undefined behavior (ví dụ SDL_FreeSurface lên một Texture pointer). Quy tắc Cấp resource ở đâu, giải phóng ở đó áp dụng nguyên xi: nơi nào Create/Load thì nơi đó phải Free/Destroy, với đúng cặp hàm.

Trải nghiệm cá nhân

Phân biệt này được internalize qua 3 game SDL2 thời 2016–2017 (repo sdl_game). Việc chọn đúng Mix_LoadMUS cho doge.wav (13MB music nền) và Mix_LoadWAV cho eat.wav (66KB sound effect) trong doge_snake/main.cpp:194-195 là quyết định đúng — game audio hoạt động bình thường, không bị cắt nhạc khi ăn. Tuy nhiên SDL_FreeSurface bị quên ở Snake.cpp:16 là một leak ngầm. Chi tiết trong transcript phỏng vấn.

Cập nhật: 2026-05-29