|
@@ -0,0 +1,1036 @@
|
|
|
|
+Unit PasImGui.Renderer.OpenGL3;
|
|
|
|
+
|
|
|
|
+{$IFDEF FPC}
|
|
|
|
+ {$mode Delphi}{$H+}
|
|
|
|
+ {$modeswitch anonymousfunctions}
|
|
|
|
+{$ENDIF}
|
|
|
|
+{$POINTERMATH ON}
|
|
|
|
+// Debugging
|
|
|
|
+{$Define IMGUI_OPENGL_DEBUG}
|
|
|
|
+
|
|
|
|
+// Specific OpenGL ES versions
|
|
|
|
+//{$Define IMGUI_OPENGL_ES2}
|
|
|
|
+//{$Define IMGUI_OPENGL_ES3}
|
|
|
|
+
|
|
|
|
+{$If not Defined(IMGUI_OPENGL_ES2) and not Defined(IMGUI_OPENGL_ES3)}
|
|
|
|
+ {$If (Defined(DARWIN) or Defined(IOS)) or Defined(Android)}
|
|
|
|
+ {$Define IMGUI_OPENGL_ES3}
|
|
|
|
+ {$ElseIf defined(AMIGA)}
|
|
|
|
+ {$Define IMGUI_OPENGL_ES2}
|
|
|
|
+ {$EndIf}
|
|
|
|
+{$EndIf}
|
|
|
|
+//{$Define IMGUI_OPENGL_LOADER_CUSTOM} // If you have your own loader :V
|
|
|
|
+
|
|
|
|
+// We already know we have 3.+ :V
|
|
|
|
+{$Define GL_VERSION_3_1}
|
|
|
|
+{$Define GL_VERSION_3_2}
|
|
|
|
+{$Define GL_VERSION_3_3}
|
|
|
|
+
|
|
|
|
+// Desktop GL 2.0+ has glPolygonMode() which GL ES and WebGL don't have.
|
|
|
|
+{$Define IMGUI_HAS_POLYGON_MODE}
|
|
|
|
+
|
|
|
|
+// Vertex arrays are not supported on ES2/WebGL1 unless Emscripten which uses an extension
|
|
|
|
+{$IfNDef IMGUI_OPENGL_ES2}
|
|
|
|
+ {$Define IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+{$EndIf}
|
|
|
|
+
|
|
|
|
+// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
|
|
|
|
+{$If not Defined(IMGUI_OPENGL_ES2) and not Defined(IMGUI_OPENGL_ES3) and Defined(GL_VERSION_3_2) }
|
|
|
|
+ {$Define IMGUI_OPENGL_MAY_HAVE_VTX_OFFSET}
|
|
|
|
+{$EndIf}
|
|
|
|
+
|
|
|
|
+// Desktop GL use extension detection
|
|
|
|
+{$If not Defined(IMGUI_OPENGL_ES2) and not Defined(IMGUI_OPENGL_ES3)}
|
|
|
|
+ {$Define IMGUI_OPENGL_MAY_HAVE_EXTENSIONS}
|
|
|
|
+{$EndIf}
|
|
|
|
+// Desktop GL 3.3+ and GL ES 3.0+ have glBindSampler()
|
|
|
|
+{$If not Defined(IMGUI_OPENGL_ES2) and not Defined(IMGUI_OPENGL_ES3) or Defined(GL_VERSION_3_2) }
|
|
|
|
+ {$Define IMGUI_OPENGL_MAY_HAVE_BIND_SAMPLER}
|
|
|
|
+{$EndIf}
|
|
|
|
+
|
|
|
|
+{$If not Defined(IMGUI_OPENGL_ES2) and not Defined(IMGUI_OPENGL_ES3) or Defined(GL_VERSION_3_1) }
|
|
|
|
+ {$Define IMGUI_OPENGL_MAY_HAVE_PRIMITIVE_RESTART}
|
|
|
|
+{$EndIf}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+Interface
|
|
|
|
+
|
|
|
|
+Uses
|
|
|
|
+ SysUtils,
|
|
|
|
+ glad_gl,
|
|
|
|
+ PasImGui,
|
|
|
|
+ PasImGui.Apis,
|
|
|
|
+ PasImGui.Enums,
|
|
|
|
+ PasImGui.Types,
|
|
|
|
+ OpenGl3.Loader;
|
|
|
|
+
|
|
|
|
+Type
|
|
|
|
+ // OpenGL Data
|
|
|
|
+ ImGui_ImplOpenGL3_Data = Record
|
|
|
|
+ GlVersion: GLuint;
|
|
|
|
+ // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries (e.g. 320 for GL 3.2)
|
|
|
|
+ GlslVersionString: Array[0..31] Of AnsiChar;
|
|
|
|
+ // Specified by user or detected based on compile time GL settings.
|
|
|
|
+ GlProfileIsES2: Boolean;
|
|
|
|
+ GlProfileIsES3: Boolean;
|
|
|
|
+ GlProfileIsCompat: Boolean;
|
|
|
|
+ GlProfileMask: GLint;
|
|
|
|
+ FontTexture: GLuint;
|
|
|
|
+ ShaderHandle: GLuint;
|
|
|
|
+ AttribLocationTex: GLint; // Uniforms location
|
|
|
|
+ AttribLocationProjMtx: GLint;
|
|
|
|
+ AttribLocationVtxPos: GLuint; // Vertex attributes location
|
|
|
|
+ AttribLocationVtxUV: GLuint;
|
|
|
|
+ AttribLocationVtxColor: GLuint;
|
|
|
|
+ VboHandle: GLuint;
|
|
|
|
+ ElementsHandle: GLuint;
|
|
|
|
+ VertexBufferSize: GLsizeiptr;
|
|
|
|
+ IndexBufferSize: GLsizeiptr;
|
|
|
|
+ HasClipOrigin: Boolean;
|
|
|
|
+ UseBufferSubData: Boolean;
|
|
|
|
+ End;
|
|
|
|
+ PImGui_ImplOpenGL3_Data = ^ImGui_ImplOpenGL3_Data;
|
|
|
|
+
|
|
|
|
+procedure ImGui_OpenGL3_RenderDrawData(draw_data: PImDrawData);
|
|
|
|
+Function ImGui_OpenGL3_Init(glsl_version: PAnsiChar): Boolean;
|
|
|
|
+Procedure ImGui_OpenGL3_NewFrame();
|
|
|
|
+Procedure ImGui_OpenGL3_Shutdown();
|
|
|
|
+
|
|
|
|
+type
|
|
|
|
+ TGLProc = reference to procedure;
|
|
|
|
+ TError = reference to procedure(msg : string);
|
|
|
|
+
|
|
|
|
+Implementation
|
|
|
|
+
|
|
|
|
+procedure OnAssert(const Message, Filename: {$IfDef FPC}ShortString{$ELSE}string{$EndIf}; LineNumber: Integer; ErrorAddr: Pointer);
|
|
|
|
+begin
|
|
|
|
+ raise EAssertionFailed.Create(Format('%s (%s, line %d)', [Message, Filename, LineNumber]));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure GL_CALL(ACall : TGLProc; AError: TError);
|
|
|
|
+{$IfDef IMGUI_OPENGL_DEBUG}
|
|
|
|
+ var
|
|
|
|
+ gl_err : GLenum;
|
|
|
|
+{$EndIf}
|
|
|
|
+begin
|
|
|
|
+ ACall();
|
|
|
|
+ {$IfDef IMGUI_OPENGL_DEBUG}
|
|
|
|
+ gl_err := glGetError();
|
|
|
|
+ if (gl_err <> 0) then
|
|
|
|
+ begin
|
|
|
|
+ AError(Format('GL error 0x%x', [gl_err]));
|
|
|
|
+ end;
|
|
|
|
+ {$EndIf}
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+Function ImGui_ImplOpenGL3_GetBackendData(): PImGui_ImplOpenGL3_Data;
|
|
|
|
+Begin
|
|
|
|
+ If ImGui.GetCurrentContext() <> nil Then
|
|
|
|
+ Result := PImGui_ImplOpenGL3_Data(ImGui.GetIO()^.BackendRendererUserData)
|
|
|
|
+ Else
|
|
|
|
+ Result := nil;
|
|
|
|
+End;
|
|
|
|
+
|
|
|
|
+procedure ImGui_ImplOpenGL3_SetupRenderState(draw_data: PImDrawData; fb_width: Integer; fb_height: Integer; vertex_array_object: GLuint);
|
|
|
|
+var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ clip_origin_lower_left: Boolean;
|
|
|
|
+ current_clip_origin: GLenum;
|
|
|
|
+ L, R, T, B, tmp: Single;
|
|
|
|
+ ortho_projection: array[0..3] of array[0..3] of Single;
|
|
|
|
+begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
|
|
|
|
+ glEnable(GL_BLEND);
|
|
|
|
+ glBlendEquation(GL_FUNC_ADD);
|
|
|
|
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
+ glDisable(GL_CULL_FACE);
|
|
|
|
+ glDisable(GL_DEPTH_TEST);
|
|
|
|
+ glDisable(GL_STENCIL_TEST);
|
|
|
|
+ glEnable(GL_SCISSOR_TEST);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_PRIMITIVE_RESTART}
|
|
|
|
+ if (bd^.GlVersion >= 310) then
|
|
|
|
+ glDisable(GL_PRIMITIVE_RESTART);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_HAS_POLYGON_MODE}
|
|
|
|
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
|
|
|
|
+ if GLAD_GL_VERSION_4_5 then
|
|
|
|
+ begin
|
|
|
|
+ clip_origin_lower_left := True;
|
|
|
|
+ if (bd^.HasClipOrigin) then
|
|
|
|
+ begin
|
|
|
|
+ current_clip_origin := GLenum(0);
|
|
|
|
+ glGetIntegerv(GL_CLIP_ORIGIN, @GLint(current_clip_origin));
|
|
|
|
+ if (current_clip_origin = GL_UPPER_LEFT) then
|
|
|
|
+ begin
|
|
|
|
+ clip_origin_lower_left := false;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ // Setup viewport, 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.
|
|
|
|
+
|
|
|
|
+ GL_CALL(procedure begin
|
|
|
|
+ glViewport(0, 0, GLsizei(fb_width), GLsizei(fb_height));
|
|
|
|
+ end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+
|
|
|
|
+ L := draw_data^.DisplayPos.x;
|
|
|
|
+ R := draw_data^.DisplayPos.x + draw_data^.DisplaySize.x;
|
|
|
|
+ T := draw_data^.DisplayPos.y;
|
|
|
|
+ B := draw_data^.DisplayPos.y + draw_data^.DisplaySize.y;
|
|
|
|
+
|
|
|
|
+ if GLAD_GL_VERSION_4_5 then
|
|
|
|
+ begin
|
|
|
|
+ if (not clip_origin_lower_left) then
|
|
|
|
+ begin
|
|
|
|
+ tmp := T;
|
|
|
|
+ T := B;
|
|
|
|
+ B := tmp;
|
|
|
|
+ end; // Swap top and bottom if origin is upper left
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ FillChar(ortho_projection, SizeOf(ortho_projection), 0);
|
|
|
|
+ // Initialize the ortho_projection matrix
|
|
|
|
+ ortho_projection[0][0] := 2.0 / (R - L);
|
|
|
|
+
|
|
|
|
+ ortho_projection[1][1] := 2.0 / (T - B);
|
|
|
|
+ ortho_projection[2][2] := -1.0;
|
|
|
|
+
|
|
|
|
+ ortho_projection[3][0] := (R + L) / (L - R);
|
|
|
|
+ ortho_projection[3][1] := (T + B) / (B - T);
|
|
|
|
+ ortho_projection[3][3] := 1.0;
|
|
|
|
+
|
|
|
|
+ glUseProgram(bd^.ShaderHandle);
|
|
|
|
+ glUniform1i(bd^.AttribLocationTex, 0);
|
|
|
|
+ glUniformMatrix4fv(bd^.AttribLocationProjMtx, 1, Boolean(GL_FALSE), @ortho_projection[0][0]);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_BIND_SAMPLER}
|
|
|
|
+ if (bd^.GlVersion >= 330) or (bd^.GlProfileIsES3) then
|
|
|
|
+ glBindSampler(0, 0);
|
|
|
|
+ // We use combined texture/sampler state. Applications using GL 3.3 and GL ES 3.0 may set that otherwise.
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ glBindVertexArray(vertex_array_object);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ // Bind vertex/index buffers and setup attributes for ImDrawVert
|
|
|
|
+ GL_CALL(procedure begin glBindBuffer(GL_ARRAY_BUFFER, bd^.VboHandle); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ GL_CALL(procedure begin glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd^.ElementsHandle); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ GL_CALL(procedure begin glEnableVertexAttribArray(bd^.AttribLocationVtxPos); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ GL_CALL(procedure begin glEnableVertexAttribArray(bd^.AttribLocationVtxUV); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ GL_CALL(procedure begin glEnableVertexAttribArray(bd^.AttribLocationVtxColor); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+
|
|
|
|
+ glVertexAttribPointer(bd^.AttribLocationVtxPos, 2, GL_FLOAT, Boolean(GL_FALSE), sizeof(ImDrawVert), Pointer(IntPtr(@PImDrawVert(nil)^.pos)));
|
|
|
|
+ glVertexAttribPointer(bd^.AttribLocationVtxUV, 2, GL_FLOAT, Boolean(GL_FALSE), sizeof(ImDrawVert), Pointer(IntPtr(@PImDrawVert(nil)^.uv)));
|
|
|
|
+ glVertexAttribPointer(bd^.AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, Boolean(GL_TRUE), sizeof(ImDrawVert), Pointer(IntPtr(@PImDrawVert(nil)^.col)));
|
|
|
|
+
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// OpenGL3 Render function.
|
|
|
|
+// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly.
|
|
|
|
+// This is in order to be able to run within an OpenGL engine that doesn't do so.
|
|
|
|
+procedure ImGui_OpenGL3_RenderDrawData(draw_data: PImDrawData);
|
|
|
|
+var
|
|
|
|
+ fb_width, fb_height , n, cmd_i: Integer;
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+
|
|
|
|
+ last_program, last_texture,
|
|
|
|
+ last_sampler, last_array_buffer,
|
|
|
|
+ last_vertex_array_object : GLuint;
|
|
|
|
+
|
|
|
|
+ last_polygon_mode : Array [0..1] of GLint;
|
|
|
|
+
|
|
|
|
+ last_viewport : Array [0..3] of GLint;
|
|
|
|
+ last_scissor_box : Array [0..3] of GLint;
|
|
|
|
+
|
|
|
|
+ last_active_texture, last_blend_src_rgb,
|
|
|
|
+ last_blend_dst_rgb, last_blend_src_alpha,
|
|
|
|
+ last_blend_dst_alpha, last_blend_equation_rgb,
|
|
|
|
+ last_blend_equation_alpha : GLenum;
|
|
|
|
+
|
|
|
|
+ last_enable_blend,
|
|
|
|
+ last_enable_cull_face,
|
|
|
|
+ last_enable_depth_test,
|
|
|
|
+ last_enable_stencil_test,
|
|
|
|
+ last_enable_scissor_test,
|
|
|
|
+ last_enable_primitive_restart: GLboolean;
|
|
|
|
+
|
|
|
|
+ vertex_array_object : GLuint;
|
|
|
|
+ clip_off, clip_scale, clip_min, clip_max: ImVec2;
|
|
|
|
+
|
|
|
|
+ cmd_list_ptr : ImDrawList;
|
|
|
|
+ vtx_buffer_size, idx_buffer_size: GLsizeiptr;
|
|
|
|
+ pcmd: ImDrawCmd;
|
|
|
|
+ temp_callback : ImDrawCallback;
|
|
|
|
+Begin
|
|
|
|
+ // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
|
|
|
+ fb_width := Trunc(draw_data^.DisplaySize.x * draw_data^.FramebufferScale.x);
|
|
|
|
+ fb_height := Trunc(draw_data^.DisplaySize.y * draw_data^.FramebufferScale.y);
|
|
|
|
+ if (fb_width <= 0) or (fb_height <= 0) then
|
|
|
|
+ Exit;
|
|
|
|
+
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+
|
|
|
|
+ // Backup GL state
|
|
|
|
+ glGetIntegerv(GL_ACTIVE_TEXTURE, @GLint(last_active_texture));
|
|
|
|
+ glActiveTexture(GL_TEXTURE0);
|
|
|
|
+ glGetIntegerv(GL_CURRENT_PROGRAM, @GLint(last_program));
|
|
|
|
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, @GLint(last_texture));
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_BIND_SAMPLER}
|
|
|
|
+ if (bd^.GlVersion >= 330) or (bd^.GlProfileIsES3) then
|
|
|
|
+ glGetIntegerv(GL_SAMPLER_BINDING, @GLint(last_sampler))
|
|
|
|
+ else
|
|
|
|
+ last_sampler := 0;
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, @GLint(last_array_buffer));
|
|
|
|
+
|
|
|
|
+ {$IfNDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ // This is part of VAO on OpenGL 3.0+ and OpenGL ES 3.0+.
|
|
|
|
+ { TODO: Get Back to this later - Time : 11/17/2023 1:45:47 AM }
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ glGetIntegerv(GL_VERTEX_ARRAY_BINDING, @GLint(last_vertex_array_object));
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_HAS_POLYGON_MODE}
|
|
|
|
+ glGetIntegerv(GL_POLYGON_MODE, @last_polygon_mode);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ glGetIntegerv(GL_VIEWPORT, @last_viewport);
|
|
|
|
+ glGetIntegerv(GL_SCISSOR_BOX, @last_scissor_box);
|
|
|
|
+ glGetIntegerv(GL_BLEND_SRC_RGB, @GLint(last_blend_src_rgb));
|
|
|
|
+ glGetIntegerv(GL_BLEND_DST_RGB, @GLint(last_blend_dst_rgb));
|
|
|
|
+ glGetIntegerv(GL_BLEND_SRC_ALPHA, @GLint(last_blend_src_alpha));
|
|
|
|
+ glGetIntegerv(GL_BLEND_DST_ALPHA, @GLint(last_blend_dst_alpha));
|
|
|
|
+ glGetIntegerv(GL_BLEND_EQUATION_RGB, @GLint(last_blend_equation_rgb));
|
|
|
|
+ glGetIntegerv(GL_BLEND_EQUATION_ALPHA, @GLint(last_blend_equation_alpha));
|
|
|
|
+
|
|
|
|
+ last_enable_blend := glIsEnabled(GL_BLEND);
|
|
|
|
+ last_enable_cull_face := glIsEnabled(GL_CULL_FACE);
|
|
|
|
+ last_enable_depth_test := glIsEnabled(GL_DEPTH_TEST);
|
|
|
|
+ last_enable_stencil_test := glIsEnabled(GL_STENCIL_TEST);
|
|
|
|
+ last_enable_scissor_test := glIsEnabled(GL_SCISSOR_TEST);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_PRIMITIVE_RESTART}
|
|
|
|
+ if (bd^.GlVersion >= 310) or (bd^.GlProfileIsES3) then
|
|
|
|
+ last_enable_primitive_restart := glIsEnabled(GL_PRIMITIVE_RESTART)
|
|
|
|
+ else
|
|
|
|
+ last_enable_primitive_restart := Boolean(GL_FALSE);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ // Setup desired GL state
|
|
|
|
+ // Recreate the VAO every time (this is to easily allow multiple GL contexts
|
|
|
|
+ // to be rendered to. VAO are not shared among GL contexts).
|
|
|
|
+ // The renderer would actually work without any VAO bound, but then our
|
|
|
|
+ // VertexAttrib calls would overwrite the default one currently bound.
|
|
|
|
+ vertex_array_object := 0;
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ GL_CALL(procedure begin glGenVertexArrays(1, @vertex_array_object); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ {$EndIf}
|
|
|
|
+ ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
|
|
|
|
+
|
|
|
|
+ // Will project scissor/clipping rectangles into framebuffer space
|
|
|
|
+ clip_off := draw_data^.DisplayPos; // (0,0) unless using multi-viewports
|
|
|
|
+ clip_scale := draw_data^.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
|
|
|
+
|
|
|
|
+ // Render command lists
|
|
|
|
+ for n := 0 to Pred(draw_data^.CmdListsCount) do
|
|
|
|
+ begin
|
|
|
|
+ //asm
|
|
|
|
+ // int3
|
|
|
|
+ //end;
|
|
|
|
+ cmd_list_ptr := draw_data^.CmdLists.Data[n]^;
|
|
|
|
+
|
|
|
|
+ // Upload vertex/index buffers
|
|
|
|
+ // - OpenGL drivers are in a very sorry state nowadays....
|
|
|
|
+ // During 2021 we attempted to switch from glBufferData() to orphaning+glBufferSubData() following reports
|
|
|
|
+ // of leaks on Intel GPU when using multi-viewports on Windows.
|
|
|
|
+ // - After this we kept hearing of various display corruptions issues. We started disabling on non-Intel GPU, but issues still got reported on Intel.
|
|
|
|
+ // - We are now back to using exclusively glBufferData(). So bd^.UseBufferSubData IS ALWAYS FALSE in this code.
|
|
|
|
+ // We are keeping the old code path for a while in case people finding new issues may want to test the bd^.UseBufferSubData path.
|
|
|
|
+ // - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues.
|
|
|
|
+ vtx_buffer_size := GLsizeiptr(cmd_list_ptr.VtxBuffer.Size * Integer(SizeOf(ImDrawVert)));
|
|
|
|
+ idx_buffer_size := GLsizeiptr(cmd_list_ptr.IdxBuffer.Size * Integer(sizeof(ImDrawIdx)));
|
|
|
|
+ if (bd^.UseBufferSubData) then
|
|
|
|
+ begin
|
|
|
|
+ if (bd^.VertexBufferSize < vtx_buffer_size) then
|
|
|
|
+ begin
|
|
|
|
+ bd^.VertexBufferSize := vtx_buffer_size;
|
|
|
|
+ GL_CALL(procedure begin glBufferData(GL_ARRAY_BUFFER, bd^.VertexBufferSize, nil, GL_STREAM_DRAW); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ end;
|
|
|
|
+ if (bd^.IndexBufferSize < idx_buffer_size) then
|
|
|
|
+ begin
|
|
|
|
+ bd^.IndexBufferSize := idx_buffer_size;
|
|
|
|
+ GL_CALL(procedure begin glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd^.IndexBufferSize, nil, GL_STREAM_DRAW); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ end;
|
|
|
|
+ GL_CALL(procedure begin glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, cmd_list_ptr.VtxBuffer.Data); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ GL_CALL(procedure begin glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, cmd_list_ptr.IdxBuffer.Data); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ GL_CALL(procedure begin glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, cmd_list_ptr.VtxBuffer.Data, GL_STREAM_DRAW); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ GL_CALL(procedure begin glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, cmd_list_ptr.IdxBuffer.Data,GL_STREAM_DRAW); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ for cmd_i := 0 to Pred(cmd_list_ptr.CmdBuffer.Size) do
|
|
|
|
+ begin
|
|
|
|
+ pcmd := cmd_list_ptr.CmdBuffer.Data[cmd_i];
|
|
|
|
+ temp_callback := pcmd.UserCallback;
|
|
|
|
+ if Assigned(temp_callback) then
|
|
|
|
+ begin
|
|
|
|
+ // 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) then
|
|
|
|
+ ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object)
|
|
|
|
+ else
|
|
|
|
+ pcmd.UserCallback(@cmd_list_ptr, @pcmd);
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ // Project scissor/clipping rectangles into framebuffer space
|
|
|
|
+ clip_min := ImVec2.New((pcmd.ClipRect.x - clip_off.x) * clip_scale.x,
|
|
|
|
+ (pcmd.ClipRect.y - clip_off.y) * clip_scale.y);
|
|
|
|
+ clip_max := ImVec2.New((pcmd.ClipRect.z - clip_off.x) * clip_scale.x,
|
|
|
|
+ (pcmd.ClipRect.w - clip_off.y) * clip_scale.y);
|
|
|
|
+ if (clip_max.x <= clip_min.x) or (clip_max.y <= clip_min.y) then
|
|
|
|
+ Continue;
|
|
|
|
+ // Apply scissor/clipping rectangle (Y is inverted in OpenGL)
|
|
|
|
+ GL_CALL(procedure begin glScissor(Trunc(clip_min.x), Trunc(Single(fb_height - clip_max.y)), Trunc(clip_max.x - clip_min.x), Trunc(clip_max.y - clip_min.y)); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ // Bind texture, Draw
|
|
|
|
+ GL_CALL(procedure begin glBindTexture(GL_TEXTURE_2D, {%H-}GLuint(pcmd.GetTexID())); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_VTX_OFFSET}
|
|
|
|
+ if (bd^.GlVersion >= 320) then
|
|
|
|
+ begin
|
|
|
|
+ GL_CALL(
|
|
|
|
+ procedure begin glDrawElementsBaseVertex(GL_TRIANGLES, GLsizei(pcmd.ElemCount), {$IfDef ImDrawIdx_32}GL_UNSIGNED_INT{$ELSE}GL_UNSIGNED_SHORT{$EndIf} ,Pointer(IntPtr(pcmd.IdxOffset * sizeof(ImDrawIdx))), GLint(pcmd.VtxOffset)); end,procedure(msg : string)begin Assert(False, msg); end);
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ {$EndIf}
|
|
|
|
+ GL_CALL(procedure begin glDrawElements(GL_TRIANGLES, GLsizei(pcmd.ElemCount), {$IfDef ImDrawIdx_32}GL_UNSIGNED_INT{$ELSE}GL_UNSIGNED_SHORT{$EndIf},Pointer(IntPtr(pcmd.IdxOffset * sizeof(ImDrawIdx)))); end,procedure(msg : string)begin Assert(False, msg);end);
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ // Destroy the temporary VAO
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ GL_CALL(procedure begin glDeleteVertexArrays(1, @vertex_array_object); end, procedure(msg : string) begin Assert(False, msg); end);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ // Restore modified GL state
|
|
|
|
+ // This "glIsProgram()" check is required because if the program is
|
|
|
|
+ // "pending deletion" at the time of binding backup, it will have been deleted by now and will cause an OpenGL error. See #6220.
|
|
|
|
+ if (last_program = 0) or (glIsProgram(last_program)) then
|
|
|
|
+ glUseProgram(last_program);
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, last_texture);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_BIND_SAMPLER}
|
|
|
|
+ if (bd^.GlVersion >= 330) or (bd^.GlProfileIsES3) then
|
|
|
|
+ glBindSampler(0, last_sampler);
|
|
|
|
+ {$EndIf}
|
|
|
|
+ glActiveTexture(last_active_texture);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ glBindVertexArray(last_vertex_array_object);
|
|
|
|
+ {$EndIf}
|
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
|
|
|
+ {$IfnDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ { TODO: implement this - Time : 11/17/2023 5:18:38 AM }
|
|
|
|
+ {$EndIf}
|
|
|
|
+ glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
|
|
|
|
+ glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
|
|
|
|
+
|
|
|
|
+ if (last_enable_blend)then
|
|
|
|
+ glEnable(GL_BLEND)
|
|
|
|
+ else
|
|
|
|
+ glDisable(GL_BLEND);
|
|
|
|
+
|
|
|
|
+ if (last_enable_cull_face) then
|
|
|
|
+ glEnable(GL_CULL_FACE)
|
|
|
|
+ else
|
|
|
|
+ glDisable(GL_CULL_FACE);
|
|
|
|
+
|
|
|
|
+ if (last_enable_depth_test) then
|
|
|
|
+ glEnable(GL_DEPTH_TEST)
|
|
|
|
+ else
|
|
|
|
+ glDisable(GL_DEPTH_TEST);
|
|
|
|
+
|
|
|
|
+ if (last_enable_stencil_test) then
|
|
|
|
+ glEnable(GL_STENCIL_TEST)
|
|
|
|
+ else
|
|
|
|
+ glDisable(GL_STENCIL_TEST);
|
|
|
|
+
|
|
|
|
+ if (last_enable_scissor_test) then
|
|
|
|
+ glEnable(GL_SCISSOR_TEST)
|
|
|
|
+ else
|
|
|
|
+ glDisable(GL_SCISSOR_TEST);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_PRIMITIVE_RESTART}
|
|
|
|
+ if (bd^.GlVersion >= 310) then
|
|
|
|
+ if (last_enable_primitive_restart) then
|
|
|
|
+ glEnable(GL_PRIMITIVE_RESTART)
|
|
|
|
+ else
|
|
|
|
+ glDisable(GL_PRIMITIVE_RESTART);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_HAS_POLYGON_MODE}
|
|
|
|
+ // Desktop OpenGL 3.0 and OpenGL 3.1 had separate polygon draw modes for front-facing and back-facing faces of polygons
|
|
|
|
+ if (bd^.GlVersion <= 310) or (bd^.GlProfileIsCompat) then
|
|
|
|
+ begin
|
|
|
|
+ glPolygonMode(GL_FRONT, GLenum(last_polygon_mode[0]));
|
|
|
|
+ glPolygonMode(GL_BACK, GLenum(last_polygon_mode[1]));
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ glPolygonMode(GL_FRONT_AND_BACK, GLenum(last_polygon_mode[0]));
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ glViewport(last_viewport[0], last_viewport[1], GLsizei(last_viewport[2]), GLsizei(last_viewport[3]));
|
|
|
|
+ glScissor(last_scissor_box[0], last_scissor_box[1], GLsizei(last_scissor_box[2]), GLsizei(last_scissor_box[3]));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure ImGui_ImplOpenGL3_RenderWindow(viewport: PImGuiViewport; render_arg: Pointer); Cdecl;
|
|
|
|
+var
|
|
|
|
+ clear_color : ImVec4;
|
|
|
|
+begin
|
|
|
|
+ if not (viewport^.Flags and ImGuiViewportFlags_NoRendererClear <> 0) then
|
|
|
|
+ begin
|
|
|
|
+ clear_color := ImVec4.New(0.0, 0.0, 0.0, 1.0);
|
|
|
|
+ glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
|
|
|
|
+ glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
+ end;
|
|
|
|
+ ImGui_OpenGL3_RenderDrawData(viewport^.DrawData);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure ImGui_ImplOpenGL3_InitPlatformInterface;
|
|
|
|
+var
|
|
|
|
+ platform_io: PImGuiPlatformIO;
|
|
|
|
+begin
|
|
|
|
+ platform_io := ImGui.GetPlatformIO();
|
|
|
|
+ platform_io^.Renderer_RenderWindow := @ImGui_ImplOpenGL3_RenderWindow;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function ImGui_OpenGL3_Init(glsl_version: PAnsiChar): Boolean;
|
|
|
|
+Var
|
|
|
|
+ io: PImGuiIO;
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ num_extensions: GLint;
|
|
|
|
+ major, minor, current_texture : GLint;
|
|
|
|
+ gl_version_str, extension: PAnsiChar;
|
|
|
|
+ i: Integer;
|
|
|
|
+Const
|
|
|
|
+ VersionInfo =
|
|
|
|
+ 'GlVersion = %d '#10 + 'GlProfileIsCompat = %d '#10 +
|
|
|
|
+ 'GlProfileMask = 0x%X'#10 + 'GlProfileIsES2 = %d, GlProfileIsES3 = %d'#10 +
|
|
|
|
+ 'GL_VENDOR = "%s"'#10 + 'GL_RENDERER = "%s"';
|
|
|
|
+Begin
|
|
|
|
+ io := ImGui.GetIO();
|
|
|
|
+ Assert(io^.BackendRendererUserData = nil, 'Already initialized a renderer backend!');
|
|
|
|
+
|
|
|
|
+ // Initialize our loader
|
|
|
|
+ {$If not Defined(IMGUI_OPENGL_ES2) and not Defined(IMGUI_OPENGL_ES3) and not Defined(IMGUI_OPENGL_LOADER_CUSTOM) }
|
|
|
|
+ If Not ImGLInit() Then
|
|
|
|
+ Begin
|
|
|
|
+ If IsConsole Then
|
|
|
|
+ WriteLn('Failed to initialize OpenGL loader!');
|
|
|
|
+ Exit(False);
|
|
|
|
+ End;
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ // Setup backend capabilities flags
|
|
|
|
+ bd := AllocMem(SizeOf(ImGui_ImplOpenGL3_Data));
|
|
|
|
+ io^.BackendRendererUserData := bd;
|
|
|
|
+ io^.BackendRendererName := 'Pas_imgui_opengl3';
|
|
|
|
+
|
|
|
|
+ // Query for GL version (e.g. 320 for GL 3.2)
|
|
|
|
+ {$IFDEF IMGUI_OPENGL_ES2}
|
|
|
|
+ // GLES 2
|
|
|
|
+ bd^.GlVersion := 200;
|
|
|
|
+ bd^.GlProfileIsES2 := True;
|
|
|
|
+ {$ELSE}
|
|
|
|
+ // Desktop or GLES 3
|
|
|
|
+ major := 0;
|
|
|
|
+ minor := 0;
|
|
|
|
+
|
|
|
|
+ glGetIntegerv(GL_MAJOR_VERSION, @major);
|
|
|
|
+ glGetIntegerv(GL_MINOR_VERSION, @minor);
|
|
|
|
+ If (major = 0) And (minor = 0) Then
|
|
|
|
+ Begin
|
|
|
|
+ gl_version_str := PAnsiChar(glGetString(GL_VERSION));
|
|
|
|
+ If IsConsole Then
|
|
|
|
+ WriteLn(Format('%s %d.%d', [gl_version_str, major, minor]));
|
|
|
|
+ End;
|
|
|
|
+ bd^.GlVersion := GLuint(major * 100 + minor * 10);
|
|
|
|
+
|
|
|
|
+ If (bd^.GlVersion >= 320) Then
|
|
|
|
+ glGetIntegerv(GL_CONTEXT_PROFILE_MASK, @bd^.GlProfileMask);
|
|
|
|
+
|
|
|
|
+ bd^.GlProfileIsCompat := (bd^.GlProfileMask And
|
|
|
|
+ GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) <> 0;
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_ES3}
|
|
|
|
+ bd^.GlProfileIsES3 := true;
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ bd^.UseBufferSubData := False;
|
|
|
|
+ {$ENDIF}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_DEBUG}
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn(Format(VersionInfo, [bd^.GlVersion, Integer(bd^.GlProfileIsCompat),
|
|
|
|
+ bd^.GlProfileMask, Integer(bd^.GlProfileIsES2), Integer(bd^.GlProfileIsES3),
|
|
|
|
+ PAnsiChar(glGetString(GL_VENDOR)), PAnsiChar(glGetString(GL_RENDERER))])); // [DEBUG]
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_VTX_OFFSET}
|
|
|
|
+ // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
|
|
|
|
+ if (bd^.GlVersion >= 320) then
|
|
|
|
+ io^.BackendFlags := io^.BackendFlags or ImGuiBackendFlags_RendererHasVtxOffset;
|
|
|
|
+ {$EndIf}
|
|
|
|
+ // We can create multi-viewports on the Renderer side (optional)
|
|
|
|
+ io^.BackendFlags := io^.BackendFlags or ImGuiBackendFlags_RendererHasViewports;
|
|
|
|
+
|
|
|
|
+ // Store GLSL version string so we can refer to it later in case we recreate shaders.
|
|
|
|
+ // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure.
|
|
|
|
+ if glsl_version = nil then
|
|
|
|
+ begin
|
|
|
|
+ {$IFDEF IMGUI_OPENGL_ES2}
|
|
|
|
+ glsl_version := '#version 100';
|
|
|
|
+ {$ELSEIF DEFINED(IMGUI_OPENGL_ES3)}
|
|
|
|
+ glsl_version := '#version 300 es';
|
|
|
|
+ {$ELSEIF DEFINED(DARWIN)}
|
|
|
|
+ glsl_version := '#version 150';
|
|
|
|
+ {$ELSE}
|
|
|
|
+ glsl_version := '#version 130';
|
|
|
|
+ {$ENDIF}
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ Assert(strlen(glsl_version) + 2 < Length(bd^.GlslVersionString), 'Ops');
|
|
|
|
+ StrPCopy(bd^.GlslVersionString, glsl_version);
|
|
|
|
+ StrCat(bd^.GlslVersionString, #10); // 1 = 1 as in C++ code :P
|
|
|
|
+
|
|
|
|
+ // Make an arbitrary GL call (we don't actually need the result)
|
|
|
|
+ // IF YOU GET A CRASH HERE: it probably means the OpenGL function loader didn't do its job. Let us know!
|
|
|
|
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, @current_texture);
|
|
|
|
+
|
|
|
|
+ // Detect extensions we support
|
|
|
|
+ bd^.HasClipOrigin := (bd^.GlVersion >= 450);
|
|
|
|
+ {$IfDef IMGUI_OPENGL_MAY_HAVE_EXTENSIONS}
|
|
|
|
+ {$IfDef IMGUI_OPENGL_DEBUG}
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn('EXTENSIONS :');
|
|
|
|
+ {$EndIf}
|
|
|
|
+ glGetIntegerv(GL_NUM_EXTENSIONS, @num_extensions);
|
|
|
|
+ for i := 0 to num_extensions - 1 do
|
|
|
|
+ begin
|
|
|
|
+ extension := PAnsiChar(glGetStringi(GL_EXTENSIONS, i));
|
|
|
|
+ {$IfDef IMGUI_OPENGL_DEBUG}
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn(' [+] ', extension);
|
|
|
|
+ {$EndIf}
|
|
|
|
+ if (extension <> nil) and (StrComp(extension, 'GL_ARB_clip_control') = 0) then
|
|
|
|
+ bd^.HasClipOrigin := True;
|
|
|
|
+ end;
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ If (io^.ConfigFlags And ImGuiConfigFlags_ViewportsEnable) <> 0 Then
|
|
|
|
+ Begin
|
|
|
|
+ ImGui_ImplOpenGL3_InitPlatformInterface();
|
|
|
|
+ End;
|
|
|
|
+
|
|
|
|
+ Result := True;
|
|
|
|
+End;
|
|
|
|
+
|
|
|
|
+function GetGLVersion(GlslVersionString : AnsiString; GLVersion : Integer = 130) : Integer;
|
|
|
|
+var
|
|
|
|
+ Code: Integer;
|
|
|
|
+begin
|
|
|
|
+ Result := GLVersion;
|
|
|
|
+ // Extract the integer following '#version '
|
|
|
|
+ if Pos('#version ', GlslVersionString) = 1 then
|
|
|
|
+ begin
|
|
|
|
+ // Delete the '#version ' part to isolate the number
|
|
|
|
+ Delete(GlslVersionString, 1, Length('#version '));
|
|
|
|
+ // Convert the remaining string to an integer
|
|
|
|
+ Val(GlslVersionString, Result, Code);
|
|
|
|
+ if Code <> 0 then
|
|
|
|
+ Result := GLVersion;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// 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.
|
|
|
|
+function CheckShader(handle: GLuint; desc: PAnsiChar) : Boolean;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ status, log_length: GLint;
|
|
|
|
+ buf : AnsiString;
|
|
|
|
+begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ status := 0; log_length := 0;
|
|
|
|
+ glGetShaderiv(handle, GL_COMPILE_STATUS, @status);
|
|
|
|
+ glGetShaderiv(handle, GL_INFO_LOG_LENGTH, @log_length);
|
|
|
|
+ if boolean(status) = boolean(GL_FALSE) then
|
|
|
|
+ begin
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn(Format('ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s! With GLSL: %s', [desc, bd^.GlslVersionString]));
|
|
|
|
+ end;
|
|
|
|
+ if (log_length > 1) then
|
|
|
|
+ begin
|
|
|
|
+ buf := '';
|
|
|
|
+ SetLength(buf, log_length);
|
|
|
|
+ glGetShaderInfoLog(handle, log_length, nil, @buf);
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn(Format('%s', [buf]));
|
|
|
|
+ buf := '';
|
|
|
|
+ end;
|
|
|
|
+ Result := boolean(status) = boolean(GL_TRUE)
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+// If you get an error please report on GitHub. You may try different GL context version or GLSL version.
|
|
|
|
+function CheckProgram(handle: GLuint; desc: PAnsiChar) : Boolean;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ status, log_length: GLint;
|
|
|
|
+ buf : AnsiString;
|
|
|
|
+begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ glGetProgramiv(handle, GL_LINK_STATUS, @status);
|
|
|
|
+ glGetProgramiv(handle, GL_INFO_LOG_LENGTH, @log_length);
|
|
|
|
+ if boolean(status) = boolean(GL_FALSE) then
|
|
|
|
+ begin
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn(Format('ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! With GLSL %s', [desc, bd^.GlslVersionString]));
|
|
|
|
+ end;
|
|
|
|
+ if (log_length > 1) then
|
|
|
|
+ begin
|
|
|
|
+ buf := '';
|
|
|
|
+ SetLength(buf, log_length);
|
|
|
|
+ glGetProgramInfoLog(handle, log_length, nil, @buf);
|
|
|
|
+ if IsConsole then
|
|
|
|
+ WriteLn(Format('%s', [buf]));
|
|
|
|
+ buf := '';
|
|
|
|
+ end;
|
|
|
|
+ Result := boolean(status) = boolean(GL_TRUE)
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function ImGui_ImplOpenGL3_CreateFontsTexture() : Boolean;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ io: PImGuiIO;
|
|
|
|
+ pixels : PImU8;
|
|
|
|
+ last_texture : GLint;
|
|
|
|
+ width, height : Integer;
|
|
|
|
+begin
|
|
|
|
+ io := ImGui.GetIO();
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+
|
|
|
|
+ // Build texture atlas
|
|
|
|
+ io^.Fonts^.GetTexDataAsRGBA32(@pixels, @width, @height);
|
|
|
|
+ // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small)
|
|
|
|
+ // because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // Upload texture to graphics system
|
|
|
|
+ // (Bilinear sampling is required by default.
|
|
|
|
+ // Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
|
|
|
|
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, @last_texture);
|
|
|
|
+
|
|
|
|
+ glGenTextures(1, @bd^.FontTexture);
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, bd^.FontTexture);
|
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
|
|
+
|
|
|
|
+ // Not on WebGL/ES
|
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
|
|
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
|
|
|
+
|
|
|
|
+ // Store our identifier
|
|
|
|
+ io.Fonts^.SetTexID(ImTextureID(IntPtr(bd^.FontTexture)));
|
|
|
|
+ // Restore state
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, last_texture);
|
|
|
|
+ Result := True;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function ImGui_ImplOpenGL3_CreateDeviceObjects : Boolean;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ vert_handle, frag_handle : GLuint;
|
|
|
|
+ last_texture, last_array_buffer, last_vertex_array : GLint;
|
|
|
|
+ glsl_version : Integer;
|
|
|
|
+ vertex_shader, fragment_shader : PGLchar;
|
|
|
|
+ vertex_shader_with_version: array[0..1] of PAnsiChar;
|
|
|
|
+ fragment_shader_with_version : array[0..1] of PAnsiChar;
|
|
|
|
+const
|
|
|
|
+ vertex_shader_glsl_120: PGLchar =
|
|
|
|
+ 'uniform mat4 ProjMtx;' + #10 +
|
|
|
|
+ 'attribute vec2 Position;' + #10 +
|
|
|
|
+ 'attribute vec2 UV;' + #10 +
|
|
|
|
+ 'attribute vec4 Color;' + #10 +
|
|
|
|
+ 'varying vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'varying vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Frag_UV = UV;' + #10 +
|
|
|
|
+ ' Frag_Color = Color;' + #10 +
|
|
|
|
+ ' gl_Position = ProjMtx * vec4(Position.xy,0,1);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ vertex_shader_glsl_130: PGLchar =
|
|
|
|
+ 'uniform mat4 ProjMtx;' + #10 +
|
|
|
|
+ 'in vec2 Position;' + #10 +
|
|
|
|
+ 'in vec2 UV;' + #10 +
|
|
|
|
+ 'in vec4 Color;' + #10 +
|
|
|
|
+ 'out vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'out vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Frag_UV = UV;' + #10 +
|
|
|
|
+ ' Frag_Color = Color;' + #10 +
|
|
|
|
+ ' gl_Position = ProjMtx * vec4(Position.xy,0,1);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ vertex_shader_glsl_300_es: PGLchar =
|
|
|
|
+ 'precision highp float;' + #10 +
|
|
|
|
+ 'layout (location = 0) in vec2 Position;' + #10 +
|
|
|
|
+ 'layout (location = 1) in vec2 UV;' + #10 +
|
|
|
|
+ 'layout (location = 2) in vec4 Color;' + #10 +
|
|
|
|
+ 'uniform mat4 ProjMtx;' + #10 +
|
|
|
|
+ 'out vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'out vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Frag_UV = UV;' + #10 +
|
|
|
|
+ ' Frag_Color = Color;' + #10 +
|
|
|
|
+ ' gl_Position = ProjMtx * vec4(Position.xy,0,1);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ vertex_shader_glsl_410_core: PGLchar =
|
|
|
|
+ 'layout (location = 0) in vec2 Position;' + #10 +
|
|
|
|
+ 'layout (location = 1) in vec2 UV;' + #10 +
|
|
|
|
+ 'layout (location = 2) in vec4 Color;' + #10 +
|
|
|
|
+ 'uniform mat4 ProjMtx;' + #10 +
|
|
|
|
+ 'out vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'out vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Frag_UV = UV;' + #10 +
|
|
|
|
+ ' Frag_Color = Color;' + #10 +
|
|
|
|
+ ' gl_Position = ProjMtx * vec4(Position.xy,0,1);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ fragment_shader_glsl_120: PGLchar =
|
|
|
|
+ '#ifdef GL_ES' + #10 +
|
|
|
|
+ ' precision mediump float;' + #10 +
|
|
|
|
+ '#endif' + #10 +
|
|
|
|
+ 'uniform sampler2D Texture;' + #10 +
|
|
|
|
+ 'varying vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'varying vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ fragment_shader_glsl_130: PGLchar =
|
|
|
|
+ 'uniform sampler2D Texture;' + #10 +
|
|
|
|
+ 'in vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'in vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'out vec4 Out_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Out_Color = Frag_Color * texture(Texture, Frag_UV.st);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ fragment_shader_glsl_300_es: PGLchar =
|
|
|
|
+ 'precision mediump float;' + #10 +
|
|
|
|
+ 'uniform sampler2D Texture;' + #10 +
|
|
|
|
+ 'in vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'in vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'layout (location = 0) out vec4 Out_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Out_Color = Frag_Color * texture(Texture, Frag_UV.st);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+ fragment_shader_glsl_410_core: PGLchar =
|
|
|
|
+ 'in vec2 Frag_UV;' + #10 +
|
|
|
|
+ 'in vec4 Frag_Color;' + #10 +
|
|
|
|
+ 'uniform sampler2D Texture;' + #10 +
|
|
|
|
+ 'layout (location = 0) out vec4 Out_Color;' + #10 +
|
|
|
|
+ 'void main()' + #10 +
|
|
|
|
+ '{' + #10 +
|
|
|
|
+ ' Out_Color = Frag_Color * texture(Texture, Frag_UV.st);' + #10 +
|
|
|
|
+ '}' + #10;
|
|
|
|
+
|
|
|
|
+Begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ // Backup GL state
|
|
|
|
+ glGetIntegerv(GL_TEXTURE_BINDING_2D, @last_texture);
|
|
|
|
+ glGetIntegerv(GL_ARRAY_BUFFER_BINDING, @last_array_buffer);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ glGetIntegerv(GL_VERTEX_ARRAY_BINDING, @last_vertex_array);
|
|
|
|
+ {$EndIf}
|
|
|
|
+ // Parse GLSL version string
|
|
|
|
+ glsl_version := 130;
|
|
|
|
+ glsl_version := GetGLVersion(AnsiString(bd^.GlslVersionString), glsl_version);
|
|
|
|
+
|
|
|
|
+ // Select shaders matching our GLSL versions
|
|
|
|
+ if glsl_version < 130 then
|
|
|
|
+ begin
|
|
|
|
+ vertex_shader := vertex_shader_glsl_120;
|
|
|
|
+ fragment_shader := fragment_shader_glsl_120;
|
|
|
|
+ end
|
|
|
|
+ else if glsl_version >= 410 then
|
|
|
|
+ begin
|
|
|
|
+ vertex_shader := vertex_shader_glsl_410_core;
|
|
|
|
+ fragment_shader := fragment_shader_glsl_410_core;
|
|
|
|
+ end
|
|
|
|
+ else if glsl_version = 300 then
|
|
|
|
+ begin
|
|
|
|
+ vertex_shader := vertex_shader_glsl_300_es;
|
|
|
|
+ fragment_shader := fragment_shader_glsl_300_es;
|
|
|
|
+ end
|
|
|
|
+ else
|
|
|
|
+ begin
|
|
|
|
+ vertex_shader := vertex_shader_glsl_130;
|
|
|
|
+ fragment_shader := fragment_shader_glsl_130;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ // Create shaders
|
|
|
|
+ vertex_shader_with_version[0] := @bd^.GlslVersionString[0];
|
|
|
|
+ vertex_shader_with_version[1] := vertex_shader;
|
|
|
|
+ vert_handle := glCreateShader(GL_VERTEX_SHADER);
|
|
|
|
+ glShaderSource(vert_handle, 2, @vertex_shader_with_version, nil);
|
|
|
|
+ glCompileShader(vert_handle);
|
|
|
|
+ CheckShader(vert_handle, 'vertex shader');
|
|
|
|
+
|
|
|
|
+ fragment_shader_with_version[0] := @bd^.GlslVersionString[0];
|
|
|
|
+ fragment_shader_with_version[1] := fragment_shader;
|
|
|
|
+ frag_handle := glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
+ glShaderSource(frag_handle, 2, @fragment_shader_with_version, nil);
|
|
|
|
+ glCompileShader(frag_handle);
|
|
|
|
+ CheckShader(frag_handle, 'fragment shader');
|
|
|
|
+
|
|
|
|
+ // Link
|
|
|
|
+ bd^.ShaderHandle := glCreateProgram();
|
|
|
|
+ glAttachShader(bd^.ShaderHandle, vert_handle);
|
|
|
|
+ glAttachShader(bd^.ShaderHandle, frag_handle);
|
|
|
|
+ glLinkProgram(bd^.ShaderHandle);
|
|
|
|
+ CheckProgram(bd^.ShaderHandle, 'shader program');
|
|
|
|
+
|
|
|
|
+ glDetachShader(bd^.ShaderHandle, vert_handle);
|
|
|
|
+ glDetachShader(bd^.ShaderHandle, frag_handle);
|
|
|
|
+ glDeleteShader(vert_handle);
|
|
|
|
+ glDeleteShader(frag_handle);
|
|
|
|
+
|
|
|
|
+ bd^.AttribLocationTex := glGetUniformLocation(bd^.ShaderHandle, 'Texture');
|
|
|
|
+ bd^.AttribLocationProjMtx := glGetUniformLocation(bd^.ShaderHandle, 'ProjMtx');
|
|
|
|
+ bd^.AttribLocationVtxPos := GLuint(glGetAttribLocation(bd^.ShaderHandle, 'Position'));
|
|
|
|
+ bd^.AttribLocationVtxUV := GLuint(glGetAttribLocation(bd^.ShaderHandle, 'UV'));
|
|
|
|
+ bd^.AttribLocationVtxColor := GLuint(glGetAttribLocation(bd^.ShaderHandle, 'Color'));
|
|
|
|
+
|
|
|
|
+ // Create buffers
|
|
|
|
+ glGenBuffers(1, @bd^.VboHandle);
|
|
|
|
+ glGenBuffers(1, @bd^.ElementsHandle);
|
|
|
|
+
|
|
|
|
+ ImGui_ImplOpenGL3_CreateFontsTexture();
|
|
|
|
+ // Restore modified GL state
|
|
|
|
+ glBindTexture(GL_TEXTURE_2D, last_texture);
|
|
|
|
+ glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
|
|
|
|
+
|
|
|
|
+ {$IfDef IMGUI_OPENGL_USE_VERTEX_ARRAY}
|
|
|
|
+ glBindVertexArray(last_vertex_array);
|
|
|
|
+ {$EndIf}
|
|
|
|
+
|
|
|
|
+ Result := True;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure ImGui_OpenGL3_NewFrame;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+Begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ Assert(bd <> nil, 'Did you call ImGui_ImplOpenGL3_Init()?');
|
|
|
|
+
|
|
|
|
+ if (bd^.ShaderHandle = 0) then
|
|
|
|
+ ImGui_ImplOpenGL3_CreateDeviceObjects();
|
|
|
|
+End;
|
|
|
|
+
|
|
|
|
+procedure ImGui_ImplOpenGL3_ShutdownPlatformInterface();
|
|
|
|
+begin
|
|
|
|
+ ImGui.DestroyPlatformWindows();
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure ImGui_ImplOpenGL3_DestroyFontsTexture;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ io: PImGuiIO;
|
|
|
|
+Begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ io := imgui.GetIO();
|
|
|
|
+ if (bd^.FontTexture > 0) then
|
|
|
|
+ begin
|
|
|
|
+ glDeleteTextures(1, @bd^.FontTexture);
|
|
|
|
+ io^.Fonts^.SetTexID(nil);
|
|
|
|
+ bd^.FontTexture := 0;
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure ImGui_ImplOpenGL3_DestroyDeviceObjects;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+Begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ if (bd^.VboHandle > 0) then
|
|
|
|
+ begin
|
|
|
|
+ glDeleteBuffers(1, @bd^.VboHandle);
|
|
|
|
+ bd^.VboHandle := 0;
|
|
|
|
+ end;
|
|
|
|
+ if (bd^.ElementsHandle > 0) then
|
|
|
|
+ begin
|
|
|
|
+ glDeleteBuffers(1, @bd^.ElementsHandle);
|
|
|
|
+ bd^.ElementsHandle := 0;
|
|
|
|
+ end;
|
|
|
|
+ if (bd^.ShaderHandle > 0) then
|
|
|
|
+ begin
|
|
|
|
+ glDeleteProgram(bd^.ShaderHandle);
|
|
|
|
+ bd^.ShaderHandle := 0;
|
|
|
|
+ end;
|
|
|
|
+ ImGui_ImplOpenGL3_DestroyFontsTexture();
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure ImGui_OpenGL3_Shutdown;
|
|
|
|
+Var
|
|
|
|
+ bd: PImGui_ImplOpenGL3_Data;
|
|
|
|
+ io: PImGuiIO;
|
|
|
|
+Begin
|
|
|
|
+ bd := ImGui_ImplOpenGL3_GetBackendData();
|
|
|
|
+ Assert(bd <> nil, 'No renderer backend to shutdown, or already shutdown?');
|
|
|
|
+ io := imgui.GetIO();
|
|
|
|
+
|
|
|
|
+ ImGui_ImplOpenGL3_ShutdownPlatformInterface();
|
|
|
|
+ ImGui_ImplOpenGL3_DestroyDeviceObjects();
|
|
|
|
+
|
|
|
|
+ io^.BackendRendererName := nil;
|
|
|
|
+ io^.BackendRendererUserData := nil;
|
|
|
|
+
|
|
|
|
+ io^.BackendFlags := io^.BackendFlags and not(ImGuiBackendFlags_RendererHasVtxOffset or ImGuiBackendFlags_RendererHasViewports);
|
|
|
|
+ // Free Allocated data
|
|
|
|
+ Freemem(bd);
|
|
|
|
+End;
|
|
|
|
+
|
|
|
|
+{$IfDef IMGUI_OPENGL_DEBUG}
|
|
|
|
+initialization
|
|
|
|
+ AssertErrorProc := @OnAssert;
|
|
|
|
+{$EndIf}
|
|
|
|
+
|
|
|
|
+End.
|