Is SDL Renderer useless if I use OpenGL for drawing?
Asked Answered
F

4

14

I'm am learning SDL2, but I am also using the imgui library that is using OpenGL calls. From what I read on various blogs online, I can't easily mix SDL2 renderer and OpenGL calls; I either use one or the other.

Most of the tutorials I've read use the renderer, so I do not quite understand how to use SDL2 without the renderer for drawing primitives, or drawing sprites.

Take this for example: http://lazyfoo.net/tutorials/SDL/11_clip_rendering_and_sprite_sheets/index.php

He creates the SDL renderer:

gRenderer = SDL_CreateRenderer( gWindow, -1, SDL_RENDERER_ACCELERATED );

Which he then uses to render images:

void LTexture::render( int x, int y, SDL_Rect* clip )
{
        //Set rendering space and render to screen
        SDL_Rect renderQuad = { x, y, mWidth, mHeight };

        //Set clip rendering dimensions
        if( clip != NULL )
        {
                renderQuad.w = clip->w;
                renderQuad.h = clip->h;
        }

        //Render to screen
        SDL_RenderCopy( gRenderer, mTexture, clip, &renderQuad );
}

He then calls his render() to draw images from a spritesheet:

//Clear screen
SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );
SDL_RenderClear( gRenderer );

//Render top left sprite
gSpriteSheetTexture.render( 0, 0, &gSpriteClips[ 0 ] );

//Render top right sprite
gSpriteSheetTexture.render( SCREEN_WIDTH - gSpriteClips[ 1 ].w, 0, &gSpriteClips[ 1 ] );

//Render bottom left sprite
gSpriteSheetTexture.render( 0, SCREEN_HEIGHT - gSpriteClips[ 2 ].h, &gSpriteClips[ 2 ] );

//Render bottom right sprite
gSpriteSheetTexture.render( SCREEN_WIDTH - gSpriteClips[ 3 ].w, SCREEN_HEIGHT - gSpriteClips[ 3 ].h, &gSpriteClips[ 3 ] );

//Update screen
SDL_RenderPresent( gRenderer );

I understand what is happening, but now that I can't use the SDL renderer, how do I accomplish the same? Can I not use any SDL2 drawing functions now? Do I only use raw OpenGL calls, and only use SDL for keyboard bindings?

Basically, I do not understand how to use SDL2 for drawing things (like in the sprite example) if I cannot use the SDL Renderer since it seems incompatible with the GUI library I'm using, which uses OpenGL calls for rendering.

Felicita answered 11/12, 2016 at 22:31 Comment(2)
Can you rephrase your question and title to be more specific? Since you've already read that the SDL renderer and the OpenGL APIs are mutually exclusive at this level, it's hard for me to understand how we can help.Arellano
@Arellano Without the SDL renderer, I don't understand how to draw things on the screen with SDL. Since the library I'm using uses opengl, does this mean that I cannot use any SDL bindings for drawing? Does SDL just become useless for drawing, can so I can only use it to read keyboard input (which opengl does not do).Felicita
A
14

Can I not use any SDL2 drawing functions now? Do I only use raw opengl calls, and only use SDL for keyboard bindings?

Correct.

In order to use OpenGL API directly, the SDL window must be created with the flag SDL_WINDOW_OPENGL, as well as a new OpenGL context for it:

auto window = SDL_CreateWindow("Window name",
                 SDL_WINDOWPOS_UNDEFINED,
                 SDL_WINDOWPOS_UNDEFINED,
                 width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN);
auto context = SDL_GL_CreateContext(window);

If successful, one would simply perform calls to OpenGL like in any other context handler. That same website linked in the question provides tutorials for using OpenGL with SDL 2, both legacy and modern. Note that none of the two involve creating an SDL renderer. In fact, using it would be inappropriate since the two APIs are meant to be mutually exclusive: you should either use one or the other. This answer makes a quick explanation for why you should not mix them.

Nevertheless, there are ways to achieve the same output from certain SDLRenderer calls using OpenGL (since the same renderer has an OpenGL implementation!), but this will naturally be case specific. Want to draw a simple rectangle? The legacy glBegin and glEnd approach might suffice. Want to draw a sprite? You'll have to send it to the OpenGL device's memory first. Once you know the ropes around OpenGL, making a suitable wrapper layer is often appropriate. If you want to use existing libraries that rely on OpenGL, replacing other drawing procedures to use this API (or utility libraries such as GLU) is the right way to go.

Also note that the SDL renderer does not have to rely on OpenGL (it may use Direct3D on Windows).

Arellano answered 11/12, 2016 at 22:46 Comment(3)
SDL_WINDOW_OPENGL is not enough, SDL_GL_CreateContext have to be used too.Spaceman
All right, I must have assumed too much. It's fixed now, but that matter is only secondary to the actual answer.Arellano
Yeah, sure, I agree. In theory GL can be used with SDL_Renderer, but only if you're sure rendering backend is GL. I don't think it is a good idea though, as way too many things can break (e.g. not restoring correct state or shaders).Spaceman
T
1

If i have understood you want to know how to render to the SDL screen without using the SDL functions, you want to use the opengl functions. SDL have support for opengl, to render things with opengl using SDL you must follow these minimum steps: before create the window make some calls to SDL_GL_SetAttribute to set OpenGL attributes and then create the window with the SDL_WINDOW_OPENGL flag, after that you must create the opengl context(each gl applciation have one) calling SDL_GL_CreateContext, See the SDL Wiki for the functions i mentioned: CategoryVideo - SDL Wiki

Here is some piece of code as example:

void start(SDL_Window** window, SDL_GLContext* gl){
if(SDL_Init(SDL_INIT_VIDEO) < 0){
    throw std::runtime_error(SDL_GetError());
}
//set the gl version to be loaded
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);

*window = SDL_CreateWindow("Widget Tests", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_OPENGL);

if(*window == nullptr){
    throw std::runtime_error(SDL_GetError());
}

//create the opengl context
*gl = SDL_GL_CreateContext(*window);

if(*gl == nullptr){
    throw std::runtime_error(SDL_GetError());
}
}

int main(){
SDL_Window* window;
SDL_GLContext gl;
...

//init sdl and opengl
start(&window, &gl);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glMatrixMode(GL_PROJECTION_MATRIX);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW_MATRIX);
glLoadIdentity();
glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

while(running){
    while(SDL_PollEvent(&event)){
        switch(event.type){
            ...
        }
    }
    glClear(GL_COLOR_BUFFER_BIT);

    //... draw calls with opengl

    SDL_GL_SwapWindow(window);//update the window
}
}
Tabethatabib answered 11/12, 2016 at 23:49 Comment(0)
D
0

Since Oct 12, 2021 Dear ImGui now officially supports a SDL_Renderer backend You need version >= 1.85 and SDL version >= 2.0.18+

Use it if you want to follow SDL_Renderer based tutorials.

Also as a technical matter, if you use OpenGL backend instead, you could still use a SDL_Renderer to render to an offscreen render target which you then upload to OpenGL as an OpenGL texture. Dear ImGui provides multiple backends by doing precisely this general process.

Alternatively, you could just write directly to the OpenGL framebuffers (no need for texture). For beginner tutorials, just use >v1.85 with SDL_Renderer.

Divestiture answered 22/9, 2022 at 5:29 Comment(0)
P
0

Since you are using ImGui, you don't need to use SDL renderer or OpenGL draw functions for a 2D game. ImGui has functions for you. That's why you are using ImGui, right? Make a full screen window and get its draw list, and use functions such as AddImage() to draw images. See imgui.h for other functions like this.

// Create a full screen window  
ImGuiIO& io = ImGui::GetIO();
const ImGuiWindowFlags mainFlags = ImGuiWindowFlags_NoResize
        | ImGuiWindowFlags_NoCollapse
        | ImGuiWindowFlags_NoTitleBar
        | ImGuiWindowFlags_NoScrollbar
        | ImGuiWindowFlags_NoBackground
        | ImGuiWindowFlags_NoMove
        | ImGuiWindowFlags_NoSavedSettings
        | ImGuiWindowFlags_NoBringToFrontOnFocus;

ImGui::SetNextWindowPos(ImVec2(0.0, 0.0));
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y));
ImGui::Begin("Game scene", NULL, mainFlags);

// Get draw list
ImDrawList* draw_list = ImGui::GetWindowDrawList();

// Get viewport
ImVec2 canvasViewportStart = ImGui::GetCursorScreenPos();

// Load texture (to be implemented by you)
auto texture = loadTexture("texture.png");

// Draw the texture
draw_list->AddImage(texture, ImVec2(canvasViewportStart.x, canvasViewportStart.y), ImVec2(canvasViewportStart.x + 32, canvasViewportStart.y + 32));

// Handle mouse input
if (ImGui::IsWindowHovered()) {
    const ImVec2 mousePosAbs = ImGui::GetMousePos();
    const ImVec2 mousePos = ImVec2(mousePosAbs.x - canvasViewportStart.x, mousePosAbs.y - canvasViewportStart.y);
    
    if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
        // ...
    }
}                   

ImGui::End();

// Create other windows, call ImGui::Render()

If you want to use OpenGL draw functions directly (perhaps for a 3D game), that is possible too. In that case, you should not create an ImGui window for the scene. Render your scene before rendering ImGui windows. Place your code between the render calls in the ImGui OpenGL example:

ImGui::Render();

// Clear the screen
ImGuiIO& io = ImGui::GetIO();
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
glClearColor(0, 0, 0, 255);
glClear(GL_COLOR_BUFFER_BIT);

// Render your own game scene here
// Note: You don't need this if you are using ImGui draw functions

// Render ImGui windows
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
SDL_GL_SwapWindow(window);
Padang answered 22/9, 2022 at 6:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.