Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sdl3-codecamp

Learning SDL3 with C23 by doing the oldest trick in graphics: keep a plain array of pixels in memory, draw into it with your own code, and hand the whole thing to the GPU once per frame.

src/main.c owns the window, the event loop and the frame pacing. framelib is the drawing layer — it knows nothing about SDL, it just writes 32-bit colors into a flat array.

How it works

The framebuffer is a uint32_t[320 * 200]. Every frame it is uploaded to a streaming texture, which the GPU then scales up to fill the window:

framebuffer            SDL_UpdateTexture      SDL_RenderTexture
uint32_t[320*200]  ──────────────────────►  texture  ──────────────►  window
                                          (320x200)              (1280x800)

The window is 4x the framebuffer and the texture uses SDL_SCALEMODE_NEAREST, so pixels stay crisp blocks instead of being blurred by interpolation.

The loop is capped at 60 fps using SDL_GetPerformanceCounter to measure the frame and SDL_DelayPrecise to sleep off the remainder. SDL_Delay would do too, but it only takes whole milliseconds: a 16.67 ms budget rounds to 16 or 17 and the frame time jitters. SDL_DelayPrecise takes nanoseconds and spins for the last stretch rather than handing it back to the scheduler.

Requirements

macOS with Homebrew:

brew install sdl3 gcc

SDL3 is found through pkg-config, so no paths are hardcoded for it.

Build and run

make           # build into build/
make run       # build, then run
make sanitize  # build and run under AddressSanitizer + UBSan
make format    # rewrite the sources using .clang-format
make clean     # remove build/

make sanitize builds into build/sanitize/ so the instrumented objects never mix with the plain ones. It is the quickest way to catch an fb_put_pixel call that runs off the end of the framebuffer — the function does no bounds checking by design, and without ASan such a write just corrupts whatever follows.

The compiler defaults to the newest gcc-NN in your Homebrew prefix, falling back to cc if there is none. Override it whenever you like:

make CC=cc                    # Apple clang instead
make -j$(sysctl -n hw.ncpu)   # parallel build

Documentation

include/framelib.h is documented with Doxygen comments, in the same style SDL3 uses in its own headers.

brew install doxygen   # once
make docs              # generate build/docs/html
make docs-open         # generate, then open it in a browser

The generated site uses this README as its landing page. Output goes under build/, so it is never committed, and make clean removes it along with the object files.

Layout

├── Makefile            one non-recursive makefile for the whole project
├── Doxyfile            documentation settings
├── compile_flags.txt   tells clangd how to parse the sources
├── .clang-format       formatting rules
├── include/
│   └── framelib.h      framelib's public API
├── src/
│   ├── main.c          window, event loop, frame pacing
│   └── framelib.c      drawing primitives
└── build/              all build output (gitignored)

There is deliberately one Makefile rather than one per directory. Recursive make cannot see dependencies across directory boundaries, so editing include/framelib.h would leave a stale main.o behind. Here a single makefile uses -MMD -MP to generate real header dependencies, and touching a header rebuilds exactly what depends on it.

Editor setup

The committed .vscode/ config uses clangd for IntelliSense and disables the C/C++ extension's own engine, including its Tag Parser fallback. Running both at once produces duplicated and contradictory diagnostics; the Tag Parser in particular cannot handle C23 and will report errors inside system headers that compile perfectly well.

clangd reads compile_flags.txt to learn the include paths and language standard. The C/C++ extension is still used for debugging (F5), which builds first and launches under lldb.

Portability

This is a macOS/Homebrew setup. compile_flags.txt and .vscode/c_cpp_properties.json still refer to /opt/homebrew, so building on Linux means dropping those entries — pkg-config already supplies SDL3's real location, and the Makefile no longer hardcodes a compiler path: it looks for the newest Homebrew gcc-NN and falls back to cc.

C23 itself needs GCC ≥ 14 or Clang ≥ 18. Apple Clang from Xcode 16 accepts -std=c23, so make CC=cc works, but it still lacks [[reproducible]] and [[unsequenced]] — nothing here uses them.

License

Apache License 2.0 — see LICENSE.

About

Learning SDL3 with C23: a software framebuffer streamed to a GPU texture, plus a non-recursive Makefile and clangd-based VS Code setup for macOS.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages