changeset 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 e0166cda75d5
children 6c9d8a3d853f
files pytouhou/lib/opengl.pxd pytouhou/ui/opengl/background.pxd pytouhou/ui/opengl/background.pyx pytouhou/ui/opengl/framebuffer.pxd pytouhou/ui/opengl/framebuffer.pyx pytouhou/ui/opengl/gamerenderer.pxd pytouhou/ui/opengl/gamerenderer.pyx pytouhou/ui/opengl/renderer.pxd pytouhou/ui/opengl/renderer.pyx
diffstat 9 files changed, 153 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/pytouhou/lib/opengl.pxd
+++ b/pytouhou/lib/opengl.pxd
@@ -132,6 +132,7 @@ cdef extern from 'epoxy/gl.h' nogil:
     void glColorPointer(GLint size, GLenum_type type_, GLsizei stride, GLvoid *pointer)
 
     void glBlendFunc(GLenum_blendfunc sfactor, GLenum_blendfunc dfactor)
+    void glDrawArrays(GLenum_mode mode, GLint first, GLsizei count)
     void glDrawElements(GLenum_mode mode, GLsizei count, GLenum_type type_, const GLvoid *indices)
     void glEnable(GLenum cap)
     void glDisable(GLenum cap)
@@ -187,9 +188,12 @@ cdef extern from 'epoxy/gl.h' nogil:
     void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)
 
     void glGenFramebuffers(GLsizei n, GLuint *ids)
+    void glDeleteFramebuffers(GLsizei n, GLuint *ids)
     void glBindFramebuffer(GLenum_framebuffer target, GLuint framebuffer)
     void glFramebufferTexture2D(GLenum_framebuffer target, GLenum_attachment attachment, GLenum_textarget textarget, GLuint texture, GLint level)
+
     void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers)
+    void glDeleteRenderbuffers(GLsizei n, GLuint *renderbuffers)
     void glBindRenderbuffer(GLenum_renderbuffer target, GLuint renderbuffer)
     void glRenderbufferStorage(GLenum_renderbuffer target, GLenum_renderbuffer_format internalformat, GLsizei width, GLsizei height)
     void glFramebufferRenderbuffer(GLenum_framebuffer target, GLenum_attachment attachment, GLenum_renderbuffer renderbuffertarget, GLuint renderbuffer)
--- a/pytouhou/ui/opengl/background.pxd
+++ b/pytouhou/ui/opengl/background.pxd
@@ -1,5 +1,4 @@
 from pytouhou.lib.opengl cimport GLuint, GLushort, GLsizei
-from .renderer cimport Renderer
 
 cdef struct Vertex:
     float x, y, z
@@ -21,4 +20,4 @@ cdef class BackgroundRenderer:
 
     cdef void set_state(self) nogil
     cdef void render_background(self) except *
-    cdef void load(self, background, Renderer renderer) except *
+    cdef void load(self, background, GLuint[MAX_TEXTURES] textures) except *
--- a/pytouhou/ui/opengl/background.pyx
+++ b/pytouhou/ui/opengl/background.pyx
@@ -109,7 +109,7 @@ cdef class BackgroundRenderer:
             glPopDebugGroup()
 
 
-    cdef void load(self, background, Renderer renderer):
+    cdef void load(self, background, GLuint[MAX_TEXTURES] textures):
         cdef float ox, oy, oz, ox2, oy2, oz2
         cdef GLsizei nb_vertices = 0, nb_indices = 0
 
@@ -153,7 +153,7 @@ cdef class BackgroundRenderer:
         # either in RAM or in VRAM, they will never change until we implement
         # background animation.
 
-        self.texture = renderer.textures[key >> 1]
+        self.texture = textures[key >> 1]
         self.nb_indices = nb_indices
 
         if is_legacy:
new file mode 100644
--- /dev/null
+++ b/pytouhou/ui/opengl/framebuffer.pxd
@@ -0,0 +1,14 @@
+from pytouhou.lib.opengl cimport GLuint
+
+cdef struct PassthroughVertex:
+    short x, y
+    float u, v
+
+
+cdef class Framebuffer:
+    cdef GLuint fbo, texture, rbo, vbo, vao
+    cdef PassthroughVertex[4] buf
+
+    cpdef bind(self)
+    cdef void set_state(self) nogil
+    cdef void render(self) nogil
new file mode 100644
--- /dev/null
+++ b/pytouhou/ui/opengl/framebuffer.pyx
@@ -0,0 +1,121 @@
+# -*- 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)
--- a/pytouhou/ui/opengl/gamerenderer.pxd
+++ b/pytouhou/ui/opengl/gamerenderer.pxd
@@ -1,7 +1,8 @@
 from pytouhou.utils.matrix cimport Matrix
 from pytouhou.game.game cimport Game
 from .background cimport BackgroundRenderer
-from .renderer cimport Renderer, Framebuffer
+from .renderer cimport Renderer
+from .framebuffer cimport Framebuffer
 from .shader cimport Shader
 
 cdef class GameRenderer(Renderer):
--- a/pytouhou/ui/opengl/gamerenderer.pyx
+++ b/pytouhou/ui/opengl/gamerenderer.pyx
@@ -71,7 +71,7 @@ cdef class GameRenderer(Renderer):
         self.background = background
         if background is not None:
             self.background_renderer = BackgroundRenderer()
-            self.background_renderer.load(background, self)
+            self.background_renderer.load(background, self.textures)
         else:
             self.background_renderer = None
 
--- a/pytouhou/ui/opengl/renderer.pxd
+++ b/pytouhou/ui/opengl/renderer.pxd
@@ -1,6 +1,7 @@
 from cpython cimport PyObject
 from pytouhou.lib.opengl cimport GLuint
 from .texture cimport TextureManager, FontManager
+from .framebuffer cimport Framebuffer
 
 cdef struct Vertex:
     short x, y, z, padding
@@ -8,11 +9,6 @@ cdef struct Vertex:
     unsigned char r, g, b, a
 
 
-cdef struct PassthroughVertex:
-    short x, y
-    float u, v
-
-
 cdef class Texture:
     cdef long key
     cdef GLuint texture
@@ -30,8 +26,8 @@ cdef class Renderer:
     cdef long x, y, width, height
 
     # For modern GL.
-    cdef GLuint vbo, framebuffer_vbo, framebuffer_ibo
-    cdef GLuint vao, framebuffer_vao
+    cdef GLuint vbo
+    cdef GLuint vao
 
     cdef GLuint textures[MAX_TEXTURES]
     cdef unsigned short *indices[MAX_TEXTURES][2]
@@ -41,13 +37,4 @@ cdef class Renderer:
     cdef void set_state(self) nogil
     cdef void render_elements(self, elements) except *
     cdef void render_quads(self, rects, colors, GLuint texture) except *
-
-    cdef void set_framebuffer_state(self) nogil
     cdef void render_framebuffer(self, Framebuffer fb) except *
-
-
-cdef class Framebuffer:
-    cdef GLuint fbo, texture, rbo
-    cdef int x, y, width, height
-
-    cpdef bind(self)
--- a/pytouhou/ui/opengl/renderer.pyx
+++ b/pytouhou/ui/opengl/renderer.pyx
@@ -23,18 +23,11 @@ from pytouhou.lib.opengl cimport \
           GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, GL_UNSIGNED_BYTE,
           GL_UNSIGNED_SHORT, GL_SHORT, GL_FLOAT, GL_SRC_ALPHA,
           GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO, GL_TEXTURE_2D, GL_TRIANGLES,
-          glGenBuffers, glBindFramebuffer, glViewport, glDeleteBuffers,
-          glGenTextures, glTexParameteri, glTexImage2D, glGenRenderbuffers,
-          glBindRenderbuffer, glRenderbufferStorage, glGenFramebuffers,
-          glFramebufferTexture2D, glFramebufferRenderbuffer,
-          glCheckFramebufferStatus, GL_FRAMEBUFFER, GL_TEXTURE_MIN_FILTER,
-          GL_LINEAR, GL_TEXTURE_MAG_FILTER, GL_RGBA, GL_RENDERBUFFER,
-          GL_DEPTH_COMPONENT16, GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
-          GL_FRAMEBUFFER_COMPLETE, glClear, GL_COLOR_BUFFER_BIT,
-          GL_DEPTH_BUFFER_BIT, GLuint, glDeleteTextures,
-          GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW, glGenVertexArrays,
+          GL_TRIANGLE_STRIP, glGenBuffers, glBindFramebuffer, glViewport,
+          glDeleteBuffers, GL_FRAMEBUFFER, glClear, GL_COLOR_BUFFER_BIT,
+          GL_DEPTH_BUFFER_BIT, GLuint, glDeleteTextures, glGenVertexArrays,
           glDeleteVertexArrays, glBindVertexArray, glPushDebugGroup,
-          GL_DEBUG_SOURCE_APPLICATION, glPopDebugGroup, GL_TRIANGLE_STRIP)
+          GL_DEBUG_SOURCE_APPLICATION, glPopDebugGroup)
 
 from pytouhou.lib.sdl import SDLError
 
@@ -95,18 +88,13 @@ cdef long find_objects(Renderer self, ob
 cdef class Renderer:
     def __dealloc__(self):
         if not is_legacy:
-            glDeleteBuffers(1, &self.framebuffer_vbo)
             glDeleteBuffers(1, &self.vbo)
 
             if use_vao:
                 glDeleteVertexArrays(1, &self.vao)
-                glDeleteVertexArrays(1, &self.framebuffer_vao)
 
 
     def __init__(self, resource_loader):
-        # Only used in modern GL.
-        cdef unsigned short framebuffer_indices[6]
-
         self.texture_manager = TextureManager(resource_loader, self, Texture)
         font_name = join(resource_loader.game_dir, 'font.ttf')
         try:
@@ -116,29 +104,16 @@ cdef class Renderer:
             logger.error('Font file “%s” not found, disabling text rendering altogether.', font_name)
 
         if not is_legacy:
-            framebuffer_indices[:] = [0, 1, 2, 2, 3, 0]
-
             if use_debug_group:
                 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Renderer creation")
 
             glGenBuffers(1, &self.vbo)
-            glGenBuffers(1, &self.framebuffer_vbo)
-            glGenBuffers(1, &self.framebuffer_ibo)
-
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.framebuffer_ibo)
-            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(framebuffer_indices), framebuffer_indices, GL_STATIC_DRAW)
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
 
             if use_vao:
                 glGenVertexArrays(1, &self.vao)
                 glBindVertexArray(self.vao)
                 self.set_state()
 
-                glGenVertexArrays(1, &self.framebuffer_vao)
-                glBindVertexArray(self.framebuffer_vao)
-                self.set_framebuffer_state()
-                glBindVertexArray(0)
-
             if use_debug_group:
                 glPopDebugGroup()
 
@@ -157,19 +132,6 @@ cdef class Renderer:
         glBindBuffer(GL_ARRAY_BUFFER, 0)
 
 
-    cdef void set_framebuffer_state(self) nogil:
-        glBindBuffer(GL_ARRAY_BUFFER, self.framebuffer_vbo)
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.framebuffer_ibo)
-
-        #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)
-
-        glBindBuffer(GL_ARRAY_BUFFER, 0)
-
-
     cdef void render_elements(self, elements):
         cdef Element element
 
@@ -309,8 +271,6 @@ cdef class Renderer:
 
 
     cdef void render_framebuffer(self, Framebuffer fb):
-        cdef PassthroughVertex[4] buf
-
         assert not is_legacy
 
         if use_debug_group:
@@ -321,69 +281,7 @@ cdef class Renderer:
         glBlendFunc(GL_ONE, GL_ZERO)
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
-        if use_vao:
-            glBindVertexArray(self.framebuffer_vao)
-        else:
-            self.set_framebuffer_state()
-
-        buf[0] = PassthroughVertex(fb.x, fb.y, 0, 1)
-        buf[1] = PassthroughVertex(fb.x + fb.width, fb.y, 1, 1)
-        buf[2] = PassthroughVertex(fb.x + fb.width, fb.y + fb.height, 1, 0)
-        buf[3] = PassthroughVertex(fb.x, fb.y + fb.height, 0, 0)
-
-        glBindBuffer(GL_ARRAY_BUFFER, self.framebuffer_vbo)
-        glBufferData(GL_ARRAY_BUFFER, sizeof(buf), buf, GL_DYNAMIC_DRAW)
-        glBindBuffer(GL_ARRAY_BUFFER, 0)
-
-        glBindTexture(GL_TEXTURE_2D, fb.texture)
-        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, NULL)
-        glBindTexture(GL_TEXTURE_2D, 0)
-
-        if use_vao:
-            glBindVertexArray(0)
-        else:
-            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
+        fb.render()
 
         if use_debug_group:
             glPopDebugGroup()
-
-
-cdef class Framebuffer:
-    def __init__(self, int x, int y, int width, int height):
-        self.x = x
-        self.y = y
-        self.width = width
-        self.height = height
-
-        if use_debug_group:
-            glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Framebuffer creation")
-
-        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)
-
-        glGenRenderbuffers(1, &self.rbo)
-        glBindRenderbuffer(GL_RENDERBUFFER, self.rbo)
-        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height)
-        glBindRenderbuffer(GL_RENDERBUFFER, 0)
-
-        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)
-
-        if use_debug_group:
-            glPopDebugGroup()
-
-    cpdef bind(self):
-        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)