1 // This is essentially a straight port of the ImGui OpenGL3 backend, removing most code that optimized for version for non-3_3.
2 // Certainly willing to revisit adding that code back in the future. It's just slimmed down for the Inochi needs for right now.
3 
4 module bindbc.imgui.ogl;
5 
6 import core.stdc.stdio;
7 
8 import bindbc.imgui.bind.imgui,
9        bindbc.opengl;
10 
11 // OpenGL Data
12 static GLuint       g_GlVersion = 0;                // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
13 
14 version(OSX) static char[32]     g_GlslVersionString = "#version 330";   // Specified by user or detected based on compile time GL settings.
15 else static char[32]     g_GlslVersionString = "#version 130";   // Specified by user or detected based on compile time GL settings.
16 static GLuint       g_FontTexture = 0;
17 static GLuint       g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
18 static GLint        g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0;                                // Uniforms location
19 static GLuint       g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
20 static uint g_VboHandle = 0, g_ElementsHandle = 0;
21 
22 class ImGuiOpenGLBackend {
23 static: 
24     // Functions
25     bool init(const (char)* glsl_version)
26     {
27         // Query for GL version (e.g. 320 for GL 3.2)
28         const GLint major = 4, minor = 2;
29         //glGetIntegerv(GL_MAJOR_VERSION, &major);
30         //glGetIntegerv(GL_MINOR_VERSION, &minor);
31         g_GlVersion = cast(GLuint)(major * 100 + minor * 10);
32 
33         // Setup back-end capabilities flags
34         ImGuiIO* io = igGetIO();
35         //io.BackendRendererName = "imgui_impl_opengl3";
36 
37         if (g_GlVersion >= 320)
38         {
39             io.BackendFlags |= cast(int)ImGuiBackendFlags.RendererHasVtxOffset;  // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
40         }
41         
42         io.BackendFlags |= cast(int)ImGuiBackendFlags.RendererHasViewports;  // We can create multi-viewports on the Renderer side (optional)
43 
44         // Make an arbitrary GL call (we don't actually need the result)
45         // IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
46         // Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
47         GLint current_texture;
48         glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
49 
50         if (io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable)
51             init_platform_interface();
52 
53         return true;
54     }
55 
56     void shutdown()
57     {
58         shutdown_platform_interface();
59         destroy_device_objects();
60     }
61 
62     void new_frame()
63     {
64         if (!g_ShaderHandle)
65             create_device_objects();
66     }
67 
68     static void setup_render_state(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
69     {
70         // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
71         glEnable(GL_BLEND);
72         glBlendEquation(GL_FUNC_ADD);
73         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
74         glDisable(GL_CULL_FACE);
75         glDisable(GL_DEPTH_TEST);
76         glEnable(GL_SCISSOR_TEST);
77 
78         // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
79         bool clip_origin_lower_left = true;
80 
81         // Setup viewport, orthographic projection matrix
82         // 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.
83         glViewport(0, 0, cast(GLsizei)fb_width, cast(GLsizei)fb_height);
84         float L = draw_data.DisplayPos.x;
85         float R = draw_data.DisplayPos.x + draw_data.DisplaySize.x;
86         float T = draw_data.DisplayPos.y;
87         float B = draw_data.DisplayPos.y + draw_data.DisplaySize.y;
88         if (!clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
89         const float[4][4] ortho_projection =
90         [
91             [ 2.0f/(R-L),   0.0f,         0.0f,   0.0f ],
92             [ 0.0f,         2.0f/(T-B),   0.0f,   0.0f ],
93             [ 0.0f,         0.0f,        -1.0f,   0.0f ],
94             [ (R+L)/(L-R),  (T+B)/(B-T),  0.0f,   1.0f ],
95         ];
96         glUseProgram(g_ShaderHandle);
97         glUniform1i(g_AttribLocationTex, 0);
98         glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
99         
100         if (g_GlVersion >= 330)
101             glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
102         
103         glBindVertexArray(vertex_array_object);
104 
105         // Bind vertex/index buffers and setup attributes for ImDrawVert
106         glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
107         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
108         glEnableVertexAttribArray(g_AttribLocationVtxPos);
109         glEnableVertexAttribArray(g_AttribLocationVtxUV);
110         glEnableVertexAttribArray(g_AttribLocationVtxColor);
111         glVertexAttribPointer(g_AttribLocationVtxPos,   2, GL_FLOAT,         GL_FALSE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.pos.offsetof);
112         glVertexAttribPointer(g_AttribLocationVtxUV,    2, GL_FLOAT,         GL_FALSE, ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.uv.offsetof);
113         glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE,  ImDrawVert.sizeof, cast(GLvoid*)ImDrawVert.col.offsetof);
114     }
115 
116     // OpenGL3 Render function.
117     // (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
118     // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
119     void render_draw_data(ImDrawData* draw_data)
120     {
121         // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
122         int fb_width = cast(int)(draw_data.DisplaySize.x * draw_data.FramebufferScale.x);
123         int fb_height = cast(int)(draw_data.DisplaySize.y * draw_data.FramebufferScale.y);
124         if (fb_width <= 0 || fb_height <= 0)
125             return;
126 
127         // Backup GL state
128         GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, cast(GLint*)&last_active_texture);
129         glActiveTexture(GL_TEXTURE0);
130         GLuint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, cast(GLint*)&last_program);
131         GLuint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, cast(GLint*)&last_texture);
132         GLuint last_sampler; if (g_GlVersion >= 330) { glGetIntegerv(GL_SAMPLER_BINDING, cast(GLint*)&last_sampler); } else { last_sampler = 0; }
133         GLuint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, cast(GLint*)&last_array_buffer);
134         GLuint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, cast(GLint*)&last_vertex_array_object);
135         GLint[4] last_viewport; glGetIntegerv(GL_VIEWPORT, last_viewport.ptr);
136         GLint[4] last_scissor_box; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box.ptr);
137         GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, cast(GLint*)&last_blend_src_rgb);
138         GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, cast(GLint*)&last_blend_dst_rgb);
139         GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, cast(GLint*)&last_blend_src_alpha);
140         GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, cast(GLint*)&last_blend_dst_alpha);
141         GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, cast(GLint*)&last_blend_equation_rgb);
142         GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, cast(GLint*)&last_blend_equation_alpha);
143         const GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
144         const GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
145         const GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
146         const GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
147 
148         // Setup desired GL state
149         // Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
150         // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
151         GLuint vertex_array_object = 0;
152         glGenVertexArrays(1, &vertex_array_object);
153         setup_render_state(draw_data, fb_width, fb_height, vertex_array_object);
154 
155         // Will project scissor/clipping rectangles into framebuffer space
156         ImVec2 clip_off = draw_data.DisplayPos;         // (0,0) unless using multi-viewports
157         ImVec2 clip_scale = draw_data.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
158 
159         // Render command lists
160         for (int n = 0; n < draw_data.CmdListsCount; n++)
161         {
162             const ImDrawList* cmd_list = draw_data.CmdLists[n];
163 
164             // Upload vertex/index buffers
165             glBufferData(GL_ARRAY_BUFFER, cast(GLsizeiptr)cmd_list.VtxBuffer.Size * cast(int)(ImDrawVert.sizeof), cast(const GLvoid*)cmd_list.VtxBuffer.Data, GL_STREAM_DRAW);
166             glBufferData(GL_ELEMENT_ARRAY_BUFFER, cast(GLsizeiptr)cmd_list.IdxBuffer.Size * cast(int)(ImDrawIdx.sizeof), cast(const GLvoid*)cmd_list.IdxBuffer.Data, GL_STREAM_DRAW);
167 
168             for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++)
169             {
170                 const (ImDrawCmd)* pcmd = &cmd_list.CmdBuffer.Data[cmd_i];
171                 if (pcmd.UserCallback != null)
172                 {
173                     // User callback, registered via ImDrawList::AddCallback()
174                     // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
175                     if (pcmd.UserCallback == cast(ImDrawCallback)(-1))
176                         setup_render_state(draw_data, fb_width, fb_height, vertex_array_object);
177                     else
178                         pcmd.UserCallback(cmd_list, pcmd);
179                 }
180                 else
181                 {
182                     // Project scissor/clipping rectangles into framebuffer space
183                     ImVec4 clip_rect;
184                     clip_rect.x = (pcmd.ClipRect.x - clip_off.x) * clip_scale.x;
185                     clip_rect.y = (pcmd.ClipRect.y - clip_off.y) * clip_scale.y;
186                     clip_rect.z = (pcmd.ClipRect.z - clip_off.x) * clip_scale.x;
187                     clip_rect.w = (pcmd.ClipRect.w - clip_off.y) * clip_scale.y;
188 
189                     if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
190                     {
191                         // Apply scissor/clipping rectangle
192                         glScissor(cast(int)clip_rect.x, cast(int)(fb_height - clip_rect.w), cast(int)(clip_rect.z - clip_rect.x), cast(int)(clip_rect.w - clip_rect.y));
193 
194                         // Bind texture, Draw
195                         glBindTexture(GL_TEXTURE_2D, cast(GLuint)(cast(int*)(pcmd.TextureId)));
196                         if (g_GlVersion >= 320)
197                             glDrawElementsBaseVertex(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, (ImDrawIdx.sizeof) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, cast(void*)cast(int*)(pcmd.IdxOffset * (ImDrawIdx.sizeof)), cast(GLint)pcmd.VtxOffset);
198                         else
199                             glDrawElements(GL_TRIANGLES, cast(GLsizei)pcmd.ElemCount, (ImDrawIdx.sizeof) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, cast(void*)cast(int*)(pcmd.IdxOffset * (ImDrawIdx.sizeof)));
200                     }
201                 }
202             }
203         }
204 
205         // Destroy the temporary VAO
206         glDeleteVertexArrays(1, &vertex_array_object);
207 
208         // Restore modified GL state
209         glUseProgram(last_program);
210         glBindTexture(GL_TEXTURE_2D, last_texture);
211         if (g_GlVersion >= 330)
212             glBindSampler(0, last_sampler);
213         glActiveTexture(last_active_texture);
214         glBindVertexArray(last_vertex_array_object);
215         glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
216         glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
217         glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
218         if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
219         if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
220         if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
221         if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
222         glViewport(last_viewport[0], last_viewport[1], cast(GLsizei)(last_viewport[2]), cast(GLsizei)(last_viewport[3]));
223         glScissor(last_scissor_box[0], last_scissor_box[1], cast(GLsizei)(last_scissor_box[2]), cast(GLsizei)(last_scissor_box[3]));
224     }
225 
226     bool create_fonts_texture()
227     {
228         // Build texture atlas
229         ImGuiIO* io = igGetIO();
230         char* pixels;
231         int width, height;
232 
233         ImFontAtlas_GetTexDataAsRGBA32(io.Fonts, &pixels, &width, &height, null); // 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.
234 
235         // Upload texture to graphics system
236         GLint last_texture;
237         glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
238         glGenTextures(1, &g_FontTexture);
239         glBindTexture(GL_TEXTURE_2D, g_FontTexture);
240         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
241         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
242         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
243 
244         // Store our identifier
245         io.Fonts.TexID = cast(ImTextureID)cast(int*)g_FontTexture;
246 
247         // Restore state
248         glBindTexture(GL_TEXTURE_2D, last_texture);
249 
250         return true;
251     }
252 
253     void destroy_fonts_texture()
254     {
255         if (g_FontTexture)
256         {
257             ImGuiIO* io = igGetIO();
258             glDeleteTextures(1, &g_FontTexture);
259             io.Fonts.TexID = cast(ImTextureID)0;
260             g_FontTexture = 0;
261         }
262     }
263 
264     // If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
265     static bool check_shader(GLuint handle, const (char)* desc)
266     {
267         GLint status = 0, log_length = 0;
268         glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
269         glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
270         if (cast(GLboolean)status == GL_FALSE)
271             fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
272         if (log_length > 1)
273         {
274             char[] buf;
275             buf.length = log_length + 1;
276             glGetShaderInfoLog(handle, log_length, null, cast(GLchar*)buf.ptr);
277             fprintf(stderr, "%s\n", buf.ptr);
278         }
279         return cast(GLboolean)status == GL_TRUE;
280     }
281 
282     // If you get an error please report on GitHub. You may try different GL context version or GLSL version.
283     static bool check_program(GLuint handle, const char* desc)
284     {
285         GLint status = 0, log_length = 0;
286         glGetProgramiv(handle, GL_LINK_STATUS, &status);
287         glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
288         if (cast(GLboolean)status == GL_FALSE)
289             fprintf(stderr, "ERROR: create_device_objects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString.ptr);
290         if (log_length > 1)
291         {
292             char[] buf;
293             buf.length = log_length + 1;
294             glGetProgramInfoLog(handle, log_length, null, cast(GLchar*)buf.ptr);
295             fprintf(stderr, "%s\n", buf.ptr);
296         }
297         return cast(GLboolean)status == GL_TRUE;
298     }
299 
300     bool create_device_objects()
301     {
302         // Backup GL state
303         GLint last_texture, last_array_buffer;
304         glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
305         glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
306         GLint last_vertex_array;
307         glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
308 
309         // Parse GLSL version string
310         int glsl_version = 130;
311         sscanf(g_GlslVersionString.ptr, "#version %d", &glsl_version);
312 
313         const GLchar* vertex_shader_glsl_120 =
314             "uniform mat4 ProjMtx;\n"
315         ~ "attribute vec2 Position;\n"
316         ~ "attribute vec2 UV;\n"
317         ~ "attribute vec4 Color;\n"
318         ~ "varying vec2 Frag_UV;\n"
319         ~ "varying vec4 Frag_Color;\n"
320         ~ "void main()\n"
321         ~ "{\n"
322         ~ "    Frag_UV = UV;\n"
323         ~ "    Frag_Color = Color;\n"
324         ~ "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
325         ~ "}\n";
326 
327         const GLchar* vertex_shader_glsl_130 = `
328         uniform mat4 ProjMtx;
329         in vec2 Position;
330         in vec2 UV;
331         in vec4 Color;
332         out vec2 Frag_UV;
333         out vec4 Frag_Color;
334         void main()
335         {
336             Frag_UV = UV;
337             Frag_Color = Color;
338             gl_Position = ProjMtx * vec4(Position.xy,0,1);
339         }
340         `;
341 
342         const GLchar* vertex_shader_glsl_300_es =
343             "precision mediump float;\n"
344         ~ "layout (location = 0) in vec2 Position;\n"
345         ~ "layout (location = 1) in vec2 UV;\n"
346         ~ "layout (location = 2) in vec4 Color;\n"
347         ~ "uniform mat4 ProjMtx;\n"
348         ~ "out vec2 Frag_UV;\n"
349         ~ "out vec4 Frag_Color;\n"
350         ~ "void main()\n"
351         ~ "{\n"
352         ~ "    Frag_UV = UV;\n"
353         ~ "    Frag_Color = Color;\n"
354         ~ "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
355         ~ "}\n";
356 
357         const GLchar* vertex_shader_glsl_410_core =
358             "layout (location = 0) in vec2 Position;\n"
359         ~ "layout (location = 1) in vec2 UV;\n"
360         ~ "layout (location = 2) in vec4 Color;\n"
361         ~ "uniform mat4 ProjMtx;\n"
362         ~ "out vec2 Frag_UV;\n"
363         ~ "out vec4 Frag_Color;\n"
364         ~ "void main()\n"
365         ~ "{\n"
366         ~ "    Frag_UV = UV;\n"
367         ~ "    Frag_Color = Color;\n"
368         ~ "    gl_Position = ProjMtx * vec4(Position.xy,0,1);\n"
369         ~ "}\n";
370 
371         const GLchar* fragment_shader_glsl_120 =
372             "#ifdef GL_ES\n"
373         ~ "    precision mediump float;\n"
374         ~ "#endif\n"
375         ~ "uniform sampler2D Texture;\n"
376         ~ "varying vec2 Frag_UV;\n"
377         ~ "varying vec4 Frag_Color;\n"
378         ~ "void main()\n"
379         ~ "{\n"
380         ~ "    gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
381         ~ "}\n";
382 
383         const GLchar* fragment_shader_glsl_130 = `
384         uniform sampler2D Texture;
385         in vec2 Frag_UV;
386         in vec4 Frag_Color;
387         out vec4 Out_Color;
388         void main()
389         {
390             Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
391         }
392         `;
393 
394         const GLchar* fragment_shader_glsl_300_es =
395             "precision mediump float;\n"
396         ~ "uniform sampler2D Texture;\n"
397         ~ "in vec2 Frag_UV;\n"
398         ~ "in vec4 Frag_Color;\n"
399         ~ "layout (location = 0) out vec4 Out_Color;\n"
400         ~ "void main()\n"
401         ~ "{\n"
402         ~ "    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
403         ~ "}\n";
404 
405         const GLchar* fragment_shader_glsl_410_core =
406             "in vec2 Frag_UV;\n"
407         ~ "in vec4 Frag_Color;\n"
408         ~ "uniform sampler2D Texture;\n"
409         ~ "layout (location = 0) out vec4 Out_Color;\n"
410         ~ "void main()\n"
411         ~ "{\n"
412         ~ "    Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
413         ~ "}\n";
414 
415         // Select shaders matching our GLSL versions
416         const (GLchar)* vertex_shader = null;
417         const (GLchar)* fragment_shader = null;
418         if (glsl_version < 130)
419         {
420             vertex_shader = vertex_shader_glsl_120;
421             fragment_shader = fragment_shader_glsl_120;
422         }
423         else if (glsl_version >= 410)
424         {
425             vertex_shader = vertex_shader_glsl_410_core;
426             fragment_shader = fragment_shader_glsl_410_core;
427         }
428         else if (glsl_version == 300)
429         {
430             vertex_shader = vertex_shader_glsl_300_es;
431             fragment_shader = fragment_shader_glsl_300_es;
432         }
433         else
434         {
435             vertex_shader = vertex_shader_glsl_130;
436             fragment_shader = fragment_shader_glsl_130;
437         }
438 
439         // Create shaders
440         const (GLchar)*[2] vertex_shader_with_version = [ g_GlslVersionString.ptr, vertex_shader ];
441         g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
442         glShaderSource(g_VertHandle, 2, vertex_shader_with_version.ptr, null);
443         glCompileShader(g_VertHandle);
444         check_shader(g_VertHandle, "vertex shader");
445 
446         const (GLchar)*[2] fragment_shader_with_version = [ g_GlslVersionString.ptr, fragment_shader ];
447         g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
448         glShaderSource(g_FragHandle, 2, fragment_shader_with_version.ptr, null);
449         glCompileShader(g_FragHandle);
450         check_shader(g_FragHandle, "fragment shader");
451 
452         g_ShaderHandle = glCreateProgram();
453         glAttachShader(g_ShaderHandle, g_VertHandle);
454         glAttachShader(g_ShaderHandle, g_FragHandle);
455         glLinkProgram(g_ShaderHandle);
456         check_program(g_ShaderHandle, "shader program");
457 
458         g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture");
459         g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx");
460         g_AttribLocationVtxPos = cast(GLuint)glGetAttribLocation(g_ShaderHandle, "Position");
461         g_AttribLocationVtxUV = cast(GLuint)glGetAttribLocation(g_ShaderHandle, "UV");
462         g_AttribLocationVtxColor = cast(GLuint)glGetAttribLocation(g_ShaderHandle, "Color");
463 
464         // Create buffers
465         glGenBuffers(1, &g_VboHandle);
466         glGenBuffers(1, &g_ElementsHandle);
467 
468         create_fonts_texture();
469 
470         // Restore modified GL state
471         glBindTexture(GL_TEXTURE_2D, last_texture);
472         glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
473         glBindVertexArray(last_vertex_array);
474 
475         return true;
476     }
477 
478     void destroy_device_objects()
479     {
480         if (g_VboHandle)        { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
481         if (g_ElementsHandle)   { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
482         if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
483         if (g_ShaderHandle && g_FragHandle) { glDetachShader(g_ShaderHandle, g_FragHandle); }
484         if (g_VertHandle)       { glDeleteShader(g_VertHandle); g_VertHandle = 0; }
485         if (g_FragHandle)       { glDeleteShader(g_FragHandle); g_FragHandle = 0; }
486         if (g_ShaderHandle)     { glDeleteProgram(g_ShaderHandle); g_ShaderHandle = 0; }
487 
488         destroy_fonts_texture();
489     }
490 
491     //--------------------------------------------------------------------------------------------------------
492     // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
493     // This is an _advanced_ and _optional_ feature, allowing the back-end to create and handle multiple viewports simultaneously.
494     // 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..
495     //--------------------------------------------------------------------------------------------------------
496     extern (C)
497     {
498         static void render_window(ImGuiViewport* viewport, void*)
499         {
500             if (!(viewport.Flags & ImGuiViewportFlags.NoRendererClear))
501             {
502                 ImVec4 clear_color = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
503                 glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
504                 glClear(GL_COLOR_BUFFER_BIT);
505             }
506             render_draw_data(viewport.DrawData);
507         }
508     }
509 
510     static void init_platform_interface()
511     {
512         ImGuiPlatformIO* platform_io = igGetPlatformIO();
513         platform_io.Renderer_RenderWindow = &render_window;
514     }
515 
516     static void shutdown_platform_interface()
517     {
518         igDestroyPlatformWindows();
519     }
520 }