ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
59.42k stars 10.12k forks source link

OpenGL: Rendering ImGui prevents other shaders from rendering #6972

Open celinedrules opened 10 months ago

celinedrules commented 10 months ago

I can successfully render OpenGL to my window and I can render ImGui to the window as well. The problem is that when I render anything ImGui related, the non ImGui stuff disappears. For example, I am rendering two shapes on the screen just fine but when I show the ImGui demo window, the shapes do not show anymore.

I think I have narrowed it down to this line in my ImGui controller vertHandle = GL.CreateShader(ShaderType.VertexShader); This is called during ImGui initialization. If I disable initialization/rendering of ImGui the shapes reappear.

Here is the function that is called during initialization

private void CreateDeviceObjects()
{
    GL.GetInteger(GetPName.TextureBinding2D, out var lastTexture);
    GL.GetInteger(GetPName.ArrayBufferBinding, out var lastArrayBuffer);
    GL.GetInteger(GetPName.VertexArrayBinding, out var lastVertexArray);
    const string vs = @"#version 330 core
    layout (location = 0) in vec2 Position;
    layout (location = 1) in vec2 UV;
    layout (location = 2) in vec4 Color;
    uniform mat4 ProjMtx;
    out vec2 Frag_UV;
    out vec4 Frag_Color;
    void main()
    {
        Frag_UV = UV;
        Frag_Color = Color;
        gl_Position = ProjMtx * vec4(Position.xy,0,1);
    }";

    const string fs = @"#version 330 core
    in vec2 Frag_UV;
    in vec4 Frag_Color;
    uniform sampler2D Texture;
    layout (location = 0) out vec4 Out_Color;
    void main()
    {
        Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
    }";

    vertHandle = GL.CreateShader(ShaderType.VertexShader);
    GL.ShaderSource(vertHandle, vs);
    GL.CompileShader(vertHandle);
    CheckShader(vertHandle);

    fragHandle = GL.CreateShader(ShaderType.FragmentShader);
    GL.ShaderSource(fragHandle, fs);
    GL.CompileShader(fragHandle);
    CheckShader(fragHandle);

    shaderHandle = GL.CreateProgram();
    GL.AttachShader(shaderHandle, vertHandle);
    GL.AttachShader(shaderHandle, fragHandle);
    GL.LinkProgram(shaderHandle);
    CheckProgram(shaderHandle);

    attribLocationTex = GL.GetUniformLocation(shaderHandle, "Texture");
    attribLocationProjMtx = GL.GetUniformLocation(shaderHandle, "ProjMtx");
    attribLocationVtxPos = GL.GetAttribLocation(shaderHandle, "Position");
    attribLocationVtxUv = GL.GetAttribLocation(shaderHandle, "UV");
    attribLocationVtxColor = GL.GetAttribLocation(shaderHandle, "Color");

    GL.GenBuffers(1, out vboHandle);
    GL.GenBuffers(1, out elementsHandle);

    CreateFontsTexture();

    GL.BindTexture(TextureTarget.Texture2D, lastTexture);
    GL.BindBuffer(BufferTarget.ArrayBuffer, lastArrayBuffer);
    GL.BindVertexArray(lastVertexArray);
}

Here is my Update

public void Update(Scene currentScene)
{
    if (!io.Fonts.IsBuilt())
        throw new Exception("Font atlas not built !");

    int w = glControl.ClientSize.Width;
    int h = glControl.ClientSize.Height;
    io.DisplaySize = new Vector2(w, h);

    if (w > 0 && h > 0)
        io.DisplayFramebufferScale = new Vector2(1, 1);

    double currentTime = GLFW.GetTime();

    io.DeltaTime = time > 0.0f ? (float)(currentTime - time) : 1.0f / 60.0f;
    time = currentTime;

    // TODO:

    ImGui.NewFrame();
    // TODO:

    ImGui.ShowDemoWindow();
    Render();
}

Here is my Render

private void Render()
{
    ImGui.Render();

    RenderDrawData(ImGui.GetDrawData());
}

Here is RenderDrawData

private void RenderDrawData(ImDrawDataPtr drawData)
{
    if (drawData.CmdListsCount == 0)
        return;

    int fbWidth = (int)(drawData.DisplaySize.X * drawData.FramebufferScale.X);
    int fbHeight = (int)(drawData.DisplaySize.Y * drawData.FramebufferScale.Y);

    if (fbWidth <= 0 || fbHeight <= 0)
        return;

    int[] lastViewport = new int[4];
    int[] lastScissorBox = new int[4];
    GL.GetInteger(GetPName.ActiveTexture, out var lastActiveTexture);
    GL.ActiveTexture(TextureUnit.Texture0);
    GL.GetInteger(GetPName.CurrentProgram, out var lastProgram);
    GL.GetInteger(GetPName.TextureBinding2D, out var lastTexture);
    GL.GetInteger(GetPName.SamplerBinding, out var lastSampler);
    GL.GetInteger(GetPName.VertexArrayBinding, out var lastVertexArrayObject);
    GL.GetInteger(GetPName.ArrayBufferBinding, out var lastArrayBuffer);
    GL.GetInteger(GetPName.PolygonMode, out var lastPolygonMode);
    GL.GetInteger(GetPName.Viewport, lastViewport);
    GL.GetInteger(GetPName.ScissorBox, lastScissorBox);
    GL.GetInteger(GetPName.BlendSrcRgb, out var lastBlendSrcRgb);
    GL.GetInteger(GetPName.BlendDstRgb, out var lastBlendDstRgb);
    GL.GetInteger(GetPName.BlendSrcAlpha, out var lastBlendSrcAlpha);
    GL.GetInteger(GetPName.BlendDstAlpha, out var lastBlendDstAlpha);
    GL.GetInteger(GetPName.BlendEquationRgb, out var lastBlendEquationRgb);
    GL.GetInteger(GetPName.BlendEquationAlpha, out var lastBlendEquationAlpha);

    bool lastEnableBlend = GL.IsEnabled(EnableCap.Blend);
    bool lastEnableCullFace = GL.IsEnabled(EnableCap.CullFace);
    bool lastEnableDepthTest = GL.IsEnabled(EnableCap.DepthTest);
    bool lastEnableStencilTest = GL.IsEnabled(EnableCap.StencilTest);
    bool lastEnableScissorTest = GL.IsEnabled(EnableCap.ScissorTest);
    bool lastEnablePrimitiveRestart = GL.IsEnabled(EnableCap.PrimitiveRestart);

    GL.GenVertexArrays(1, out int vertexArrayObject);
    SetupRenderState(drawData, fbWidth, fbHeight, vertexArrayObject);

    Vector2 clipOff = drawData.DisplayPos;
    Vector2 clipScale = drawData.FramebufferScale;

    for (int n = 0; n < drawData.CmdListsCount; n++)
    {
        ImDrawListPtr cmdList = drawData.CmdListsRange[n];

        GL.BufferData(BufferTarget.ArrayBuffer, (int)(cmdList.VtxBuffer.Size * Unsafe.SizeOf<ImDrawVert>()),
            cmdList.VtxBuffer.Data, BufferUsageHint.StreamDraw);
        GL.BufferData(BufferTarget.ElementArrayBuffer, (int)cmdList.IdxBuffer.Size * sizeof(ushort),
            cmdList.IdxBuffer.Data, BufferUsageHint.StreamDraw);

        for (int cmdI = 0; cmdI < cmdList.CmdBuffer.Size; cmdI++)
        {
            ImDrawCmdPtr pcmd = cmdList.CmdBuffer[cmdI];

            if (pcmd.UserCallback != IntPtr.Zero)
                throw new NotImplementedException();

            Vector4 clipRect = new Vector4
            {
                X = (pcmd.ClipRect.X - clipOff.X) * clipScale.X,
                Y = (pcmd.ClipRect.Y - clipOff.Y) * clipScale.Y,
                Z = (pcmd.ClipRect.Z - clipOff.X) * clipScale.X,
                W = (pcmd.ClipRect.W - clipOff.Y) * clipScale.Y
            };

            if (!(clipRect.X < fbWidth) || !(clipRect.Y < fbHeight) || !(clipRect.Z >= 0.0f) ||
                !(clipRect.W >= 0.0f)) continue;

            GL.Scissor((int)clipRect.X, (int)(fbHeight - clipRect.W), (int)(clipRect.Z - clipRect.X),
                (int)(clipRect.W - clipRect.Y));

            GL.BindTexture(TextureTarget.Texture2D, (int)pcmd.TextureId);
            GL.DrawElementsBaseVertex(PrimitiveType.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort,
                (IntPtr)(pcmd.IdxOffset * sizeof(ushort)), (int)pcmd.VtxOffset);
        }
    }

    GL.DeleteVertexArray(vertexArrayObject);

    GL.UseProgram(lastProgram);
    GL.BindTexture(TextureTarget.Texture2D, lastTexture);
    GL.BindSampler(0, lastSampler);
    GL.ActiveTexture((TextureUnit)lastActiveTexture);
    GL.BindVertexArray(lastVertexArrayObject);
    GL.BindBuffer(BufferTarget.ArrayBuffer, lastArrayBuffer);
    GL.BlendEquationSeparate((BlendEquationMode)lastBlendEquationRgb, (BlendEquationMode)lastBlendEquationAlpha);
    GL.BlendFuncSeparate((BlendingFactorSrc)lastBlendSrcRgb, (BlendingFactorDest)lastBlendDstRgb, (BlendingFactorSrc)lastBlendSrcAlpha,
        (BlendingFactorDest)lastBlendDstAlpha);

    if (lastEnableBlend)
        GL.Enable(EnableCap.Blend);
    else
        GL.Disable(EnableCap.Blend);

    if (lastEnableCullFace)
        GL.Enable(EnableCap.CullFace);
    else
        GL.Disable(EnableCap.CullFace);

    if (lastEnableDepthTest)
        GL.Enable(EnableCap.DepthTest);
    else
        GL.Disable(EnableCap.DepthTest);

    if (lastEnableStencilTest)
        GL.Enable(EnableCap.StencilTest);
    else
        GL.Disable(EnableCap.StencilTest);

    if (lastEnableScissorTest)
        GL.Enable(EnableCap.ScissorTest);
    else
        GL.Disable(EnableCap.ScissorTest);

    if (lastEnablePrimitiveRestart)
        GL.Enable(EnableCap.PrimitiveRestart);
    else
        GL.Disable(EnableCap.PrimitiveRestart);

    GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
    GL.Viewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3]);
    GL.Scissor(lastScissorBox[0], lastScissorBox[1], lastScissorBox[2], lastScissorBox[3]);
}
GamingMinds-DanielC commented 10 months ago

You should try capturing some frames (without and with ImGui active) with RenderDoc, there you can analyze your pipeline state for each draw call in detail. You should be able to pinpoint your exact problem that way. Basically compare the pipeline state when your shapes show up versus the state of the same draw call when they don't.

ocornut commented 3 months ago

Hello @celinedrules have you found what was causing your issue?