SDL_RenderGeometry - jagged edges?

I’m on macOS Monterery (don’t know if that’s relevant) and just downloaded 2.0.18. I thought I’d try out the new ability to render triangles with SDL_RenderGeometry, so I called it in the following way:

SDL_Vertex vs[4] = {
    {{40+40*cosf(M_PI/6),          40+40*sinf(M_PI/6)         }, {0, 0, 255, 255}},
    {{40+40*cosf(M_PI/6+M_PI/2),   40+40*sinf(M_PI/6+M_PI/2)  }, {0, 0, 255, 255}},
    {{40+40*cosf(M_PI/6+M_PI),     40+40*sinf(M_PI/6+M_PI)    }, {0, 0, 255, 255}},
    {{40+40*cosf(M_PI/6+3*M_PI/2), 40+40*sinf(M_PI/6+3*M_PI/2)}, {0, 0, 255, 255}},
};
int indices[6] = {0, 1, 2, 2, 3, 0};
SDL_RenderGeometry(renderer, NULL, vs, 4, indices, 6);

This results in the following image:

image

The edges of the square are jagged. Is that expected? It is very noticeable with small triangles. Is it possible to get smooth edges or am I using the API wrong?

Do you have high-dpi support enabled for your window?

I had forgotten to set that flag.

I get an image like this now:

image

Full source in case I’m missing something:

#include <SDL2/SDL.h>
#include <math.h>

int
main(){
    SDL_Window* window;
    SDL_Renderer* renderer;
    if (SDL_Init(SDL_INIT_VIDEO) < 0)
            return 1;

    window = SDL_CreateWindow("Test",
                    SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                    100, 100,
                    SDL_WINDOW_ALLOW_HIGHDPI);

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED|SDL_RENDERER_PRESENTVSYNC);

    float spin = 0;
    int paused = 1;
    for(;;){
        SDL_Event event;
        while (SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT)
                return 0;
            if(event.type == SDL_KEYDOWN){
                switch(event.key.keysym.sym){
                    case 'q': return 0;
                    case 'p': paused = !paused; break;
                }
            }
        }
        SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
        SDL_RenderClear(renderer);

        SDL_Vertex vs[4] = {
            {{40+40*cosf(M_PI*(spin+1./6)),        40+40*sinf(M_PI*(spin+1./6))       }, {0, 0, 255, 255}},
            {{40+40*cosf(M_PI*(spin+1./6+1./2)),   40+40*sinf(M_PI*(spin+1./6+1./2))  }, {0, 0, 255, 255}},
            {{40+40*cosf(M_PI*(spin+1./6+1.)),     40+40*sinf(M_PI*(spin+1./6+1.))    }, {0, 0, 255, 255}},
            {{40+40*cosf(M_PI*(spin+1./6+3*1./2)), 40+40*sinf(M_PI*(spin+1./6+3*1./2))}, {0, 0, 255, 255}},
        };
        int indices[6] = {0, 1, 2, 2, 3, 0};
        SDL_RenderGeometry(renderer, 0, vs, 4, indices, 6);

        SDL_RenderPresent(renderer);
        if(!paused)
            spin += 0.003;
    }
    return 0;
}

I don’t think SDL_Render supports MSAA or other forms of antialiasing, so for pure geometry you’ll get aliased edges like that because the GPU will treat a pixel as fully covered or fully uncovered by geometry depending on if the geometry intersects the pixel’s center, there’s no in between.

One handy trick is to have a texture that’s fully transparent around its edges, and opaque elsewhere. It’s effectively free antialiasing if the texture is rendered at its native scale using bilinear filtering, even when it’s rotated.

Add this:

SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");

I may be totally wrong, but I was under the impression that SDL_HINT_RENDER_SCALE_QUALITY only affects texture sampling (Nearest Neighbor vs Bilinear).

I think maybe the jagged edges are because the geometry is being drawn without subpixel precision? Each pixel of the target is either fully covered by the triangle or completely outside of it.

Yes, there’s no anti-aliasing

SDL_RenderGeometry() is giving me anti-aliasing, no problem.

geometry

This was rendered on Windows 10, direct3d, NVIDIA GeForce video card with the following settings:

SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2");

Lines were drawn with the following option:

SDL_SetHint(SDL_HINT_RENDER_LINE_METHOD, "3");

1 Like

I guess it must be hardware/driver dependent then. Like the OP, my RenderGeometry and Line drawing is jagged, with no antialiasing, regardless of settings.

I have added this line but I have the error: argument 2: <class 'TypeError'>: wrong type

Traceback (most recent call last):
  File "main.py", line 37, in <module>
    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2")
ctypes.ArgumentError: argument 2: <class 'TypeError'>: wrong type

pip install numpy PyOpenGL PySDL2 pysdl2-dll

main.py

import ctypes

import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from sdl2 import *

window = None
maxFPS = 5

vertexShaderSource = """
    attribute vec2 aPosition;
    void main()
    {
        gl_Position = vec4(aPosition, 0.0, 1.0);
    }
"""

fragmentShaderSource = """
    void main()
    {
        gl_FragColor = vec4(0.5, 0.8, 0.7, 1.0);
    }
"""

def fatalError(message):
    print(message)
    if window:
        SDL_DestroyWindow(window)
    SDL_Quit()
    exit(-1)

if __name__ == "__main__":
    if SDL_Init(SDL_INIT_VIDEO) < 0:
        fatalError(SDL_GetError())

    SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2")

    winW, winH = 350, 350
    window = SDL_CreateWindow(b"OpenGL21, SDL2, Python",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        winW, winH, SDL_WINDOW_OPENGL)
    if not window:
        fatalError(SDL_GetError())

    context = SDL_GL_CreateContext(window)
    if not context:
        fatalError(SDL_GetError())

    glClearColor(0.2, 0.2, 0.2, 1)
    glViewport(0, 0, winW, winH)

    program = compileProgram(
        compileShader(vertexShaderSource, GL_VERTEX_SHADER),
        compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
    glUseProgram(program)

    vertPositions = np.array([
        -0.5, -0.5,
        0.5, -0.5,
        0.0, 0.5
    ], dtype=np.float32)
    vertPosBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
        vertPositions, GL_STATIC_DRAW)
    aPositionLocation = glGetAttribLocation(program, "aPosition")
    glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, GL_FALSE,
        0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aPositionLocation)

    event = SDL_Event()
    running = True
    while running:
        while SDL_PollEvent(ctypes.byref(event)) != 0:
            if event.type == SDL_QUIT:
                running = False
        startTicks = SDL_GetTicks()

        glClear(GL_COLOR_BUFFER_BIT)
        glDrawArrays(GL_TRIANGLES, 0, 3)
        SDL_GL_SwapWindow(window)

        # Limit the FPS to the max FPS
        frameTicks = SDL_GetTicks() - startTicks
        if 1000 / maxFPS > frameTicks:
            SDL_Delay(int(1000 / maxFPS - frameTicks))

    SDL_GL_DeleteContext(context)
    SDL_DestroyWindow(window)
    SDL_Quit()

I’m no expert on Python but searching for “SDL_SetHint wrong type” took me to this page which suggests you should put a b in front of the string literal.

SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, b"2")
1 Like

This problem was solved. Thank you very much!

But for shader OpenGL 2.1, calling this function SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, b"2") has no effect:

image

You must replace it with this line:

SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4)

And it’s important to call this function before SDL_CreateWindow:

image

pip install numpy PyOpenGL PySDL2 pysdl2-dll

main.py

import ctypes

import numpy as np
from OpenGL.GL import *
from OpenGL.GL.shaders import *
from sdl2 import *

window = None
maxFPS = 5

vertexShaderSource = """
    attribute vec2 aPosition;
    void main()
    {
        gl_Position = vec4(aPosition, 0.0, 1.0);
    }
"""

fragmentShaderSource = """
    void main()
    {
        gl_FragColor = vec4(0.5, 0.8, 0.7, 1.0);
    }
"""

def fatalError(message):
    print(message)
    if window:
        SDL_DestroyWindow(window)
    SDL_Quit()
    exit(-1)

if __name__ == "__main__":
    if SDL_Init(SDL_INIT_VIDEO) < 0:
        fatalError(SDL_GetError())

    # SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, b"2")
    SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4)

    winW, winH = 350, 350
    window = SDL_CreateWindow(b"OpenGL21, SDL2, Python",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        winW, winH, SDL_WINDOW_OPENGL)
    if not window:
        fatalError(SDL_GetError())

    context = SDL_GL_CreateContext(window)
    if not context:
        fatalError(SDL_GetError())

    glClearColor(0.2, 0.2, 0.2, 1)
    glViewport(0, 0, winW, winH)

    program = compileProgram(
        compileShader(vertexShaderSource, GL_VERTEX_SHADER),
        compileShader(fragmentShaderSource, GL_FRAGMENT_SHADER))
    glUseProgram(program)

    vertPositions = np.array([
        -0.5, -0.5,
        0.5, -0.5,
        0.0, 0.5
    ], dtype=np.float32)
    vertPosBuffer = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vertPosBuffer)
    glBufferData(GL_ARRAY_BUFFER, len(vertPositions) * 4,
        vertPositions, GL_STATIC_DRAW)
    aPositionLocation = glGetAttribLocation(program, "aPosition")
    glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, GL_FALSE,
        0, ctypes.c_void_p(0))
    glEnableVertexAttribArray(aPositionLocation)

    event = SDL_Event()
    running = True
    while running:
        while SDL_PollEvent(ctypes.byref(event)) != 0:
            if event.type == SDL_QUIT:
                running = False
        startTicks = SDL_GetTicks()

        glClear(GL_COLOR_BUFFER_BIT)
        glDrawArrays(GL_TRIANGLES, 0, 3)
        SDL_GL_SwapWindow(window)

        # Limit the FPS to the max FPS
        frameTicks = SDL_GetTicks() - startTicks
        if 1000 / maxFPS > frameTicks:
            SDL_Delay(int(1000 / maxFPS - frameTicks))

    SDL_GL_DeleteContext(context)
    SDL_DestroyWindow(window)
    SDL_Quit()