Library / SDK
glampert/debug-draw avatar
glampert/debug-draw

Debug Draw is one header file that draws debug primitives through your renderer

Immediate-mode, renderer agnostic, lightweight debug drawing API.

540 stars40 forksC++License varies

At a glance

What is it?
Debug Draw is an immediate-mode, renderer agnostic debug drawing API for C++ that ships as a single header file, batches its primitives, and asks you to implement one abstract class, RenderInterface, to bridge it to Direct3D or OpenGL.
Who is it for?
Debug Draw fits a graphics programmer who needs lines, points and text in a shipping build without taking on a rendering framework, and who can implement a small abstract class against an existing engine. It does not fit a project that needs baked or retained-mode tooling, a scene graph, or an editor, and the procedural global-context mode it defaults to is not thread safe, so a multi-threaded renderer should switch to the explicit context mode.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 31 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The whole library is debug_draw.hpp

Debug Draw is an immediate-mode, renderer agnostic, lightweight debug drawing API for C++, and it is distributed as a single source file. The forward declarations and the implementation live in the same file, debug_draw.hpp, which the README presents as a deployment convenience: you include the library file in one of your own source files and define DEBUG_DRAW_IMPLEMENTATION there to generate the implementation, and you can include the library in other places as a normal header.

So the integration is two lines in one translation unit:

cpp
#define DEBUG_DRAW_IMPLEMENTATION
#include "debug_draw.hpp"

and a plain include anywhere else:

cpp
#include "debug_draw.hpp"

That arrangement is the old single-header library idiom, and it is worth knowing the trade it makes. There is no package manager step, no build system integration and no link step, which is a genuine advantage when you are dropping a tool into a codebase you do not control. The cost is that the implementation is compiled into your binary even if you never draw anything, and the configuration surface moves into preprocessor defines rather than build options.

RenderInterface is the entire integration surface

Debug Draw makes no assumptions about the underlying renderer API, which is what lets it work with Direct3D, OpenGL or anything else. All you have to provide is an implementation of the dd::RenderInterface abstract class, which gives the library the basic methods it needs to draw points, lines and character glyphs.

The interface is small by design. The drawing half is a begin and an end, a pair of methods to create and destroy a glyph texture, and a pair of draw calls for point lists and line lists, each taking a vertex array, a count and a depth flag, and a glyph list call taking a vertex array, a count and the texture handle:

cpp
    virtual void drawPointList(const DrawVertex * points, int count, bool depthEnabled);
    virtual void drawLineList(const DrawVertex * lines, int count, bool depthEnabled);
    virtual void drawGlyphList(const DrawVertex * glyphs, int count, GlyphTextureHandle glyphTex);

The remark in the README that not all methods have to be implemented is the important part. You cherry-pick the features you support, and the library adapts. Each method is commented in the source with the behaviour it expects, and the samples directory holds reference implementations for standard APIs, including one built on OpenGL.

So the cost of adopting this library is writing a small renderer bridge, and the benefit is that the bridge is the same shape whether your engine is Direct3D 11, OpenGL core, legacy OpenGL, or something you wrote yourself.

initialize, draw, flush, shutdown

Once the interface is implemented, the lifecycle is four steps. You construct your render interface, hand it to the library with dd::initialize, add primitives during the frame, and call dd::flush at the end before the buffers are presented:

cpp
MyRenderInterface renderIface;
dd::initialize(&renderIface);

The flush call is the part that surprises people:

cpp
dd::flush(getTimeMilliseconds());

Debug Draw batches all primitives to reduce the number of calls into the interface, so drawing does not actually take place until you flush, which is normally done at the end of a frame before flipping the screen buffers. The time argument is only needed if you have timed debug draws in the queues, and can be omitted or passed as zero otherwise.

The overall shape is a main loop where your own drawing happens first, then the debug primitives are added to the draw queues, then dd::flush is called, then the buffers are swapped, and dd::shutdown is called when the application ends.

Three context modes, and the default one is not thread safe

By default Debug Draw uses a static global context internally, which gives a procedural-style API that is not thread safe. The README calls this the classic mode, the easiest to set up, and then offers two alternatives that are thread safe and selectable at compile time.

DEBUG_DRAW_PER_THREAD_CONTEXT switches the library to a thread-local context instead of the global one. Each thread then keeps its own context and draw queues, so the library can be called from different threads, with the same public interface, at the cost of needing Thread Local Storage support from the compiler.

DEBUG_DRAW_EXPLICIT_CONTEXT goes further and makes the library fully stateless. The user supplies a handle to a context, the dd::ContextHandle type is exposed, and every function in the library takes that handle as its first argument. Each rendering thread can then create and maintain its own instance.

The recommendation is clear: the explicit context mode is the cleaner, more functional-style API and should be the one new users pick, by defining it together with DEBUG_DRAW_IMPLEMENTATION. The procedural stateful mode is kept as the default for compatibility with older versions, and the README says it will be deprecated in favour of the explicit mode in the future. The samples directory has a multithreaded example for each of the two safe modes.

Small requirements, no RTTI, and tunable memory

The language requirements are deliberately minimal. The library needs a fairly recent C++ compiler with minimal Standard Library support, and assumes a few C++11 features such as nullptr and the cstdint header. The samples make heavier use of the standard library, but only to demonstrate usage with threads.

Two properties are called out because they decide whether the library can go into a project you do not fully control. RTTI and C++ exceptions are not used, so integrating into a project that disables those features is not a problem. That is a real constraint for large engines and console toolchains, and it is the kind of detail that turns a maybe into a yes.

The memory footprint is also small, and the amount committed to the internal queues is managed through preprocessor directives. The only dynamic memory allocated at startup goes to decompressing the font glyphs used for the debug text drawing, and to the draw queues and the library context data.

The configuration switches are documented inside debug_draw.hpp rather than in the README, and the README points you there for the list of switches and the description of each. That is worth knowing before you start, because the switches cover the context mode, the memory tuning and the feature selection in one place.

Samples that show the bridge, including a null renderer

The samples directory is more than demos. It contains reference implementations of the interface for standard APIs, which is where the practical value sits for anyone integrating the library.

There is a sample for Direct3D 11, one for OpenGL core, one for legacy OpenGL, and one for a null renderer that draws nothing, which is the fast way to disable the debug drawing without removing the calls. The multithreaded examples come in two variants, one using the thread-local context mode and one using the explicit context mode, so the two safe configurations can be compared side by side. The samples also carry their own Makefile, a readme, a gl3w directory, a vectormath directory and a Visual Studio 2015 project directory, and they use the standard library more heavily than the library itself to show usage with threads.

The sample images in the repository show the shapes the API can produce, and the repository topics list Direct3D and OpenGL as the two graphics APIs involved.

Taken together the samples give you a working bridge for each API, which is the fastest way to answer the only hard question in adopting this library, which is how to feed your own renderer into it.

Where Debug Draw is the wrong tool

The first boundary is the rendering model. This is an immediate-mode API for throwing primitives at the screen for a frame or two. It is not a retained-mode or baked system, it does not maintain a scene graph, and it does not give you an editor, a picking system or a way to annotate a frame after the fact. If you need to inspect a recorded frame offline, this is the wrong category of tool.

The second boundary is text. The library draws character glyphs through a glyph texture you create yourself, and it decompresses its own font glyphs at startup for the debug text functions. That is enough for a label and a frame counter, and it is not a text layout engine, so anything beyond short strings is your own work.

The third is the default thread safety, already discussed, which matters because a modern renderer with several drawing threads will hit it.

The last is project shape. The repository is a single header plus a samples directory, with no package manifest, no CI configuration beyond a Travis file, no releases published on GitHub, and a last push on 2026-09-23. It is a small, stable piece of code that you vendor, and the licence makes that easy, but nobody is publishing versioned builds for you to depend on, so you own the copy.

Editorial conclusion

Debug Draw fits a graphics programmer who needs lines, points and text in a shipping build without taking on a rendering framework, and who can implement a small abstract class against an existing engine. It does not fit a project that needs baked or retained-mode tooling, a scene graph, or an editor, and the procedural global-context mode it defaults to is not thread safe, so a multi-threaded renderer should switch to the explicit context mode. Before you adopt it, check three things: that your engine can supply point, line and glyph drawing, since the interface is written in those terms, that you call dd::flush at the end of each frame, because the library batches and nothing appears until you do, and that you read the compiler switch documentation in the header, since the memory footprint of the internal queues is controlled by preprocessor directives. The code is in the public domain, the repository publishes no GitHub releases, and the last push was on 2026-09-23.

Frequently asked questions

How do I add Debug Draw to a C++ project?

It is a single header file. Define `DEBUG_DRAW_IMPLEMENTATION` and include `debug_draw.hpp` in one source file to generate the implementation, and include it as a normal header elsewhere. No build system or link step is needed.

What do I have to implement to use Debug Draw?

An implementation of the `dd::RenderInterface` abstract class, which gives the library methods to draw point lists, line lists and glyph lists, plus begin, end and glyph texture handling. You do not need to implement all of it, since you choose which features to support, and reference implementations are in the samples directory.

Is Debug Draw thread safe?

Not by default. The default procedural mode uses a static global context and is not thread safe. Define `DEBUG_DRAW_PER_THREAD_CONTEXT` for a thread-local context, or `DEBUG_DRAW_EXPLICIT_CONTEXT` for a fully stateless API where every call takes a context handle, which the README recommends for new users.

Why do my debug shapes not appear until the end of the frame?

Debug Draw batches all primitives to reduce the number of calls into the render interface, so nothing is drawn until you call `dd::flush`, normally at the end of the frame before flipping the screen buffers.

What licence does Debug Draw use?

It is in the public domain, and where that dedication is not recognised you are granted a perpetual, irrevocable licence to copy, distribute and modify the source as you see fit. No attribution is required, though a mention of the authors is appreciated.

Official sources

  1. glampert/debug-draw on GitHub
  2. Issues
  3. Project website
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/glampert-debug-draw.svg)](https://hysenlabs.com/projects/glampert-debug-draw)

Was this useful?