ocornut / imgui

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

Using Metal: why are there some garbage/remains along the edges of the imgui window? #7364

Closed laiyierjiangsu closed 8 months ago

laiyierjiangsu commented 8 months ago

Version/Branch of Dear ImGui:

Branch:master , the latest verison

Back-ends:

imgui_impl_metal.cpp

Compiler, OS:

macos + clang12

Full config/build information:

No response

Details:

I have a third-party library. After it finishes rendering, I continue to draw ImGui on the rendered result with the following code. However, a strange phenomenon occurred: there are some extra elements along the edges of the window. I'm not very familiar with graphic APIs.

I'm looking for some information:

What configurations might cause this border issue? It's possible that there is an error in my API settings. Are there any tools I can use to debug this issue?

Screenshots/Video:

Screenshot 2024-03-04 at 20 27 05

Minimal, Complete and Verifiable Example code:

        //id <MTLDevice> device = (__bridge id<MTLDevice>)third->MetalDevice;
        CAMetalLayer * metalLayer = (__bridge CAMetalLayer *)third->MetalLayer;
        id <MTLCommandQueue> commandQueue = (__bridge id<MTLCommandQueue>)third->CommandQueue;

        @autoreleasepool {
            id <CAMetalDrawable> nextDrawable = [metalLayer nextDrawable];
            if( nextDrawable != nullptr ) {
                //Gamelib rendering
               // MTLRenderPassDescriptor * lastRenderPassDesc = (__bridge MTLRenderPassDescriptor //*)third->RenderOneFrame((__bridge void*)nextDrawable);
                //lastRenderPassDesc = nil;

                //draw something on top of it
                id <MTLCommandBuffer> commandBuffer = [commandQueue commandBuffer];
               MTLRenderPassDescriptor * renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor];
                MTLRenderPassColorAttachmentDescriptor *colorAttachment = renderPassDescriptor.colorAttachments[0];
                colorAttachment.texture = nextDrawable.texture;

           //try to render imgui
                [VC_GlView renderImgui:self.glView:commandBuffer:renderPassDescriptor];

                //You need to manage the present
                [commandBuffer presentDrawable:nextDrawable];
                [commandBuffer commit];
            }
            nextDrawable = nullptr;
        }

//imp for rendering imgui

{ ImGuiIO& io = ImGui::GetIO();

io.DisplaySize.x = view.bounds.size.width; io.DisplaySize.y = view.bounds.size.height;

if TARGET_OS_OSX

CGFloat framebufferScale = view.window.screen.backingScaleFactor ?: NSScreen.mainScreen.backingScaleFactor;

else

CGFloat framebufferScale = view.window.screen.scale ?: UIScreen.mainScreen.scale;

endif

io.DisplayFramebufferScale = ImVec2(framebufferScale, framebufferScale);

// id commandBuffer = [self.commandQueue commandBuffer];

// MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; if (renderPassDescriptor == nil) { [commandBuffer commit]; return; }

// Start the Dear ImGui frame
ImGui_ImplMetal_NewFrame(renderPassDescriptor);

if TARGET_OS_OSX

ImGui_ImplOSX_NewFrame(view);

endif

ImGui::NewFrame();

// Our state (make them static = more or less global) as a convenience to keep the example terse.
static bool show_demo_window = true;
static bool show_another_window = false;
static ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);

// 2. Show a simple window that we create ourselves. We use a Begin/End pair to create a named window.
{
    static float f = 0.0f;
    static int counter = 0;

    ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

    ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)
    ImGui::Checkbox("Demo Window", &show_demo_window);      // Edit bools storing our window open/close state
    ImGui::Checkbox("Another Window", &show_another_window);

    ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
    ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color

    if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
        counter++;
    ImGui::SameLine();
    ImGui::Text("counter = %d", counter);

    ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
    ImGui::End();
}

// Rendering
ImGui::Render();
ImDrawData* draw_data = ImGui::GetDrawData();

//renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color.x clear_color.w, clear_color.y clear_color.w, clear_color.z * clear_color.w, clear_color.w); id renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; [renderEncoder pushDebugGroup:@"Dear ImGui rendering"]; ImGui_ImplMetal_RenderDrawData(draw_data, commandBuffer, renderEncoder); [renderEncoder popDebugGroup]; [renderEncoder endEncoding]; }

ocornut commented 8 months ago

//renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color.x clear_color.w, clear_color.y clear_color.w, clear_color.z * clear_color.w, clear_color.w)

Why is this commented?

It seems like maybe you are not properly clearing your framebuffer so previous frame or garbage data may be visible.

laiyierjiangsu commented 8 months ago

//renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(clear_color.x clear_color.w, clear_color.y clear_color.w, clear_color.z * clear_color.w, clear_color.w)

Why is this commented?

It seems like maybe you are not properly clearing your framebuffer so previous frame or garbage data may be visible.

Thanks ocornut.

The purpose of this line of code is to clear the render target. If enabled, it can solve the issue of extra rendering areas appearing outside the window, but this was not my original design. In this process, a third party first calls RenderOneFrame to render the image, and then I recreate a command buffer to render ImGui on the original texture. You can consider the black background in my screenshot as the game content, which I cannot capture due to sensitivity concerns.

I am wondering if it's possible to use some graphical debugging tools to find out exactly what is drawing the extra content on each edge?

laiyierjiangsu commented 8 months ago

https://github.com/ocornut/imgui/assets/5535552/6c3686fc-b05a-4719-9f21-13ebf4fd1421

I've uploaded a video that, despite being a bit blurry, shows the basic rendering commands. Between command 18 and command 19, the entire UI is drawn, introducing additional borders, so we can at least be sure it's not due to a lack of cleaning. I feel it's related to the overall window drawing size, but the ImGui API is very concise, and I didn't see a place to set this. Graphics APIs are new to me, so I hope a friend can give me some guidance?

ocornut commented 8 months ago

You can browse the code and see exactly what ImGui_ImplMetal_RenderDrawData(), but I am afraid this is a Metal question rather than a Dear ImGui question. Perhaps you need to set that clear color to 0,0,0,0, or find a way to disable the clearing in that render pass. I am sympathetic to your issue but those things are common when doing graphics programming, and we don't have resources to help those cases as it is draining resources away from focusing on Dear ImGui, so I need to close this.

It'd be best if you ask on graphics or Metal forums. When you find your issue feel free to post here again as it may help future users searching for clues, if they have a similar issue.

laiyierjiangsu commented 8 months ago

Sure, if I have any further discoveries, I'll let you know.