ocornut/imgui

How to draw the imgui interface and something from SDL_Renderer at the same time.

NePutin94 opened this issue ยท 14 comments

I want to draw the imgui interface and some sdl texture, how should I do this?
I wrote something like this:

void draw(){   
            SDL_RenderClear(Context::GetContext());
            SDL_RenderCopy(Context::GetContext(), txt, NULL, NULL);
            SDL_RenderPresent(Context::GetContext());

            ImGui_ImplOpenGL3_NewFrame();
            ImGui_ImplSDL2_NewFrame(window);
            ImGui::NewFrame();
            ImGui::Begin("Test");
            ImGui::Text("TEST TEST TEST");
            ImGui::End();
            ImGui::EndFrame();
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            ImGui::Render();
            ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
            SDL_GL_SwapWindow(window);
}

But it doesn't work:
GIF

I can't mix opengl and sdl rendering, but I don't understand how to do it correctly. And I don't want to use opengl to draw textures.

Linking this to #1116, #408

This feels more like an SDL question than a Dear ImGui one.

SDL_RenderPresent calls SDL_GL_SwapWindow internally, so you're swapping the back buffer twice, hence the strobing effect between the two scenes.

I am not very familiar with the SDL render API, but I believe replacing SDL_RenderPresent with SDL_RenderFlush will probably fix this. (The latter flushes render commands without swapping the back buffer.)

I tried calling SDL_RenderFlush, the "flashing" disappeared and the imgui interface was displayed correctly, but the texture was not drawn.

 void render(){
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            
            SDL_Rect s{100, 100, 300, 300};
            SDL_RenderCopy(Context::GetContext(), txt, &s, &s);
            SDL_RenderFlush(Context::GetContext());
            
            ImGui_ImplOpenGL3_NewFrame();
            ImGui_ImplSDL2_NewFrame(window);
            ImGui::NewFrame();
            ImGui::Begin("Test");
            ImGui::Text("TEST TEST TEST");
            ImGui::End();
            ImGui::Render();
            SDL_GL_SwapWindow(window);
 }

Are you explicitly specifying the underlying driver for SDL_Render to use? It appears it will default to Direct3D/Metal on Windows/macOS before trying OpenGL otherwise.

Otherwise at this point I'd suggest asking the SDL folks or look through the discussions Omar linked earlier. From what I can tell, mixing OpenGL and SDL_Render stuff together isn't even something they explicitly support.

@NePutin94 you need to fix your render order, and like @PathogenDavid not render two frames in one loop iteration. See snippet below, works flawless when added to example application.

        // Rendering
        ImGui::Render();
        glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
        glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
        glClear(GL_COLOR_BUFFER_BIT);

        // SDL_renderer
        SDL_Rect vp;
        vp.x = vp.y = 0;
        vp.w = (int) ImGui::GetIO().DisplaySize.x;
        vp.h = (int) ImGui::GetIO().DisplaySize.y;
        SDL_RenderSetViewport (renderer, &vp);
        SDL_SetRenderDrawColor (renderer, clear_color.x * 255.0f, clear_color.y * 255.0f, clear_color.z * 255.0f, clear_color.w * 255.0f);
        SDL_Rect rct2;
        rct2.x = rct2.y = 200;
        rct2.w = rct2.h = 400;
        SDL_RenderCopy (renderer, bitmapTex, 0, &rct2);

        // Render Dear ImGui
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        SDL_GL_SwapWindow(window);

I am not sure what's going on there....

@NePutin94

imgui interface was displayed correctly, but the texture was not drawn

The code you pasted never calls ImGui_ImplOpenGL3_RenderDrawData() therefore it is impossible that dear imgui was showing with that code, therefore you are not sharing the right code.

@rokups

you need to fix your render order,

Ensure what you did there. ImGui::Render() doesn't touch GPU state so why moving it so high? What exactly is wrong with the order posted by @NePutin94 ? Apart from lack of calling ImGui_ImplOpenGL3_RenderDrawData().

Generally you ought to try to have the dear imgui "frame" covers as much of your code, so calling ImGui::NewFrame() as early as possible in your frame, and ImGui::Render() + ImGui_ImplOpenGL3_RenderDrawData() as late as possible. Only that last function will ever touch something related to OpenGL, other won't. This way you can use dear imgui functions between NewFrame and Render, which means you can even use dear imgui functions in-between your SDL_Renderer usage.

@PathogenDavid
Yes, I definitely use OpenGL. Initializing sdl and imgui:

void init(){
            ....
             SDL_WindowFlags window_flags = (SDL_WindowFlags) (
                    SDL_WINDOW_OPENGL
                    | SDL_WINDOW_RESIZABLE
                    | SDL_WINDOW_ALLOW_HIGHDPI
            );
            window = SDL_CreateWindow("test",
                                      SDL_WINDOWPOS_UNDEFINED,
                                      SDL_WINDOWPOS_UNDEFINED,
                                      size.x, size.y,
                                      window_flags);
            SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
            SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
            SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
            SDL_GL_SetAttribute(
                    SDL_GL_CONTEXT_PROFILE_MASK,
                    SDL_GL_CONTEXT_PROFILE_CORE
            );
            std::string glsl_version =  "#version 130";
            SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
            SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
            gl_context = SDL_GL_CreateContext(window);
            SDL_GL_MakeCurrent(window, gl_context);
            gladLoadGLLoader((GLADloadproc) SDL_GL_GetProcAddress);
            glViewport(0, 0, size.x, size.y);
            ImGui::CreateContext();
            io = &ImGui::GetIO();
            ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
            ImGui_ImplOpenGL3_Init(glsl_version.c_str());
            ImGui::StyleColorsClassic();
}

@rokups
Shouldn't I call SDL_RenderPresent to update the rendering and display the texture added with SDL_RenderCopy?
I tried to do as you said:

void render(){
            ImVec4 clear_color{35.f / 255, 123.f / 255, 123.f / 255, 1};
            ImGui_ImplOpenGL3_NewFrame();
            ImGui_ImplSDL2_NewFrame(window);
            ImGui::NewFrame();
            ImGui::Begin("Test");
            ImGui::Text("TEST TEST TEST");
            ImGui::End();
            glViewport(0, 0, (int)io->DisplaySize.x, (int)io->DisplaySize.y);
            glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
            glClear(GL_COLOR_BUFFER_BIT);
            SDL_Rect vp;
            vp.x = vp.y = 0;
            vp.w = (int) ImGui::GetIO().DisplaySize.x;
            vp.h = (int) ImGui::GetIO().DisplaySize.y;
            SDL_RenderSetViewport (Context::GetContext(), &vp);
            SDL_SetRenderDrawColor (Context::GetContext(), clear_color.x * 255.0f, clear_color.y * 255.0f, clear_color.z * 255.0f,    clear_color.w * 255.0f);
            SDL_Rect rct2;
            rct2.x = rct2.y = 200;
            rct2.w = rct2.h = 400;
            SDL_RenderCopy(Context::GetContext(), txt, 0, &rct2);
            //SDL_RenderPresent(Context::GetContext());
            ImGui::Render();
            ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
            SDL_GL_SwapWindow(window);
}

But it still doesn't work, only the imgui interface is displayed (probably because I don't call any function for SDL rendering), if I call the SDL_RenderPresent function, then a flashing appears, as I described above. Probably this is a question more about SDL and I will try to ask elsewhere.

Ensure what you did there. ImGui::Render() doesn't touch GPU state so why moving it so high?

ImGui::Render() wasnt really moved, SDL_Renderer code was inserted before imgui draw data is rendered for clarity.

What exactly is wrong with the order posted by @NePutin94 ? Apart from lack of calling ImGui_ImplOpenGL3_RenderDrawData().

Initial code does this:

  1. Render using SDL_Renderer
  2. Present
  3. Render imgui draw data
  4. Present
  5. goto 1
    That is why its flickering. Instead SDL_Renderer should render before imgui draw data is rendered (presumably it will act as background with UI in front layer.

Shouldn't I call SDL_RenderPresent to update the rendering and display the texture added with SDL_RenderCopy?

Lets see docs \o/

SDL's rendering functions operate on a backbuffer; that is, calling a rendering function such as SDL_RenderDrawLine() does not directly put a line on the screen, but rather updates the backbuffer. As such, you compose your entire scene and present the composed backbuffer to the screen as a complete picture.

Key piece here is "and present the composed backbuffer to the screen". It may not be obvious without a bit of graphics programming basics, but this is essentially same as SDL_GL_SwapWindow(window);, so whenever you call SDL_RenderPresent() - you render a frame, after that you start composing a new frame from scratch. This is why it was flickering - two different things being rendered, alternating between them.

You seem to be having an issue in your own program, entirety of which we are not aware. Since rendering in example application works, i suggest you try getting image rendered in official example, and once it does work - go through pieces of your application, evaluating if it does everything as example does. Chances are something simple may be overlooked somewhere.

Initial code does this:

Sorry I mean the second blurb posted here: #4264 (comment)
I am not sure WHICH is the important difference between that one and yours #4264 (comment)

Oh i meant this one #4264 (comment)

@NePutin94 i took another look, and it appears merely this is enough to render an image using SDL_Renderer:

        // Rendering
        ImGui::Render();
        glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
        glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
        glClear(GL_COLOR_BUFFER_BIT);

        // SDL_renderer
        SDL_Rect rct2;
        rct2.x = rct2.y = 200;
        rct2.w = rct2.h = 400;
        SDL_RenderCopy(renderer, bitmapTex, 0, &rct2);

        // Render Dear ImGui
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        SDL_GL_SwapWindow(window);

image

@NePutin94

Yes, I definitely use OpenGL. Initializing sdl and imgui:

I'm not asking about your window or Dear ImGui. I'm asking about SDL_Render. It has its own configuration and initialization which you're not showing. You need to explicitly specify SDL_HINT_RENDER_DRIVER as "opengl".

If you don't do this, it will default to something else. On Linux the default is OpenGL, on Windows it is Direct3D9, on macOS it is Metal.

@PathogenDavid Oh, yes, I used directx, instead of opengl.
I added SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl"); after initializing sdl and now the @rokups code is working. Thank you for your help!

New imgui_impl_sdlrenderer.cpp backend!

FYI, although I personally recommend using native-backends, SDL 2.0.18 (tentative scheduled in 52 days) will add support for a new SDL_RenderGeometry() function which allowed a SDL_Renderer backend to be created.

This is now available on master (currently requires building SDL from latest sources)
#3926 (comment)

(Note the backend currently doesn't support multi-viewports.)