2021-11-11 21:22:24 +01:00
// dear imgui: Renderer Backend for DirectX9
// This needs to be used along with a Platform Backend (e.g. Win32)
// Implemented features:
// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID!
2023-04-14 17:44:25 +02:00
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
2023-04-15 00:23:52 +02:00
// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
2021-11-11 21:22:24 +01:00
2023-04-14 17:44:25 +02:00
// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
2021-11-11 21:22:24 +01:00
// 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
// (minor and older changes stripped away, please see git history for details)
2023-04-15 00:23:52 +02:00
// 2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
2023-04-14 17:44:25 +02:00
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
// 2021-06-25: DirectX9: Explicitly disable texture state stages after >= 1.
// 2021-05-19: DirectX9: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
// 2021-04-23: DirectX9: Explicitly setting up more graphics states to increase compatibility with unusual non-default states.
// 2021-03-18: DirectX9: Calling IDirect3DStateBlock9::Capture() after CreateStateBlock() as a workaround for state restoring issues (see #3857).
// 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file.
// 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer.
2021-11-11 21:22:24 +01:00
// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects().
// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288.
// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example.
// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud.
// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself.
// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
#include "imgui.h"
#include "imgui_impl_dx9.h"
// DirectX
#include <d3d9.h>
// DirectX data
2023-04-14 17:44:25 +02:00
struct ImGui_ImplDX9_Data
{
LPDIRECT3DDEVICE9 pd3dDevice ;
LPDIRECT3DVERTEXBUFFER9 pVB ;
LPDIRECT3DINDEXBUFFER9 pIB ;
LPDIRECT3DTEXTURE9 FontTexture ;
int VertexBufferSize ;
int IndexBufferSize ;
ImGui_ImplDX9_Data () { memset (( void * ) this , 0 , sizeof ( * this )); VertexBufferSize = 5000 ; IndexBufferSize = 10000 ; }
};
2021-11-11 21:22:24 +01:00
struct CUSTOMVERTEX
{
float pos [ 3 ];
D3DCOLOR col ;
float uv [ 2 ];
};
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)
2023-04-14 17:44:25 +02:00
#ifdef IMGUI_USE_BGRA_PACKED_COLOR
#define IMGUI_COL_TO_DX9_ARGB(_COL) (_COL)
#else
#define IMGUI_COL_TO_DX9_ARGB(_COL) (((_COL) & 0xFF00FF00) | (((_COL) & 0xFF0000) >> 16) | (((_COL) & 0xFF) << 16))
#endif
2021-11-11 21:22:24 +01:00
2023-04-14 17:44:25 +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.
static ImGui_ImplDX9_Data * ImGui_ImplDX9_GetBackendData ()
{
return ImGui :: GetCurrentContext () ? ( ImGui_ImplDX9_Data * ) ImGui :: GetIO (). BackendRendererUserData : nullptr ;
}
2023-04-15 00:23:52 +02:00
// Forward Declarations
static void ImGui_ImplDX9_InitPlatformInterface ();
static void ImGui_ImplDX9_ShutdownPlatformInterface ();
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows ();
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows ();
2023-04-14 17:44:25 +02:00
// Functions
2021-11-11 21:22:24 +01:00
static void ImGui_ImplDX9_SetupRenderState ( ImDrawData * draw_data )
{
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
2021-11-11 21:22:24 +01:00
// Setup viewport
D3DVIEWPORT9 vp ;
vp . X = vp . Y = 0 ;
vp . Width = ( DWORD ) draw_data -> DisplaySize . x ;
vp . Height = ( DWORD ) draw_data -> DisplaySize . y ;
vp . MinZ = 0.0f ;
vp . MaxZ = 1.0f ;
2023-04-14 17:44:25 +02:00
bd -> pd3dDevice -> SetViewport ( & vp );
2021-11-11 21:22:24 +01:00
2023-04-14 17:44:25 +02:00
// Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient), bilinear sampling.
bd -> pd3dDevice -> SetPixelShader ( nullptr );
bd -> pd3dDevice -> SetVertexShader ( nullptr );
bd -> pd3dDevice -> SetRenderState ( D3DRS_FILLMODE , D3DFILL_SOLID );
bd -> pd3dDevice -> SetRenderState ( D3DRS_SHADEMODE , D3DSHADE_GOURAUD );
bd -> pd3dDevice -> SetRenderState ( D3DRS_ZWRITEENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_ALPHATESTENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_CULLMODE , D3DCULL_NONE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_ZENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_ALPHABLENDENABLE , TRUE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_BLENDOP , D3DBLENDOP_ADD );
bd -> pd3dDevice -> SetRenderState ( D3DRS_SRCBLEND , D3DBLEND_SRCALPHA );
bd -> pd3dDevice -> SetRenderState ( D3DRS_DESTBLEND , D3DBLEND_INVSRCALPHA );
bd -> pd3dDevice -> SetRenderState ( D3DRS_SEPARATEALPHABLENDENABLE , TRUE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_SRCBLENDALPHA , D3DBLEND_ONE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_DESTBLENDALPHA , D3DBLEND_INVSRCALPHA );
bd -> pd3dDevice -> SetRenderState ( D3DRS_SCISSORTESTENABLE , TRUE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_FOGENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_RANGEFOGENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_SPECULARENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_STENCILENABLE , FALSE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_CLIPPING , TRUE );
bd -> pd3dDevice -> SetRenderState ( D3DRS_LIGHTING , FALSE );
bd -> pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLOROP , D3DTOP_MODULATE );
bd -> pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG1 , D3DTA_TEXTURE );
bd -> pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_COLORARG2 , D3DTA_DIFFUSE );
bd -> pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAOP , D3DTOP_MODULATE );
bd -> pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG1 , D3DTA_TEXTURE );
bd -> pd3dDevice -> SetTextureStageState ( 0 , D3DTSS_ALPHAARG2 , D3DTA_DIFFUSE );
bd -> pd3dDevice -> SetTextureStageState ( 1 , D3DTSS_COLOROP , D3DTOP_DISABLE );
bd -> pd3dDevice -> SetTextureStageState ( 1 , D3DTSS_ALPHAOP , D3DTOP_DISABLE );
bd -> pd3dDevice -> SetSamplerState ( 0 , D3DSAMP_MINFILTER , D3DTEXF_LINEAR );
bd -> pd3dDevice -> SetSamplerState ( 0 , D3DSAMP_MAGFILTER , D3DTEXF_LINEAR );
2021-11-11 21:22:24 +01:00
// Setup orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
// Being agnostic of whether <d3dx9.h> or <DirectXMath.h> can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH()
{
float L = draw_data -> DisplayPos . x + 0.5f ;
float R = draw_data -> DisplayPos . x + draw_data -> DisplaySize . x + 0.5f ;
float T = draw_data -> DisplayPos . y + 0.5f ;
float B = draw_data -> DisplayPos . y + draw_data -> DisplaySize . y + 0.5f ;
D3DMATRIX mat_identity = { { { 1.0f , 0.0f , 0.0f , 0.0f , 0.0f , 1.0f , 0.0f , 0.0f , 0.0f , 0.0f , 1.0f , 0.0f , 0.0f , 0.0f , 0.0f , 1.0f } } };
D3DMATRIX mat_projection =
{ { {
2.0f / ( R - L ), 0.0f , 0.0f , 0.0f ,
0.0f , 2.0f / ( T - B ), 0.0f , 0.0f ,
0.0f , 0.0f , 0.5f , 0.0f ,
( L + R ) / ( L - R ), ( T + B ) / ( B - T ), 0.5f , 1.0f
} } };
2023-04-14 17:44:25 +02:00
bd -> pd3dDevice -> SetTransform ( D3DTS_WORLD , & mat_identity );
bd -> pd3dDevice -> SetTransform ( D3DTS_VIEW , & mat_identity );
bd -> pd3dDevice -> SetTransform ( D3DTS_PROJECTION , & mat_projection );
2021-11-11 21:22:24 +01:00
}
}
// Render function.
void ImGui_ImplDX9_RenderDrawData ( ImDrawData * draw_data )
{
// Avoid rendering when minimized
if ( draw_data -> DisplaySize . x <= 0.0f || draw_data -> DisplaySize . y <= 0.0f )
return ;
// Create and grow buffers if needed
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
if ( ! bd -> pVB || bd -> VertexBufferSize < draw_data -> TotalVtxCount )
2021-11-11 21:22:24 +01:00
{
2023-04-14 17:44:25 +02:00
if ( bd -> pVB ) { bd -> pVB -> Release (); bd -> pVB = nullptr ; }
bd -> VertexBufferSize = draw_data -> TotalVtxCount + 5000 ;
if ( bd -> pd3dDevice -> CreateVertexBuffer ( bd -> VertexBufferSize * sizeof ( CUSTOMVERTEX ), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY , D3DFVF_CUSTOMVERTEX , D3DPOOL_DEFAULT , & bd -> pVB , nullptr ) < 0 )
2021-11-11 21:22:24 +01:00
return ;
}
2023-04-14 17:44:25 +02:00
if ( ! bd -> pIB || bd -> IndexBufferSize < draw_data -> TotalIdxCount )
2021-11-11 21:22:24 +01:00
{
2023-04-14 17:44:25 +02:00
if ( bd -> pIB ) { bd -> pIB -> Release (); bd -> pIB = nullptr ; }
bd -> IndexBufferSize = draw_data -> TotalIdxCount + 10000 ;
if ( bd -> pd3dDevice -> CreateIndexBuffer ( bd -> IndexBufferSize * sizeof ( ImDrawIdx ), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY , sizeof ( ImDrawIdx ) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32 , D3DPOOL_DEFAULT , & bd -> pIB , nullptr ) < 0 )
2021-11-11 21:22:24 +01:00
return ;
}
// Backup the DX9 state
2023-04-14 17:44:25 +02:00
IDirect3DStateBlock9 * d3d9_state_block = nullptr ;
if ( bd -> pd3dDevice -> CreateStateBlock ( D3DSBT_ALL , & d3d9_state_block ) < 0 )
2021-11-11 21:22:24 +01:00
return ;
2023-04-14 17:44:25 +02:00
if ( d3d9_state_block -> Capture () < 0 )
{
d3d9_state_block -> Release ();
return ;
}
2021-11-11 21:22:24 +01:00
// Backup the DX9 transform (DX9 documentation suggests that it is included in the StateBlock but it doesn't appear to)
D3DMATRIX last_world , last_view , last_projection ;
2023-04-14 17:44:25 +02:00
bd -> pd3dDevice -> GetTransform ( D3DTS_WORLD , & last_world );
bd -> pd3dDevice -> GetTransform ( D3DTS_VIEW , & last_view );
bd -> pd3dDevice -> GetTransform ( D3DTS_PROJECTION , & last_projection );
2021-11-11 21:22:24 +01:00
2023-04-14 17:44:25 +02:00
// Allocate buffers
2021-11-11 21:22:24 +01:00
CUSTOMVERTEX * vtx_dst ;
ImDrawIdx * idx_dst ;
2023-04-14 17:44:25 +02:00
if ( bd -> pVB -> Lock ( 0 , ( UINT )( draw_data -> TotalVtxCount * sizeof ( CUSTOMVERTEX )), ( void ** ) & vtx_dst , D3DLOCK_DISCARD ) < 0 )
{
d3d9_state_block -> Release ();
2021-11-11 21:22:24 +01:00
return ;
2023-04-14 17:44:25 +02:00
}
if ( bd -> pIB -> Lock ( 0 , ( UINT )( draw_data -> TotalIdxCount * sizeof ( ImDrawIdx )), ( void ** ) & idx_dst , D3DLOCK_DISCARD ) < 0 )
{
bd -> pVB -> Unlock ();
d3d9_state_block -> Release ();
2021-11-11 21:22:24 +01:00
return ;
2023-04-14 17:44:25 +02:00
}
// Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format.
// FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and
// 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR
// 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; }
2021-11-11 21:22:24 +01:00
for ( int n = 0 ; n < draw_data -> CmdListsCount ; n ++ )
{
const ImDrawList * cmd_list = draw_data -> CmdLists [ n ];
const ImDrawVert * vtx_src = cmd_list -> VtxBuffer . Data ;
for ( int i = 0 ; i < cmd_list -> VtxBuffer . Size ; i ++ )
{
vtx_dst -> pos [ 0 ] = vtx_src -> pos . x ;
vtx_dst -> pos [ 1 ] = vtx_src -> pos . y ;
vtx_dst -> pos [ 2 ] = 0.0f ;
2023-04-14 17:44:25 +02:00
vtx_dst -> col = IMGUI_COL_TO_DX9_ARGB ( vtx_src -> col );
2021-11-11 21:22:24 +01:00
vtx_dst -> uv [ 0 ] = vtx_src -> uv . x ;
vtx_dst -> uv [ 1 ] = vtx_src -> uv . y ;
vtx_dst ++ ;
vtx_src ++ ;
}
memcpy ( idx_dst , cmd_list -> IdxBuffer . Data , cmd_list -> IdxBuffer . Size * sizeof ( ImDrawIdx ));
idx_dst += cmd_list -> IdxBuffer . Size ;
}
2023-04-14 17:44:25 +02:00
bd -> pVB -> Unlock ();
bd -> pIB -> Unlock ();
bd -> pd3dDevice -> SetStreamSource ( 0 , bd -> pVB , 0 , sizeof ( CUSTOMVERTEX ));
bd -> pd3dDevice -> SetIndices ( bd -> pIB );
bd -> pd3dDevice -> SetFVF ( D3DFVF_CUSTOMVERTEX );
2021-11-11 21:22:24 +01:00
// Setup desired DX state
ImGui_ImplDX9_SetupRenderState ( draw_data );
// Render command lists
// (Because we merged all buffers into a single one, we maintain our own offset into them)
int global_vtx_offset = 0 ;
int global_idx_offset = 0 ;
ImVec2 clip_off = draw_data -> DisplayPos ;
for ( int n = 0 ; n < draw_data -> CmdListsCount ; n ++ )
{
const ImDrawList * cmd_list = draw_data -> CmdLists [ n ];
for ( int cmd_i = 0 ; cmd_i < cmd_list -> CmdBuffer . Size ; cmd_i ++ )
{
const ImDrawCmd * pcmd = & cmd_list -> CmdBuffer [ cmd_i ];
2023-04-14 17:44:25 +02:00
if ( pcmd -> UserCallback != nullptr )
2021-11-11 21:22:24 +01:00
{
// 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 )
ImGui_ImplDX9_SetupRenderState ( draw_data );
else
pcmd -> UserCallback ( cmd_list , pcmd );
}
else
{
2023-04-14 17:44:25 +02:00
// Project scissor/clipping rectangles into framebuffer space
ImVec2 clip_min ( pcmd -> ClipRect . x - clip_off . x , pcmd -> ClipRect . y - clip_off . y );
ImVec2 clip_max ( pcmd -> ClipRect . z - clip_off . x , pcmd -> ClipRect . w - clip_off . y );
if ( clip_max . x <= clip_min . x || clip_max . y <= clip_min . y )
continue ;
// Apply Scissor/clipping rectangle, Bind texture, Draw
const RECT r = { ( LONG ) clip_min . x , ( LONG ) clip_min . y , ( LONG ) clip_max . x , ( LONG ) clip_max . y };
const LPDIRECT3DTEXTURE9 texture = ( LPDIRECT3DTEXTURE9 ) pcmd -> GetTexID ();
bd -> pd3dDevice -> SetTexture ( 0 , texture );
bd -> pd3dDevice -> SetScissorRect ( & r );
bd -> pd3dDevice -> DrawIndexedPrimitive ( D3DPT_TRIANGLELIST , pcmd -> VtxOffset + global_vtx_offset , 0 , ( UINT ) cmd_list -> VtxBuffer . Size , pcmd -> IdxOffset + global_idx_offset , pcmd -> ElemCount / 3 );
2021-11-11 21:22:24 +01:00
}
}
global_idx_offset += cmd_list -> IdxBuffer . Size ;
global_vtx_offset += cmd_list -> VtxBuffer . Size ;
}
2023-04-15 00:23:52 +02:00
// When using multi-viewports, it appears that there's an odd logic in DirectX9 which prevent subsequent windows
// from rendering until the first window submits at least one draw call, even once. That's our workaround. (see #2560)
if ( global_vtx_offset == 0 )
bd -> pd3dDevice -> DrawIndexedPrimitive ( D3DPT_TRIANGLELIST , 0 , 0 , 0 , 0 , 0 );
2021-11-11 21:22:24 +01:00
// Restore the DX9 transform
2023-04-14 17:44:25 +02:00
bd -> pd3dDevice -> SetTransform ( D3DTS_WORLD , & last_world );
bd -> pd3dDevice -> SetTransform ( D3DTS_VIEW , & last_view );
bd -> pd3dDevice -> SetTransform ( D3DTS_PROJECTION , & last_projection );
2021-11-11 21:22:24 +01:00
// Restore the DX9 state
d3d9_state_block -> Apply ();
d3d9_state_block -> Release ();
}
bool ImGui_ImplDX9_Init ( IDirect3DDevice9 * device )
{
ImGuiIO & io = ImGui :: GetIO ();
2023-04-14 17:44:25 +02:00
IM_ASSERT ( io . BackendRendererUserData == nullptr && "Already initialized a renderer backend!" );
// Setup backend capabilities flags
ImGui_ImplDX9_Data * bd = IM_NEW ( ImGui_ImplDX9_Data )();
io . BackendRendererUserData = ( void * ) bd ;
2021-11-11 21:22:24 +01:00
io . BackendRendererName = "imgui_impl_dx9" ;
io . BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset ; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
2023-04-15 00:23:52 +02:00
io . BackendFlags |= ImGuiBackendFlags_RendererHasViewports ; // We can create multi-viewports on the Renderer side (optional)
2021-11-11 21:22:24 +01:00
2023-04-14 17:44:25 +02:00
bd -> pd3dDevice = device ;
bd -> pd3dDevice -> AddRef ();
2021-11-11 21:22:24 +01:00
2023-04-15 00:23:52 +02:00
if ( io . ConfigFlags & ImGuiConfigFlags_ViewportsEnable )
ImGui_ImplDX9_InitPlatformInterface ();
2021-11-11 21:22:24 +01:00
return true ;
}
void ImGui_ImplDX9_Shutdown ()
{
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
IM_ASSERT ( bd != nullptr && "No renderer backend to shutdown, or already shutdown?" );
ImGuiIO & io = ImGui :: GetIO ();
2023-04-15 00:23:52 +02:00
ImGui_ImplDX9_ShutdownPlatformInterface ();
2021-11-11 21:22:24 +01:00
ImGui_ImplDX9_InvalidateDeviceObjects ();
2023-04-14 17:44:25 +02:00
if ( bd -> pd3dDevice ) { bd -> pd3dDevice -> Release (); }
io . BackendRendererName = nullptr ;
io . BackendRendererUserData = nullptr ;
IM_DELETE ( bd );
2021-11-11 21:22:24 +01:00
}
static bool ImGui_ImplDX9_CreateFontsTexture ()
{
// Build texture atlas
ImGuiIO & io = ImGui :: GetIO ();
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
2021-11-11 21:22:24 +01:00
unsigned char * pixels ;
int width , height , bytes_per_pixel ;
io . Fonts -> GetTexDataAsRGBA32 ( & pixels , & width , & height , & bytes_per_pixel );
2023-04-14 17:44:25 +02:00
// Convert RGBA32 to BGRA32 (because RGBA32 is not well supported by DX9 devices)
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
if ( io . Fonts -> TexPixelsUseColors )
{
ImU32 * dst_start = ( ImU32 * ) ImGui :: MemAlloc (( size_t ) width * height * bytes_per_pixel );
for ( ImU32 * src = ( ImU32 * ) pixels , * dst = dst_start , * dst_end = dst_start + ( size_t ) width * height ; dst < dst_end ; src ++ , dst ++ )
* dst = IMGUI_COL_TO_DX9_ARGB ( * src );
pixels = ( unsigned char * ) dst_start ;
}
#endif
2021-11-11 21:22:24 +01:00
// Upload texture to graphics system
2023-04-14 17:44:25 +02:00
bd -> FontTexture = nullptr ;
if ( bd -> pd3dDevice -> CreateTexture ( width , height , 1 , D3DUSAGE_DYNAMIC , D3DFMT_A8R8G8B8 , D3DPOOL_DEFAULT , & bd -> FontTexture , nullptr ) < 0 )
2021-11-11 21:22:24 +01:00
return false ;
D3DLOCKED_RECT tex_locked_rect ;
2023-04-14 17:44:25 +02:00
if ( bd -> FontTexture -> LockRect ( 0 , & tex_locked_rect , nullptr , 0 ) != D3D_OK )
2021-11-11 21:22:24 +01:00
return false ;
for ( int y = 0 ; y < height ; y ++ )
2023-04-14 17:44:25 +02:00
memcpy (( unsigned char * ) tex_locked_rect . pBits + ( size_t ) tex_locked_rect . Pitch * y , pixels + ( size_t ) width * bytes_per_pixel * y , ( size_t ) width * bytes_per_pixel );
bd -> FontTexture -> UnlockRect ( 0 );
2021-11-11 21:22:24 +01:00
// Store our identifier
2023-04-14 17:44:25 +02:00
io . Fonts -> SetTexID (( ImTextureID ) bd -> FontTexture );
#ifndef IMGUI_USE_BGRA_PACKED_COLOR
if ( io . Fonts -> TexPixelsUseColors )
ImGui :: MemFree ( pixels );
#endif
2021-11-11 21:22:24 +01:00
return true ;
}
bool ImGui_ImplDX9_CreateDeviceObjects ()
{
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
if ( ! bd || ! bd -> pd3dDevice )
2021-11-11 21:22:24 +01:00
return false ;
if ( ! ImGui_ImplDX9_CreateFontsTexture ())
return false ;
2023-04-15 00:23:52 +02:00
ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows ();
2021-11-11 21:22:24 +01:00
return true ;
}
void ImGui_ImplDX9_InvalidateDeviceObjects ()
{
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
if ( ! bd || ! bd -> pd3dDevice )
2021-11-11 21:22:24 +01:00
return ;
2023-04-14 17:44:25 +02:00
if ( bd -> pVB ) { bd -> pVB -> Release (); bd -> pVB = nullptr ; }
if ( bd -> pIB ) { bd -> pIB -> Release (); bd -> pIB = nullptr ; }
if ( bd -> FontTexture ) { bd -> FontTexture -> Release (); bd -> FontTexture = nullptr ; ImGui :: GetIO (). Fonts -> SetTexID ( 0 ); } // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well.
2023-04-15 00:23:52 +02:00
ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows ();
2021-11-11 21:22:24 +01:00
}
void ImGui_ImplDX9_NewFrame ()
{
2023-04-14 17:44:25 +02:00
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
IM_ASSERT ( bd != nullptr && "Did you call ImGui_ImplDX9_Init()?" );
if ( ! bd -> FontTexture )
2021-11-11 21:22:24 +01:00
ImGui_ImplDX9_CreateDeviceObjects ();
}
2023-04-15 00:23:52 +02:00
//--------------------------------------------------------------------------------------------------------
// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
//--------------------------------------------------------------------------------------------------------
// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
struct ImGui_ImplDX9_ViewportData
{
IDirect3DSwapChain9 * SwapChain ;
D3DPRESENT_PARAMETERS d3dpp ;
ImGui_ImplDX9_ViewportData () { SwapChain = nullptr ; ZeroMemory ( & d3dpp , sizeof ( D3DPRESENT_PARAMETERS )); }
~ ImGui_ImplDX9_ViewportData () { IM_ASSERT ( SwapChain == nullptr ); }
};
static void ImGui_ImplDX9_CreateWindow ( ImGuiViewport * viewport )
{
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
ImGui_ImplDX9_ViewportData * vd = IM_NEW ( ImGui_ImplDX9_ViewportData )();
viewport -> RendererUserData = vd ;
// PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL_Window*).
// Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
HWND hwnd = viewport -> PlatformHandleRaw ? ( HWND ) viewport -> PlatformHandleRaw : ( HWND ) viewport -> PlatformHandle ;
IM_ASSERT ( hwnd != 0 );
ZeroMemory ( & vd -> d3dpp , sizeof ( D3DPRESENT_PARAMETERS ));
vd -> d3dpp . Windowed = TRUE ;
vd -> d3dpp . SwapEffect = D3DSWAPEFFECT_DISCARD ;
vd -> d3dpp . BackBufferWidth = ( UINT ) viewport -> Size . x ;
vd -> d3dpp . BackBufferHeight = ( UINT ) viewport -> Size . y ;
vd -> d3dpp . BackBufferFormat = D3DFMT_UNKNOWN ;
vd -> d3dpp . hDeviceWindow = hwnd ;
vd -> d3dpp . EnableAutoDepthStencil = FALSE ;
vd -> d3dpp . AutoDepthStencilFormat = D3DFMT_D16 ;
vd -> d3dpp . PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE ; // Present without vsync
HRESULT hr = bd -> pd3dDevice -> CreateAdditionalSwapChain ( & vd -> d3dpp , & vd -> SwapChain ); IM_UNUSED ( hr );
IM_ASSERT ( hr == D3D_OK );
IM_ASSERT ( vd -> SwapChain != nullptr );
}
static void ImGui_ImplDX9_DestroyWindow ( ImGuiViewport * viewport )
{
// The main viewport (owned by the application) will always have RendererUserData == 0 since we didn't create the data for it.
if ( ImGui_ImplDX9_ViewportData * vd = ( ImGui_ImplDX9_ViewportData * ) viewport -> RendererUserData )
{
if ( vd -> SwapChain )
vd -> SwapChain -> Release ();
vd -> SwapChain = nullptr ;
ZeroMemory ( & vd -> d3dpp , sizeof ( D3DPRESENT_PARAMETERS ));
IM_DELETE ( vd );
}
viewport -> RendererUserData = nullptr ;
}
static void ImGui_ImplDX9_SetWindowSize ( ImGuiViewport * viewport , ImVec2 size )
{
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
ImGui_ImplDX9_ViewportData * vd = ( ImGui_ImplDX9_ViewportData * ) viewport -> RendererUserData ;
if ( vd -> SwapChain )
{
vd -> SwapChain -> Release ();
vd -> SwapChain = nullptr ;
vd -> d3dpp . BackBufferWidth = ( UINT ) size . x ;
vd -> d3dpp . BackBufferHeight = ( UINT ) size . y ;
HRESULT hr = bd -> pd3dDevice -> CreateAdditionalSwapChain ( & vd -> d3dpp , & vd -> SwapChain ); IM_UNUSED ( hr );
IM_ASSERT ( hr == D3D_OK );
}
}
static void ImGui_ImplDX9_RenderWindow ( ImGuiViewport * viewport , void * )
{
ImGui_ImplDX9_Data * bd = ImGui_ImplDX9_GetBackendData ();
ImGui_ImplDX9_ViewportData * vd = ( ImGui_ImplDX9_ViewportData * ) viewport -> RendererUserData ;
ImVec4 clear_color = ImVec4 ( 0.0f , 0.0f , 0.0f , 1.0f );
LPDIRECT3DSURFACE9 render_target = nullptr ;
LPDIRECT3DSURFACE9 last_render_target = nullptr ;
LPDIRECT3DSURFACE9 last_depth_stencil = nullptr ;
vd -> SwapChain -> GetBackBuffer ( 0 , D3DBACKBUFFER_TYPE_MONO , & render_target );
bd -> pd3dDevice -> GetRenderTarget ( 0 , & last_render_target );
bd -> pd3dDevice -> GetDepthStencilSurface ( & last_depth_stencil );
bd -> pd3dDevice -> SetRenderTarget ( 0 , render_target );
bd -> pd3dDevice -> SetDepthStencilSurface ( nullptr );
if ( ! ( viewport -> Flags & ImGuiViewportFlags_NoRendererClear ))
{
D3DCOLOR clear_col_dx = D3DCOLOR_RGBA (( int )( clear_color . x * 255.0f ), ( int )( clear_color . y * 255.0f ), ( int )( clear_color . z * 255.0f ), ( int )( clear_color . w * 255.0f ));
bd -> pd3dDevice -> Clear ( 0 , nullptr , D3DCLEAR_TARGET , clear_col_dx , 1.0f , 0 );
}
ImGui_ImplDX9_RenderDrawData ( viewport -> DrawData );
// Restore render target
bd -> pd3dDevice -> SetRenderTarget ( 0 , last_render_target );
bd -> pd3dDevice -> SetDepthStencilSurface ( last_depth_stencil );
render_target -> Release ();
last_render_target -> Release ();
if ( last_depth_stencil ) last_depth_stencil -> Release ();
}
static void ImGui_ImplDX9_SwapBuffers ( ImGuiViewport * viewport , void * )
{
ImGui_ImplDX9_ViewportData * vd = ( ImGui_ImplDX9_ViewportData * ) viewport -> RendererUserData ;
HRESULT hr = vd -> SwapChain -> Present ( nullptr , nullptr , vd -> d3dpp . hDeviceWindow , nullptr , 0 );
// Let main application handle D3DERR_DEVICELOST by resetting the device.
IM_ASSERT ( hr == D3D_OK || hr == D3DERR_DEVICELOST );
}
static void ImGui_ImplDX9_InitPlatformInterface ()
{
ImGuiPlatformIO & platform_io = ImGui :: GetPlatformIO ();
platform_io . Renderer_CreateWindow = ImGui_ImplDX9_CreateWindow ;
platform_io . Renderer_DestroyWindow = ImGui_ImplDX9_DestroyWindow ;
platform_io . Renderer_SetWindowSize = ImGui_ImplDX9_SetWindowSize ;
platform_io . Renderer_RenderWindow = ImGui_ImplDX9_RenderWindow ;
platform_io . Renderer_SwapBuffers = ImGui_ImplDX9_SwapBuffers ;
}
static void ImGui_ImplDX9_ShutdownPlatformInterface ()
{
ImGui :: DestroyPlatformWindows ();
}
static void ImGui_ImplDX9_CreateDeviceObjectsForPlatformWindows ()
{
ImGuiPlatformIO & platform_io = ImGui :: GetPlatformIO ();
for ( int i = 1 ; i < platform_io . Viewports . Size ; i ++ )
if ( ! platform_io . Viewports [ i ] -> RendererUserData )
ImGui_ImplDX9_CreateWindow ( platform_io . Viewports [ i ]);
}
static void ImGui_ImplDX9_InvalidateDeviceObjectsForPlatformWindows ()
{
ImGuiPlatformIO & platform_io = ImGui :: GetPlatformIO ();
for ( int i = 1 ; i < platform_io . Viewports . Size ; i ++ )
if ( platform_io . Viewports [ i ] -> RendererUserData )
ImGui_ImplDX9_DestroyWindow ( platform_io . Viewports [ i ]);
}