If you’ve ever toggled VSync on and watched your frame rate seesaw between smooth and stuttery, you’ve met the limitations of double buffering. Triple buffering adds a third buffer to the swapchain so that the GPU can keep rendering even when a frame is queued for presentation, delivering smoother animation and higher average FPS with VSync on. Double buffering With double buffering, we use two buffers, one for presenting to the screen and one for rendering off-screen, swapping between them as new frames are produced. Buffer Role Front Scanned out to the display Back GPU renders into this buffer VSync ON Buffer Behavior Front Shown on screen until the next VSync Back Swapped with the front buffer only at VSync Key Effects: If rendering finishes early, GPU waits (stall). If rendering finishes late, Frame is delayed until the next refresh (stutter). No tearing is observed, but can cause uneven frame pacing. VSync OFF Buffer Behavior Front Can be replaced mid-scanout Back Swapped with the front buffer immediately when ready Key Effects: GPU never waits, lowest latency. Can cause screen tearing (front buffer changes mid-frame). Smoother if GPU is consistently faster than refresh rate, but can look jittery otherwise. Triple buffering With triple buffering we use three buffers: Front + Back-1 + Back-2. Even if one back buffer is queued for the next VSync, there’s still another one free to render into, keeping the pipeline busy. Buffer Role Front Scanned out to the display Back 1 First render target Back 2 Second render target VSync ON Buffer Behavior Front Shown on screen until the next VSync Back 1 Queued for presentation once rendering is complete Back 2 GPU can start rendering here immediately, even if Back 1 is still queued Key Effects: GPU never stalls, always has a buffer to render into. No tearing Much smoother frame pacing than double-buffered VSync. Trade-offs to consider: Higher memory footprint as one extra full-resolution color buffer + depth/stencil used. Slightly higher input latency than double buffering (VSync off), because a displayed frame may be 1 to 2 frames old. Slightly higher power usage as the GPU idles less. VSync OFF This configuration is included for completeness only. In practice, triple buffering without VSync provides very little benefit over double buffering without VSync. Buffer Behavior Front Can be replaced mid-scanout (tearing possible) Back 1 Can be swapped immediately when rendering is complete Back 2 GPU can start rendering here while Back 1 is waiting to be displayed Key Effects: GPU does not stall, always has a free buffer to render into. Screen tearing can still occur because frames are not synchronized with refresh. Lowest possible latency (even lower than triple-buffer + VSync ON). Provides little benefit over double-buffer + VSync OFF unless CPU/GPU are out of sync (it helps absorb frame spikes). Triple buffering vs. “true” triple buffering Terminology differs: Mailbox / flip model: (Vulkan MAILBOX, DXGI flip): the compositor takes the latest rendered frame and drops older ones.great for latency. FIFO with 3 images: frames queue in order, can increase latency but guarantees no frame is skipped. Both avoid GPU stalls but their latency behavior differs. If MAILBOX is available, it’s often the best-feeling option. VRR (Variable Refresh Rate) VRR is a display technology (e.g., G-SYNC from NVIDIA, FreeSync from AMD) where the monitor’s refresh rate dynamically adapts to match the GPU’s frame output rate. The result: No tearing, low latency, and smoother frame pacing. This gives competitive players the best of both worlds: no VSync-induced stutter or lag, but also no screen tearing. With variable refresh rate: Double buffering + VRR already eliminates most VSync stalls and tearing. Triple buffering can still help if frame time fluctuates or the VRR window is exceeded (e.g., below min Hz), but the benefit is smaller. The big picture For playing a single-player game, watching animations, or working on a 3D application where smoothness matters more than shaving off the last few milliseconds of input delay, triple buffering with VSync enabled becomes the “sweet spot”: It removes tearing. Keeps frame pacing smooth (no microstutter from GPU stalls). Gives a higher average FPS than double-buffered VSync. The slight extra input lag (usually 1 frame of latency) is rarely noticeable outside of competitive contexts. In summary: Mode Tearing? Smoothness Latency Best For Double Buffer + VSync OFF Yes Can stutter Lowest Competitive esports, latency-critical apps Double Buffer + VSync ON No Can stutter if GPU misses VSync Higher (GPU stalls) Casual players who hate tearing Triple Buffer + VSync ON No Smooth (no stalls) Slightly higher than DB - VSync OFF Most games, general use VRR No Smooth Low Competitive or casual, if hardware supports it Modern API mapping Let’s explore how triple buffering is implemented in modern graphics APIs. Vulkan Swapchain images: choose minImageCount = 3 . . Present mode determines queueing semantics: VK_PRESENT_MODE_FIFO_KHR : Always VSync (queue behaves like triple buffer when minImageCount ≥ 3 ). VK_PRESENT_MODE_MAILBOX_KHR : “One in flight, one mailboxed”, effectively triple-buffer-like with latest-frame-wins (low latency, no tearing on supported displays). VK_PRESENT_MODE_IMMEDIATE_KHR : No VSync (can tear). VkSwapchainCreateInfoKHR sci { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR }; sci . surface = surface ; sci . minImageCount = 3 ; // request triple buffering sci . imageFormat = format ; sci . imageExtent = extent ; sci . presentMode = VK_PRESENT_MODE_FIFO_KHR ; // or MAILBOX if available vkCreateSwapchainKHR ( device , & sci , nullptr , & swapchain ); Direct3D 12 / DXGI Use flip model swap effects ( DXGI_SWAP_EFFECT_FLIP_DISCARD or FLIP_SEQUENTIAL ). swap effects ( or ). Set BufferCount = 3 . . With WaitableObject /fences, you can tune in-flight frames. DXGI_SWAP_CHAIN_DESC1 desc = {}; desc . Width = width ; desc . Height = height ; desc . Format = DXGI_FORMAT_R8G8B8A8_UNORM ; desc . BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT ; desc . BufferCount = 3 ; // triple buffering desc . SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD ; // flip model desc . SampleDesc = { 1 , 0 }; OpenGL In OpenGL, glSwapInterval(1) toggles VSync. The actual buffer count is driver / window-system dependent. On many platforms you can request a three buffer swap via the windowing layer (WGL/GLX/EGL attributes) or by using a framework that exposes it. Practical Approach Start with 3 images in your swapchain by default Creating three buffers (instead of two) ensures the GPU always has a free image to render into, even if one is on screen and another is queued for display. This is what enables triple buffering and prevents GPU stalls. Creating three buffers (instead of two) ensures the GPU always has a free image to render into, even if one is on screen and another is queued for display. This is what enables triple buffering and prevents GPU stalls. Limit CPU frames in flight using fences/semaphores “Frames in flight” means how many frames the CPU has submitted to the GPU before waiting for one to finish. If you never wait, the CPU can outrun the GPU and produce unbounded latency (your input feels delayed). Use a fence per frame to ensure you have only 1 or 2 frames in flight. This keeps the pipeline full but latency predictable. “Frames in flight” means how many frames the CPU has submitted to the GPU before waiting for one to finish. Use a frame pacing strategy When the engine runs faster than the display refresh rate, frames may be unevenly spaced, causing micro-stutter. Delay presentation slightly to make frame delivery evenly spaced. Some engines implement a pacing library or simply sleep until the next ideal present time. When the engine runs faster than the display refresh rate, frames may be unevenly spaced, causing micro-stutter. Measure, don’t guess Frame time histogram: Shows the spread of frame times, not just the average FPS. Present-to-present intervals: Check that frames are arriving at consistent intervals. Input latency: If you are building a game or interactive app, measure from input event to on-screen effect. Common Pitfalls There are some common pitfalls to be aware of when implementing triple buffering. Starvation via unlimited in-flight work Without fences, the CPU can get several frames ahead of the GPU. This means the frame you just rendered might only display several refreshes later, adding input lag. The solution is to use fences / semaphores to wait when you have too many frames queued. Each swapchain image is a full-resolution color buffer. Triple buffering means three copies in memory. If MSAA is used, you also need resolve targets and a depth/stencil buffer. Reuse depth/stencil buffers across swapchain images when possible. When we enable MSAA (Multisample Anti-Aliasing), each pixel stores multiple samples (e.g., 4 or 8). This has a few consequences: Multisampled Color Buffer: We render into a multisampled image that typically cannot be presented directly. We render into a multisampled image that typically cannot be presented directly. Resolve Targets (Per Swapchain Image): At the end of each frame, the multisampled image must be resolved into a single-sample image. One resolve target is needed per swapchain image (2 for double buffering, 3 for triple buffering). At the end of each frame, the multisampled image must be resolved into a single-sample image. One resolve target is needed per swapchain image (2 for double buffering, 3 for triple buffering). Depth/Stencil Buffer: A multisampled depth / stencil buffer is also needed for depth testing. This buffer can usually be reused every frame and does not need to be unique per swapchain image. Here are the memory and performance implications: Increased VRAM usage: MSAA multiplies the storage size of color / depth buffers by the sample count. MSAA multiplies the storage size of color / depth buffers by the sample count. Extra bandwidth cost: GPU must resolve the MSAA buffer each frame. GPU must resolve the MSAA buffer each frame. Optimization: Use one MSAA color buffer + one MSAA depth buffer reused every frame, plus a single-sample resolve target for each swapchain image. This minimizes memory usage. Assuming MAILBOX mode is supported VK_PRESENT_MODE_MAILBOX_KHR is great when available, but not all platforms support it. Always query supported present modes and fall back to FIFO if needed. Here’s a simple frame loop using fences and semaphores in Vulkan: