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