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

SDL3: Successfully drawn, but no image displayed #12592

Closed
PulpFiction98 opened this issue Mar 20, 2025 · 30 comments
Closed

SDL3: Successfully drawn, but no image displayed #12592

PulpFiction98 opened this issue Mar 20, 2025 · 30 comments
Milestone

Comments

@PulpFiction98
Copy link

PulpFiction98 commented Mar 20, 2025

Image

Image

Image
Click the button, clear the screen, and then draw four times, each time drawing an image on top of the original. But there is a probability that the image cannot be drawn. Did I make a mistake?

@PulpFiction98 PulpFiction98 changed the title The drawing was successful, but it was not added SDL3: Successfully drawn, but no image displayed Mar 20, 2025
@madebr
Copy link
Contributor

madebr commented Mar 20, 2025

Remove the calls to SDL_RenderPresent when you're doing SDL_SetRenderPresent (with the target != NULL).

@PulpFiction98
Copy link
Author

If I wait for 10ms between each drawing, then the image is drawn every time.

Image

@slouken slouken closed this as not planned Won't fix, can't repro, duplicate, stale Mar 20, 2025
@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

Remove all calls to render present except for the last one. You asked about this multiple times on the forums and got the same answer.

@PulpFiction98
Copy link
Author

删除除最后一个调用外的所有 render present 调用。您在论坛上多次询问这个问题,得到的答案都是一样的。

I'm not going to draw these four images all at once, I just wrote a demo and put them in one method.

Actually, every time I draw, it's done in a different way,
The first step is to clear the screen.
Step two, draw an image on it.
Step three, draw another image based on the previous one.
Step four, draw another image based on the previous one.
Step five, draw another image based on the previous one.

In my program, they are not in the same method, but are called sequentially in the same thread, so when I wrote the test demo again, I wrote them in the same method.

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

You have to fill the entire screen between each present. The contents of frames after a present are undefined. The best way to simulate this is to follow each present with a clear to green. That way if the green color shows up you know that you haven’t drawn something correctly.

@PulpFiction98
Copy link
Author

Remove all calls to render present except for the last one. You asked about this multiple times on the forums and got the same answer.

If I only call persent at the end, I don't need to use targettexture, do I?

I used Target Texture because I don't need them to draw it all at once. Just need them to quickly draw them one by one.
@slouken

@slouken slouken reopened this Mar 20, 2025
@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

Sorry, I missed that you were using a target texture. Can you copy and paste your demo code here so I can play with it tomorrow?

@PulpFiction98
Copy link
Author

PulpFiction98 commented Mar 20, 2025

https://github.com/PulpFiction98/SDL3_TargetTexture
This project can be opened and run using Rider or VS2022 IDE.
This example is written in C #, and you can open it using Rider like last time, but this time on a Windows system.
In the C # code, I also added a comparison of using targetTexture in SDL2, and everything is normal in SDL2.

@slouken
And I tried to reproduce this problem with C.

int main(void) {
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("SDL3", 1920, 1080,SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window,NULL);
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    SDL_SetRenderTarget(renderer, targetTexture);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");
            SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture,NULL,NULL);
            SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_FRect  frect=(SDL_FRect){0,0,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
              frect=(SDL_FRect){1920/2,0,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect=(SDL_FRect){0,1080/2,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect=(SDL_FRect){1920/2,1080/2,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);
        }
    }
    return 0;
}

Every time you click on the screen, there should be a momentary gray color, then quickly draw, with red in the upper left, green in the upper right, blue in the lower left, and white in the lower right. Is that right?
But what I saw was not like that, I don't know if my understanding of the target text was wrong.

Image

@Sackzement
Copy link
Contributor

SDL_RenderPresent() should only be called once per frame(or mouseclick in your case).

Quote from SDL_RenderPresent():

The backbuffer should be considered invalidated after each present

int main(void) {
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("SDL3", 1920, 1080,SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window,NULL);
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    SDL_SetRenderTarget(renderer, targetTexture);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");
            
            SDL_SetRenderTarget(renderer, targetTexture); // <==========
            
            SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture,NULL,NULL);
            //SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_FRect  frect=(SDL_FRect){0,0,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            //SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
              frect=(SDL_FRect){1920/2,0,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            //SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect=(SDL_FRect){0,1080/2,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            //SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect=(SDL_FRect){1920/2,1080/2,1920/2,1080/2};
            SDL_RenderTexture(renderer, targetTexture,NULL,&frect);
            //SDL_RenderPresent(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);
            
            SDL_SetRenderTarget(renderer, NULL); // <==========
            SDL_RenderPresent(renderer);         // <==========
        }
    }
    return 0;
}

@PulpFiction98
Copy link
Author

PulpFiction98 commented Mar 20, 2025

SDL_RenderPresent() should only be called once per frame(or mouseclick in your case).

Quote from SDL_RenderPresent():

The backbuffer should be considered invalidated after each present

Oh my god, I'm using the target texture. I have a reason for writing this.
I have my own reasons for calling ‘persist’ every time, because in real projects, I need to use it this way. The above is just an example of imitating a real environment.

The problem I am facing now is that the target texture of SDL3 has such an error, while I have also tried to write a similar demo for SDL2, but everything is normal.

@PulpFiction98
Copy link
Author

In real projects, they are not executed in one method, but in the same thread in sequence, so in the minimum demo, I put them in one method.
@Sackzement

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

Ah, I understand what's going on here.

You're writing to the screen and you want each thing that you write to be seen in sequence. Most applications write the same (or similar thing) each frame, so it doesn't matter if some of the frames aren't seen, but in your case it does.

The screen can only display things at the refresh rate of the monitor. So if your monitor has a 60 Hz refresh rate, the fastest you can display things is (1000 ms / 60 Hz), or 16.666666~ milliseconds apart. If your applications makes frames faster than that, then the graphics driver will just drop whatever won't be displayed.

In your case, you don't want that, so setting SDL_SetRenderVsync(renderer, 1) is actually the right fix for you. This guarantees that the graphics driver will wait until it can display the next frame so each frame will be seen on the screen.

Apologies for the misunderstanding!

@PulpFiction98
Copy link
Author

I will try this, but I actually have a question. In SDL2, I did not set up synchronization, and it runs perfectly (as can be seen in the SDL2 example I gave you in the GitHub project). Here, I want to know why there is no frame loss issue in SDL2, and whether I can achieve the same effect as SDL2 without setting synchronization. I discovered this problem when upgrading it to SDL3. I used another function (SDL_SetHint (SDL_INT-RENDER_VSYNC, "1")) to set vertical synchronization, which solved the problem of some frames not being displayed in the example I gave you. But in the real project, it had multiple cameras playing, and setting the SDL_SetHint function caused significant lag in my multi screen video playback.

I will try the SDL_SetRenderVsync function tomorrow to see how it goes.

If it's not possible, I will package my project in its entirety and send it to your email (including the code, instructions, and screen recording of any issues)

@slouken

@PulpFiction98
Copy link
Author

PulpFiction98 commented Mar 20, 2025

Just like, one of my programs can play up to 25 cameras at the same time, each with 25 frames per second. When I used SDL2, I used it. Whenever a new frame from a certain camera comes, I draw it into the target texture and then submit the rendering using persent. It runs perfectly. 25 * 25=625 frames, I didn't set vertical synchronization and didn't feel any lag.

Today, when I am using sdl3 without setting vertical synchronization, it can still play 25 frames smoothly. There was just a problem when I was initializing their background images. In order to solve the problem of frame loss during initialization, I used SDL_SetHint to set synchronization, which caused significant lag when playing my 25 images.
This question is very troubling me.

@PulpFiction98
Copy link
Author

Ah, I understand what's going on here.

You're writing to the screen and you want each thing that you write to be seen in sequence. Most applications write the same (or similar thing) each frame, so it doesn't matter if some of the frames aren't seen, but in your case it does.

The screen can only display things at the refresh rate of the monitor. So if your monitor has a 60 Hz refresh rate, the fastest you can display things is (1000 ms / 60 Hz), or 16.666666~ milliseconds apart. If your applications makes frames faster than that, then the graphics driver will just drop whatever won't be displayed.

In your case, you don't want that, so setting SDL_SetRenderVsync(renderer, 1) is actually the right fix for you. This guarantees that the graphics driver will wait until it can display the next frame so each frame will be seen on the screen.

Apologies for the misunderstanding!

Did SDL2 make any optimizations in this section?

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

I'm looking at your code and you are running into the first issue we thought - you're not clearing the screen correctly.

Here's code that demonstrates this - the magenta (purple) color is uninitialized memory, which may or may not be pixels you expect in practice:

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define printf SDL_Log

static void Present(SDL_Renderer* renderer)
{
    SDL_RenderPresent(renderer);
    SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
    SDL_RenderClear(renderer);
}

int main(int argc, char *argv[])
{
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("SDL3", 1920, 1080, SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window, NULL);
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_RenderPresent(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    SDL_SetRenderTarget(renderer, targetTexture);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");

            SDL_SetRenderTarget(renderer, targetTexture); // <==========

            SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_FRect frect = (SDL_FRect){ 0, 0, 1920 / 2, 1080 / 2 };
            SDL_RenderTexture(renderer, targetTexture, NULL, &frect);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect = (SDL_FRect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
            SDL_RenderTexture(renderer, targetTexture, NULL, &frect);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect = (SDL_FRect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
            SDL_RenderTexture(renderer, targetTexture, NULL, &frect);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderClear(renderer);
            SDL_SetRenderTarget(renderer, NULL);
            frect = (SDL_FRect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
            SDL_RenderTexture(renderer, targetTexture, NULL, &frect);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderTarget(renderer, NULL); // <==========
            Present(renderer);         // <==========
        }
    }
    return 0;
}

I'll change this example to do what you want shortly.

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

Here is code that does what you want.

Note that we're always drawing into the render target, but we set the viewport to set the area of the render target that we want to draw into. Then we always (always!) copy the entire render target to the screen before presenting.

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define printf SDL_Log

static void Present(SDL_Renderer* renderer)
{
    SDL_RenderPresent(renderer);
    SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
    SDL_RenderClear(renderer);
}

int main(int argc, char *argv[])
{
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("SDL3", 1920, 1080, SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window, NULL);
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    Present(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    SDL_SetRenderTarget(renderer, targetTexture);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");

            SDL_SetRenderTarget(renderer, targetTexture); // <==========

            SDL_SetRenderViewport(renderer, NULL);
            SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
            SDL_RenderFillRect(renderer, NULL);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_Rect rect = (SDL_Rect){ 0, 0, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
            SDL_RenderFillRect(renderer, NULL);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            rect = (SDL_Rect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
            SDL_RenderFillRect(renderer, NULL);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            rect = (SDL_Rect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderFillRect(renderer, NULL);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderFillRect(renderer, NULL);
            SDL_SetRenderTarget(renderer, NULL);
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL);
            Present(renderer);
            SDL_SetRenderTarget(renderer, targetTexture);

            SDL_SetRenderTarget(renderer, NULL); // <==========
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
            Present(renderer);         // <==========
        }
    }
    return 0;
}

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

Also note that you can entirely omit the intermediate present calls:

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define printf SDL_Log

static void Present(SDL_Renderer *renderer)
{
    SDL_RenderPresent(renderer);
    SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
    SDL_RenderClear(renderer);
}

int main(int argc, char *argv[])
{
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("SDL3", 1920, 1080, SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window, NULL);
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    Present(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");

            SDL_SetRenderTarget(renderer, targetTexture); // <==========

            SDL_SetRenderViewport(renderer, NULL);
            SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
            SDL_RenderFillRect(renderer, NULL);

            SDL_Rect rect = (SDL_Rect){ 0, 0, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
            SDL_RenderFillRect(renderer, NULL);

            rect = (SDL_Rect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
            SDL_RenderFillRect(renderer, NULL);

            rect = (SDL_Rect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
            SDL_RenderFillRect(renderer, NULL);

            rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
            SDL_SetRenderViewport(renderer, &rect);
            SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
            SDL_RenderFillRect(renderer, NULL);

            SDL_SetRenderTarget(renderer, NULL);                    // <==========
            SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
            Present(renderer);                                      // <==========
        }
    }
    return 0;
}

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

Also note that when you are using the render target, you don't have to draw the entire thing. The areas you don't draw will remain what was drawn previously, which is the behavior you want:

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define printf SDL_Log

const bool debug = true;

static void Present(SDL_Renderer *renderer)
{
    SDL_RenderPresent(renderer);
    if (debug) {
        SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); // <=== if you see this color (magenta), you haven't drawn the whole frame
        SDL_RenderClear(renderer);
    }
}

int main(int argc, char *argv[])
{
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("SDL3", 1920, 1080, SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window, NULL);
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    Present(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");

            if (event.button.button == SDL_BUTTON_LEFT) {
                printf("left button, drawing all four squares\n");

                SDL_SetRenderTarget(renderer, targetTexture); // <==========

                SDL_SetRenderViewport(renderer, NULL);
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
                SDL_RenderFillRect(renderer, NULL);

                SDL_Rect rect = (SDL_Rect){ 0, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
                SDL_RenderFillRect(renderer, NULL);

                rect = (SDL_Rect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
                SDL_RenderFillRect(renderer, NULL);

                rect = (SDL_Rect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
                SDL_RenderFillRect(renderer, NULL);

                rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
                SDL_RenderFillRect(renderer, NULL);

                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========
            } else if (event.button.button == SDL_BUTTON_RIGHT) {
                printf("right button, drawing one square\n");

                SDL_SetRenderTarget(renderer, targetTexture); // <==========

                SDL_Rect rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 230, 128, 255);
                SDL_RenderFillRect(renderer, NULL);

                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========
            }
        }
    }
    return 0;
}

@PulpFiction98
Copy link
Author

PulpFiction98 commented Mar 20, 2025

Also note that when you are using the render target, you don't have to draw the entire thing. The areas you don't draw will remain what was drawn previously, which is the behavior you want:

The areas you don't draw will remain what was drawn previously, Yes Yes Yes,What I want is this, I really want to try it now, damn time difference. I will try it as soon as possible after work, thank you again.

@slouken
Copy link
Collaborator

slouken commented Mar 20, 2025

You're welcome!

@PulpFiction98
Copy link
Author

else if (event.button.button == SDL_BUTTON_RIGHT) {
                printf("right button, drawing one square\n");

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
                SDL_RenderClear(renderer);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========


                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                SDL_Rect rect = (SDL_Rect){ 0, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 230, 128, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========


                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                 rect = (SDL_Rect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 0, 128, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                rect = (SDL_Rect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 128, 0, 128, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 128, 0, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);
            }

I just tried your code and carefully understood it. I don't think this is what I wanted, so I made some minor modifications to it.
In actual project use, I did not perform frame synchronization because the latest frames from multiple cameras did not arrive simultaneously. Instead, I repeatedly drew the target texture and then rendered it. When I didn't switch the number of cameras displayed on the screen and kept playing, SDL3 ran smoothly.
When I switch from 16 video frames to 4 video frames, or from 4 video frames to 16 video frames, I need to first use renderclear to clear the current display screen, and then render the corresponding number of no video images. At this point, I noticed that not all the no video images were displayed.
As mentioned above, when using SDL2 to render the target texture with the right mouse button, I did not use synchronization and it did not experience frame loss. But there was a frame loss phenomenon when upgrading to SDL3. You told me that vertical synchronization can be used, but if I use it in my project, it will cause my video frame to lag.
Try running the code I modified above, and then click the right mouse button frantically. You may see unexpected images. It stays in the red screen after renderclear, and cannot be seen in any of the last four persions.
Please don't tell me again, I need to use 'persist' only once after completing all renderings. I can't use it this way in the project, I can only draw the target texture again and then persist the target texture.

@PulpFiction98
Copy link
Author

Image

Image

Image

Enabling vertical synchronization will solve the issue of no show, but there will be noticeable lag during video playback

@slouken

@slouken
Copy link
Collaborator

slouken commented Mar 21, 2025

Interesting, I am able to reproduce what you're seeing when spamming the right mouse button. I'm investigating...

@slouken
Copy link
Collaborator

slouken commented Mar 21, 2025

So this appears to be a bug in the direct3d11 driver. SDL2 uses the direct3d driver by default, which is why you're seeing it now and not before.

Testing the different drivers, it looks like this bug only happens on the direct3d11 driver, all the others work correctly. I'm investigating...

@PulpFiction98
Copy link
Author

So this appears to be a bug in the direct3d11 driver. SDL2 uses the direct3d driver by default, which is why you're seeing it now and not before.

Testing the different drivers, it looks like this bug only happens on the direct3d11 driver, all the others work correctly. I'm investigating...

thanks thanks

@slouken slouken added this to the 3.x milestone Mar 21, 2025
@slouken
Copy link
Collaborator

slouken commented Mar 21, 2025

I'm not sure what's happening here, but in the meantime you can create the renderer with "direct3d" as the name of the renderer to get the original renderer that you had before.

@slouken
Copy link
Collaborator

slouken commented Mar 21, 2025

For future reference, this is the complete test case, and the issue is sometimes the final color on the screen is red when spamming right clicks:

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define printf SDL_Log

const bool debug = true;

static void Present(SDL_Renderer *renderer)
{
    SDL_RenderPresent(renderer);
    if (debug) {
        SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); // <=== if you see this color (magenta), you haven't drawn the whole frame
        SDL_RenderClear(renderer);
    }
}

int main(int argc, char *argv[])
{
    printf("Hello world\n");
    SDL_Window *window;
    SDL_Renderer *renderer;
    SDL_Texture *targetTexture;
    SDL_Event event;
    SDL_Init(SDL_INIT_VIDEO);

    SDL_SetHint(SDL_HINT_RENDER_DIRECT3D11_DEBUG, "1");
    window = SDL_CreateWindow("SDL3", 1920, 1080, SDL_WINDOW_RESIZABLE);
    renderer = SDL_CreateRenderer(window, "direct3d11");
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    SDL_RenderClear(renderer);
    Present(renderer);
    targetTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 1920, 1080);
    printf("%p\n", window);
    while (1) {
        SDL_PollEvent(&event);
        if (event.type == SDL_EVENT_QUIT) {
            break;
        }

        if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
            printf("mouse button down\n");

            if (event.button.button == SDL_BUTTON_LEFT) {
                printf("left button, drawing all four squares\n");

                SDL_SetRenderTarget(renderer, targetTexture); // <==========

                SDL_SetRenderViewport(renderer, NULL);
                SDL_SetRenderDrawColor(renderer, 128, 128, 128, 255);
                SDL_RenderFillRect(renderer, NULL);

                SDL_Rect rect = (SDL_Rect){ 0, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
                SDL_RenderFillRect(renderer, NULL);

                rect = (SDL_Rect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
                SDL_RenderFillRect(renderer, NULL);

                rect = (SDL_Rect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
                SDL_RenderFillRect(renderer, NULL);

                rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
                SDL_RenderFillRect(renderer, NULL);

                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========
            } else if (event.button.button == SDL_BUTTON_RIGHT) {
                printf("right button, drawing one square\n");

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
                SDL_RenderClear(renderer);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                SDL_Rect rect = (SDL_Rect){ 0, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 230, 128, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);                                      // <==========

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                rect = (SDL_Rect){ 1920 / 2, 0, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 0, 128, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                rect = (SDL_Rect){ 0, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 128, 0, 128, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);

                SDL_SetRenderTarget(renderer, targetTexture); // <==========
                rect = (SDL_Rect){ 1920 / 2, 1080 / 2, 1920 / 2, 1080 / 2 };
                SDL_SetRenderViewport(renderer, &rect);
                SDL_SetRenderDrawColor(renderer, 255, 128, 0, 255);
                SDL_RenderFillRect(renderer, NULL);
                SDL_SetRenderTarget(renderer, NULL);                    // <==========
                SDL_RenderTexture(renderer, targetTexture, NULL, NULL); // <==== copy to screen before present!
                Present(renderer);
            }
        }
    }
    return 0;
}

@PulpFiction98
Copy link
Author

I'm not sure what's happening here, but in the meantime you can create the renderer with "direct3d" as the name of the renderer to get the original renderer that you had before.

Yes, after specifying direct3d in my project, there has been no frame loss phenomenon.

@slouken
Copy link
Collaborator

slouken commented Mar 21, 2025

Great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants