view pytouhou/ui/opengl/framebuffer.pyx @ 586:4b0593da29d5

Simplify framebuffer rendering with glDrawArrays, and move it all to its own file.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Wed, 08 Oct 2014 16:34:24 +0200
parents
children 6c9d8a3d853f
line wrap: on
line source

# -*- encoding: utf-8 -*-
##
## Copyright (C) 2014 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##

from pytouhou.lib.opengl cimport \
         (glPushDebugGroup, glPopDebugGroup, GL_DEBUG_SOURCE_APPLICATION,
          glGenTextures, glDeleteTextures, glBindTexture, glTexParameteri,
          glTexImage2D, GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
          GL_TEXTURE_MAG_FILTER, GL_LINEAR, GL_RGBA, GL_UNSIGNED_BYTE,
          glGenRenderbuffers, glDeleteRenderbuffers, glBindRenderbuffer,
          glRenderbufferStorage, GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
          glGenFramebuffers, glDeleteFramebuffers, glBindFramebuffer,
          glFramebufferTexture2D, glFramebufferRenderbuffer,
          glCheckFramebufferStatus, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
          GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_COMPLETE, glGenBuffers,
          glDeleteBuffers, glBindBuffer, glBufferData, GL_ARRAY_BUFFER,
          GL_STATIC_DRAW, glGenVertexArrays, glDeleteVertexArrays,
          glBindVertexArray, glVertexAttribPointer, GL_SHORT, GL_FLOAT,
          glEnableVertexAttribArray, glDrawArrays, GL_TRIANGLE_STRIP)

from .backend cimport use_debug_group, use_vao

cdef class Framebuffer:
    def __init__(self, int x, int y, int width, int height):
        if use_debug_group:
            glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Framebuffer creation")

        # This texture will receive the game area in native resolution.
        glGenTextures(1, &self.texture)
        glBindTexture(GL_TEXTURE_2D, self.texture)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexImage2D(GL_TEXTURE_2D, 0,
                     GL_RGBA,
                     width, height,
                     0,
                     GL_RGBA, GL_UNSIGNED_BYTE,
                     NULL)
        glBindTexture(GL_TEXTURE_2D, 0)

        # We need a depth buffer, but don’t care about retrieving it.
        glGenRenderbuffers(1, &self.rbo)
        glBindRenderbuffer(GL_RENDERBUFFER, self.rbo)
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height)
        glBindRenderbuffer(GL_RENDERBUFFER, 0)

        # The framebuffer is there as a rendering target.
        glGenFramebuffers(1, &self.fbo)
        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.texture, 0)
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, self.rbo)
        assert glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE
        glBindFramebuffer(GL_FRAMEBUFFER, 0)

        # We’ll use only those vertices, everytime.
        self.buf[0] = PassthroughVertex(x, y, 0, 1)
        self.buf[1] = PassthroughVertex(x + width, y, 1, 1)
        self.buf[2] = PassthroughVertex(x, y + height, 0, 0)
        self.buf[3] = PassthroughVertex(x + width, y + height, 1, 0)

        # Now we upload those vertices into a static vbo.
        glGenBuffers(1, &self.vbo)
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)
        glBufferData(GL_ARRAY_BUFFER, sizeof(self.buf), self.buf, GL_STATIC_DRAW)

        # As a performance optimisation, if supported, store the rendering state into a vao.
        if use_vao:
            glGenVertexArrays(1, &self.vao)
            glBindVertexArray(self.vao)
            self.set_state()
            glBindVertexArray(0)

        glBindBuffer(GL_ARRAY_BUFFER, 0)

        if use_debug_group:
            glPopDebugGroup()

    def __dealloc__(self):
        if use_vao:
            glDeleteVertexArrays(1, &self.vao)
        glDeleteTextures(1, &self.texture)
        glDeleteRenderbuffers(1, &self.rbo)
        glDeleteFramebuffers(1, &self.fbo)
        glDeleteBuffers(1, &self.vbo)

    cpdef bind(self):
        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)

    cdef void set_state(self) nogil:
        glBindBuffer(GL_ARRAY_BUFFER, self.vbo)

        #TODO: find a way to use offsetof() instead of those ugly hardcoded values.
        glVertexAttribPointer(0, 2, GL_SHORT, False, sizeof(PassthroughVertex), <void*>0)
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(PassthroughVertex), <void*>4)
        glEnableVertexAttribArray(1)

    cdef void render(self) nogil:
        if use_vao:
            glBindVertexArray(self.vao)
        else:
            self.set_state()

        glBindTexture(GL_TEXTURE_2D, self.texture)
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
        glBindTexture(GL_TEXTURE_2D, 0)

        if use_vao:
            glBindVertexArray(0)
        else:
            glBindBuffer(GL_ARRAY_BUFFER, 0)