/** * Looking Glass * Copyright (C) 2017-2021 The Looking Glass Authors * https://looking-glass.io * * 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; either version 2 of the License, or (at your option) * any later version. * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "damage.h" #include "common/debug.h" #include "common/KVMFR.h" #include "common/locking.h" #include "app.h" #include "shader.h" #include #include // these headers are auto generated by cmake #include "damage.vert.h" #include "damage.frag.h" struct EGL_Damage { EGL_Shader * shader; GLfloat transform[6]; GLuint buffers[2]; GLuint vao; int count; bool show; KeybindHandle toggleHandle; int width , height; float translateX, translateY; float scaleX , scaleY; bool rotate; // uniforms GLint uTransform; }; void egl_damage_show_toggle(int key, void * opaque) { EGL_Damage * damage = opaque; damage->show ^= true; } bool egl_damage_init(EGL_Damage ** damage) { *damage = (EGL_Damage *)malloc(sizeof(EGL_Damage)); if (!*damage) { DEBUG_ERROR("Failed to malloc EGL_Alert"); return false; } memset(*damage, 0, sizeof(EGL_Damage)); if (!egl_shader_init(&(*damage)->shader)) { DEBUG_ERROR("Failed to initialize the damage shader"); return false; } if (!egl_shader_compile((*damage)->shader, b_shader_damage_vert, b_shader_damage_vert_size, b_shader_damage_frag, b_shader_damage_frag_size)) { DEBUG_ERROR("Failed to compile the damage shader"); return false; } glGenVertexArrays(1, &(*damage)->vao); glBindVertexArray((*damage)->vao); glGenBuffers(2, (*damage)->buffers); glBindBuffer(GL_ARRAY_BUFFER, (*damage)->buffers[0]); glBufferData(GL_ARRAY_BUFFER, KVMFR_MAX_DAMAGE_RECTS * 8 * sizeof(GLfloat), NULL, GL_STREAM_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL); glBindBuffer(GL_ARRAY_BUFFER, 0); GLushort indices[KVMFR_MAX_DAMAGE_RECTS * 6]; for (int i = 0; i < KVMFR_MAX_DAMAGE_RECTS; ++i) { indices[6 * i + 0] = 4 * i + 0; indices[6 * i + 1] = 4 * i + 1; indices[6 * i + 2] = 4 * i + 2; indices[6 * i + 3] = 4 * i + 0; indices[6 * i + 4] = 4 * i + 2; indices[6 * i + 5] = 4 * i + 3; } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (*damage)->buffers[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW); glBindVertexArray(0); (*damage)->count = -1; (*damage)->uTransform = egl_shader_get_uniform_location((*damage)->shader, "transform"); (*damage)->toggleHandle = app_registerKeybind(KEY_A, egl_damage_show_toggle, *damage, "Toggle damage display"); return true; } void egl_damage_free(EGL_Damage ** damage) { if (!*damage) return; app_releaseKeybind(&(*damage)->toggleHandle); glDeleteVertexArrays(1, &(*damage)->vao); glDeleteBuffers(2, (*damage)->buffers); egl_shader_free(&(*damage)->shader); free(*damage); *damage = NULL; } static void update_matrix(EGL_Damage * damage) { int width = damage->rotate ? damage->height : damage->width; int height = damage->rotate ? damage->width : damage->height; damage->transform[0] = 2.0f * damage->scaleX / width; damage->transform[1] = 0.0f; damage->transform[2] = 0.0f; damage->transform[3] = -2.0f * damage->scaleY / height; damage->transform[4] = damage->translateX - damage->scaleX; damage->transform[5] = damage->translateY + damage->scaleY; } void egl_damage_setup(EGL_Damage * damage, int width, int height) { damage->width = width; damage->height = height; update_matrix(damage); } void egl_damage_resize(EGL_Damage * damage, float translateX, float translateY, float scaleX, float scaleY) { damage->translateX = translateX; damage->translateY = translateY; damage->scaleX = scaleX; damage->scaleY = scaleY; update_matrix(damage); } inline static void rectToVertices(GLfloat * vertex, const FrameDamageRect * rect) { vertex[0] = rect->x; vertex[1] = rect->y; vertex[2] = rect->x + rect->width; vertex[3] = rect->y; vertex[4] = rect->x + rect->width; vertex[5] = rect->y + rect->height; vertex[6] = rect->x; vertex[7] = rect->y + rect->height; } bool egl_damage_render(EGL_Damage * damage, bool rotate, const struct DesktopDamage * data) { if (!damage->show || (!data && damage->count == -1)) return false; if (rotate != damage->rotate) { damage->rotate = rotate; update_matrix(damage); } glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); egl_shader_use(damage->shader); glUniformMatrix3x2fv(damage->uTransform, 1, GL_FALSE, damage->transform); if (data) { damage->count = data->count; GLfloat vertices[KVMFR_MAX_DAMAGE_RECTS * 8]; if (damage->count == 0) { FrameDamageRect full = { .x = 0, .y = 0, .width = damage->width, .height = damage->height, }; damage->count = 1; rectToVertices(vertices, &full); } else { for (int i = 0; i < damage->count; ++i) rectToVertices(vertices + i * 8, data->rects + i); } glBindBuffer(GL_ARRAY_BUFFER, damage->buffers[0]); glBufferSubData(GL_ARRAY_BUFFER, 0, damage->count * 8 * sizeof(GLfloat), vertices); glBindBuffer(GL_ARRAY_BUFFER, 0); } glBindVertexArray(damage->vao); glDrawElements(GL_TRIANGLES, 6 * damage->count, GL_UNSIGNED_SHORT, NULL); glBindVertexArray(0); glDisable(GL_BLEND); return true; }