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: MaOS 15.2: metal renderer artifacts on SetRenderTexture #12047

Closed
jawaidbazyar2 opened this issue Jan 22, 2025 · 3 comments
Closed

SDL3: MaOS 15.2: metal renderer artifacts on SetRenderTexture #12047

jawaidbazyar2 opened this issue Jan 22, 2025 · 3 comments

Comments

@jawaidbazyar2
Copy link

Hi,

I have an application (an 8-bit computer emulator) I am writing.

I am testing on a MacBook Pro M1 (ca 2020).

I draw the emulated screen into a Texture, and periodically RenderTexture and RenderPresent to load latest version of frame onto the screen.

I do use LockTexture, UnlockTexture. (see code below).

When SDL uses the default renderer (NULL selects Metal I believe), and SDL_RenderTexture specifies a destination Rectangle, I get artifacts outside of the destination rectangle. See attached image. There is a border area, the window is about 25 pixels border larger than the target rectangle.

Image

If I get rid of the target rectangle, and have SDL_RenderTexture blit to the whole window, there are no artifacts. (of course, if there were they might be outside the window area).
If I change the renderer from default to OpenGL, there are no artifacts either. The artifacts vary over time. As the cursor in my emulator blinks, different randomish pixels are displayed in the borders.

here is my SDL init routine, and here is the function that does the update.

I have been over the update function and the functions it calls with a fine-toothed comb and cannot find any out of bounds memory references. And again with opengl as the renderer there are no artifacts.

I am thinking this must be a bug in SDL somewhere because, to reiterate:
no artifacts using opengl renderer
no artifacts if I do not specify a destination rectangle

However, I am very new to SDL programming and would be happy to be wrong :-)

uint64_t init_display_sdl(display_state_t *ds) {
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        fprintf(stderr, "Error initializing SDL: %s\n", SDL_GetError());
        return 1;
    }

    ds->window = SDL_CreateWindow(
        "GSSquared - Apple ][ Emulator", 
        /* SDL_WINDOWPOS_UNDEFINED, 
        SDL_WINDOWPOS_UNDEFINED,  */
        (BASE_WIDTH + BORDER_WIDTH*2) * SCALE_X, 
        (BASE_HEIGHT + BORDER_HEIGHT*2) * SCALE_Y, 
        0 /* SDL_WINDOW_SHOWN */
    );

    if (!ds->window) {
        fprintf(stderr, "Error creating window: %s\n", SDL_GetError());
        return 1;
    }
    for (int i = 0; i < SDL_GetNumRenderDrivers(); i++) {
        const char *name = SDL_GetRenderDriver(i);
        printf("Render driver %d: %s\n", i, name);
    }

    // Create renderer with nearest-neighbor scaling (sharp pixels)
    ds->renderer = SDL_CreateRenderer(ds->window, /* "opengl" */ NULL /* -1, 
        SDL_RENDERER_ACCELERATED */ );
    
    if (!ds->renderer) {
        fprintf(stderr, "Error creating renderer: %s\n", SDL_GetError());
        return 1;
    }

    // Set scaling quality to nearest neighbor for sharp pixels
    /* SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); */
    SDL_SetRenderScale(ds->renderer, SCALE_X, SCALE_Y);

    // Create the screen texture
    ds->screenTexture = SDL_CreateTexture(ds->renderer,
        SDL_PIXELFORMAT_RGBA8888,
        SDL_TEXTUREACCESS_STREAMING,
        BASE_WIDTH, BASE_HEIGHT);

    if (!ds->screenTexture) {
        fprintf(stderr, "Error creating screen texture: %s\n", SDL_GetError());
        return 1;
    }

    SDL_SetTextureBlendMode(ds->screenTexture, SDL_BLENDMODE_NONE); /* GRRRRRRR. This was defaulting to SDL_BLENDMODE_BLEND. */
    // LINEAR gets us appropriately blurred pixels.
    // NEAREST gets us sharp pixels.
    // TODO: provide a UI toggle for this.
    SDL_SetTextureScaleMode(ds->screenTexture, SDL_SCALEMODE_LINEAR);

    // Clear the texture to black
    SDL_SetRenderDrawColor(ds->renderer, 0, 0, 0, 255);
    SDL_RenderClear(ds->renderer);
    SDL_RenderPresent(ds->renderer);

    SDL_RaiseWindow(ds->window);

    return 0;
}

void update_display(cpu_state *cpu) {
    display_state_t *ds = (display_state_t *)get_module_state(cpu, MODULE_DISPLAY);
    int updated = 0;
    for (int line = 0; line < 24; line++) {
        if (ds->dirty_line[line]) {
            render_line(cpu, line);
            ds->dirty_line[line] = 0;
            updated = 1;
        }
    }

    if (updated) {
        SDL_FRect dstrect = {
            (float)BORDER_WIDTH,
            (float)BORDER_HEIGHT,
            (float)BASE_WIDTH, 
            (float)BASE_HEIGHT
        };

        SDL_RenderTexture(ds->renderer, ds->screenTexture, NULL, &dstrect);
        SDL_RenderPresent(ds->renderer);
    }
}
@slouken
Copy link
Collaborator

slouken commented Jan 22, 2025

* The contents of a texture when first created are not defined.

You need to clear the texture after you create it.

@slouken slouken closed this as completed Jan 22, 2025
@jawaidbazyar2
Copy link
Author

Here is code I added at setup time. I still get the artifacts, again, outside the destination rectangle. I would understand the Texture not being set filled with random junk but wouldn't that just show And I do clear the Render target (the window) also.

I must be missing something?

    {
        void *pixels = NULL;
        int pitch = 0;
        if (!SDL_LockTexture(ds->screenTexture, NULL, &pixels, &pitch)) {
            fprintf(stderr, "Could not lock texture: %s\n", SDL_GetError());
            // handle error
        }

        // pitch is the number of bytes in one row of the texture
        // BASE_HEIGHT is the number of rows
        memset(pixels, 0, pitch * BASE_HEIGHT);

        // Unlock texture so we can use it
        SDL_UnlockTexture(ds->screenTexture);
    }

    // Clear the texture to black
    SDL_SetRenderDrawColor(ds->renderer, 0, 0, 0, 255);
    SDL_RenderClear(ds->renderer);
    SDL_RenderPresent(ds->renderer);

@slouken
Copy link
Collaborator

slouken commented Jan 22, 2025

Your backbuffer contents are also not defined. You need to clear the screen each frame before rendering your texture.

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

2 participants