2023-06-08 01:29:16 -04:00
// dear imgui: Renderer Backend for SDL_Renderer for SDL2
2022-05-18 16:23:10 -04:00
// (Requires: SDL 2.0.17+)
2025-08-11 17:28:38 -04:00
// Note that SDL_Renderer is an _optional_ component of SDL2, which IMHO is now largely obsolete.
// For a multi-platform app consider using other technologies:
// - SDL3+SDL_GPU: SDL_GPU is SDL3 new graphics abstraction API. You will need to update to SDL3.
// - SDL2+DirectX, SDL2+OpenGL, SDL2+Vulkan: combine SDL with dedicated renderers.
// If your application wants to render any non trivial amount of graphics other than UI,
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user
// and it might be difficult to step out of those boundaries.
2022-05-18 16:23:10 -04:00
// Implemented features:
2025-08-11 21:40:50 -04:00
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
2025-08-11 16:50:18 -04:00
// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
2025-08-11 21:40:50 -04:00
// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
2025-08-11 15:52:57 -04:00
// [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'.
2022-05-18 16:23:10 -04:00
// Missing features:
// [ ] Renderer: Multi-viewport support (multiple windows).
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
2025-08-09 22:26:36 -04:00
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
2022-05-18 16:23:10 -04:00
// CHANGELOG
2025-08-11 21:40:50 -04:00
// 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplSDLRenderer2_CreateFontsTexture() and ImGui_ImplSDLRenderer2_DestroyFontsTexture().
2025-08-11 17:28:38 -04:00
// 2025-01-18: Use endian-dependent RGBA32 texture format, to match SDL_Color.
2025-08-11 15:52:57 -04:00
// 2024-10-09: Expose selected render state in ImGui_ImplSDLRenderer2_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
2025-08-10 05:23:51 -04:00
// 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
2023-06-08 01:29:16 -04:00
// 2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3.
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
2022-05-18 16:23:10 -04:00
// 2021-12-21: Update SDL_RenderGeometryRaw() format to work with SDL 2.0.19.
// 2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2021-10-06: Backup and restore modified ClipRect/Viewport.
// 2021-09-21: Initial version.
# include "imgui.h"
2023-08-30 01:42:51 -04:00
# ifndef IMGUI_DISABLE
2023-06-08 01:29:16 -04:00
# include "imgui_impl_sdlrenderer2.h"
2022-05-18 16:23:10 -04:00
# include <stdint.h> // intptr_t
2023-06-08 01:29:16 -04:00
// Clang warnings with -Weverything
# if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
2025-08-11 21:40:50 -04:00
# elif defined(__GNUC__)
# pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
2023-06-08 01:29:16 -04:00
# endif
2022-05-18 16:23:10 -04:00
// SDL
# include <SDL.h>
# if !SDL_VERSION_ATLEAST(2,0,17)
# error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function
# endif
// SDL_Renderer data
2023-06-08 01:29:16 -04:00
struct ImGui_ImplSDLRenderer2_Data
2022-05-18 16:23:10 -04:00
{
2025-08-10 05:23:51 -04:00
SDL_Renderer * Renderer ; // Main viewport's renderer
2025-08-11 21:40:50 -04:00
2025-08-10 05:23:51 -04:00
ImGui_ImplSDLRenderer2_Data ( ) { memset ( ( void * ) this , 0 , sizeof ( * this ) ) ; }
2022-05-18 16:23:10 -04:00
} ;
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
2023-06-08 01:29:16 -04:00
static ImGui_ImplSDLRenderer2_Data * ImGui_ImplSDLRenderer2_GetBackendData ( )
2022-05-18 16:23:10 -04:00
{
2023-06-08 01:29:16 -04:00
return ImGui : : GetCurrentContext ( ) ? ( ImGui_ImplSDLRenderer2_Data * ) ImGui : : GetIO ( ) . BackendRendererUserData : nullptr ;
2022-05-18 16:23:10 -04:00
}
// Functions
2023-06-08 01:29:16 -04:00
bool ImGui_ImplSDLRenderer2_Init ( SDL_Renderer * renderer )
2022-05-18 16:23:10 -04:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2025-08-10 05:23:51 -04:00
IMGUI_CHECKVERSION ( ) ;
2023-06-08 01:29:16 -04:00
IM_ASSERT ( io . BackendRendererUserData = = nullptr & & " Already initialized a renderer backend! " ) ;
IM_ASSERT ( renderer ! = nullptr & & " SDL_Renderer not initialized! " ) ;
2022-05-18 16:23:10 -04:00
// Setup backend capabilities flags
2023-06-08 01:29:16 -04:00
ImGui_ImplSDLRenderer2_Data * bd = IM_NEW ( ImGui_ImplSDLRenderer2_Data ) ( ) ;
2022-05-18 16:23:10 -04:00
io . BackendRendererUserData = ( void * ) bd ;
2023-06-08 01:29:16 -04:00
io . BackendRendererName = " imgui_impl_sdlrenderer2 " ;
2022-05-18 16:23:10 -04:00
io . BackendFlags | = ImGuiBackendFlags_RendererHasVtxOffset ; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
2025-08-11 21:40:50 -04:00
io . BackendFlags | = ImGuiBackendFlags_RendererHasTextures ; // We can honor ImGuiPlatformIO::Textures[] requests during render.
2022-05-18 16:23:10 -04:00
2025-08-10 05:23:51 -04:00
bd - > Renderer = renderer ;
2022-05-18 16:23:10 -04:00
return true ;
}
2023-06-08 01:29:16 -04:00
void ImGui_ImplSDLRenderer2_Shutdown ( )
2022-05-18 16:23:10 -04:00
{
2023-06-08 01:29:16 -04:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
IM_ASSERT ( bd ! = nullptr & & " No renderer backend to shutdown, or already shutdown? " ) ;
2022-05-18 16:23:10 -04:00
ImGuiIO & io = ImGui : : GetIO ( ) ;
2023-06-08 01:29:16 -04:00
ImGui_ImplSDLRenderer2_DestroyDeviceObjects ( ) ;
2022-05-18 16:23:10 -04:00
2023-06-08 01:29:16 -04:00
io . BackendRendererName = nullptr ;
io . BackendRendererUserData = nullptr ;
2025-08-11 21:40:50 -04:00
io . BackendFlags & = ~ ( ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures ) ;
2022-05-18 16:23:10 -04:00
IM_DELETE ( bd ) ;
}
2025-08-10 05:23:51 -04:00
static void ImGui_ImplSDLRenderer2_SetupRenderState ( SDL_Renderer * renderer )
2022-05-18 16:23:10 -04:00
{
2025-08-11 17:52:18 -04:00
// Clear out any viewports and cliprect set by the user
2022-05-18 16:23:10 -04:00
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
2025-08-11 17:52:18 -04:00
SDL_RenderSetViewport ( renderer , nullptr ) ;
SDL_RenderSetClipRect ( renderer , nullptr ) ;
2022-05-18 16:23:10 -04:00
}
2025-08-12 03:22:26 -04:00
void ImGui_ImplSDLRenderer2_NewFrame ( )
2022-05-18 16:23:10 -04:00
{
2023-06-08 01:29:16 -04:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
2025-08-10 05:10:39 -04:00
IM_ASSERT ( bd ! = nullptr & & " Context or backend not initialized! Did you call ImGui_ImplSDLRenderer2_Init()? " ) ;
2025-08-11 21:40:50 -04:00
IM_UNUSED ( bd ) ;
2022-05-18 16:23:10 -04:00
}
2025-08-10 05:23:51 -04:00
void ImGui_ImplSDLRenderer2_RenderDrawData ( ImDrawData * draw_data , SDL_Renderer * renderer )
2022-05-18 16:23:10 -04:00
{
2025-08-11 17:52:18 -04:00
// If there's a scale factor set by the user, use that instead
2022-05-18 16:23:10 -04:00
// If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
// to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
float rsx = 1.0f ;
2025-08-11 17:52:18 -04:00
float rsy = 1.0f ;
SDL_RenderGetScale ( renderer , & rsx , & rsy ) ;
2022-05-18 16:23:10 -04:00
ImVec2 render_scale ;
2025-08-11 17:52:18 -04:00
render_scale . x = ( rsx = = 1.0f ) ? draw_data - > FramebufferScale . x : 1.0f ;
render_scale . y = ( rsy = = 1.0f ) ? draw_data - > FramebufferScale . y : 1.0f ;
2022-05-18 16:23:10 -04:00
2025-08-11 17:52:18 -04:00
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = ( int ) ( draw_data - > DisplaySize . x * render_scale . x ) ;
int fb_height = ( int ) ( draw_data - > DisplaySize . y * render_scale . y ) ;
if ( fb_width = = 0 | | fb_height = = 0 )
return ;
2022-05-18 16:23:10 -04:00
2025-08-11 21:40:50 -04:00
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
if ( draw_data - > Textures ! = nullptr )
for ( ImTextureData * tex : * draw_data - > Textures )
if ( tex - > Status ! = ImTextureStatus_OK )
ImGui_ImplSDLRenderer2_UpdateTexture ( tex ) ;
2022-05-18 16:23:10 -04:00
// Backup SDL_Renderer state that will be modified to restore it afterwards
struct BackupSDLRendererState
{
SDL_Rect Viewport ;
bool ClipEnabled ;
SDL_Rect ClipRect ;
} ;
BackupSDLRendererState old = { } ;
2025-08-10 05:23:51 -04:00
old . ClipEnabled = SDL_RenderIsClipEnabled ( renderer ) = = SDL_TRUE ;
SDL_RenderGetViewport ( renderer , & old . Viewport ) ;
SDL_RenderGetClipRect ( renderer , & old . ClipRect ) ;
2022-05-18 16:23:10 -04:00
2025-08-11 15:52:57 -04:00
// Setup desired state
ImGui_ImplSDLRenderer2_SetupRenderState ( renderer ) ;
// Setup render state structure (for callbacks and custom texture bindings)
ImGuiPlatformIO & platform_io = ImGui : : GetPlatformIO ( ) ;
ImGui_ImplSDLRenderer2_RenderState render_state ;
render_state . Renderer = renderer ;
platform_io . Renderer_RenderState = & render_state ;
2025-08-11 17:52:18 -04:00
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data - > DisplayPos ; // (0,0) unless using multi-viewports
ImVec2 clip_scale = render_scale ;
2022-05-18 16:23:10 -04:00
// Render command lists
2025-08-13 16:08:45 -04:00
for ( const ImDrawList * draw_list : draw_data - > CmdLists )
2022-05-18 16:23:10 -04:00
{
2025-08-11 15:52:57 -04:00
const ImDrawVert * vtx_buffer = draw_list - > VtxBuffer . Data ;
const ImDrawIdx * idx_buffer = draw_list - > IdxBuffer . Data ;
2022-05-18 16:23:10 -04:00
2025-08-11 15:52:57 -04:00
for ( int cmd_i = 0 ; cmd_i < draw_list - > CmdBuffer . Size ; cmd_i + + )
2022-05-18 16:23:10 -04:00
{
2025-08-11 15:52:57 -04:00
const ImDrawCmd * pcmd = & draw_list - > CmdBuffer [ cmd_i ] ;
2022-05-18 16:23:10 -04:00
if ( pcmd - > UserCallback )
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if ( pcmd - > UserCallback = = ImDrawCallback_ResetRenderState )
2025-08-10 05:23:51 -04:00
ImGui_ImplSDLRenderer2_SetupRenderState ( renderer ) ;
2022-05-18 16:23:10 -04:00
else
2025-08-11 15:52:57 -04:00
pcmd - > UserCallback ( draw_list , pcmd ) ;
2022-05-18 16:23:10 -04:00
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min ( ( pcmd - > ClipRect . x - clip_off . x ) * clip_scale . x , ( pcmd - > ClipRect . y - clip_off . y ) * clip_scale . y ) ;
ImVec2 clip_max ( ( pcmd - > ClipRect . z - clip_off . x ) * clip_scale . x , ( pcmd - > ClipRect . w - clip_off . y ) * clip_scale . y ) ;
if ( clip_min . x < 0.0f ) { clip_min . x = 0.0f ; }
if ( clip_min . y < 0.0f ) { clip_min . y = 0.0f ; }
2023-06-08 01:29:16 -04:00
if ( clip_max . x > ( float ) fb_width ) { clip_max . x = ( float ) fb_width ; }
if ( clip_max . y > ( float ) fb_height ) { clip_max . y = ( float ) fb_height ; }
2022-05-18 16:23:10 -04:00
if ( clip_max . x < = clip_min . x | | clip_max . y < = clip_min . y )
continue ;
SDL_Rect r = { ( int ) ( clip_min . x ) , ( int ) ( clip_min . y ) , ( int ) ( clip_max . x - clip_min . x ) , ( int ) ( clip_max . y - clip_min . y ) } ;
2025-08-10 05:23:51 -04:00
SDL_RenderSetClipRect ( renderer , & r ) ;
2022-05-18 16:23:10 -04:00
2025-08-09 22:26:36 -04:00
const float * xy = ( const float * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , pos ) ) ;
const float * uv = ( const float * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , uv ) ) ;
2022-05-18 16:23:10 -04:00
# if SDL_VERSION_ATLEAST(2,0,19)
2025-08-09 22:26:36 -04:00
const SDL_Color * color = ( const SDL_Color * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , col ) ) ; // SDL 2.0.19+
2022-05-18 16:23:10 -04:00
# else
2025-08-09 22:26:36 -04:00
const int * color = ( const int * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , col ) ) ; // SDL 2.0.17 and 2.0.18
2022-05-18 16:23:10 -04:00
# endif
// Bind texture, Draw
2025-08-11 17:52:18 -04:00
SDL_Texture * tex = ( SDL_Texture * ) pcmd - > GetTexID ( ) ;
2025-08-10 05:23:51 -04:00
SDL_RenderGeometryRaw ( renderer , tex ,
2022-05-18 16:23:10 -04:00
xy , ( int ) sizeof ( ImDrawVert ) ,
color , ( int ) sizeof ( ImDrawVert ) ,
uv , ( int ) sizeof ( ImDrawVert ) ,
2025-08-11 15:52:57 -04:00
draw_list - > VtxBuffer . Size - pcmd - > VtxOffset ,
2022-05-18 16:23:10 -04:00
idx_buffer + pcmd - > IdxOffset , pcmd - > ElemCount , sizeof ( ImDrawIdx ) ) ;
}
}
}
2025-08-11 16:50:18 -04:00
platform_io . Renderer_RenderState = nullptr ;
2022-05-18 16:23:10 -04:00
// Restore modified SDL_Renderer state
2025-08-10 05:23:51 -04:00
SDL_RenderSetViewport ( renderer , & old . Viewport ) ;
SDL_RenderSetClipRect ( renderer , old . ClipEnabled ? & old . ClipRect : nullptr ) ;
2022-05-18 16:23:10 -04:00
}
2025-08-11 21:40:50 -04:00
void ImGui_ImplSDLRenderer2_UpdateTexture ( ImTextureData * tex )
2022-05-18 16:23:10 -04:00
{
2023-06-08 01:29:16 -04:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
2022-05-18 16:23:10 -04:00
2025-08-11 21:40:50 -04:00
if ( tex - > Status = = ImTextureStatus_WantCreate )
2022-05-18 16:23:10 -04:00
{
2025-08-11 21:40:50 -04:00
// Create and upload new texture to graphics system
//IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
IM_ASSERT ( tex - > TexID = = ImTextureID_Invalid & & tex - > BackendUserData = = nullptr ) ;
IM_ASSERT ( tex - > Format = = ImTextureFormat_RGBA32 ) ;
// Create texture
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
SDL_Texture * sdl_texture = SDL_CreateTexture ( bd - > Renderer , SDL_PIXELFORMAT_RGBA32 , SDL_TEXTUREACCESS_STATIC , tex - > Width , tex - > Height ) ;
IM_ASSERT ( sdl_texture ! = nullptr & & " Backend failed to create texture! " ) ;
SDL_UpdateTexture ( sdl_texture , nullptr , tex - > GetPixels ( ) , tex - > GetPitch ( ) ) ;
SDL_SetTextureBlendMode ( sdl_texture , SDL_BLENDMODE_BLEND ) ;
SDL_SetTextureScaleMode ( sdl_texture , SDL_ScaleModeLinear ) ;
// Store identifiers
tex - > SetTexID ( ( ImTextureID ) ( intptr_t ) sdl_texture ) ;
tex - > SetStatus ( ImTextureStatus_OK ) ;
2022-05-18 16:23:10 -04:00
}
2025-08-11 21:40:50 -04:00
else if ( tex - > Status = = ImTextureStatus_WantUpdates )
{
// Update selected blocks. We only ever write to textures regions which have never been used before!
// This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
SDL_Texture * sdl_texture = ( SDL_Texture * ) ( intptr_t ) tex - > TexID ;
for ( ImTextureRect & r : tex - > Updates )
{
SDL_Rect sdl_r = { r . x , r . y , r . w , r . h } ;
SDL_UpdateTexture ( sdl_texture , & sdl_r , tex - > GetPixelsAt ( r . x , r . y ) , tex - > GetPitch ( ) ) ;
}
tex - > SetStatus ( ImTextureStatus_OK ) ;
}
else if ( tex - > Status = = ImTextureStatus_WantDestroy )
2022-05-18 16:23:10 -04:00
{
2025-08-11 21:40:50 -04:00
SDL_Texture * sdl_texture = ( SDL_Texture * ) ( intptr_t ) tex - > TexID ;
if ( sdl_texture = = nullptr )
return ;
SDL_DestroyTexture ( sdl_texture ) ;
// Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
tex - > SetTexID ( ImTextureID_Invalid ) ;
tex - > SetStatus ( ImTextureStatus_Destroyed ) ;
2022-05-18 16:23:10 -04:00
}
}
2025-08-11 21:40:50 -04:00
void ImGui_ImplSDLRenderer2_CreateDeviceObjects ( )
2022-05-18 16:23:10 -04:00
{
}
2023-06-08 01:29:16 -04:00
void ImGui_ImplSDLRenderer2_DestroyDeviceObjects ( )
2022-05-18 16:23:10 -04:00
{
2025-08-11 21:40:50 -04:00
// Destroy all textures
for ( ImTextureData * tex : ImGui : : GetPlatformIO ( ) . Textures )
if ( tex - > RefCount = = 1 )
{
tex - > SetStatus ( ImTextureStatus_WantDestroy ) ;
ImGui_ImplSDLRenderer2_UpdateTexture ( tex ) ;
}
2022-05-18 16:23:10 -04:00
}
2023-06-08 01:29:16 -04:00
2023-08-30 01:42:51 -04:00
//-----------------------------------------------------------------------------
2023-06-08 01:29:16 -04:00
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
2023-08-30 01:42:51 -04:00
# endif // #ifndef IMGUI_DISABLE