# HG changeset patch # User Emmanuel Gil Peyrot # Date 1401359515 -7200 # Node ID 8f51e34d911c37ca844c5f6bde862931cad32b06 # Parent aad758aef26d11bc26363f9f9a757a8ca01c28f2 Refactor graphics backend selection, to make them fallbackable and optional. diff --git a/pytouhou/ui/opengl/backend.pxd b/pytouhou/ui/opengl/backend.pxd new file mode 100644 --- /dev/null +++ b/pytouhou/ui/opengl/backend.pxd @@ -0,0 +1,6 @@ +cdef str flavor +cdef float version +cdef int major +cdef int minor +cdef bint double_buffer +cdef bint is_legacy diff --git a/pytouhou/ui/opengl/backend.pyx b/pytouhou/ui/opengl/backend.pyx new file mode 100644 --- /dev/null +++ b/pytouhou/ui/opengl/backend.pyx @@ -0,0 +1,63 @@ +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) + +IF USE_GLEW: + from pytouhou.lib.opengl cimport glewInit + + +GameRenderer = None + + +def init(options): + global flavor, version, major, minor, double_buffer, is_legacy, GameRenderer + + flavor = options['flavor'] + assert flavor in ('core', 'es', 'compatibility', 'legacy') + + version = options['version'] + major = int(version) + minor = (version * 10) % 10 + + double_buffer = options['double-buffer'] + is_legacy = flavor == 'legacy' + + #TODO: check for framebuffer/renderbuffer support. + + from pytouhou.ui.opengl.gamerenderer import GameRenderer + + +def create_window(title, x, y, width, height): + 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_DOUBLEBUFFER, double_buffer) + sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24) + + flags = sdl.WINDOW_SHOWN | sdl.WINDOW_OPENGL + + #TODO: legacy can support one of the framebuffer extensions. + if not is_legacy: + flags |= sdl.WINDOW_RESIZABLE + + window = Window(title, x, y, width, height, flags) + window.gl_create_context() + + if USE_GLEW: + if glewInit() != 0: + raise Exception('GLEW init fail!') + + # 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) + + return window diff --git a/pytouhou/ui/opengl/gamerenderer.pyx b/pytouhou/ui/opengl/gamerenderer.pyx --- a/pytouhou/ui/opengl/gamerenderer.pyx +++ b/pytouhou/ui/opengl/gamerenderer.pyx @@ -27,6 +27,7 @@ from pytouhou.utils.maths cimport perspe from pytouhou.game.text cimport NativeText, GlyphCollection from .shaders.eosd import GameShader, BackgroundShader, PassthroughShader from .renderer cimport Texture +from .backend cimport is_legacy from collections import namedtuple Rect = namedtuple('Rect', 'x y w h') @@ -34,8 +35,8 @@ Color = namedtuple('Color', 'r g b a') cdef class GameRenderer(Renderer): - def __init__(self, resource_loader, window): - self.use_fixed_pipeline = window.use_fixed_pipeline #XXX + def __init__(self, resource_loader, _): + self.use_fixed_pipeline = is_legacy #XXX Renderer.__init__(self, resource_loader) diff --git a/pytouhou/ui/sdl/backend.pyx b/pytouhou/ui/sdl/backend.pyx new file mode 100644 --- /dev/null +++ b/pytouhou/ui/sdl/backend.pyx @@ -0,0 +1,16 @@ +from pytouhou.lib cimport sdl +from pytouhou.lib.sdl cimport Window + + +GameRenderer = None + + +def init(_): + global GameRenderer + from pytouhou.ui.sdl.gamerenderer import GameRenderer + + +def create_window(title, x, y, width, height): + window = Window(title, x, y, width, height, sdl.WINDOW_SHOWN) + window.create_renderer(0) + return window diff --git a/pytouhou/ui/window.pxd b/pytouhou/ui/window.pxd --- a/pytouhou/ui/window.pxd +++ b/pytouhou/ui/window.pxd @@ -20,7 +20,6 @@ cdef class Runner: cdef class Window: cdef sdl.Window win - cdef public bint use_fixed_pipeline cdef Runner runner cdef Clock clock diff --git a/pytouhou/ui/window.pyx b/pytouhou/ui/window.pyx --- a/pytouhou/ui/window.pyx +++ b/pytouhou/ui/window.pyx @@ -14,15 +14,6 @@ cimport cython -IF USE_OPENGL: - 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) - - IF USE_GLEW: - from pytouhou.lib.opengl cimport glewInit - cdef class Clock: def __init__(self, long fps=-1): @@ -86,48 +77,14 @@ cdef class Runner: cdef class Window: - def __init__(self, bint double_buffer=True, long fps_limit=-1, - bint fixed_pipeline=False, bint sound=True, bint opengl=True): - self.use_fixed_pipeline = fixed_pipeline + def __init__(self, backend, long fps_limit=-1): self.runner = None - flags = sdl.WINDOW_SHOWN - - if USE_OPENGL and opengl: - sdl.gl_set_attribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2) - sdl.gl_set_attribute(sdl.GL_CONTEXT_MINOR_VERSION, 1) - sdl.gl_set_attribute(sdl.GL_DOUBLEBUFFER, int(double_buffer)) - sdl.gl_set_attribute(sdl.GL_DEPTH_SIZE, 24) - - flags |= sdl.WINDOW_OPENGL - - #TODO: implement it in the SDL backend too. - if not self.use_fixed_pipeline: - flags |= sdl.WINDOW_RESIZABLE - - self.win = sdl.Window('PyTouhou', - sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, - 640, 480, #XXX - flags) - - if USE_OPENGL and opengl: - self.win.gl_create_context() - - IF USE_GLEW: - if glewInit() != 0: - raise Exception('GLEW init fail!') - - # Initialize OpenGL - glEnable(GL_BLEND) - if self.use_fixed_pipeline: - 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) - else: - self.win.create_renderer(0) + if backend is not None: + self.win = backend.create_window( + 'PyTouhou', + sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, + 640, 480) #XXX self.clock = Clock(fps_limit) @@ -154,7 +111,8 @@ cdef class Window: cdef bint running = False if self.runner is not None: running = self.runner.update() - self.win.present() + if self.win is not None: + self.win.present() self.clock.tick() return running diff --git a/scripts/pytouhou b/scripts/pytouhou --- a/scripts/pytouhou +++ b/scripts/pytouhou @@ -52,14 +52,17 @@ netplay_group.add_argument('--remote', m netplay_group.add_argument('--friendly-fire', action='store_true', help='Allow friendly-fire during netplay.') graphics_group = parser.add_argument_group('Graphics options') -graphics_group.add_argument('--backend', metavar='BACKEND', choices=['opengl', 'sdl'], default='opengl', help='Which backend to use (opengl or sdl for now).') -graphics_group.add_argument('--fixed-pipeline', action='store_true', help='Use the fixed pipeline instead of the new programmable one.') -graphics_group.add_argument('--single-buffer', action='store_true', help='Disable double buffering.') +graphics_group.add_argument('--backend', metavar='BACKEND', choices=['opengl', 'sdl'], default=['opengl', 'sdl'], nargs='*', help='Which backend to use (opengl or sdl).') graphics_group.add_argument('--fps-limit', metavar='FPS', default=-1, type=int, help='Set fps limit. A value of 0 disables fps limiting, while a negative value limits to 60 fps if and only if vsync doesn’t work.') graphics_group.add_argument('--no-background', action='store_false', help='Disable background display (huge performance boost on slow systems).') graphics_group.add_argument('--no-particles', action='store_false', help='Disable particles handling (huge performance boost on slow systems).') graphics_group.add_argument('--no-sound', action='store_false', help='Disable music and sound effects.') +opengl_group = parser.add_argument_group('OpenGL backend options') +opengl_group.add_argument('--single-buffer', action='store_true', help='Disable double buffering.') +opengl_group.add_argument('--gl-flavor', choices=['core', 'es', 'compatibility', 'legacy'], default='compatibility', help='OpenGL profile to use.') +opengl_group.add_argument('--gl-version', default=2.1, type=float, help='OpenGL version to use.') + args = parser.parse_args() @@ -88,16 +91,34 @@ from pytouhou.formats.hint import Hint from pytouhou.network import Network -if args.backend == 'opengl': +from importlib import import_module +for backend in args.backend: + if backend == 'opengl': + options = { + 'double-buffer': not args.single_buffer, + 'flavor': args.gl_flavor, + 'version': args.gl_version + } + else: + options = {} + try: - from pytouhou.ui.opengl.gamerenderer import GameRenderer - opengl = True - except ImportError: - args.backend = 'sdl' + backend = import_module('pytouhou.ui.%s.backend' % backend) + except ImportError as e: + continue -if args.backend == 'sdl': - from pytouhou.ui.sdl.gamerenderer import GameRenderer - opengl = False + try: + backend.init(options) + except Exception as e: + print('Backend', backend, 'failed to initialize:', e) + pass + else: + GameRenderer = backend.GameRenderer + break +else: + show_simple_message_box(u'No graphical backend could be used, continuing with a windowless game.') + backend = None + GameRenderer = None class GameBossRush(Game): @@ -196,7 +217,7 @@ def main(window, path, data, stage_num, common = Common(resource_loader, characters, continues, stage_num - 1) interface = Interface(resource_loader, common.players[0]) #XXX common.interface = interface #XXX - renderer = GameRenderer(resource_loader, window) + renderer = GameRenderer(resource_loader, window) if GameRenderer is not None else None runner = GameRunner(window, renderer, common, resource_loader, skip_replay, con) window.set_runner(runner) @@ -276,9 +297,7 @@ def main(window, path, data, stage_num, with SDL(sound=args.no_sound): - window = Window(double_buffer=(not args.single_buffer), - fps_limit=args.fps_limit, - fixed_pipeline=args.fixed_pipeline, opengl=opengl) + window = Window(backend, fps_limit=args.fps_limit) main(window, args.path, tuple(args.data), args.stage, args.rank, args.character, args.replay, args.save_replay, args.skip_replay, diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -104,8 +104,6 @@ for directory, _, files in os.walk('pyto extension_names.append(extension_name) if extension_name == 'pytouhou.lib.sdl': compile_args = sdl_args - elif extension_name == 'pytouhou.ui.window' and use_opengl: - compile_args = opengl_args elif extension_name == 'pytouhou.ui.anmrenderer' and not anmviewer: extension_names.pop() continue