diff --git a/client/renderers/egl.c b/client/renderers/egl.c index ae845575..1db3dacb 100644 --- a/client/renderers/egl.c +++ b/client/renderers/egl.c @@ -44,7 +44,9 @@ struct Models struct Shaders { - struct EGL_Shader * desktop; + struct EGL_Shader * rgba; + struct EGL_Shader * bgra; + struct EGL_Shader * yuv; }; struct Textures @@ -68,11 +70,13 @@ struct Inst struct Shaders shaders; struct Textures textures; - LG_RendererFormat format; - bool sourceChanged; - size_t frameSize; - const uint8_t * data; - bool update; + LG_RendererFormat format; + enum EGL_PixelFormat pixFmt; + EGL_Shader * shader; + bool sourceChanged; + size_t frameSize; + const uint8_t * data; + bool update; }; const char * egl_get_name() @@ -114,7 +118,9 @@ void egl_deinitialize(void * opaque) struct Inst * this = (struct Inst *)opaque; egl_model_free (&this->models .desktop); - egl_shader_free (&this->shaders .desktop); + egl_shader_free (&this->shaders .rgba ); + egl_shader_free (&this->shaders .bgra ); + egl_shader_free (&this->shaders .yuv ); egl_texture_free(&this->textures.desktop); free(this); } @@ -137,8 +143,6 @@ bool egl_on_mouse_event(void * opaque, const bool visible , const int x, const i 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; this->sourceChanged = ( this->sourceChanged || @@ -151,7 +155,24 @@ bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uin if (this->sourceChanged) { memcpy(&this->format, &format, sizeof(LG_RendererFormat)); - this->frameSize = format.height * format.pitch; + + switch(format.type) + { + case FRAME_TYPE_ARGB: + this->pixFmt = EGL_PF_RGBA; + this->shader = this->shaders.rgba; + this->frameSize = format.height * format.pitch; + break; + + case FRAME_TYPE_YUV420: + this->pixFmt = EGL_PF_YUV420; + this->shader = this->shaders.yuv; + this->frameSize = format.width * format.height * 3 / 2; + break; + + default: + return false; + } } this->data = data; @@ -249,14 +270,13 @@ bool egl_render_startup(void * opaque, SDL_Window * window) 1.0f, 0.0f }; - if (!egl_shader_init(&this->shaders.desktop)) - return false; + if (!egl_shader_init(&this->shaders.rgba)) return false; + if (!egl_shader_init(&this->shaders.bgra)) return false; + if (!egl_shader_init(&this->shaders.yuv )) return false; - if (!egl_shader_compile(this->shaders.desktop, - egl_vertex_shader_basic, sizeof(egl_vertex_shader_basic), - egl_fragment_shader_bgra, sizeof(egl_fragment_shader_bgra) - )) - return false; + if (!egl_shader_compile(this->shaders.rgba, egl_vertex_shader_basic, sizeof(egl_vertex_shader_basic), egl_fragment_shader_rgba, sizeof(egl_fragment_shader_rgba))) return false; + if (!egl_shader_compile(this->shaders.bgra, egl_vertex_shader_basic, sizeof(egl_vertex_shader_basic), egl_fragment_shader_bgra, sizeof(egl_fragment_shader_bgra))) return false; + if (!egl_shader_compile(this->shaders.yuv , egl_vertex_shader_basic, sizeof(egl_vertex_shader_basic), egl_fragment_shader_yuv , sizeof(egl_fragment_shader_yuv ))) return false; if (!egl_texture_init(&this->textures.desktop)) return false; @@ -266,7 +286,6 @@ bool egl_render_startup(void * opaque, SDL_Window * window) 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); egl_model_set_texture (this->models.desktop, this->textures.desktop); eglSwapInterval(this->display, this->opt.vsync ? 1 : 0); @@ -284,12 +303,15 @@ bool egl_render(void * opaque, SDL_Window * window) this->sourceChanged = false; if (!egl_texture_init_streaming( this->textures.desktop, + this->pixFmt, this->format.width, this->format.height, this->frameSize )) return false; - } + + egl_model_set_shader(this->models.desktop, this->shader); + } if (!egl_texture_stream_buffer(this->textures.desktop, this->data)) return false; diff --git a/client/renderers/egl_model.c b/client/renderers/egl_model.c index e3fd87c4..f97ed4c8 100644 --- a/client/renderers/egl_model.c +++ b/client/renderers/egl_model.c @@ -43,6 +43,8 @@ struct EGL_Model EGL_Texture * texture; }; +void update_uniform_bindings(EGL_Model * model); + bool egl_model_init(EGL_Model ** model) { *model = (EGL_Model *)malloc(sizeof(EGL_Model)); @@ -138,9 +140,20 @@ void egl_model_render(EGL_Model * model) void egl_model_set_shader(EGL_Model * model, EGL_Shader * shader) { model->shader = shader; + update_uniform_bindings(model); } void egl_model_set_texture(EGL_Model * model, EGL_Texture * texture) { model->texture = texture; + update_uniform_bindings(model); +} + +void update_uniform_bindings(EGL_Model * model) +{ + if (!model->shader || !model->texture) + return; + + const int count = egl_texture_count(model->texture); + egl_shader_associate_textures(model->shader, count); } \ No newline at end of file diff --git a/client/renderers/egl_shader.c b/client/renderers/egl_shader.c index 0f725849..88e9552e 100644 --- a/client/renderers/egl_shader.c +++ b/client/renderers/egl_shader.c @@ -193,4 +193,22 @@ void egl_shader_use(EGL_Shader * shader) glUseProgram(shader->shader); else DEBUG_ERROR("Shader program has not been compiled"); +} + +void egl_shader_associate_textures(EGL_Shader * shader, const int count) +{ + char name[] = "sampler1"; + glUseProgram(shader->shader); + for(int i = 0; i < count; ++i, name[7]++) + { + GLint loc = glGetUniformLocation(shader->shader, name); + if (loc == -1) + { + DEBUG_WARN("Shader uniform location `%s` not found", name); + continue; + } + + glUniform1i(loc, i); + } + glUseProgram(0); } \ No newline at end of file diff --git a/client/renderers/egl_shader.h b/client/renderers/egl_shader.h index cbd12068..04a8c416 100644 --- a/client/renderers/egl_shader.h +++ b/client/renderers/egl_shader.h @@ -31,4 +31,6 @@ 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 +void egl_shader_use (EGL_Shader * shader); + +void egl_shader_associate_textures(EGL_Shader * shader, const int count); \ No newline at end of file diff --git a/client/renderers/egl_shader_progs.h b/client/renderers/egl_shader_progs.h index fb46385d..87292724 100644 --- a/client/renderers/egl_shader_progs.h +++ b/client/renderers/egl_shader_progs.h @@ -38,16 +38,16 @@ void main()\ "; static const char egl_fragment_shader_rgba[] = "\ -#version 300 es\ +#version 300 es\n\ \ in highp vec2 uv;\ out highp vec3 color;\ \ -uniform sampler2D sampler;\ +uniform sampler2D sampler1;\ \ void main()\ {\ - color = texture(sampler, uv).rgb;\ + color = texture(sampler1, uv).rgb;\ }\ "; @@ -57,15 +57,48 @@ static const char egl_fragment_shader_bgra[] = "\ in highp vec2 uv;\ out highp vec3 color;\ \ -uniform sampler2D sampler;\ +uniform sampler2D sampler1;\ \ void main()\ {\ - highp vec3 tmp = texture(sampler, uv).rgb;\ + highp vec3 tmp = texture(sampler1, uv).rgb;\ color.r = tmp.b;\ color.g = tmp.g;\ color.b = tmp.r;\ }\ "; +static const char egl_fragment_shader_yuv[] = "\ +#version 300 es\n\ +\ +in highp vec2 uv;\ +out highp vec3 color;\ +\ +uniform sampler2D sampler1;\ +uniform sampler2D sampler2;\ +uniform sampler2D sampler3;\ +\ +void main()\ +{\ + highp vec4 yuv = vec4(\ + texture(sampler1, uv).r,\ + texture(sampler2, uv).r,\ + texture(sampler3, uv).r,\ + 1.0\ + );\ + \ + highp mat4 yuv_to_rgb = mat4(\ + 1.0, 0.0 , 1.402, -0.701,\ + 1.0, -0.344, -0.714, 0.529,\ + 1.0, 1.772, 0.0 , -0.886,\ + 1.0, 1.0 , 1.0 , 1.0\ + );\ + yuv = yuv * yuv_to_rgb;\ + \ + color.r = yuv.r;\ + color.g = yuv.g;\ + color.b = yuv.b;\ +}\ +"; + #endif \ No newline at end of file diff --git a/client/renderers/egl_texture.c b/client/renderers/egl_texture.c index 14a91d3d..29cb9fc6 100644 --- a/client/renderers/egl_texture.c +++ b/client/renderers/egl_texture.c @@ -29,9 +29,16 @@ Place, Suite 330, Boston, MA 02111-1307 USA struct EGL_Texture { - GLuint texture; + enum EGL_PixelFormat pixFmt; size_t width, height; + int textureCount; + GLuint textures[3]; + GLuint samplers[3]; + size_t planes[3][2]; + GLintptr offsets[3]; + GLenum format; + bool hasPBO; GLuint pbo[2]; int pboIndex; @@ -48,7 +55,6 @@ bool egl_texture_init(EGL_Texture ** texture) } memset(*texture, 0, sizeof(EGL_Texture)); - glGenTextures(1, &(*texture)->texture); return true; } @@ -58,7 +64,11 @@ void egl_texture_free(EGL_Texture ** texture) if (!*texture) return; - glDeleteTextures(1, &(*texture)->texture); + if ((*texture)->textureCount > 0) + { + glDeleteTextures((*texture)->textureCount, (*texture)->textures); + glDeleteSamplers((*texture)->textureCount, (*texture)->samplers); + } if ((*texture)->hasPBO) glDeleteBuffers(2, (*texture)->pbo); @@ -67,19 +77,63 @@ void egl_texture_free(EGL_Texture ** texture) *texture = NULL; } -bool egl_texture_init_streaming(EGL_Texture * texture, size_t width, size_t height, size_t bufferSize) +bool egl_texture_init_streaming(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t bufferSize) { - texture->width = width; - texture->height = height; + if (texture->textureCount > 0) + { + glDeleteTextures(texture->textureCount, texture->textures); + texture->textureCount = 0; + } + + texture->pixFmt = pixFmt; + texture->width = width; + texture->height = height; texture->pboBufferSize = bufferSize; - glBindTexture(GL_TEXTURE_2D, texture->texture); - 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); + switch(pixFmt) + { + case EGL_PF_RGBA: + case EGL_PF_BGRA: + texture->textureCount = 1; + texture->format = GL_BGRA; + texture->planes[0][0] = width; + texture->planes[0][1] = height; + texture->offsets[0] = 0; + break; + + case EGL_PF_YUV420: + texture->textureCount = 3; + texture->format = GL_RED; + texture->planes[0][0] = width; + texture->planes[0][1] = height; + texture->planes[1][0] = width / 2; + texture->planes[1][1] = height / 2; + texture->planes[2][0] = width / 2; + texture->planes[2][1] = height / 2; + texture->offsets[0] = 0; + texture->offsets[1] = width * height; + texture->offsets[2] = texture->offsets[1] + (texture->offsets[1] / 4); + break; + + default: + DEBUG_ERROR("Unsupported pixel format"); + return false; + } + + glGenTextures(texture->textureCount, texture->textures); + glGenSamplers(texture->textureCount, texture->samplers); + for(int i = 0; i < texture->textureCount; ++i) + { + glSamplerParameteri(texture->samplers[i], GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(texture->samplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(texture->samplers[i], GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); + glSamplerParameteri(texture->samplers[i], GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + + glBindTexture(GL_TEXTURE_2D, texture->textures[i]); + glTexImage2D(GL_TEXTURE_2D, 0, texture->format, texture->planes[i][0], texture->planes[i][1], + 0, texture->format, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + } if (!texture->hasPBO) { @@ -108,10 +162,13 @@ bool egl_texture_stream_buffer(EGL_Texture * texture, const uint8_t * buffer) texture->pboIndex = 0; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->pbo[texture->pboIndex]); - glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, texture->pboBufferSize, buffer); - glBindTexture(GL_TEXTURE_2D, texture->texture); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->width, texture->height, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glBindTexture(GL_TEXTURE_2D, 0); + glBufferSubData(GL_PIXEL_UNPACK_BUFFER, 0, texture->pboBufferSize, buffer); + for(int i = 0; i < texture->textureCount; ++i) + { + glBindTexture(GL_TEXTURE_2D, texture->textures[i]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[i][0], texture->planes[i][1], + texture->format, GL_UNSIGNED_BYTE, (const void *)texture->offsets[i]); + } glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return true; @@ -119,5 +176,15 @@ bool egl_texture_stream_buffer(EGL_Texture * texture, const uint8_t * buffer) void egl_texture_bind(EGL_Texture * texture) { - glBindTexture(GL_TEXTURE_2D, texture->texture); + for(int i = 0; i < texture->textureCount; ++i) + { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(GL_TEXTURE_2D, texture->textures[i]); + glBindSampler(i, texture->samplers[i]); + } +} + +int egl_texture_count(EGL_Texture * texture) +{ + return texture->textureCount; } \ No newline at end of file diff --git a/client/renderers/egl_texture.h b/client/renderers/egl_texture.h index 8dc5d0d1..857c897b 100644 --- a/client/renderers/egl_texture.h +++ b/client/renderers/egl_texture.h @@ -27,9 +27,17 @@ Place, Suite 330, Boston, MA 02111-1307 USA typedef struct EGL_Texture EGL_Texture; +enum EGL_PixelFormat +{ + EGL_PF_RGBA, + EGL_PF_BGRA, + EGL_PF_YUV420 +}; + bool egl_texture_init(EGL_Texture ** tex); void egl_texture_free(EGL_Texture ** tex); -bool egl_texture_init_streaming(EGL_Texture * texture, size_t width, size_t height, size_t bufferSize); +bool egl_texture_init_streaming(EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t bufferSize); bool egl_texture_stream_buffer (EGL_Texture * texture, const uint8_t * buffer); -void egl_texture_bind (EGL_Texture * texture); \ No newline at end of file +void egl_texture_bind (EGL_Texture * texture); +int egl_texture_count (EGL_Texture * texture); \ No newline at end of file