diff --git a/Dear ImGui Sample/ImGuiController.cs b/Dear ImGui Sample/ImGuiController.cs index 36c2188..50f31af 100644 --- a/Dear ImGui Sample/ImGuiController.cs +++ b/Dear ImGui Sample/ImGuiController.cs @@ -7,6 +7,8 @@ using OpenTK.Windowing.Common.Input; using OpenTK.Windowing.Desktop; using OpenTK.Windowing.GraphicsLibraryFramework; +using System.Diagnostics; +using ErrorCode = OpenTK.Graphics.OpenGL4.ErrorCode; namespace Dear_ImGui_Sample { @@ -20,14 +22,21 @@ public class ImGuiController : IDisposable private int _indexBuffer; private int _indexBufferSize; - private Texture _fontTexture; - private Shader _shader; + //private Texture _fontTexture; + + private int _fontTexture; + + private int _shader; + private int _shaderFontTextureLocation; + private int _shaderProjectionMatrixLocation; private int _windowWidth; private int _windowHeight; private System.Numerics.Vector2 _scaleFactor = System.Numerics.Vector2.One; + private static bool KHRDebugAvailable = false; + /// /// Constructs a new ImGuiController. /// @@ -36,6 +45,11 @@ public ImGuiController(int width, int height) _windowWidth = width; _windowHeight = height; + int major = GL.GetInteger(GetPName.MajorVersion); + int minor = GL.GetInteger(GetPName.MinorVersion); + + KHRDebugAvailable = (major == 4 && minor >= 3) || IsExtensionSupported("KHR_debug"); + IntPtr context = ImGui.CreateContext(); ImGui.SetCurrentContext(context); var io = ImGui.GetIO(); @@ -65,15 +79,25 @@ public void DestroyDeviceObjects() public void CreateDeviceResources() { - Util.CreateVertexArray("ImGui", out _vertexArray); - _vertexBufferSize = 10000; _indexBufferSize = 2000; - Util.CreateVertexBuffer("ImGui", out _vertexBuffer); - Util.CreateElementBuffer("ImGui", out _indexBuffer); - GL.NamedBufferData(_vertexBuffer, _vertexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); - GL.NamedBufferData(_indexBuffer, _indexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding); + int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding); + + _vertexArray = GL.GenVertexArray(); + GL.BindVertexArray(_vertexArray); + LabelObject(ObjectLabelIdentifier.VertexArray, _vertexArray, "ImGui"); + + _vertexBuffer = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer); + LabelObject(ObjectLabelIdentifier.Buffer, _vertexBuffer, "VBO: ImGui"); + GL.BufferData(BufferTarget.ArrayBuffer, _vertexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + + _indexBuffer = GL.GenBuffer(); + GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer); + LabelObject(ObjectLabelIdentifier.Buffer, _indexBuffer, "EBO: ImGui"); + GL.BufferData(BufferTarget.ElementArrayBuffer, _indexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); RecreateFontDeviceTexture(); @@ -107,24 +131,24 @@ void main() { outputColor = color * texture(in_fontTexture, texCoord); }"; - _shader = new Shader("ImGui", VertexSource, FragmentSource); - GL.VertexArrayVertexBuffer(_vertexArray, 0, _vertexBuffer, IntPtr.Zero, Unsafe.SizeOf()); - GL.VertexArrayElementBuffer(_vertexArray, _indexBuffer); + _shader = CreateProgram("ImGui", VertexSource, FragmentSource); + _shaderProjectionMatrixLocation = GL.GetUniformLocation(_shader, "projection_matrix"); + _shaderFontTextureLocation = GL.GetUniformLocation(_shader, "in_fontTexture"); - GL.EnableVertexArrayAttrib(_vertexArray, 0); - GL.VertexArrayAttribBinding(_vertexArray, 0, 0); - GL.VertexArrayAttribFormat(_vertexArray, 0, 2, VertexAttribType.Float, false, 0); + int stride = Unsafe.SizeOf(); + GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, stride, 0); + GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, stride, 8); + GL.VertexAttribPointer(2, 4, VertexAttribPointerType.UnsignedByte, true, stride, 16); - GL.EnableVertexArrayAttrib(_vertexArray, 1); - GL.VertexArrayAttribBinding(_vertexArray, 1, 0); - GL.VertexArrayAttribFormat(_vertexArray, 1, 2, VertexAttribType.Float, false, 8); + GL.EnableVertexAttribArray(0); + GL.EnableVertexAttribArray(1); + GL.EnableVertexAttribArray(2); - GL.EnableVertexArrayAttrib(_vertexArray, 2); - GL.VertexArrayAttribBinding(_vertexArray, 2, 0); - GL.VertexArrayAttribFormat(_vertexArray, 2, 4, VertexAttribType.UnsignedByte, true, 16); + GL.BindVertexArray(prevVAO); + GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer); - Util.CheckGLError("End of ImGui setup"); + CheckGLError("End of ImGui setup"); } /// @@ -135,11 +159,34 @@ public void RecreateFontDeviceTexture() ImGuiIOPtr io = ImGui.GetIO(); io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height, out int bytesPerPixel); - _fontTexture = new Texture("ImGui Text Atlas", width, height, pixels); - _fontTexture.SetMagFilter(TextureMagFilter.Linear); - _fontTexture.SetMinFilter(TextureMinFilter.Linear); - - io.Fonts.SetTexID((IntPtr)_fontTexture.GLTexture); + int mips = (int)Math.Floor(Math.Log(Math.Max(width, height), 2)); + + int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(0); + int prevTexture2D = GL.GetInteger(GetPName.Texture2D); + + _fontTexture = GL.GenTexture(); + GL.BindTexture(TextureTarget.Texture2D, _fontTexture); + GL.TexStorage2D(TextureTarget2d.Texture2D, mips, SizedInternalFormat.Rgba8, width, height); + LabelObject(ObjectLabelIdentifier.Texture, _fontTexture, "ImGui Text Atlas"); + + GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, width, height, PixelFormat.Bgra, PixelType.UnsignedByte, pixels); + + GL.GenerateMipmap(GenerateMipmapTarget.Texture2D); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, mips - 1); + + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); + GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); + + // Restore state + GL.BindTexture(TextureTarget.Texture2D, prevTexture2D); + GL.ActiveTexture((TextureUnit)prevActiveTexture); + + io.Fonts.SetTexID((IntPtr)_fontTexture); io.Fonts.ClearTexData(); } @@ -270,6 +317,36 @@ private void RenderImDrawData(ImDrawDataPtr draw_data) return; } + // Get intial state. + int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding); + int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding); + int prevProgram = GL.GetInteger(GetPName.CurrentProgram); + bool prevBlendEnabled = GL.GetBoolean(GetPName.Blend); + bool prevScissorTestEnabled = GL.GetBoolean(GetPName.ScissorTest); + int prevBlendEquationRgb = GL.GetInteger(GetPName.BlendEquationRgb); + int prevBlendEquationAlpha = GL.GetInteger(GetPName.BlendEquationAlpha); + int prevBlendFuncSrcRgb = GL.GetInteger(GetPName.BlendSrcRgb); + int prevBlendFuncSrcAlpha = GL.GetInteger(GetPName.BlendSrcAlpha); + int prevBlendFuncDstRgb = GL.GetInteger(GetPName.BlendDstRgb); + int prevBlendFuncDstAlpha = GL.GetInteger(GetPName.BlendDstAlpha); + bool prevCullFaceEnabled = GL.GetBoolean(GetPName.CullFace); + bool prevDepthTestEnabled = GL.GetBoolean(GetPName.DepthTest); + int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture); + GL.ActiveTexture(TextureUnit.Texture0); + int prevTexture2D = GL.GetInteger(GetPName.Texture2D); + Span prevScissorBox = stackalloc int[4]; + unsafe + { + fixed (int* iptr = &prevScissorBox[0]) + { + GL.GetInteger(GetPName.ScissorBox, iptr); + } + } + + // Bind the element buffer (thru the VAO) so that we can resize it. + GL.BindVertexArray(_vertexArray); + // Bind the vertex buffer so that we can resize it. + GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer); for (int i = 0; i < draw_data.CmdListsCount; i++) { ImDrawListPtr cmd_list = draw_data.CmdListsRange[i]; @@ -278,7 +355,8 @@ private void RenderImDrawData(ImDrawDataPtr draw_data) if (vertexSize > _vertexBufferSize) { int newSize = (int)Math.Max(_vertexBufferSize * 1.5f, vertexSize); - GL.NamedBufferData(_vertexBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + + GL.BufferData(BufferTarget.ArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); _vertexBufferSize = newSize; Console.WriteLine($"Resized dear imgui vertex buffer to new size {_vertexBufferSize}"); @@ -288,7 +366,7 @@ private void RenderImDrawData(ImDrawDataPtr draw_data) if (indexSize > _indexBufferSize) { int newSize = (int)Math.Max(_indexBufferSize * 1.5f, indexSize); - GL.NamedBufferData(_indexBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); + GL.BufferData(BufferTarget.ElementArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw); _indexBufferSize = newSize; Console.WriteLine($"Resized dear imgui index buffer to new size {_indexBufferSize}"); @@ -305,13 +383,13 @@ private void RenderImDrawData(ImDrawDataPtr draw_data) -1.0f, 1.0f); - _shader.UseShader(); - GL.UniformMatrix4(_shader.GetUniformLocation("projection_matrix"), false, ref mvp); - GL.Uniform1(_shader.GetUniformLocation("in_fontTexture"), 0); - Util.CheckGLError("Projection"); + GL.UseProgram(_shader); + GL.UniformMatrix4(_shaderProjectionMatrixLocation, false, ref mvp); + GL.Uniform1(_shaderFontTextureLocation, 0); + CheckGLError("Projection"); GL.BindVertexArray(_vertexArray); - Util.CheckGLError("VAO"); + CheckGLError("VAO"); draw_data.ScaleClipRects(io.DisplayFramebufferScale); @@ -327,11 +405,11 @@ private void RenderImDrawData(ImDrawDataPtr draw_data) { ImDrawListPtr cmd_list = draw_data.CmdListsRange[n]; - GL.NamedBufferSubData(_vertexBuffer, IntPtr.Zero, cmd_list.VtxBuffer.Size * Unsafe.SizeOf(), cmd_list.VtxBuffer.Data); - Util.CheckGLError($"Data Vert {n}"); + GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, cmd_list.VtxBuffer.Size * Unsafe.SizeOf(), cmd_list.VtxBuffer.Data); + CheckGLError($"Data Vert {n}"); - GL.NamedBufferSubData(_indexBuffer, IntPtr.Zero, cmd_list.IdxBuffer.Size * sizeof(ushort), cmd_list.IdxBuffer.Data); - Util.CheckGLError($"Data Idx {n}"); + GL.BufferSubData(BufferTarget.ElementArrayBuffer, IntPtr.Zero, cmd_list.IdxBuffer.Size * sizeof(ushort), cmd_list.IdxBuffer.Data); + CheckGLError($"Data Idx {n}"); for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++) { @@ -344,28 +422,46 @@ private void RenderImDrawData(ImDrawDataPtr draw_data) { GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, (int)pcmd.TextureId); - Util.CheckGLError("Texture"); + CheckGLError("Texture"); // We do _windowHeight - (int)clip.W instead of (int)clip.Y because gl has flipped Y when it comes to these coordinates var clip = pcmd.ClipRect; GL.Scissor((int)clip.X, _windowHeight - (int)clip.W, (int)(clip.Z - clip.X), (int)(clip.W - clip.Y)); - Util.CheckGLError("Scissor"); + CheckGLError("Scissor"); if ((io.BackendFlags & ImGuiBackendFlags.RendererHasVtxOffset) != 0) { - GL.DrawElementsBaseVertex(PrimitiveType.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (IntPtr)(pcmd.IdxOffset * sizeof(ushort)), (int)pcmd.VtxOffset); + GL.DrawElementsBaseVertex(PrimitiveType.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (IntPtr)(pcmd.IdxOffset * sizeof(ushort)), unchecked((int)pcmd.VtxOffset)); } else { GL.DrawElements(BeginMode.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (int)pcmd.IdxOffset * sizeof(ushort)); } - Util.CheckGLError("Draw"); + CheckGLError("Draw"); } } } GL.Disable(EnableCap.Blend); GL.Disable(EnableCap.ScissorTest); + + // Reset state + GL.BindTexture(TextureTarget.Texture2D, prevTexture2D); + GL.ActiveTexture((TextureUnit)prevActiveTexture); + GL.UseProgram(prevProgram); + GL.BindVertexArray(prevVAO); + GL.Scissor(prevScissorBox[0], prevScissorBox[1], prevScissorBox[2], prevScissorBox[3]); + GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer); + GL.BlendEquationSeparate((BlendEquationMode)prevBlendEquationRgb, (BlendEquationMode)prevBlendEquationAlpha); + GL.BlendFuncSeparate( + (BlendingFactorSrc)prevBlendFuncSrcRgb, + (BlendingFactorDest)prevBlendFuncDstRgb, + (BlendingFactorSrc)prevBlendFuncSrcAlpha, + (BlendingFactorDest)prevBlendFuncDstAlpha); + if (prevBlendEnabled) GL.Enable(EnableCap.Blend); else GL.Disable(EnableCap.Blend); + if (prevDepthTestEnabled) GL.Enable(EnableCap.DepthTest); else GL.Disable(EnableCap.DepthTest); + if (prevCullFaceEnabled) GL.Enable(EnableCap.CullFace); else GL.Disable(EnableCap.CullFace); + if (prevScissorTestEnabled) GL.Enable(EnableCap.ScissorTest); else GL.Disable(EnableCap.ScissorTest); } /// @@ -377,8 +473,83 @@ public void Dispose() GL.DeleteBuffer(_vertexBuffer); GL.DeleteBuffer(_indexBuffer); - _fontTexture.Dispose(); - _shader.Dispose(); + GL.DeleteTexture(_fontTexture); + GL.DeleteProgram(_shader); + } + + public static void LabelObject(ObjectLabelIdentifier objLabelIdent, int glObject, string name) + { + if (KHRDebugAvailable) + GL.ObjectLabel(objLabelIdent, glObject, name.Length, name); + } + + static bool IsExtensionSupported(string name) + { + int n = GL.GetInteger(GetPName.NumExtensions); + for (int i = 0; i < n; i++) + { + string extension = GL.GetString(StringNameIndexed.Extensions, i); + if (extension == name) return true; + } + + return false; + } + + public static int CreateProgram(string name, string vertexSource, string fragmentSoruce) + { + int program = GL.CreateProgram(); + LabelObject(ObjectLabelIdentifier.Program, program, $"Program: {name}"); + + int vertex = CompileShader(name, ShaderType.VertexShader, vertexSource); + int fragment = CompileShader(name, ShaderType.FragmentShader, fragmentSoruce); + + GL.AttachShader(program, vertex); + GL.AttachShader(program, fragment); + + GL.LinkProgram(program); + + GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int success); + if (success == 0) + { + string info = GL.GetProgramInfoLog(program); + Debug.WriteLine($"GL.LinkProgram had info log [{name}]:\n{info}"); + } + + GL.DetachShader(program, vertex); + GL.DetachShader(program, fragment); + + GL.DeleteShader(vertex); + GL.DeleteShader(fragment); + + return program; + } + + private static int CompileShader(string name, ShaderType type, string source) + { + int shader = GL.CreateShader(type); + LabelObject(ObjectLabelIdentifier.Shader, shader, $"Shader: {name}"); + + GL.ShaderSource(shader, source); + GL.CompileShader(shader); + + GL.GetShader(shader, ShaderParameter.CompileStatus, out int success); + if (success == 0) + { + string info = GL.GetShaderInfoLog(shader); + Debug.WriteLine($"GL.CompileShader for shader '{name}' [{type}] had info log:\n{info}"); + } + + return shader; + } + + public static void CheckGLError(string title) + { + ErrorCode error; + int i = 1; + while ((error = GL.GetError()) != ErrorCode.NoError) + { + Debug.Print($"{title} ({i++}): {error}"); + } } } } diff --git a/Dear ImGui Sample/Shader.cs b/Dear ImGui Sample/Shader.cs deleted file mode 100644 index b381030..0000000 --- a/Dear ImGui Sample/Shader.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Runtime.CompilerServices; -using System.Threading; -using OpenTK.Graphics.OpenGL4; - -namespace Dear_ImGui_Sample -{ - struct UniformFieldInfo - { - public int Location; - public string Name; - public int Size; - public ActiveUniformType Type; - } - - class Shader - { - public readonly string Name; - public int Program { get; private set; } - private readonly Dictionary UniformToLocation = new Dictionary(); - private bool Initialized = false; - - private readonly (ShaderType Type, string Path)[] Files; - - public Shader(string name, string vertexShader, string fragmentShader) - { - Name = name; - Files = new[]{ - (ShaderType.VertexShader, vertexShader), - (ShaderType.FragmentShader, fragmentShader), - }; - Program = CreateProgram(name, Files); - } - public void UseShader() - { - GL.UseProgram(Program); - } - - public void Dispose() - { - if (Initialized) - { - GL.DeleteProgram(Program); - Initialized = false; - } - } - - public UniformFieldInfo[] GetUniforms() - { - GL.GetProgram(Program, GetProgramParameterName.ActiveUniforms, out int UnifromCount); - - UniformFieldInfo[] Uniforms = new UniformFieldInfo[UnifromCount]; - - for (int i = 0; i < UnifromCount; i++) - { - string Name = GL.GetActiveUniform(Program, i, out int Size, out ActiveUniformType Type); - - UniformFieldInfo FieldInfo; - FieldInfo.Location = GetUniformLocation(Name); - FieldInfo.Name = Name; - FieldInfo.Size = Size; - FieldInfo.Type = Type; - - Uniforms[i] = FieldInfo; - } - - return Uniforms; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetUniformLocation(string uniform) - { - if (UniformToLocation.TryGetValue(uniform, out int location) == false) - { - location = GL.GetUniformLocation(Program, uniform); - UniformToLocation.Add(uniform, location); - - if (location == -1) - { - Debug.Print($"The uniform '{uniform}' does not exist in the shader '{Name}'!"); - } - } - - return location; - } - - private int CreateProgram(string name, params (ShaderType Type, string source)[] shaderPaths) - { - Util.CreateProgram(name, out int Program); - - int[] Shaders = new int[shaderPaths.Length]; - for (int i = 0; i < shaderPaths.Length; i++) - { - Shaders[i] = CompileShader(name, shaderPaths[i].Type, shaderPaths[i].source); - } - - foreach (var shader in Shaders) - GL.AttachShader(Program, shader); - - GL.LinkProgram(Program); - - GL.GetProgram(Program, GetProgramParameterName.LinkStatus, out int Success); - if (Success == 0) - { - string Info = GL.GetProgramInfoLog(Program); - Debug.WriteLine($"GL.LinkProgram had info log [{name}]:\n{Info}"); - } - - foreach (var Shader in Shaders) - { - GL.DetachShader(Program, Shader); - GL.DeleteShader(Shader); - } - - Initialized = true; - - return Program; - } - - private int CompileShader(string name, ShaderType type, string source) - { - Util.CreateShader(type, name, out int Shader); - GL.ShaderSource(Shader, source); - GL.CompileShader(Shader); - - GL.GetShader(Shader, ShaderParameter.CompileStatus, out int success); - if (success == 0) - { - string Info = GL.GetShaderInfoLog(Shader); - Debug.WriteLine($"GL.CompileShader for shader '{Name}' [{type}] had info log:\n{Info}"); - } - - return Shader; - } - } -} diff --git a/Dear ImGui Sample/Texture.cs b/Dear ImGui Sample/Texture.cs deleted file mode 100644 index cebf83a..0000000 --- a/Dear ImGui Sample/Texture.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Drawing; -using System.Drawing.Imaging; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using OpenTK.Graphics.OpenGL4; -using PixelFormat = OpenTK.Graphics.OpenGL4.PixelFormat; - -namespace Dear_ImGui_Sample -{ - public enum TextureCoordinate - { - S = TextureParameterName.TextureWrapS, - T = TextureParameterName.TextureWrapT, - R = TextureParameterName.TextureWrapR - } - - class Texture : IDisposable - { - public const SizedInternalFormat Srgb8Alpha8 = (SizedInternalFormat)All.Srgb8Alpha8; - public const SizedInternalFormat RGB32F = (SizedInternalFormat)All.Rgb32f; - - public const GetPName MAX_TEXTURE_MAX_ANISOTROPY = (GetPName)0x84FF; - - public static readonly float MaxAniso; - - static Texture() - { - MaxAniso = GL.GetFloat(MAX_TEXTURE_MAX_ANISOTROPY); - } - - public readonly string Name; - public readonly int GLTexture; - public readonly int Width, Height; - public readonly int MipmapLevels; - public readonly SizedInternalFormat InternalFormat; - - public Texture(string name, Bitmap image, bool generateMipmaps, bool srgb) - { - Name = name; - Width = image.Width; - Height = image.Height; - InternalFormat = srgb ? Srgb8Alpha8 : SizedInternalFormat.Rgba8; - - if (generateMipmaps) - { - // Calculate how many levels to generate for this texture - MipmapLevels = (int)Math.Floor(Math.Log(Math.Max(Width, Height), 2)); - } - else - { - // There is only one level - MipmapLevels = 1; - } - - Util.CheckGLError("Clear"); - - Util.CreateTexture(TextureTarget.Texture2D, Name, out GLTexture); - GL.TextureStorage2D(GLTexture, MipmapLevels, InternalFormat, Width, Height); - Util.CheckGLError("Storage2d"); - - BitmapData data = image.LockBits(new Rectangle(0, 0, Width, Height), - ImageLockMode.ReadOnly, global::System.Drawing.Imaging.PixelFormat.Format32bppArgb); - - GL.TextureSubImage2D(GLTexture, 0, 0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0); - Util.CheckGLError("SubImage"); - - image.UnlockBits(data); - - if (generateMipmaps) GL.GenerateTextureMipmap(GLTexture); - - GL.TextureParameter(GLTexture, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat); - Util.CheckGLError("WrapS"); - GL.TextureParameter(GLTexture, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat); - Util.CheckGLError("WrapT"); - - GL.TextureParameter(GLTexture, TextureParameterName.TextureMinFilter, (int)(generateMipmaps ? TextureMinFilter.Linear : TextureMinFilter.LinearMipmapLinear)); - GL.TextureParameter(GLTexture, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); - Util.CheckGLError("Filtering"); - - GL.TextureParameter(GLTexture, TextureParameterName.TextureMaxLevel, MipmapLevels - 1); - - // This is a bit weird to do here - image.Dispose(); - } - - public Texture(string name, int GLTex, int width, int height, int mipmaplevels, SizedInternalFormat internalFormat) - { - Name = name; - GLTexture = GLTex; - Width = width; - Height = height; - MipmapLevels = mipmaplevels; - InternalFormat = internalFormat; - } - - public Texture(string name, int width, int height, IntPtr data, bool generateMipmaps = false, bool srgb = false) - { - Name = name; - Width = width; - Height = height; - InternalFormat = srgb ? Srgb8Alpha8 : SizedInternalFormat.Rgba8; - MipmapLevels = generateMipmaps == false ? 1 : (int)Math.Floor(Math.Log(Math.Max(Width, Height), 2)); - - Util.CreateTexture(TextureTarget.Texture2D, Name, out GLTexture); - GL.TextureStorage2D(GLTexture, MipmapLevels, InternalFormat, Width, Height); - - GL.TextureSubImage2D(GLTexture, 0, 0, 0, Width, Height, PixelFormat.Bgra, PixelType.UnsignedByte, data); - - if (generateMipmaps) GL.GenerateTextureMipmap(GLTexture); - - SetWrap(TextureCoordinate.S, TextureWrapMode.Repeat); - SetWrap(TextureCoordinate.T, TextureWrapMode.Repeat); - - GL.TextureParameter(GLTexture, TextureParameterName.TextureMaxLevel, MipmapLevels - 1); - } - - public void SetMinFilter(TextureMinFilter filter) - { - GL.TextureParameter(GLTexture, TextureParameterName.TextureMinFilter, (int)filter); - } - - public void SetMagFilter(TextureMagFilter filter) - { - GL.TextureParameter(GLTexture, TextureParameterName.TextureMagFilter, (int)filter); - } - - public void SetAnisotropy(float level) - { - const TextureParameterName TEXTURE_MAX_ANISOTROPY = (TextureParameterName)0x84FE; - GL.TextureParameter(GLTexture, TEXTURE_MAX_ANISOTROPY, Util.Clamp(level, 1, MaxAniso)); - } - - public void SetLod(int @base, int min, int max) - { - GL.TextureParameter(GLTexture, TextureParameterName.TextureLodBias, @base); - GL.TextureParameter(GLTexture, TextureParameterName.TextureMinLod, min); - GL.TextureParameter(GLTexture, TextureParameterName.TextureMaxLod, max); - } - - public void SetWrap(TextureCoordinate coord, TextureWrapMode mode) - { - GL.TextureParameter(GLTexture, (TextureParameterName)coord, (int)mode); - } - - public void Dispose() - { - GL.DeleteTexture(GLTexture); - } - } -} diff --git a/Dear ImGui Sample/Util.cs b/Dear ImGui Sample/Util.cs deleted file mode 100644 index f70865d..0000000 --- a/Dear ImGui Sample/Util.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; -using OpenTK.Graphics.OpenGL4; - -namespace Dear_ImGui_Sample -{ - static class Util - { - [Pure] - public static float Clamp(float value, float min, float max) - { - return value < min ? min : value > max ? max : value; - } - - [Conditional("DEBUG")] - public static void CheckGLError(string title) - { - var error = GL.GetError(); - if (error != ErrorCode.NoError) - { - Debug.Print($"{title}: {error}"); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void LabelObject(ObjectLabelIdentifier objLabelIdent, int glObject, string name) - { - GL.ObjectLabel(objLabelIdent, glObject, name.Length, name); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateTexture(TextureTarget target, string Name, out int Texture) - { - GL.CreateTextures(target, 1, out Texture); - LabelObject(ObjectLabelIdentifier.Texture, Texture, $"Texture: {Name}"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateProgram(string Name, out int Program) - { - Program = GL.CreateProgram(); - LabelObject(ObjectLabelIdentifier.Program, Program, $"Program: {Name}"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateShader(ShaderType type, string Name, out int Shader) - { - Shader = GL.CreateShader(type); - LabelObject(ObjectLabelIdentifier.Shader, Shader, $"Shader: {type}: {Name}"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateBuffer(string Name, out int Buffer) - { - GL.CreateBuffers(1, out Buffer); - LabelObject(ObjectLabelIdentifier.Buffer, Buffer, $"Buffer: {Name}"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateVertexBuffer(string Name, out int Buffer) => CreateBuffer($"VBO: {Name}", out Buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateElementBuffer(string Name, out int Buffer) => CreateBuffer($"EBO: {Name}", out Buffer); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CreateVertexArray(string Name, out int VAO) - { - GL.CreateVertexArrays(1, out VAO); - LabelObject(ObjectLabelIdentifier.VertexArray, VAO, $"VAO: {Name}"); - } - } -} diff --git a/Dear ImGui Sample/Window.cs b/Dear ImGui Sample/Window.cs index 652cf16..3413d9d 100644 --- a/Dear ImGui Sample/Window.cs +++ b/Dear ImGui Sample/Window.cs @@ -17,7 +17,7 @@ public class Window : GameWindow { ImGuiController _controller; - public Window() : base(GameWindowSettings.Default, new NativeWindowSettings(){ Size = new Vector2i(1600, 900), APIVersion = new Version(4, 5) }) + public Window() : base(GameWindowSettings.Default, new NativeWindowSettings(){ Size = new Vector2i(1600, 900), APIVersion = new Version(3, 3) }) { } protected override void OnLoad() @@ -53,7 +53,7 @@ protected override void OnRenderFrame(FrameEventArgs e) _controller.Render(); - Util.CheckGLError("End of frame"); + ImGuiController.CheckGLError("End of frame"); SwapBuffers(); } diff --git a/README.md b/README.md index 9a9c0ef..be017e0 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,17 @@ # Dear Imgui Sample using OpenTK -## OpenGL 4.5 +## OpenTK 4 -This version of the repository uses a lot of opengl 4.5 features for convenience (not having to bind buffers and stuff). +This version of the repo is targeting OpenTK 4. +The minimum OpenGL version is 3.3. + +## Your OpenGL state will be untouched! + +Previous versions of this repo required users to manually reset the OpenGL state that the ImGui renderer changed. +This is no longer needed! +The renderer resets the OpenGL state itself. -For a opengl 3.3 version of the repo using opentk 3.2 checkout the `opengl3.3` branch. -The `opengl3.3` branch is written for OpenTK 3 so some changes will have to be made. -There is no version of this sample that uses opentk 4 and opengl 3.3. ## BadImageFormatexception @@ -21,18 +25,3 @@ To do that, right click your project (all projects referencing ImGui.NET), click To see more info and a potential fix you can take a look at issue #2. -## Remember to enable/disable OpenGL features after use! - -Note that `ImGuiController.Render()` enables and disables some OpenGL features. The following is a list of state that `ImGuiController.Render()` changes: - -- Disbles blending -- Disables scissor test -- Disables culling -- Disables depth test -- Changes blend function -- Changes scissor rectangle -- Changes the shader program in use -- Sets texture unit `0`'s `Texture2D` to `zero` -- Sets blend equation to `FuncAdd` -- Sets blend func to `SrcAlpha`, `OneMinusSrcAlpha` -- Unbinds any Vertex Array Object \ No newline at end of file