view pytouhou/ui/opengl/backend.pyx @ 617:a6af3ff86612

Change all “void except *” function into “bint except True”, to prevent PyErr_Occurred() from being called at each call.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sun, 29 Mar 2015 00:08:20 +0100
parents 73f134f84c7f
children 80687f258001
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 bint discover_features() except True:
    '''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