view pytouhou/ui/opengl/backend.pyx @ 612:73f134f84c7f

Request a RGB888 context, since SDL2’s default of RGB332 sucks. On X11/GLX, it will select the first config available, that is the best one, while on EGL it will iterate over them to select the one closest to what the application requested. Of course, anything lower than RGB888 looks bad and we really don’t want that.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 26 Mar 2015 20:20:37 +0100
parents a6a191e371c7
children a6af3ff86612
line wrap: on
line source

from pytouhou.lib import sdl
from pytouhou.lib cimport sdl
from pytouhou.lib.sdl cimport Window

from pytouhou.lib.opengl cimport \
         (glEnable, glHint, glEnableClientState, GL_TEXTURE_2D, GL_BLEND,
          GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST,
          GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY,
          glPushDebugGroup, GL_DEBUG_SOURCE_APPLICATION, glPopDebugGroup,
          epoxy_gl_version, epoxy_is_desktop_gl, epoxy_has_gl_extension,
          GL_PRIMITIVE_RESTART, glPrimitiveRestartIndex, glPixelStorei,
          GL_PACK_INVERT_MESA, GL_QUADS, GL_TRIANGLE_STRIP, GL_TRIANGLES)


GameRenderer = None


def init(options):
    '''
    Initialize the OpenGL module, and raise if something bad prevents it from
    working.
    '''

    cdef str flavor

    global profile, major, minor, double_buffer, is_legacy, GameRenderer

    flavor = options['flavor']
    assert flavor in ('core', 'es', 'compatibility', 'legacy')
    profile = (sdl.GL_CONTEXT_PROFILE_CORE if flavor == 'core' else
               sdl.GL_CONTEXT_PROFILE_ES if flavor == 'es' else
               sdl.GL_CONTEXT_PROFILE_COMPATIBILITY)

    version = str(options['version'])
    assert len(version) == 3 and version[1] == '.'
    major = int(version[0])
    minor = int(version[2])

    maybe_double_buffer = options['double-buffer']
    double_buffer = maybe_double_buffer if maybe_double_buffer is not None else -1

    is_legacy = flavor == 'legacy' or flavor == 'compatibility' and major < 2

    #TODO: check for framebuffer/renderbuffer support.

    from pytouhou.ui.opengl.gamerenderer import GameRenderer


cdef void discover_features() except *:
    '''Discover which features are supported by our context.'''

    global use_debug_group, use_vao, use_primitive_restart, use_framebuffer_blit, use_pack_invert, use_scaled_rendering
    global primitive_mode
    global shader_header
    global is_legacy

    version = epoxy_gl_version()
    is_desktop = epoxy_is_desktop_gl()
    is_legacy = is_legacy or (is_desktop and version < 20)

    use_debug_group = (is_desktop and version >= 43) or epoxy_has_gl_extension('GL_KHR_debug')
    use_vao = (is_desktop and version >= 30) or epoxy_has_gl_extension('GL_ARB_vertex_array_object')
    use_primitive_restart = (is_desktop and version >= 31)
    use_framebuffer_blit = (is_desktop and version >= 30)
    use_pack_invert = epoxy_has_gl_extension('GL_MESA_pack_invert')
    use_scaled_rendering = not is_legacy  #TODO: try to use the EXT framebuffer extension.

    primitive_mode = (GL_QUADS if is_legacy else
                      GL_TRIANGLE_STRIP if use_primitive_restart else
                      GL_TRIANGLES)

    if not is_legacy:
        if is_desktop:
            # gl_FragColor isn’t supported anymore starting with GLSL 4.2.
            if version >= 42:
                version = 41
            try:
                glsl_version = {20: 110, 21: 120, 30: 130, 31: 140, 32: 150}[version]
            except KeyError:
                assert version >= 33
                glsl_version = version * 10
            shader_header = ('#version %d\n\n' % glsl_version).encode()
        else:
            # The attribute keyword isn’t supported past GLSL ES 3.0.
            if version >= 30:
                version = 20
            glsl_version = {20: '100', 30: '300 es'}[version]
            shader_header = ('#version %s\n\nprecision highp float;\n\n' % glsl_version).encode()


def create_window(title, x, y, width, height, swap_interval):
    '''Create a window (using SDL) and an OpenGL context.'''

    sdl.gl_set_attribute(sdl.GL_CONTEXT_PROFILE_MASK, profile)
    sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, major)
    sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, minor)
    sdl.gl_set_attribute(sdl.GL_RED_SIZE, 8)
    sdl.gl_set_attribute(sdl.GL_GREEN_SIZE, 8)
    sdl.gl_set_attribute(sdl.GL_BLUE_SIZE, 8)
    sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24 if is_legacy else 0)
    if double_buffer >= 0:
        sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, double_buffer)

    flags = sdl.WINDOW_SHOWN | sdl.WINDOW_OPENGL

    # Legacy contexts don’t support our required extensions for scaling.
    if not is_legacy:
        flags |= sdl.WINDOW_RESIZABLE

    window = Window(title, x, y, width, height, flags)
    window.gl_create_context()

    discover_features()

    # If we can’t use scaling but have previously created a resizable window,
    # recreate it unresizable.
    if not use_scaled_rendering and flags & sdl.WINDOW_RESIZABLE:
        flags &= ~sdl.WINDOW_RESIZABLE
        window = Window(title, x, y, width, height, flags)
        window.gl_create_context()

    if use_debug_group:
        glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "OpenGL initialisation")

    # Initialize OpenGL
    glEnable(GL_BLEND)
    if is_legacy:
        glEnable(GL_TEXTURE_2D)
        glHint(GL_FOG_HINT, GL_NICEST)
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
        glEnableClientState(GL_COLOR_ARRAY)
        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_TEXTURE_COORD_ARRAY)

    if use_primitive_restart:
        glEnable(GL_PRIMITIVE_RESTART)
        glPrimitiveRestartIndex(0xFFFF);

    if use_pack_invert:
        glPixelStorei(GL_PACK_INVERT_MESA, True)

    if use_debug_group:
        glPopDebugGroup()

    if swap_interval is not None:
        try:
            sdl.gl_set_swap_interval(swap_interval)
        except sdl.SDLError:
            # The OpenGL context doesn’t support setting the swap interval,
            # we’ll probably fallback to SDL_Delay-based clocking.
            pass

    return window