Direct2D is the new default renderer for JUCE on Windows. An older Direct2D renderer was nursed back to life by Matt Gonzalez and brought over the finish line in a collaboration with the JUCE team.
What’s new in JUCE 8
- The Direct2D renderer is now the default JUCE renderer on Windows.
- Built on modern native platform APIs, Direct2D brings significant rendering and performance improvements to JUCE 8.
- Both JUCE desktop windows and JUCE images are GPU-backed on Windows.
- The old “software renderer” remains an alternative graphics context on Windows. See “Toggling between renderers” below.
Breaking changes and deprecations
- JUCE 8 now only supports Windows 10 and higher.
Many improvements were made to the Direct2D renderer since the initial JUCE 8 launch, including large image support and performance improvements to setBufferedToImage.
Previous advice in this article (to avoid get/setPixelAt and to scope Graphics objects when manually modifying pixels) no longer applies. See “Improvements since initial release” at the bottom of this article.
Definitions
- Discrete GPU. A GPU separate from the CPU with its own dedicated video memory not shared with the CPU. For example an NVIDIA GPU card.
- Integrated GPU. A GPU embedded alongside the processor, does not come on its own card and typically shares RAM with the CPU.
- Renderer. The entity responsible for converting JUCE’s drawing commands into bitmaps. Depending on the platform and renderer chosen, it may or may not be hardware accelerated by the GPU. For example, there’s the software renderer and the OpenGL renderer.
- Rasterization. Taking vector graphics and converting them into a bitmap-like format (a 2D image with pixels).
- Tessellation. The process of covering a surface with geometric shapes (tiles) with no overlaps and no gaps. In Direct2D, tessellation is the process of decomposing a 2D area into triangles.
- Texture. 2D Image or Bitmap living on the GPU.
- VRAM or Video Ram. Stores textures and other graphics data to be used by the GPU.
- Software Image An image representation on the CPU. In JUCE, this can be accessed with
juce::Image::BitmapData
What is Direct2D
This is how Microsoft describes Direct2D:
Direct2D is a hardware-accelerated, immediate-mode, 2D graphics API that provides high performance and high-quality rendering for 2D geometry, bitmaps, and text.
In layperson’s terms: it takes the stuff you want to draw, preps and translates it into queued calls for later rasterization on GPU. For example, it might take a line or font shape, chop it up into triangles and send that to the GPU.

Direct2D is a black box in the sense that the dividing line between GPU and CPU work is blurry. in JUCE, lots of effort went into optimizing things that seemed harmless (such as Graphics#setTransform) which initially happened to be CPU heavy in Direct2D’s paradigm.
JUCE Windows and Images are now GPU-backed on Windows
Issuing commands like g.fillAll() take immediate effect when using the software renderer. The pixels on the context’s image are immediately modified.
The new Direct2D renderer on Windows works differently. It preps and queues a list of deferred draw commands to be later executed on the GPU. The resulting image is then stored as a texture (living temporarily on the GPU in the VRAM).
Code running on the CPU cannot directly access the individual pixels in VRAM. There’s a cost incurred when taking pixels from the GPU and syncing to the CPU, so this is highly optimized to be as infrequent as possible.
Specifying SoftwareImageType
As we have seen, images are GPU backed by the Direct2D renderer.
There are rare times when you might want to explicitly create a juce::Image stored on the CPU (not GPU). This only is helpful when when working with images not shown onscreen or only being modified manually by a Graphics context (in which case the benefits of being GPU accelerated are minimal). And it comes at the cost of performance.
To accomplish this, you can specify juce::SoftwareImageType() in the constructor for a JUCE image. You can also convert between image types with juce::ImageType::convert, like so:
myImage = juce::SoftwareImageType().convert (myImage); Toggling between renderers
While migrating to the new renderer, you might find it advantageous to toggle between the Direct2D and the old software renderer. You can accomplish this on a per-window basis at runtime with getPeer()->setCurrentRenderingEngine.
Note that getPeer() returns nullptr until the component has been added to a platform window via addToDesktop(). Therefore it’s often not possible to configure the renderer from within a component’s constructor.
Instead, wait until the component has been added to a peer, which can be detected by overriding the parentHierarchyChanged callback. An example can be seen here.
Direct2D Performance

One of Direct2D’s primary advantages over the software renderer is raw performance. In a large part, this is because rasterization has been moved off the CPU and onto the more efficient GPU.
This also means GPU performance now becomes relevant on Windows. One can track this with Window’s Task Manager, or download Microsoft’s official Process Explorer app which lets one inspect texture memory usage.
It’s up to the operating system which GPU (integrated or discrete) will be used when multiple are present. Please measure on your own machine. At this time there’s no way to guarantee a computer’s discrete GPU (vs. integrated) will be used by JUCE’s draw calls.
Performance should improve on the vast majority of machines. Note that it is possible for some older machines with integrated GPUs only (no dedicated VRAM) to not perform as well on Direct2D as the software renderer.

To see the Direct2D renderer’s performance improvements in action, check out the GraphicsDemo.h in JUCE’s DemoRunner. It features the ability to switch between renderers in Settings and reports on timings. Both the component transforms and tiled image fills should demonstrate the biggest wins that JUCE’s Direct2D renderer brings.
Direct2D “Golden Rules”
- In general (with any renderer), when drawing at high FPS, the same principles as real-time audio tend to apply — don’t lock, don’t allocate.
- Bandwidth within the GPU is enormous; bandwidth between the CPU and the GPU is narrow. Even though the GPU <-> CPU sync is highly optimized in JUCE, it’s a good idea to do as much as possible with GPU drawing operations.
- Regardless of renderer or platform, avoid using
setBufferedToImage (true)in components that animate (change every frame). With Direct2D, there’s an additional cost per frame (syncing data from GPU to the CPU image), and in almost all cases will be slower.
Direct2D technical esoterica
- Direct2D GPU-backed images are inherently ephemeral. This is because the graphics device could be disconnected or switched at runtime, at which point access to the underlying GPU image in VRAM is lost. To address these edge cases, JUCE stores a backup software image (a copy on the CPU) internally for every Direct2D backed image (JUCE does not store a backup for Direct2D backed windows, only images). Having a backup software image incurs a small cost but allows for the recreation of the image on the GPU.
Improvements since initial release
Direct2D has seen significant improvements since it’s initial JUCE 8 release. More edge cases have seen their performance dramatically improved.
As of 8.0.2, a software backup (CPU image) is the source of truth for a Direct2D image instead of the hardware textures (GPU). This means that working with BitmapData and getPixelAt/setPixelAt are now as fast on Direct2D as other renderers and do not require special handling. Those calls write immediately to a CPU image, marking the GPU textures as dirty. The hardware textures are updated when read by the GPU, ensuring a single flush from main memory to GPU memory, rather than on each setPixel call.
Support for reading “large images” was added in 8.0.2. This allows Direct2D to read large sprites or “filmstrip” images even when they are larger than what the end user’s GPU device supports (for example there is a max dimension size of 16384 when on Direct3D feature level 11_0).
In the (upcoming as of January 2025) 8.0.7 release, setBackupEnabled(false) was added to Image, allowing for temporary GPU-only images (no CPU backup). This greatly improves performance for setBufferedToImage, component effects, and shadows. Note that this feature is intended for only temporary or ephemeral images that can be trivially recreated on-demand, as there are several scenarios where the GPU texture will go away (disconnecting an external GPU, disconnecting a remote desktop session, updating graphics drivers). Check out “Blur” in the Graphics Demo in the DemoRunner demonstrating these improvements.
