# HG changeset patch # User Emmanuel Gil Peyrot # Date 1412778864 -7200 # Node ID 4b0593da29d51df347c78a25c2613d4a797a8d0e # Parent e0166cda75d50efc90bdb234ed341391520c27f6 Simplify framebuffer rendering with glDrawArrays, and move it all to its own file. diff --git a/pytouhou/lib/opengl.pxd b/pytouhou/lib/opengl.pxd --- 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) diff --git a/pytouhou/ui/opengl/background.pxd b/pytouhou/ui/opengl/background.pxd --- 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 * diff --git a/pytouhou/ui/opengl/background.pyx b/pytouhou/ui/opengl/background.pyx --- 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: diff --git a/pytouhou/ui/opengl/framebuffer.pxd b/pytouhou/ui/opengl/framebuffer.pxd 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 diff --git a/pytouhou/ui/opengl/framebuffer.pyx b/pytouhou/ui/opengl/framebuffer.pyx 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 +## +## 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), 0) + glEnableVertexAttribArray(0) + glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(PassthroughVertex), 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) diff --git a/pytouhou/ui/opengl/gamerenderer.pxd b/pytouhou/ui/opengl/gamerenderer.pxd --- 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): 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 @@ -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 diff --git a/pytouhou/ui/opengl/renderer.pxd b/pytouhou/ui/opengl/renderer.pxd --- 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) diff --git a/pytouhou/ui/opengl/renderer.pyx b/pytouhou/ui/opengl/renderer.pyx --- 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), 0) - glEnableVertexAttribArray(0) - glVertexAttribPointer(1, 2, GL_FLOAT, False, sizeof(PassthroughVertex), 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)