1// dear imgui: Renderer Backend for DirectX10
2// This needs to be used along with a Platform Backend (e.g. Win32)
3
4// Implemented features:
5// [X] Renderer: User texture binding. Use 'ID3D10ShaderResourceView*' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef!
6// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset).
7// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures).
8// [X] Renderer: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
9
10// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
11// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
12// Learn about Dear ImGui:
13// - FAQ https://dearimgui.com/faq
14// - Getting Started https://dearimgui.com/getting-started
15// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
16// - Introduction, links and more at the top of imgui.cpp
17
18// CHANGELOG
19// (minor and older changes stripped away, please see git history for details)
20// 2025-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
21// 2025-06-11: DirectX10: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas.
22// 2025-05-07: DirectX10: Honor draw_data->FramebufferScale to allow for custom backends and experiment using it (consistently with other renderer backends, even though in normal condition it is not set under Windows).
23// 2025-01-06: DirectX10: Expose selected render state in ImGui_ImplDX10_RenderState, which you can access in 'void* platform_io.Renderer_RenderState' during draw callbacks.
24// 2024-10-07: DirectX10: Changed default texture sampler to Clamp instead of Repeat/Wrap.
25// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
26// 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).
27// 2021-05-19: DirectX10: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement)
28// 2021-02-18: DirectX10: Change blending equation to preserve alpha in output buffer.
29// 2019-07-21: DirectX10: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData().
30// 2019-05-29: DirectX10: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
31// 2019-04-30: DirectX10: Added support for special ImDrawCallback_ResetRenderState callback to reset render state.
32// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile().
33// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window.
34// 2018-07-13: DirectX10: Fixed unreleased resources in Init and Shutdown functions.
35// 2018-06-08: Misc: Extracted imgui_impl_dx10.cpp/.h away from the old combined DX10+Win32 example.
36// 2018-06-08: DirectX10: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle.
37// 2018-04-09: Misc: Fixed erroneous call to io.Fonts->ClearInputData() + ClearTexData() that was left in DX10 example but removed in 1.47 (Nov 2015) on other backends.
38// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX10_RenderDrawData() in the .h file so you can call it yourself.
39// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
40// 2016-05-07: DirectX10: Disabling depth-write.
41
42#include "imgui.h"
43#ifndef IMGUI_DISABLE
44#include "imgui_impl_dx10.h"
45
46// DirectX
47#include <stdio.h>
48#include <d3d10_1.h>
49#include <d3d10.h>
50#include <d3dcompiler.h>
51#ifdef _MSC_VER
52#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below.
53#endif
54
55// Clang/GCC warnings with -Weverything
56#if defined(__clang__)
57#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
58#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
59#endif
60
61// DirectX10 data
62struct ImGui_ImplDX10_Texture
63{
64 ID3D10Texture2D* pTexture;
65 ID3D10ShaderResourceView* pTextureView;
66};
67
68struct ImGui_ImplDX10_Data
69{
70 ID3D10Device* pd3dDevice;
71 IDXGIFactory* pFactory;
72 ID3D10Buffer* pVB;
73 ID3D10Buffer* pIB;
74 ID3D10VertexShader* pVertexShader;
75 ID3D10InputLayout* pInputLayout;
76 ID3D10Buffer* pVertexConstantBuffer;
77 ID3D10PixelShader* pPixelShader;
78 ID3D10SamplerState* pFontSampler;
79 ID3D10RasterizerState* pRasterizerState;
80 ID3D10BlendState* pBlendState;
81 ID3D10DepthStencilState* pDepthStencilState;
82 int VertexBufferSize;
83 int IndexBufferSize;
84
85 ImGui_ImplDX10_Data() { memset(s: (void*)this, c: 0, n: sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; }
86};
87
88struct VERTEX_CONSTANT_BUFFER_DX10
89{
90 float mvp[4][4];
91};
92
93// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
94// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
95static ImGui_ImplDX10_Data* ImGui_ImplDX10_GetBackendData()
96{
97 return ImGui::GetCurrentContext() ? (ImGui_ImplDX10_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
98}
99
100// Forward Declarations
101static void ImGui_ImplDX10_InitMultiViewportSupport();
102static void ImGui_ImplDX10_ShutdownMultiViewportSupport();
103
104// Functions
105static void ImGui_ImplDX10_SetupRenderState(ImDrawData* draw_data, ID3D10Device* device)
106{
107 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
108
109 // Setup viewport
110 D3D10_VIEWPORT vp = {};
111 vp.Width = (UINT)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
112 vp.Height = (UINT)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
113 vp.MinDepth = 0.0f;
114 vp.MaxDepth = 1.0f;
115 vp.TopLeftX = vp.TopLeftY = 0;
116 device->RSSetViewports(1, &vp);
117
118 // Setup orthographic projection matrix into our constant buffer
119 // 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.
120 void* mapped_resource;
121 if (bd->pVertexConstantBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &mapped_resource) == S_OK)
122 {
123 VERTEX_CONSTANT_BUFFER_DX10* constant_buffer = (VERTEX_CONSTANT_BUFFER_DX10*)mapped_resource;
124 float L = draw_data->DisplayPos.x;
125 float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
126 float T = draw_data->DisplayPos.y;
127 float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
128 float mvp[4][4] =
129 {
130 { 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
131 { 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
132 { 0.0f, 0.0f, 0.5f, 0.0f },
133 { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f },
134 };
135 memcpy(dest: &constant_buffer->mvp, src: mvp, n: sizeof(mvp));
136 bd->pVertexConstantBuffer->Unmap();
137 }
138
139 // Setup shader and vertex buffers
140 unsigned int stride = sizeof(ImDrawVert);
141 unsigned int offset = 0;
142 device->IASetInputLayout(bd->pInputLayout);
143 device->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset);
144 device->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0);
145 device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
146 device->VSSetShader(bd->pVertexShader);
147 device->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer);
148 device->PSSetShader(bd->pPixelShader);
149 device->PSSetSamplers(0, 1, &bd->pFontSampler);
150 device->GSSetShader(nullptr);
151
152 // Setup render state
153 const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
154 device->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff);
155 device->OMSetDepthStencilState(bd->pDepthStencilState, 0);
156 device->RSSetState(bd->pRasterizerState);
157}
158
159// Render function
160void ImGui_ImplDX10_RenderDrawData(ImDrawData* draw_data)
161{
162 // Avoid rendering when minimized
163 if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f)
164 return;
165
166 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
167 ID3D10Device* device = bd->pd3dDevice;
168
169 // Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
170 // (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
171 if (draw_data->Textures != nullptr)
172 for (ImTextureData* tex : *draw_data->Textures)
173 if (tex->Status != ImTextureStatus_OK)
174 ImGui_ImplDX10_UpdateTexture(tex);
175
176 // Create and grow vertex/index buffers if needed
177 if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount)
178 {
179 if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
180 bd->VertexBufferSize = draw_data->TotalVtxCount + 5000;
181 D3D10_BUFFER_DESC desc = {};
182 desc.Usage = D3D10_USAGE_DYNAMIC;
183 desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert);
184 desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
185 desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
186 desc.MiscFlags = 0;
187 if (device->CreateBuffer(&desc, nullptr, &bd->pVB) < 0)
188 return;
189 }
190
191 if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount)
192 {
193 if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
194 bd->IndexBufferSize = draw_data->TotalIdxCount + 10000;
195 D3D10_BUFFER_DESC desc = {};
196 desc.Usage = D3D10_USAGE_DYNAMIC;
197 desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx);
198 desc.BindFlags = D3D10_BIND_INDEX_BUFFER;
199 desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
200 if (device->CreateBuffer(&desc, nullptr, &bd->pIB) < 0)
201 return;
202 }
203
204 // Copy and convert all vertices into a single contiguous buffer
205 ImDrawVert* vtx_dst = nullptr;
206 ImDrawIdx* idx_dst = nullptr;
207 bd->pVB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&vtx_dst);
208 bd->pIB->Map(D3D10_MAP_WRITE_DISCARD, 0, (void**)&idx_dst);
209 for (const ImDrawList* draw_list : draw_data->CmdLists)
210 {
211 memcpy(dest: vtx_dst, src: draw_list->VtxBuffer.Data, n: draw_list->VtxBuffer.Size * sizeof(ImDrawVert));
212 memcpy(dest: idx_dst, src: draw_list->IdxBuffer.Data, n: draw_list->IdxBuffer.Size * sizeof(ImDrawIdx));
213 vtx_dst += draw_list->VtxBuffer.Size;
214 idx_dst += draw_list->IdxBuffer.Size;
215 }
216 bd->pVB->Unmap();
217 bd->pIB->Unmap();
218
219 // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!)
220 struct BACKUP_DX10_STATE
221 {
222 UINT ScissorRectsCount, ViewportsCount;
223 D3D10_RECT ScissorRects[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
224 D3D10_VIEWPORT Viewports[D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE];
225 ID3D10RasterizerState* RS;
226 ID3D10BlendState* BlendState;
227 FLOAT BlendFactor[4];
228 UINT SampleMask;
229 UINT StencilRef;
230 ID3D10DepthStencilState* DepthStencilState;
231 ID3D10ShaderResourceView* PSShaderResource;
232 ID3D10SamplerState* PSSampler;
233 ID3D10PixelShader* PS;
234 ID3D10VertexShader* VS;
235 ID3D10GeometryShader* GS;
236 D3D10_PRIMITIVE_TOPOLOGY PrimitiveTopology;
237 ID3D10Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer;
238 UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset;
239 DXGI_FORMAT IndexBufferFormat;
240 ID3D10InputLayout* InputLayout;
241 };
242 BACKUP_DX10_STATE old = {};
243 old.ScissorRectsCount = old.ViewportsCount = D3D10_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE;
244 device->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects);
245 device->RSGetViewports(&old.ViewportsCount, old.Viewports);
246 device->RSGetState(&old.RS);
247 device->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask);
248 device->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef);
249 device->PSGetShaderResources(0, 1, &old.PSShaderResource);
250 device->PSGetSamplers(0, 1, &old.PSSampler);
251 device->PSGetShader(&old.PS);
252 device->VSGetShader(&old.VS);
253 device->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer);
254 device->GSGetShader(&old.GS);
255 device->IAGetPrimitiveTopology(&old.PrimitiveTopology);
256 device->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset);
257 device->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset);
258 device->IAGetInputLayout(&old.InputLayout);
259
260 // Setup desired DX state
261 ImGui_ImplDX10_SetupRenderState(draw_data, device);
262 // Setup render state structure (for callbacks and custom texture bindings)
263 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
264 ImGui_ImplDX10_RenderState render_state;
265 render_state.Device = bd->pd3dDevice;
266 render_state.SamplerDefault = bd->pFontSampler;
267 render_state.VertexConstantBuffer = bd->pVertexConstantBuffer;
268 platform_io.Renderer_RenderState = &render_state;
269
270 // Render command lists
271 // (Because we merged all buffers into a single one, we maintain our own offset into them)
272 int global_vtx_offset = 0;
273 int global_idx_offset = 0;
274 ImVec2 clip_off = draw_data->DisplayPos;
275 ImVec2 clip_scale = draw_data->FramebufferScale;
276 for (const ImDrawList* draw_list : draw_data->CmdLists)
277 {
278 for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++)
279 {
280 const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i];
281 if (pcmd->UserCallback != nullptr)
282 {
283 // User callback, registered via ImDrawList::AddCallback()
284 // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
285 if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
286 ImGui_ImplDX10_SetupRenderState(draw_data, device);
287 else
288 pcmd->UserCallback(draw_list, pcmd);
289 }
290 else
291 {
292 // Project scissor/clipping rectangles into framebuffer space
293 ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y);
294 ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y);
295 if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y)
296 continue;
297
298 // Apply scissor/clipping rectangle
299 const D3D10_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y };
300 device->RSSetScissorRects(1, &r);
301
302 // Bind texture, Draw
303 ID3D10ShaderResourceView* texture_srv = (ID3D10ShaderResourceView*)pcmd->GetTexID();
304 device->PSSetShaderResources(0, 1, &texture_srv);
305 device->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset);
306 }
307 }
308 global_idx_offset += draw_list->IdxBuffer.Size;
309 global_vtx_offset += draw_list->VtxBuffer.Size;
310 }
311 platform_io.Renderer_RenderState = nullptr;
312
313 // Restore modified DX state
314 device->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects);
315 device->RSSetViewports(old.ViewportsCount, old.Viewports);
316 device->RSSetState(old.RS); if (old.RS) old.RS->Release();
317 device->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release();
318 device->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release();
319 device->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release();
320 device->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release();
321 device->PSSetShader(old.PS); if (old.PS) old.PS->Release();
322 device->VSSetShader(old.VS); if (old.VS) old.VS->Release();
323 device->GSSetShader(old.GS); if (old.GS) old.GS->Release();
324 device->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release();
325 device->IASetPrimitiveTopology(old.PrimitiveTopology);
326 device->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release();
327 device->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release();
328 device->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release();
329}
330
331static void ImGui_ImplDX10_DestroyTexture(ImTextureData* tex)
332{
333 ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData;
334 if (backend_tex == nullptr)
335 return;
336 IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID);
337 backend_tex->pTexture->Release();
338 backend_tex->pTextureView->Release();
339 IM_DELETE(p: backend_tex);
340
341 // Clear identifiers and mark as destroyed (in order to allow e.g. calling InvalidateDeviceObjects while running)
342 tex->SetTexID(ImTextureID_Invalid);
343 tex->SetStatus(ImTextureStatus_Destroyed);
344 tex->BackendUserData = nullptr;
345}
346
347void ImGui_ImplDX10_UpdateTexture(ImTextureData* tex)
348{
349 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
350 if (tex->Status == ImTextureStatus_WantCreate)
351 {
352 // Create and upload new texture to graphics system
353 //IMGUI_DEBUG_LOG("UpdateTexture #%03d: WantCreate %dx%d\n", tex->UniqueID, tex->Width, tex->Height);
354 IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr);
355 IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
356 unsigned int* pixels = (unsigned int*)tex->GetPixels();
357 ImGui_ImplDX10_Texture* backend_tex = IM_NEW(ImGui_ImplDX10_Texture)();
358
359 // Create texture
360 D3D10_TEXTURE2D_DESC desc;
361 ZeroMemory(&desc, sizeof(desc));
362 desc.Width = (UINT)tex->Width;
363 desc.Height = (UINT)tex->Height;
364 desc.MipLevels = 1;
365 desc.ArraySize = 1;
366 desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
367 desc.SampleDesc.Count = 1;
368 desc.Usage = D3D10_USAGE_DEFAULT;
369 desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
370 desc.CPUAccessFlags = 0;
371
372 D3D10_SUBRESOURCE_DATA subResource;
373 subResource.pSysMem = pixels;
374 subResource.SysMemPitch = desc.Width * 4;
375 subResource.SysMemSlicePitch = 0;
376 bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &backend_tex->pTexture);
377 IM_ASSERT(backend_tex->pTexture != nullptr && "Backend failed to create texture!");
378
379 // Create texture view
380 D3D10_SHADER_RESOURCE_VIEW_DESC srv_desc;
381 ZeroMemory(&srv_desc, sizeof(srv_desc));
382 srv_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
383 srv_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
384 srv_desc.Texture2D.MipLevels = desc.MipLevels;
385 srv_desc.Texture2D.MostDetailedMip = 0;
386 bd->pd3dDevice->CreateShaderResourceView(backend_tex->pTexture, &srv_desc, &backend_tex->pTextureView);
387 IM_ASSERT(backend_tex->pTextureView != nullptr && "Backend failed to create texture!");
388
389 // Store identifiers
390 tex->SetTexID((ImTextureID)(intptr_t)backend_tex->pTextureView);
391 tex->SetStatus(ImTextureStatus_OK);
392 tex->BackendUserData = backend_tex;
393 }
394 else if (tex->Status == ImTextureStatus_WantUpdates)
395 {
396 // Update selected blocks. We only ever write to textures regions which have never been used before!
397 // This backend choose to use tex->Updates[] but you can use tex->UpdateRect to upload a single region.
398 ImGui_ImplDX10_Texture* backend_tex = (ImGui_ImplDX10_Texture*)tex->BackendUserData;
399 IM_ASSERT(backend_tex->pTextureView == (ID3D10ShaderResourceView*)(intptr_t)tex->TexID);
400 for (ImTextureRect& r : tex->Updates)
401 {
402 D3D10_BOX box = { (UINT)r.x, (UINT)r.y, (UINT)0, (UINT)(r.x + r.w), (UINT)(r.y + r.h), (UINT)1 };
403 bd->pd3dDevice->UpdateSubresource(backend_tex->pTexture, 0, &box, tex->GetPixelsAt(r.x, r.y), (UINT)tex->GetPitch(), 0);
404 }
405 tex->SetStatus(ImTextureStatus_OK);
406 }
407 if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0)
408 ImGui_ImplDX10_DestroyTexture(tex);
409}
410
411bool ImGui_ImplDX10_CreateDeviceObjects()
412{
413 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
414 if (!bd->pd3dDevice)
415 return false;
416 ImGui_ImplDX10_InvalidateDeviceObjects();
417
418 // By using D3DCompile() from <d3dcompiler.h> / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A)
419 // If you would like to use this DX10 sample code but remove this dependency you can:
420 // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution]
421 // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL.
422 // See https://github.com/ocornut/imgui/pull/638 for sources and details.
423
424 // Create the vertex shader
425 {
426 static const char* vertexShader =
427 "cbuffer vertexBuffer : register(b0) \
428 {\
429 float4x4 ProjectionMatrix; \
430 };\
431 struct VS_INPUT\
432 {\
433 float2 pos : POSITION;\
434 float4 col : COLOR0;\
435 float2 uv : TEXCOORD0;\
436 };\
437 \
438 struct PS_INPUT\
439 {\
440 float4 pos : SV_POSITION;\
441 float4 col : COLOR0;\
442 float2 uv : TEXCOORD0;\
443 };\
444 \
445 PS_INPUT main(VS_INPUT input)\
446 {\
447 PS_INPUT output;\
448 output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\
449 output.col = input.col;\
450 output.uv = input.uv;\
451 return output;\
452 }";
453
454 ID3DBlob* vertexShaderBlob;
455 if (FAILED(D3DCompile(vertexShader, strlen(s: vertexShader), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vertexShaderBlob, nullptr)))
456 return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
457 if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pVertexShader) != S_OK)
458 {
459 vertexShaderBlob->Release();
460 return false;
461 }
462
463 // Create the input layout
464 D3D10_INPUT_ELEMENT_DESC local_layout[] =
465 {
466 { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, pos), D3D10_INPUT_PER_VERTEX_DATA, 0 },
467 { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)offsetof(ImDrawVert, uv), D3D10_INPUT_PER_VERTEX_DATA, 0 },
468 { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)offsetof(ImDrawVert, col), D3D10_INPUT_PER_VERTEX_DATA, 0 },
469 };
470 if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK)
471 {
472 vertexShaderBlob->Release();
473 return false;
474 }
475 vertexShaderBlob->Release();
476
477 // Create the constant buffer
478 {
479 D3D10_BUFFER_DESC desc = {};
480 desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER_DX10);
481 desc.Usage = D3D10_USAGE_DYNAMIC;
482 desc.BindFlags = D3D10_BIND_CONSTANT_BUFFER;
483 desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
484 desc.MiscFlags = 0;
485 bd->pd3dDevice->CreateBuffer(&desc, nullptr, &bd->pVertexConstantBuffer);
486 }
487 }
488
489 // Create the pixel shader
490 {
491 static const char* pixelShader =
492 "struct PS_INPUT\
493 {\
494 float4 pos : SV_POSITION;\
495 float4 col : COLOR0;\
496 float2 uv : TEXCOORD0;\
497 };\
498 sampler sampler0;\
499 Texture2D texture0;\
500 \
501 float4 main(PS_INPUT input) : SV_Target\
502 {\
503 float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \
504 return out_col; \
505 }";
506
507 ID3DBlob* pixelShaderBlob;
508 if (FAILED(D3DCompile(pixelShader, strlen(s: pixelShader), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &pixelShaderBlob, nullptr)))
509 return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob!
510 if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), &bd->pPixelShader) != S_OK)
511 {
512 pixelShaderBlob->Release();
513 return false;
514 }
515 pixelShaderBlob->Release();
516 }
517
518 // Create the blending setup
519 {
520 D3D10_BLEND_DESC desc;
521 ZeroMemory(&desc, sizeof(desc));
522 desc.AlphaToCoverageEnable = false;
523 desc.BlendEnable[0] = true;
524 desc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
525 desc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
526 desc.BlendOp = D3D10_BLEND_OP_ADD;
527 desc.SrcBlendAlpha = D3D10_BLEND_ONE;
528 desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
529 desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
530 desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
531 bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState);
532 }
533
534 // Create the rasterizer state
535 {
536 D3D10_RASTERIZER_DESC desc;
537 ZeroMemory(&desc, sizeof(desc));
538 desc.FillMode = D3D10_FILL_SOLID;
539 desc.CullMode = D3D10_CULL_NONE;
540 desc.ScissorEnable = true;
541 desc.DepthClipEnable = true;
542 bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState);
543 }
544
545 // Create depth-stencil State
546 {
547 D3D10_DEPTH_STENCIL_DESC desc;
548 ZeroMemory(&desc, sizeof(desc));
549 desc.DepthEnable = false;
550 desc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL;
551 desc.DepthFunc = D3D10_COMPARISON_ALWAYS;
552 desc.StencilEnable = false;
553 desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP;
554 desc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;
555 desc.BackFace = desc.FrontFace;
556 bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState);
557 }
558
559 // Create texture sampler
560 // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
561 {
562 D3D10_SAMPLER_DESC desc;
563 ZeroMemory(&desc, sizeof(desc));
564 desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
565 desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
566 desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
567 desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
568 desc.MipLODBias = 0.f;
569 desc.ComparisonFunc = D3D10_COMPARISON_ALWAYS;
570 desc.MinLOD = 0.f;
571 desc.MaxLOD = 0.f;
572 bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler);
573 }
574
575 return true;
576}
577
578void ImGui_ImplDX10_InvalidateDeviceObjects()
579{
580 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
581 if (!bd->pd3dDevice)
582 return;
583
584 // Destroy all textures
585 for (ImTextureData* tex : ImGui::GetPlatformIO().Textures)
586 if (tex->RefCount == 1)
587 ImGui_ImplDX10_DestroyTexture(tex);
588 if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = nullptr; }
589 if (bd->pIB) { bd->pIB->Release(); bd->pIB = nullptr; }
590 if (bd->pVB) { bd->pVB->Release(); bd->pVB = nullptr; }
591 if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = nullptr; }
592 if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = nullptr; }
593 if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = nullptr; }
594 if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = nullptr; }
595 if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = nullptr; }
596 if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = nullptr; }
597 if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = nullptr; }
598}
599
600bool ImGui_ImplDX10_Init(ID3D10Device* device)
601{
602 ImGuiIO& io = ImGui::GetIO();
603 IMGUI_CHECKVERSION();
604 IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!");
605
606 // Setup backend capabilities flags
607 ImGui_ImplDX10_Data* bd = IM_NEW(ImGui_ImplDX10_Data)();
608 io.BackendRendererUserData = (void*)bd;
609 io.BackendRendererName = "imgui_impl_dx10";
610 io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
611 io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; // We can honor ImGuiPlatformIO::Textures[] requests during render.
612 io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports; // We can create multi-viewports on the Renderer side (optional)
613
614 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
615 platform_io.Renderer_TextureMaxWidth = platform_io.Renderer_TextureMaxHeight = D3D10_REQ_TEXTURE2D_U_OR_V_DIMENSION;
616
617 // Get factory from device
618 IDXGIDevice* pDXGIDevice = nullptr;
619 IDXGIAdapter* pDXGIAdapter = nullptr;
620 IDXGIFactory* pFactory = nullptr;
621 if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK)
622 if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK)
623 if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK)
624 {
625 bd->pd3dDevice = device;
626 bd->pFactory = pFactory;
627 }
628 if (pDXGIDevice) pDXGIDevice->Release();
629 if (pDXGIAdapter) pDXGIAdapter->Release();
630 bd->pd3dDevice->AddRef();
631
632 ImGui_ImplDX10_InitMultiViewportSupport();
633
634 return true;
635}
636
637void ImGui_ImplDX10_Shutdown()
638{
639 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
640 IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?");
641 ImGuiIO& io = ImGui::GetIO();
642
643 ImGui_ImplDX10_ShutdownMultiViewportSupport();
644 ImGui_ImplDX10_InvalidateDeviceObjects();
645 if (bd->pFactory) { bd->pFactory->Release(); }
646 if (bd->pd3dDevice) { bd->pd3dDevice->Release(); }
647 io.BackendRendererName = nullptr;
648 io.BackendRendererUserData = nullptr;
649 io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures | ImGuiBackendFlags_RendererHasViewports);
650 IM_DELETE(p: bd);
651}
652
653void ImGui_ImplDX10_NewFrame()
654{
655 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
656 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplDX10_Init()?");
657
658 if (!bd->pVertexShader)
659 if (!ImGui_ImplDX10_CreateDeviceObjects())
660 IM_ASSERT(0 && "ImGui_ImplDX10_CreateDeviceObjects() failed!");
661}
662
663//--------------------------------------------------------------------------------------------------------
664// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
665// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
666// 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..
667//--------------------------------------------------------------------------------------------------------
668
669// Helper structure we store in the void* RendererUserData field of each ImGuiViewport to easily retrieve our backend data.
670struct ImGui_ImplDX10_ViewportData
671{
672 IDXGISwapChain* SwapChain;
673 ID3D10RenderTargetView* RTView;
674
675 ImGui_ImplDX10_ViewportData() { SwapChain = nullptr; RTView = nullptr; }
676 ~ImGui_ImplDX10_ViewportData() { IM_ASSERT(SwapChain == nullptr && RTView == nullptr); }
677};
678
679static void ImGui_ImplDX10_CreateWindow(ImGuiViewport* viewport)
680{
681 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
682 ImGui_ImplDX10_ViewportData* vd = IM_NEW(ImGui_ImplDX10_ViewportData)();
683 viewport->RendererUserData = vd;
684
685 // PlatformHandleRaw should always be a HWND, whereas PlatformHandle might be a higher-level handle (e.g. GLFWWindow*, SDL's WindowID).
686 // Some backends will leave PlatformHandleRaw == 0, in which case we assume PlatformHandle will contain the HWND.
687 HWND hwnd = viewport->PlatformHandleRaw ? (HWND)viewport->PlatformHandleRaw : (HWND)viewport->PlatformHandle;
688 IM_ASSERT(hwnd != 0);
689
690 // Create swap chain
691 DXGI_SWAP_CHAIN_DESC sd;
692 ZeroMemory(&sd, sizeof(sd));
693 sd.BufferDesc.Width = (UINT)viewport->Size.x;
694 sd.BufferDesc.Height = (UINT)viewport->Size.y;
695 sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
696 sd.SampleDesc.Count = 1;
697 sd.SampleDesc.Quality = 0;
698 sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
699 sd.BufferCount = 1;
700 sd.OutputWindow = hwnd;
701 sd.Windowed = TRUE;
702 sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
703 sd.Flags = 0;
704
705 IM_ASSERT(vd->SwapChain == nullptr && vd->RTView == nullptr);
706 bd->pFactory->CreateSwapChain(bd->pd3dDevice, &sd, &vd->SwapChain);
707
708 // Create the render target
709 if (vd->SwapChain)
710 {
711 ID3D10Texture2D* pBackBuffer;
712 vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
713 bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView);
714 pBackBuffer->Release();
715 }
716}
717
718static void ImGui_ImplDX10_DestroyWindow(ImGuiViewport* viewport)
719{
720 // The main viewport (owned by the application) will always have RendererUserData == 0 here since we didn't create the data for it.
721 if (ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData)
722 {
723 if (vd->SwapChain)
724 vd->SwapChain->Release();
725 vd->SwapChain = nullptr;
726 if (vd->RTView)
727 vd->RTView->Release();
728 vd->RTView = nullptr;
729 IM_DELETE(p: vd);
730 }
731 viewport->RendererUserData = nullptr;
732}
733
734static void ImGui_ImplDX10_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
735{
736 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
737 ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData;
738 if (vd->RTView)
739 {
740 vd->RTView->Release();
741 vd->RTView = nullptr;
742 }
743 if (vd->SwapChain)
744 {
745 ID3D10Texture2D* pBackBuffer = nullptr;
746 vd->SwapChain->ResizeBuffers(0, (UINT)size.x, (UINT)size.y, DXGI_FORMAT_UNKNOWN, 0);
747 vd->SwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
748 if (pBackBuffer == nullptr) { fprintf(stderr, format: "ImGui_ImplDX10_SetWindowSize() failed creating buffers.\n"); return; }
749 bd->pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &vd->RTView);
750 pBackBuffer->Release();
751 }
752}
753
754static void ImGui_ImplDX10_RenderViewport(ImGuiViewport* viewport, void*)
755{
756 ImGui_ImplDX10_Data* bd = ImGui_ImplDX10_GetBackendData();
757 ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData;
758 ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
759 bd->pd3dDevice->OMSetRenderTargets(1, &vd->RTView, nullptr);
760 if (!(viewport->Flags & ImGuiViewportFlags_NoRendererClear))
761 bd->pd3dDevice->ClearRenderTargetView(vd->RTView, (float*)&clear_color);
762 ImGui_ImplDX10_RenderDrawData(draw_data: viewport->DrawData);
763}
764
765static void ImGui_ImplDX10_SwapBuffers(ImGuiViewport* viewport, void*)
766{
767 ImGui_ImplDX10_ViewportData* vd = (ImGui_ImplDX10_ViewportData*)viewport->RendererUserData;
768 vd->SwapChain->Present(0, 0); // Present without vsync
769}
770
771void ImGui_ImplDX10_InitMultiViewportSupport()
772{
773 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
774 platform_io.Renderer_CreateWindow = ImGui_ImplDX10_CreateWindow;
775 platform_io.Renderer_DestroyWindow = ImGui_ImplDX10_DestroyWindow;
776 platform_io.Renderer_SetWindowSize = ImGui_ImplDX10_SetWindowSize;
777 platform_io.Renderer_RenderWindow = ImGui_ImplDX10_RenderViewport;
778 platform_io.Renderer_SwapBuffers = ImGui_ImplDX10_SwapBuffers;
779}
780
781void ImGui_ImplDX10_ShutdownMultiViewportSupport()
782{
783 ImGui::DestroyPlatformWindows();
784}
785
786//-----------------------------------------------------------------------------
787
788#endif // #ifndef IMGUI_DISABLE
789

source code of imgui/backends/imgui_impl_dx10.cpp