From 26434f7baf03e79c01eed6df5a7e39f8018604ed Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 22 Sep 2018 16:26:10 +1000 Subject: [PATCH] [egl] initial commit of new modern OpenGL ES renderer --- client/CMakeLists.txt | 5 + client/renderers/egl.c | 291 ++++++++++++++++++++++++++++++++++ client/renderers/egl_model.c | 237 +++++++++++++++++++++++++++ client/renderers/egl_model.h | 41 +++++ client/renderers/egl_shader.c | 196 +++++++++++++++++++++++ client/renderers/egl_shader.h | 34 ++++ client/utils.c | 70 ++++++++ client/utils.h | 7 +- 8 files changed, 880 insertions(+), 1 deletion(-) create mode 100644 client/renderers/egl.c create mode 100644 client/renderers/egl_model.c create mode 100644 client/renderers/egl_model.h create mode 100644 client/renderers/egl_shader.c create mode 100644 client/renderers/egl_shader.h create mode 100644 client/utils.c diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index cf1347f7..c51677e1 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -12,6 +12,7 @@ pkg_check_modules(PKGCONFIG REQUIRED SDL2_ttf gl glu + egl spice-protocol fontconfig x11 @@ -50,11 +51,15 @@ set(SOURCES main.c lg-renderer.c ll.c + utils.c spice/rsa.c spice/spice.c decoders/null.c decoders/yuv420.c renderers/opengl.c + renderers/egl.c + renderers/egl_shader.c + renderers/egl_model.c ) add_executable(looking-glass-client ${SOURCES}) diff --git a/client/renderers/egl.c b/client/renderers/egl.c new file mode 100644 index 00000000..236e4733 --- /dev/null +++ b/client/renderers/egl.c @@ -0,0 +1,291 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 "lg-renderer.h" +#include "debug.h" + +#include +#include + +#include "egl_model.h" +#include "egl_shader.h" + +struct Options +{ +}; + +static struct Options defaultOptions = +{ +}; + +struct Models +{ + struct EGL_Model * desktop; +}; + +struct Shaders +{ + struct EGL_Shader * desktop; +}; + +struct Inst +{ + LG_RendererParams params; + struct Options opt; + + Display * xDisplay; + Window xWindow; + EGLDisplay display; + EGLConfig configs; + EGLSurface surface; + EGLContext context; + + struct Models models; + struct Shaders shaders; + + LG_RendererFormat format; + size_t frameSize; + const uint8_t * data; + bool update; +}; + +const char * egl_get_name() +{ + return "EGL"; +} + +bool egl_create(void ** opaque, const LG_RendererParams params) +{ + // create our local storage + *opaque = malloc(sizeof(struct Inst)); + if (!*opaque) + { + DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst)); + return false; + } + memset(*opaque, 0, sizeof(struct Inst)); + + // safe off parameteres and init our default option values + struct Inst * this = (struct Inst *)*opaque; + memcpy(&this->params, ¶ms , sizeof(LG_RendererParams)); + memcpy(&this->opt , &defaultOptions, sizeof(struct Options )); + + return true; +} + +bool egl_initialize(void * opaque, Uint32 * sdlFlags) +{ + *sdlFlags = SDL_WINDOW_OPENGL; + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER , 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS , 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES , 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + return true; +} + +void egl_deinitialize(void * opaque) +{ + struct Inst * this = (struct Inst *)opaque; + + egl_model_free (&this->models .desktop); + egl_shader_free(&this->shaders.desktop); + free(this); +} + +void egl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect) +{ + glViewport(0, 0, width, height); +} + +bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data) +{ + return true; +} + +bool egl_on_mouse_event(void * opaque, const bool visible , const int x, const int y) +{ + return true; +} + +bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data) +{ + struct Inst * this = (struct Inst *)opaque; + if (format.type != FRAME_TYPE_ARGB) + return false; + + memcpy(&this->format, &format, sizeof(LG_RendererFormat)); + this->frameSize = format.height * format.pitch; + this->data = data; + this->update = true; + + return true; +} + +void egl_on_alert(void * opaque, const LG_RendererAlert alert, const char * message, bool ** closeFlag) +{ +} + +bool egl_render_startup(void * opaque, SDL_Window * window) +{ + struct Inst * this = (struct Inst *)opaque; + + SDL_SysWMinfo wminfo; + SDL_VERSION(&wminfo.version); + if (!SDL_GetWindowWMInfo(window, &wminfo)) + { + DEBUG_ERROR("SDL_GetWindowWMInfo failed"); + return false; + } + + this->xDisplay = wminfo.info.x11.display; + this->xWindow = wminfo.info.x11.window; + + this->display = eglGetDisplay((EGLNativeDisplayType)this->xDisplay); + if (this->display == EGL_NO_DISPLAY) + { + DEBUG_ERROR("eglGetDisplay failed"); + return false; + } + + if (!eglInitialize(this->display, NULL, NULL)) + { + DEBUG_ERROR("Unable to initialize EGL"); + return false; + } + + EGLint attr[] = + { + EGL_BUFFER_SIZE, 16, + EGL_RENDERABLE_TYPE, + EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + EGLint num_config; + if (!eglChooseConfig(this->display, attr, &this->configs, 1, &num_config)) + { + DEBUG_ERROR("Failed to choose config (eglError: 0x%x)", eglGetError()); + return false; + } + + this->surface = eglCreateWindowSurface(this->display, this->configs, this->xWindow, NULL); + if (this->surface == EGL_NO_SURFACE) + { + DEBUG_ERROR("Failed to create EGL surface (eglError: 0x%x)", eglGetError()); + return false; + } + + EGLint ctxattr[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + this->context = eglCreateContext(this->display, this->configs, EGL_NO_CONTEXT, ctxattr); + if (this->context == EGL_NO_CONTEXT) + { + DEBUG_ERROR("Failed to create EGL context (eglError: 0x%x)", eglGetError()); + return false; + } + + eglMakeCurrent(this->display, this->surface, this->surface, this->context); + + DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR )); + DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER)); + DEBUG_INFO("Version : %s", glGetString(GL_VERSION )); + + static const GLfloat desktop[] = + { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + -1.0f, 1.0f, 0.0f, + 1.0f, 1.0f, 0.0f + }; + + static const GLfloat uvs[] = + { + 0.0f, 1.0f, + 1.0f, 1.0f, + 0.0f, 0.0f, + 1.0f, 0.0f + }; + + if (!egl_shader_init(&this->shaders.desktop) ) return false; + if (!egl_shader_load(this->shaders.desktop, "test.vs", "test.fs")) return false; + if (!egl_model_init(&this->models.desktop) ) return false; + + egl_model_set_verticies(this->models.desktop, desktop, sizeof(desktop) / sizeof(GLfloat)); + egl_model_set_uvs (this->models.desktop, uvs , sizeof(uvs ) / sizeof(GLfloat)); + egl_model_set_shader (this->models.desktop, this->shaders.desktop); + + return true; +} + +bool egl_render(void * opaque, SDL_Window * window) +{ + struct Inst * this = (struct Inst *)opaque; + + if (this->update) + { + if (!egl_model_is_streaming(this->models.desktop)) + egl_model_init_streaming( + this->models.desktop, + this->format.width, + this->format.height, + this->frameSize + ); + + egl_model_stream_buffer( + this->models.desktop, + this->data, + this->frameSize + ); + + this->update = false; + } + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + egl_model_render(this->models.desktop); + + eglSwapBuffers(this->display, this->surface); + return true; +} + +static LG_RendererOpt egl_options[] = +{ +}; + +struct LG_Renderer LGR_EGL = +{ + .create = egl_create, + .get_name = egl_get_name, + .options = egl_options, + .option_count = LGR_OPTION_COUNT(egl_options), + .initialize = egl_initialize, + .deinitialize = egl_deinitialize, + .on_resize = egl_on_resize, + .on_mouse_shape = egl_on_mouse_shape, + .on_mouse_event = egl_on_mouse_event, + .on_frame_event = egl_on_frame_event, + .on_alert = egl_on_alert, + .render_startup = egl_render_startup, + .render = egl_render +}; \ No newline at end of file diff --git a/client/renderers/egl_model.c b/client/renderers/egl_model.c new file mode 100644 index 00000000..ab7850cf --- /dev/null +++ b/client/renderers/egl_model.c @@ -0,0 +1,237 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 "egl_model.h" +#include "egl_shader.h" +#include "debug.h" +#include "utils.h" + +#include +#include +#include + +#include + +struct EGL_Model +{ + bool hasVertexBuffer; + GLuint vertexBuffer; + GLsizei vertexCount; + + bool hasUVBuffer; + GLuint uvBuffer; + + EGL_Shader * shader; + + bool hasTexture; + GLuint texture; + + bool hasPBO, pboUpdate; + GLuint pbo[2]; + int pboIndex; + size_t pboWidth, pboHeight; +}; + +bool egl_model_init(EGL_Model ** model) +{ + *model = (EGL_Model *)malloc(sizeof(EGL_Model)); + if (!*model) + { + DEBUG_ERROR("Failed to malloc EGL_Model"); + return false; + } + + memset(*model, 0, sizeof(EGL_Model)); + return true; +} + +void egl_model_free(EGL_Model ** model) +{ + if (!*model) + return; + + if ((*model)->hasVertexBuffer) + glDeleteBuffers(1, &(*model)->vertexBuffer); + + if ((*model)->hasUVBuffer) + glDeleteBuffers(1, &(*model)->uvBuffer); + + if ((*model)->hasTexture) + glDeleteTextures(1, &(*model)->texture); + + if ((*model)->hasPBO) + glDeleteBuffers(2, (*model)->pbo); + + free(*model); + *model = NULL; +} + +bool egl_model_init_streaming(EGL_Model * model, size_t width, size_t height, size_t bufferSize) +{ + model->pboWidth = width; + model->pboHeight = height; + + glBindTexture(GL_TEXTURE_2D, egl_model_get_texture_id(model)); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + + if (!model->hasPBO) + glGenBuffers(2, model->pbo); + + for(int i = 0; i < 2; ++i) + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, model->pbo[i]); + glBufferData( + GL_PIXEL_UNPACK_BUFFER, + bufferSize, + NULL, + GL_STREAM_DRAW + ); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + + model->hasPBO = true; + return true; +} + +bool egl_model_is_streaming(EGL_Model * model) +{ + return model->hasPBO; +} + +bool egl_model_stream_buffer(EGL_Model * model, const uint8_t * buffer, size_t bufferSize) +{ + if (++model->pboIndex == 2) + model->pboIndex = 0; + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, model->pbo[model->pboIndex]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, bufferSize, 0, GL_STREAM_DRAW); + GLubyte * ptr = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (!ptr) + { + DEBUG_ERROR("Failed to map the buffer"); + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + return false; + } + + memcpy(ptr, buffer, bufferSize); + + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + model->pboUpdate = true; + return true; +} + +void egl_model_set_verticies(EGL_Model * model, const GLfloat * verticies, const size_t count) +{ + if (model->hasVertexBuffer) + glDeleteBuffers(1, &model->vertexBuffer); + + glGenBuffers(1, &model->vertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, model->vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * count, verticies, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + model->hasVertexBuffer = true; + model->vertexCount = count / 3; +} + +void egl_model_set_uvs(EGL_Model * model, const GLfloat * uvs, const size_t count) +{ + if (model->hasUVBuffer) + glDeleteBuffers(1, &model->uvBuffer); + + glGenBuffers(1, &model->uvBuffer); + glBindBuffer(GL_ARRAY_BUFFER, model->uvBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * count, uvs, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + model->hasUVBuffer = true; +} + +void egl_model_render(EGL_Model * model) +{ + if (!model->hasVertexBuffer) + { + DEBUG_ERROR("Model has no verticies"); + return; + } + + if (model->shader) + egl_shader_use(model->shader); + + GLuint location = 0; + glEnableVertexAttribArray(location); + glBindBuffer(GL_ARRAY_BUFFER, model->vertexBuffer); + glVertexAttribPointer(location, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + if (model->hasUVBuffer) + { + ++location; + glEnableVertexAttribArray(location); + glBindBuffer(GL_ARRAY_BUFFER, model->uvBuffer); + glVertexAttribPointer(location, 2, GL_FLOAT, GL_FALSE, 0, (void*)0); + } + + if (model->hasTexture) + glBindTexture(GL_TEXTURE_2D, model->texture); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, model->vertexCount); + + if (model->hasTexture) + glBindTexture(GL_TEXTURE_2D, 0); + + while(location > 0) + glDisableVertexAttribArray(location--); + glDisableVertexAttribArray(0); + + if (model->shader) + glUseProgram(0); + + if (model->pboUpdate) + { + glBindTexture(GL_TEXTURE_2D, egl_model_get_texture_id(model)); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, model->pbo[model->pboIndex]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, model->pboWidth, model->pboHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0); + model->pboUpdate = false; + } +} + +void egl_model_set_shader(EGL_Model * model, EGL_Shader * shader) +{ + model->shader = shader; +} + +GLuint egl_model_get_texture_id(EGL_Model * model) +{ + if (model->hasTexture) + return model->texture; + + glGenTextures(1, &model->texture); + model->hasTexture = true; + + return model->texture; +} \ No newline at end of file diff --git a/client/renderers/egl_model.h b/client/renderers/egl_model.h new file mode 100644 index 00000000..f2b422aa --- /dev/null +++ b/client/renderers/egl_model.h @@ -0,0 +1,41 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 +*/ + +#pragma once + +#include +#include "egl_shader.h" + +#define GL_GLEXT_PROTOTYPES +#include + +typedef struct EGL_Model EGL_Model; + +bool egl_model_init(EGL_Model ** model); +void egl_model_free(EGL_Model ** model); + +bool egl_model_init_streaming(EGL_Model * model, size_t width, size_t height, size_t bufferSize); +bool egl_model_is_streaming (EGL_Model * model); +bool egl_model_stream_buffer (EGL_Model * model, const uint8_t * buffer, size_t bufferSize); +void egl_model_set_verticies (EGL_Model * model, const GLfloat * verticies, const size_t count); +void egl_model_set_uvs (EGL_Model * model, const GLfloat * uvs , const size_t count); +void egl_model_set_shader (EGL_Model * model, EGL_Shader * shader); +GLuint egl_model_get_texture_id(EGL_Model * model); + +void egl_model_render(EGL_Model * model); \ No newline at end of file diff --git a/client/renderers/egl_shader.c b/client/renderers/egl_shader.c new file mode 100644 index 00000000..0f725849 --- /dev/null +++ b/client/renderers/egl_shader.c @@ -0,0 +1,196 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 "egl_shader.h" +#include "debug.h" +#include "utils.h" + +#include +#include +#include + +#include + +struct EGL_Shader +{ + bool hasShader; + GLuint shader; +}; + +bool egl_shader_init(EGL_Shader ** shader) +{ + *shader = (EGL_Shader *)malloc(sizeof(EGL_Shader)); + if (!*shader) + { + DEBUG_ERROR("Failed to malloc EGL_Shader"); + return false; + } + + memset(*shader, 0, sizeof(EGL_Shader)); + return true; +} + +void egl_shader_free(EGL_Shader ** shader) +{ + if (!*shader) + return; + + if ((*shader)->hasShader) + glDeleteProgram((*shader)->shader); + + free(*shader); + *shader = NULL; +} + +bool egl_shader_load(EGL_Shader * shader, const char * vertex_file, const char * fragment_file) +{ + char * vertex_code, * fragment_code; + size_t vertex_size, fragment_size; + + if (!file_get_contents(vertex_file, &vertex_code, &vertex_size)) + { + DEBUG_ERROR("Failed to read vertex shader"); + return false; + } + + DEBUG_INFO("Loaded vertex shader: %s", vertex_file); + + if (!file_get_contents(fragment_file, &fragment_code, &fragment_size)) + { + DEBUG_ERROR("Failed to read fragment shader"); + free(vertex_code); + return false; + } + + DEBUG_INFO("Loaded fragment shader: %s", fragment_file); + + bool ret = egl_shader_compile(shader, vertex_code, vertex_size, fragment_code, fragment_size); + free(vertex_code); + free(fragment_code); + return ret; +} + +bool egl_shader_compile(EGL_Shader * shader, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size) +{ + if (shader->hasShader) + { + glDeleteProgram(shader->shader); + shader->hasShader = false; + } + + GLint length; + GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); + + length = vertex_size; + glShaderSource(vertexShader, 1, (const char**)&vertex_code, &length); + glCompileShader(vertexShader); + + GLint result = GL_FALSE; + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &result); + if (result == GL_FALSE) + { + DEBUG_ERROR("Failed to compile vertex shader"); + + int logLength; + glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) + { + char *log = malloc(logLength + 1); + glGetShaderInfoLog(vertexShader, logLength, NULL, log); + log[logLength] = 0; + DEBUG_ERROR("%s", log); + free(log); + } + + glDeleteShader(vertexShader); + return false; + } + + GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + length = fragment_size; + glShaderSource(fragmentShader, 1, (const char**)&fragment_code, &length); + glCompileShader(fragmentShader); + + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &result); + if (result == GL_FALSE) + { + DEBUG_ERROR("Failed to compile fragment shader"); + + int logLength; + glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) + { + char *log = malloc(logLength + 1); + glGetShaderInfoLog(fragmentShader, logLength, NULL, log); + log[logLength] = 0; + DEBUG_ERROR("%s", log); + free(log); + } + + glDeleteShader(fragmentShader); + glDeleteShader(vertexShader ); + return false; + } + + shader->shader = glCreateProgram(); + glAttachShader(shader->shader, vertexShader ); + glAttachShader(shader->shader, fragmentShader); + glLinkProgram(shader->shader); + + glGetProgramiv(shader->shader, GL_LINK_STATUS, &result); + if (result == GL_FALSE) + { + DEBUG_ERROR("Failed to link shader program"); + + int logLength; + glGetProgramiv(shader->shader, GL_INFO_LOG_LENGTH, &logLength); + if (logLength > 0) + { + char *log = malloc(logLength + 1); + glGetProgramInfoLog(shader->shader, logLength, NULL, log); + log[logLength] = 0; + DEBUG_ERROR("%s", log); + free(log); + } + + glDetachShader(shader->shader, vertexShader ); + glDetachShader(shader->shader, fragmentShader); + glDeleteShader(fragmentShader); + glDeleteShader(vertexShader ); + glDeleteProgram(shader->shader ); + return false; + } + + glDetachShader(shader->shader, vertexShader ); + glDetachShader(shader->shader, fragmentShader); + glDeleteShader(fragmentShader); + glDeleteShader(vertexShader ); + + shader->hasShader = true; + return true; +} + +void egl_shader_use(EGL_Shader * shader) +{ + if (shader->hasShader) + glUseProgram(shader->shader); + else + DEBUG_ERROR("Shader program has not been compiled"); +} \ No newline at end of file diff --git a/client/renderers/egl_shader.h b/client/renderers/egl_shader.h new file mode 100644 index 00000000..cbd12068 --- /dev/null +++ b/client/renderers/egl_shader.h @@ -0,0 +1,34 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 +*/ + +#pragma once + +#include + +#define GL_GLEXT_PROTOTYPES +#include + +typedef struct EGL_Shader EGL_Shader; + +bool egl_shader_init(EGL_Shader ** shader); +void egl_shader_free(EGL_Shader ** shader); + +bool egl_shader_load (EGL_Shader * model, const char * vertex_file, const char * fragment_file); +bool egl_shader_compile(EGL_Shader * model, const char * vertex_code, size_t vertex_size, const char * fragment_code, size_t fragment_size); +void egl_shader_use (EGL_Shader * shader); \ No newline at end of file diff --git a/client/utils.c b/client/utils.c new file mode 100644 index 00000000..4b3fb926 --- /dev/null +++ b/client/utils.c @@ -0,0 +1,70 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae +https://looking-glass.hostfission.com + +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 "utils.h" +#include "debug.h" + +#include +#include + +bool file_get_contents(const char * filename, char ** buffer, size_t * length) +{ + FILE * fh = fopen(filename, "r"); + if (!fh) + { + DEBUG_ERROR("Failed to open the file: %s", filename); + return false; + } + + if (fseek(fh, 0, SEEK_END) != 0) + { + DEBUG_ERROR("Failed to seek"); + fclose(fh); + return false; + } + + long fsize = ftell(fh); + if (fseek(fh, 0, SEEK_SET) != 0) + { + DEBUG_ERROR("Failed to seek"); + fclose(fh); + return false; + } + + *buffer = malloc(fsize + 1); + if (!*buffer) + { + DEBUG_ERROR("Failed to allocate buffer of %lu bytes", fsize + 1); + fclose(fh); + return false; + } + + if (fread(*buffer, 1, fsize, fh) != fsize) + { + DEBUG_ERROR("Failed to read the entire file"); + fclose(fh); + free(*buffer); + return false; + } + + fclose(fh); + buffer[fsize] = 0; + *length = fsize; + return true; +} \ No newline at end of file diff --git a/client/utils.h b/client/utils.h index 62e00b3d..a8b4d140 100644 --- a/client/utils.h +++ b/client/utils.h @@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include +#include static inline uint64_t microtime() { @@ -91,4 +92,8 @@ static inline int32_t decode_s_golomb(const uint8_t * const base, size_t * const { const uint32_t g = decode_u_golomb(base, offset); return (g & 0x1) ? (g + 1) / 2 : -(g / 2); -} \ No newline at end of file +} + +// reads the specified file into a new buffer +// the callee must free the buffer +bool file_get_contents(const char * filename, char ** buffer, size_t * length); \ No newline at end of file