Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to properly render my drawing (drawlist) in the example "example_sdl_vulkan/main.cpp"? #4644

Closed
mandaxyzw opened this issue Oct 12, 2021 · 11 comments

Comments

@mandaxyzw
Copy link

mandaxyzw commented Oct 12, 2021

Version/Branch of Dear ImGui:

Version: v1.84
Branch:

Back-end/Renderer/Compiler/OS

Back-ends: imgui_impl_sdl.cpp + imgui_impl_vulkan.cpp
Compiler: Visual Studio 2015
Operating System: Windows 10

My Issue/Question:
I'm not a beginner in programming but I'm a beginner with Vulkan because it's too abstract with multi threads theories, I cannot ask this question to the Vulkan forum because it's about ImGui.
How to properly render my drawing in the example "example_sdl_vulkan/main.cpp"? I was migrating from another base called "Vulkan C++ examples and demos" to the official imgui vulkan example, I mean that before my render was perfect without cut glitch not just like in the screenshot.
That text "Hello World!" is not just text, but it's a multiple rectangles with texture.
The reason of the migration is the ImGui example has a perfect mouse behavior, it can drag outside the window, and with dynamic mouse icon change, and it supports keyboard input just like how I typed on the Console in the screenshot.

Screenshot (the glitch is my drawing is cut with unexpected rectangles)
FrameRender_glitch

Standalone, minimal, complete and verifiable example:

The function code below is originated from the official Dear ImGui v1.84 >
"examples/example_sdl_vulkan/main.cpp" > FrameRender
There is no modification, but the only change is beetween //==== and //====.

static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
{
    VkResult err;

    VkSemaphore image_acquired_semaphore  = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
    VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
    err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
    if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR)
    {
        g_SwapChainRebuild = true;
        return;
    }
    check_vk_result(err);

    ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
    {
        err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX);    // wait indefinitely instead of periodically checking
        check_vk_result(err);

        err = vkResetFences(g_Device, 1, &fd->Fence);
        check_vk_result(err);
    }
    {
        err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
        check_vk_result(err);
        VkCommandBufferBeginInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
        info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
        err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
        check_vk_result(err);
    }
    {
        VkRenderPassBeginInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
        info.renderPass = wd->RenderPass;
        info.framebuffer = fd->Framebuffer;
        info.renderArea.extent.width = wd->Width;
        info.renderArea.extent.height = wd->Height;
        info.clearValueCount = 1;
        info.pClearValues = &wd->ClearValue;
        vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
    }

    // <-- In the end, the render of the content of the window should be placed here before ImGui render.

    // Record dear imgui primitives into command buffer
    ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);

    //====================================================================================================
    // I place the render of the content of the window here (the unfilled rectangles and the text "Hello World!"),
    // such as g_window is my engine's window linked with ImGui device, with same area extent { wd->Width, wd->Height }.
    {
        g_window->draw(fd->CommandBuffer, g_Queue);
    }
    //====================================================================================================

    // Submit command buffer
    vkCmdEndRenderPass(fd->CommandBuffer);
    {
        VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
        VkSubmitInfo info = {};
        info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
        info.waitSemaphoreCount = 1;
        info.pWaitSemaphores = &image_acquired_semaphore;
        info.pWaitDstStageMask = &wait_stage;
        info.commandBufferCount = 1;
        info.pCommandBuffers = &fd->CommandBuffer;
        info.signalSemaphoreCount = 1;
        info.pSignalSemaphores = &render_complete_semaphore;

        err = vkEndCommandBuffer(fd->CommandBuffer);
        check_vk_result(err);
        err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence);
        check_vk_result(err);
    }
}

Peek definition of MyEngine::Window::draw

// This function is separated from updateBuffers because there can be multiple draw command buffers where this function is called multiple times.
void MyEngine::Window::draw(VkCommandBuffer commandBuffer, VkQueue copyQueue)
{
    _drawList->draw(commandBuffer, copyQueue);
}
@mandaxyzw mandaxyzw changed the title How to properly render my drawing in the example "example_sdl_vulkan/main.cpp"? How to properly render my drawing (drawlist) in the example "example_sdl_vulkan/main.cpp"? Oct 12, 2021
@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 13, 2021

Solved, I explored the line ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); then I found the problem, it changes the scissor. So here is the solution.

{
    // Apply scissor/clipping rectangle
    VkRect2D scissor;
    scissor.offset.x = 0;
    scissor.offset.y = 0;
    scissor.extent.width = wd->Width;
    scissor.extent.height = wd->Height;
    vkCmdSetScissor(fd->CommandBuffer, 0, 1, &scissor);

    g_window->draw(fd->CommandBuffer, g_Queue);
}

@ocornut
Copy link
Owner

ocornut commented Oct 13, 2021

I guess if your vkPipeline has VK_DYNAMIC_STATE_SCISSOR in its VkDynamicState it will use that left-over state.

If you use VK_DYNAMIC_STATE_SCISSOR in your pipeline you'd probably need to explicitly set the scissor value?

@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 13, 2021

Thanks, the code below is referenced from "Vulkan C++ examples and demos", and it's the default value, so for the moment, I keep it like this but in the future when I need high performance, I will go back to this forum, that's why I always post solution in forums because it will always help me in the future.

std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };

Anyway, I will always think about these new lessons. Code simplicity and readability is also important.

@ocornut
Copy link
Owner

ocornut commented Oct 15, 2021

Your solution is to call vkCmdSetScissor() yourself to cover the full viewport.

I suppose we could amend the Vulkan renderer to do that. Not as correct/good as restoring old value (I don't know if we can?) but this would likely be better for a majority of users.

ocornut added a commit that referenced this issue Oct 15, 2021
… / VK_DYNAMIC_STATE_SCISSOR and not calling vkCmdSetViewport() / vkCmdSetScissor(). (#4644)
@ocornut
Copy link
Owner

ocornut commented Oct 15, 2021

I made a small change + comment about this:

// Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called.
// Our last values will leak into user/application rendering IF:
// - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state
// - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitely set that state.
// If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering.
// In theory we should aim to backup/restore those values but I am not sure this is possible.
// We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644)
VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } };
vkCmdSetScissor(command_buffer, 0, 1, &scissor);

Not this is technically still not correct but I don't think it is possible to backup/restore that information.
Our "fix" will merely fix things for a large majority of users, you included.

@ocornut ocornut closed this as completed Oct 15, 2021
@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 17, 2021

Thanks a lot. I still have another question related to this thread and I'm going to prepare that, it's "How to get ImGui Windows rectangles? Because I want to make mouse click obstacle of ImGui rectangles from my engine's Window content?".

I see hints but it always make zero results.

@ocornut
Copy link
Owner

ocornut commented Oct 17, 2021 via email

@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 18, 2021

I'm back to say thank you and your "change + comment" is already in my real project.

The thing that doesn't make me sure is the variable WasActive, it seems to work well without bug but I'm not sure if "does Active means Enabled?". In a 3D software called Blender, active means the very first selected object.

I'm not sure if my principle of getting the ImGui windows rectangles is correct because I don't want to be tricked with bugs in the far future.

// This code is just a debug test, and the rectangles are too thick just to make them visible.
ImGuiContext& g = *GImGui;
for (int i = 0; i < g.Windows.Size; i++) {
    ImGuiWindow *w = g.Windows[i];
    ImRect r = w->Rect();
    float thikness = 3.0f;
    _drawList->addRect(
        vec2(r.Min) - 0.5 * thikness * vec2(1, 1),
        vec2(r.Max) + 0.5 * thikness * vec2(1, 1),
        w->WasActive ? 0xff00ff00 : 0xff000000, // if was active then green, otherwise black.
        0.0f,
        240,
        thikness);
}

@ocornut
Copy link
Owner

ocornut commented Oct 18, 2021

I don't understand why you are using ImGuiWindow* internal data, you should not do that.

@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 18, 2021

I'm too late, I edited the code, and it was like this before edit:

ImVector<ImGuiWindow*> windows = (*GImGui).Windows;

Do you think it will crash my program in the future? C++ programming is sometimes very abstract in things like this. But it seems currently work without bug. I don't know if it will cause a random time crash in the far future, just trust.

@ocornut
Copy link
Owner

ocornut commented Oct 18, 2021

You should not copy those vectors (use a reference). You should not iterate windows this way unless you have a valid reason. You haven't explained what you are trying to do and why.
I already told you that the answer to your question is most likely in the FAQ but you are pushing a XY Problem instead of clarifying your initial goal.

Because I want to make mouse click obstacle of ImGui rectangles from my engine's Window content?".

READ THE FAQ

actondev pushed a commit to actondev/imgui that referenced this issue Nov 26, 2021
… / VK_DYNAMIC_STATE_SCISSOR and not calling vkCmdSetViewport() / vkCmdSetScissor(). (ocornut#4644)
actondev pushed a commit to actondev/imgui that referenced this issue Nov 26, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants