From 063ec7dc02ffd87271462264cf35ec2dc9bbb927 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 11 Jan 2017 23:36:43 +0530 Subject: [PATCH] Fallback implementation for glCopyImageSubData OS X is missing this function so we fallback to a very slow, GPU->CPU->GPU roundtrip. Fortunately this is needed only rarely so it should not have a noticeable performance imapct. Le bubbling sigh! --- kitty/gl.h | 47 ++++++++++++++++++++++++++++++++++++++++++----- kitty/shaders.py | 18 +++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/kitty/gl.h b/kitty/gl.h index 340f42f78..dee085985 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -127,10 +127,6 @@ _glewInit(PyObject UNUSED *self) { PyErr_Format(PyExc_RuntimeError, "GLEW init failed: %s", glewGetErrorString(err)); return NULL; } - if(!GLEW_ARB_copy_image) { - PyErr_SetString(PyExc_RuntimeError, "OpenGL is missing the required ARB_copy_image extension"); - return NULL; - } if(!GLEW_ARB_texture_storage) { PyErr_SetString(PyExc_RuntimeError, "OpenGL is missing the required ARB_texture_storage extension"); return NULL; @@ -439,6 +435,10 @@ static PyObject* CopyImageSubData(PyObject UNUSED *self, PyObject *args) { int src_target, src_level, srcX, srcY, srcZ, dest_target, dest_level, destX, destY, destZ; unsigned int src, dest, width, height, depth; + if(!GLEW_ARB_copy_image) { + PyErr_SetString(PyExc_RuntimeError, "OpenGL is missing the required ARB_copy_image extension"); + return NULL; + } if (!PyArg_ParseTuple(args, "IiiiiiIiiiiiIII", &src, &src_target, &src_level, &srcX, &srcY, &srcZ, &dest, &dest_target, &dest_level, &destX, &destY, &destZ, @@ -451,6 +451,26 @@ CopyImageSubData(PyObject UNUSED *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject* +copy_image_sub_data(PyObject UNUSED *self, PyObject *args) { + int src_target, dest_target; + unsigned int width, height, num_levels; + if (!PyArg_ParseTuple(args, "iiIII", &src_target, &dest_target, &width, &height, &num_levels)) return NULL; + uint8_t *src = (uint8_t*)PyMem_Malloc(5 * width * height * num_levels); + if (src == NULL) return PyErr_NoMemory(); + uint8_t *dest = src + (4 * width * height * num_levels); + Py_BEGIN_ALLOW_THREADS; + glBindTexture(GL_TEXTURE_2D_ARRAY, src_target); + glGetTexImage(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, GL_UNSIGNED_BYTE, src); + glBindTexture(GL_TEXTURE_2D_ARRAY, dest_target); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + for(unsigned int i = 0; i < width * height * num_levels; i++) dest[i] = src[4*i]; + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, num_levels, GL_RED, GL_UNSIGNED_BYTE, dest); + Py_END_ALLOW_THREADS; + PyMem_Free(src); + Py_RETURN_NONE; +} + static PyObject* TexSubImage3D(PyObject UNUSED *self, PyObject *args) { int target, level, x, y, z, fmt, type; @@ -466,6 +486,21 @@ TexSubImage3D(PyObject UNUSED *self, PyObject *args) { Py_RETURN_NONE; } + +static PyObject* +GetTexImage(PyObject UNUSED *self, PyObject *args) { + int target, level, fmt, type; + PyObject *pixels; + if (!PyArg_ParseTuple(args, "iiiiO!", &target, &level, &fmt, &type, &PyLong_Type, &pixels)) return NULL; + void *data = PyLong_AsVoidPtr(pixels); + if (data == NULL) { PyErr_SetString(PyExc_TypeError, "Not a valid data pointer"); return NULL; } + Py_BEGIN_ALLOW_THREADS; + glGetTexImage(target, level, fmt, type, data); + Py_END_ALLOW_THREADS; + CHECK_ERROR; + Py_RETURN_NONE; +} + static PyObject* NamedBufferData(PyObject UNUSED *self, PyObject *args) { int usage; @@ -609,7 +644,7 @@ int add_module_gl_constants(PyObject *module) { GLC(GL_TEXTURE_MIN_FILTER); GLC(GL_TEXTURE_MAG_FILTER); GLC(GL_TEXTURE_WRAP_S); GLC(GL_TEXTURE_WRAP_T); GLC(GL_UNPACK_ALIGNMENT); - GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_RGB32UI); + GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_RGB32UI); GLC(GL_RGBA); GLC(GL_TEXTURE_BUFFER); GLC(GL_STATIC_DRAW); GLC(GL_STREAM_DRAW); GLC(GL_SRC_ALPHA); GLC(GL_ONE_MINUS_SRC_ALPHA); GLC(GL_BLEND); GLC(GL_FLOAT); GLC(GL_ARRAY_BUFFER); @@ -619,6 +654,7 @@ int add_module_gl_constants(PyObject *module) { #define GL_METHODS \ {"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, \ + {"copy_image_sub_data", (PyCFunction)copy_image_sub_data, METH_VARARGS, NULL}, \ {"glewInit", (PyCFunction)_glewInit, METH_NOARGS, NULL}, \ METH(Viewport, METH_VARARGS) \ METH(CheckError, METH_NOARGS) \ @@ -668,6 +704,7 @@ int add_module_gl_constants(PyObject *module) { METH(TexStorage3D, METH_VARARGS) \ METH(CopyImageSubData, METH_VARARGS) \ METH(TexSubImage3D, METH_VARARGS) \ + METH(GetTexImage, METH_VARARGS) \ METH(NamedBufferData, METH_VARARGS) \ METH(BlendFunc, METH_VARARGS) \ diff --git a/kitty/shaders.py b/kitty/shaders.py index c404869a5..adc055512 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -2,6 +2,7 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal +import sys from ctypes import addressof, sizeof from functools import lru_cache from threading import Lock @@ -23,8 +24,9 @@ from .fast_data_types import ( GL_TEXTURE_BUFFER, GL_RGB32UI, GL_FLOAT, GL_ARRAY_BUFFER, glBindBuffer, glPixelStorei, glTexBuffer, glActiveTexture, glTexStorage3D, glCopyImageSubData, glTexSubImage3D, ITALIC, BOLD, SpriteMap, - glEnableVertexAttribArray, glVertexAttribPointer + glEnableVertexAttribArray, glVertexAttribPointer, copy_image_sub_data ) +from .utils import safe_print GL_VERSION = (3, 3) VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10 @@ -120,8 +122,18 @@ class Sprites: ynum = self.backend.ynum if self.backend.z == 0: ynum -= 1 # Only copy the previous rows - glCopyImageSubData(self.texture_id, tgt, 0, 0, 0, 0, tex, tgt, 0, 0, 0, 0, - width, ynum * self.cell_height, self.last_num_of_layers) + try: + glCopyImageSubData(self.texture_id, tgt, 0, 0, 0, 0, tex, tgt, 0, 0, 0, 0, + width, ynum * self.cell_height, self.last_num_of_layers) + except RuntimeError: + # OpenGL does not have ARB_copy_image + if not hasattr(self, 'realloc_warned'): + safe_print( + 'WARNING: Your system\'s OpenGL implementation does not have glCopyImageSubData, falling back to a slower implementation', + file=sys.stderr) + self.realloc_warned = True + copy_image_sub_data(self.texture_id, tex, width, ynum * self.cell_height, self.last_num_of_layers) + glBindTexture(tgt, tex) glDeleteTexture(self.texture_id) self.last_num_of_layers = znum self.last_ynum = self.backend.ynum