2023-05-30 20:36:07 +02:00
// dear imgui: Renderer Backend for SDL_Renderer for SDL3
2023-05-30 20:30:30 +02:00
// (Requires: SDL 3.0.0+)
2023-05-30 20:36:07 +02:00
// Note how SDL_Renderer is an _optional_ component of SDL3.
2023-05-30 20:30:30 +02:00
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
// If your application will want to render any non trivial amount of graphics other than UI,
2023-05-30 20:36:07 +02:00
// 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.
2023-05-30 20:30:30 +02:00
// Implemented features:
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
// CHANGELOG
2023-05-30 20:36:07 +02:00
// 2023-05-30: Initial version.
2023-05-30 20:30:30 +02:00
# include "imgui.h"
# include "imgui_impl_sdlrenderer3.h"
# if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier
# include <stddef.h> // intptr_t
# else
# include <stdint.h> // intptr_t
# endif
// Clang warnings with -Weverything
# if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
# endif
// SDL
# include <SDL3/SDL.h>
# if !SDL_VERSION_ATLEAST(3,0,0)
# error This backend requires SDL 3.0.0+
# endif
// SDL_Renderer data
2023-05-30 20:36:07 +02:00
struct ImGui_ImplSDLRenderer3_Data
2023-05-30 20:30:30 +02:00
{
SDL_Renderer * SDLRenderer ;
SDL_Texture * FontTexture ;
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data ( ) { memset ( ( void * ) this , 0 , sizeof ( * this ) ) ; }
2023-05-30 20:30:30 +02: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-05-30 20:36:07 +02:00
static ImGui_ImplSDLRenderer3_Data * ImGui_ImplSDLRenderer3_GetBackendData ( )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
return ImGui : : GetCurrentContext ( ) ? ( ImGui_ImplSDLRenderer3_Data * ) ImGui : : GetIO ( ) . BackendRendererUserData : nullptr ;
2023-05-30 20:30:30 +02:00
}
// Functions
2023-05-30 20:36:07 +02:00
bool ImGui_ImplSDLRenderer3_Init ( SDL_Renderer * renderer )
2023-05-30 20:30:30 +02:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
IM_ASSERT ( io . BackendRendererUserData = = nullptr & & " Already initialized a renderer backend! " ) ;
IM_ASSERT ( renderer ! = nullptr & & " SDL_Renderer not initialized! " ) ;
// Setup backend capabilities flags
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = IM_NEW ( ImGui_ImplSDLRenderer3_Data ) ( ) ;
2023-05-30 20:30:30 +02:00
io . BackendRendererUserData = ( void * ) bd ;
2023-05-30 20:36:07 +02:00
io . BackendRendererName = " imgui_impl_sdlrenderer3 " ;
2023-05-30 20:30:30 +02:00
io . BackendFlags | = ImGuiBackendFlags_RendererHasVtxOffset ; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
bd - > SDLRenderer = renderer ;
return true ;
}
2023-05-30 20:36:07 +02:00
void ImGui_ImplSDLRenderer3_Shutdown ( )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = ImGui_ImplSDLRenderer3_GetBackendData ( ) ;
2023-05-30 20:30:30 +02:00
IM_ASSERT ( bd ! = nullptr & & " No renderer backend to shutdown, or already shutdown? " ) ;
ImGuiIO & io = ImGui : : GetIO ( ) ;
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_DestroyDeviceObjects ( ) ;
2023-05-30 20:30:30 +02:00
io . BackendRendererName = nullptr ;
io . BackendRendererUserData = nullptr ;
2023-05-30 20:36:07 +02:00
io . BackendFlags & = ~ ImGuiBackendFlags_RendererHasVtxOffset ;
2023-05-30 20:30:30 +02:00
IM_DELETE ( bd ) ;
}
2023-05-30 20:36:07 +02:00
static void ImGui_ImplSDLRenderer3_SetupRenderState ( )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = ImGui_ImplSDLRenderer3_GetBackendData ( ) ;
2023-05-30 20:30:30 +02:00
// Clear out any viewports and cliprect set by the user
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
SDL_SetRenderViewport ( bd - > SDLRenderer , nullptr ) ;
SDL_SetRenderClipRect ( bd - > SDLRenderer , nullptr ) ;
}
2023-05-30 20:36:07 +02:00
void ImGui_ImplSDLRenderer3_NewFrame ( )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = ImGui_ImplSDLRenderer3_GetBackendData ( ) ;
IM_ASSERT ( bd ! = nullptr & & " Did you call ImGui_ImplSDLRenderer3_Init()? " ) ;
2023-05-30 20:30:30 +02:00
if ( ! bd - > FontTexture )
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_CreateDeviceObjects ( ) ;
2023-05-30 20:30:30 +02:00
}
2023-05-30 20:36:07 +02:00
void ImGui_ImplSDLRenderer3_RenderDrawData ( ImDrawData * draw_data )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = ImGui_ImplSDLRenderer3_GetBackendData ( ) ;
2023-05-30 20:30:30 +02:00
// If there's a scale factor set by the user, use that instead
// 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 ;
float rsy = 1.0f ;
SDL_GetRenderScale ( bd - > SDLRenderer , & rsx , & rsy ) ;
ImVec2 render_scale ;
render_scale . x = ( rsx = = 1.0f ) ? draw_data - > FramebufferScale . x : 1.0f ;
render_scale . y = ( rsy = = 1.0f ) ? draw_data - > FramebufferScale . y : 1.0f ;
// 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 ;
// Backup SDL_Renderer state that will be modified to restore it afterwards
struct BackupSDLRendererState
{
SDL_Rect Viewport ;
bool ClipEnabled ;
SDL_Rect ClipRect ;
} ;
BackupSDLRendererState old = { } ;
old . ClipEnabled = SDL_RenderClipEnabled ( bd - > SDLRenderer ) = = SDL_TRUE ;
SDL_GetRenderViewport ( bd - > SDLRenderer , & old . Viewport ) ;
SDL_GetRenderClipRect ( bd - > SDLRenderer , & old . ClipRect ) ;
// 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 ;
// Render command lists
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_SetupRenderState ( ) ;
2023-05-30 20:30:30 +02:00
for ( int n = 0 ; n < draw_data - > CmdListsCount ; n + + )
{
const ImDrawList * cmd_list = draw_data - > CmdLists [ n ] ;
const ImDrawVert * vtx_buffer = cmd_list - > VtxBuffer . Data ;
const ImDrawIdx * idx_buffer = cmd_list - > IdxBuffer . Data ;
for ( int cmd_i = 0 ; cmd_i < cmd_list - > CmdBuffer . Size ; cmd_i + + )
{
const ImDrawCmd * pcmd = & cmd_list - > CmdBuffer [ cmd_i ] ;
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 )
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_SetupRenderState ( ) ;
2023-05-30 20:30:30 +02:00
else
pcmd - > UserCallback ( cmd_list , pcmd ) ;
}
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 ; }
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 ; }
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 ) } ;
SDL_SetRenderClipRect ( bd - > SDLRenderer , & r ) ;
const float * xy = ( const float * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + IM_OFFSETOF ( ImDrawVert , pos ) ) ;
const float * uv = ( const float * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + IM_OFFSETOF ( ImDrawVert , uv ) ) ;
# if SDL_VERSION_ATLEAST(2,0,19)
const SDL_Color * color = ( const SDL_Color * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + IM_OFFSETOF ( ImDrawVert , col ) ) ; // SDL 2.0.19+
# else
const int * color = ( const int * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + IM_OFFSETOF ( ImDrawVert , col ) ) ; // SDL 2.0.17 and 2.0.18
# endif
// Bind texture, Draw
SDL_Texture * tex = ( SDL_Texture * ) pcmd - > GetTexID ( ) ;
SDL_RenderGeometryRaw ( bd - > SDLRenderer , tex ,
xy , ( int ) sizeof ( ImDrawVert ) ,
color , ( int ) sizeof ( ImDrawVert ) ,
uv , ( int ) sizeof ( ImDrawVert ) ,
cmd_list - > VtxBuffer . Size - pcmd - > VtxOffset ,
idx_buffer + pcmd - > IdxOffset , pcmd - > ElemCount , sizeof ( ImDrawIdx ) ) ;
}
}
}
// Restore modified SDL_Renderer state
SDL_SetRenderViewport ( bd - > SDLRenderer , & old . Viewport ) ;
SDL_SetRenderClipRect ( bd - > SDLRenderer , old . ClipEnabled ? & old . ClipRect : nullptr ) ;
}
// Called by Init/NewFrame/Shutdown
2023-05-30 20:36:07 +02:00
bool ImGui_ImplSDLRenderer3_CreateFontsTexture ( )
2023-05-30 20:30:30 +02:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = ImGui_ImplSDLRenderer3_GetBackendData ( ) ;
2023-05-30 20:30:30 +02:00
// Build texture atlas
unsigned char * pixels ;
int width , height ;
io . Fonts - > GetTexDataAsRGBA32 ( & pixels , & width , & height ) ; // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
bd - > FontTexture = SDL_CreateTexture ( bd - > SDLRenderer , SDL_PIXELFORMAT_ABGR8888 , SDL_TEXTUREACCESS_STATIC , width , height ) ;
if ( bd - > FontTexture = = nullptr )
{
SDL_Log ( " error creating texture " ) ;
return false ;
}
SDL_UpdateTexture ( bd - > FontTexture , nullptr , pixels , 4 * width ) ;
SDL_SetTextureBlendMode ( bd - > FontTexture , SDL_BLENDMODE_BLEND ) ;
SDL_SetTextureScaleMode ( bd - > FontTexture , SDL_SCALEMODE_LINEAR ) ;
// Store our identifier
io . Fonts - > SetTexID ( ( ImTextureID ) ( intptr_t ) bd - > FontTexture ) ;
return true ;
}
2023-05-30 20:36:07 +02:00
void ImGui_ImplSDLRenderer3_DestroyFontsTexture ( )
2023-05-30 20:30:30 +02:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_Data * bd = ImGui_ImplSDLRenderer3_GetBackendData ( ) ;
2023-05-30 20:30:30 +02:00
if ( bd - > FontTexture )
{
io . Fonts - > SetTexID ( 0 ) ;
SDL_DestroyTexture ( bd - > FontTexture ) ;
bd - > FontTexture = nullptr ;
}
}
2023-05-30 20:36:07 +02:00
bool ImGui_ImplSDLRenderer3_CreateDeviceObjects ( )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
return ImGui_ImplSDLRenderer3_CreateFontsTexture ( ) ;
2023-05-30 20:30:30 +02:00
}
2023-05-30 20:36:07 +02:00
void ImGui_ImplSDLRenderer3_DestroyDeviceObjects ( )
2023-05-30 20:30:30 +02:00
{
2023-05-30 20:36:07 +02:00
ImGui_ImplSDLRenderer3_DestroyFontsTexture ( ) ;
2023-05-30 20:30:30 +02:00
}
# if defined(__clang__)
# pragma clang diagnostic pop
# endif