From 13d9c84dc9f058a0ad01740e056bb78a1465fac7 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Mon, 2 Aug 2021 23:37:33 +1000 Subject: [PATCH] [client] egl: replace monolithic EGLTexture with modular version The way things were handled in EGLTexture is not only very hard to follow, but broken. This change set breaks up EGLTexture into a modular design making it easier to implement the various versions. Note that DMABUF is currently broken and needs to be re-implemented. --- client/include/interface/renderer.h | 4 +- client/include/util.h | 3 + client/renderers/EGL/CMakeLists.txt | 4 + client/renderers/EGL/cursor.c | 8 +- client/renderers/EGL/desktop.c | 25 +- client/renderers/EGL/desktop.h | 4 +- client/renderers/EGL/egl.c | 8 +- client/renderers/EGL/model.c | 3 +- client/renderers/EGL/texture.c | 592 +++------------------ client/renderers/EGL/texture.h | 115 +++- client/renderers/EGL/texture_buffer.c | 278 ++++++++++ client/renderers/EGL/texture_buffer.h | 53 ++ client/renderers/EGL/texture_dmabuf.c | 88 +++ client/renderers/EGL/texture_framebuffer.c | 59 ++ client/renderers/EGL/texture_util.c | 166 ++++++ client/renderers/EGL/texture_util.h | 54 ++ client/renderers/OpenGL/opengl.c | 4 +- client/src/main.c | 24 +- client/src/main.h | 1 + 19 files changed, 910 insertions(+), 583 deletions(-) create mode 100644 client/renderers/EGL/texture_buffer.c create mode 100644 client/renderers/EGL/texture_buffer.h create mode 100644 client/renderers/EGL/texture_dmabuf.c create mode 100644 client/renderers/EGL/texture_framebuffer.c create mode 100644 client/renderers/EGL/texture_util.c create mode 100644 client/renderers/EGL/texture_util.h diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index 50274465..7f49b6ff 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -110,9 +110,9 @@ typedef void (* LG_RendererOnRestart )(void * opaque); typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const double scale, const LG_RendererRect destRect, LG_RendererRotate rotate); typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data); typedef bool (* LG_RendererOnMouseEvent )(void * opaque, const bool visible , const int x, const int y); -typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format, bool useDMA); +typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format); typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD, const FrameDamageRect * damage, int damageCount); -typedef bool (* LG_RendererRenderStartup)(void * opaque); +typedef bool (* LG_RendererRenderStartup)(void * opaque, bool useDMA); typedef bool (* LG_RendererNeedsRender )(void * opaque); typedef bool (* LG_RendererRender )(void * opaque, LG_RendererRotate rotate, const bool newFrame, const bool invalidateWindow); diff --git a/client/include/util.h b/client/include/util.h index a58c80c9..da6dfcdc 100644 --- a/client/include/util.h +++ b/client/include/util.h @@ -28,6 +28,9 @@ #define min(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; }) #define max(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; }) +#define UPCAST(type, x) \ + (type *)((uintptr_t)(x) - offsetof(type, base)) + // reads the specified file into a new buffer // the callee must free the buffer bool util_fileGetContents(const char * filename, char ** buffer, size_t * length); diff --git a/client/renderers/EGL/CMakeLists.txt b/client/renderers/EGL/CMakeLists.txt index c2645045..ecf04064 100644 --- a/client/renderers/EGL/CMakeLists.txt +++ b/client/renderers/EGL/CMakeLists.txt @@ -36,7 +36,11 @@ add_library(renderer_EGL STATIC egl.c egldebug.c shader.c + texture_util.c texture.c + texture_buffer.c + texture_framebuffer.c + texture_dmabuf.c model.c desktop.c cursor.c diff --git a/client/renderers/EGL/cursor.c b/client/renderers/EGL/cursor.c index b3a3ee32..df8dc476 100644 --- a/client/renderers/EGL/cursor.c +++ b/client/renderers/EGL/cursor.c @@ -84,7 +84,7 @@ static bool egl_cursor_tex_init(struct CursorTex * t, const char * vertex_code , size_t vertex_size, const char * fragment_code, size_t fragment_size) { - if (!egl_texture_init(&t->texture, NULL)) + if (!egl_texture_init(&t->texture, NULL, EGL_TEXTYPE_BUFFER, false)) { DEBUG_ERROR("Failed to initialize the cursor texture"); return false; @@ -256,7 +256,7 @@ struct CursorState egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rota case LG_CURSOR_COLOR: { - egl_texture_setup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false, false); + egl_texture_setup(cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride); egl_texture_update(cursor->norm.texture, data); egl_model_set_texture(cursor->model, cursor->norm.texture); break; @@ -280,8 +280,8 @@ struct CursorState egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rota xor[y * cursor->width + x] = xorMask; } - egl_texture_setup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false); - egl_texture_setup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false); + egl_texture_setup (cursor->norm.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4); + egl_texture_setup (cursor->mono.texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4); egl_texture_update(cursor->norm.texture, (uint8_t *)and); egl_texture_update(cursor->mono.texture, (uint8_t *)xor); break; diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index 7cef18f0..b414cb55 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -104,7 +104,7 @@ static bool egl_init_desktop_shader( return true; } -bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display) +bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display, bool useDMA) { *desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop)); if (!*desktop) @@ -116,7 +116,8 @@ bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display) memset(*desktop, 0, sizeof(EGL_Desktop)); (*desktop)->display = display; - if (!egl_texture_init(&(*desktop)->texture, display)) + if (!egl_texture_init(&(*desktop)->texture, display, + useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true)) { DEBUG_ERROR("Failed to initialize the desktop texture"); return false; @@ -209,7 +210,7 @@ void egl_desktop_free(EGL_Desktop ** desktop) *desktop = NULL; } -bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA) +bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format) { enum EGL_PixelFormat pixFmt; switch(format.type) @@ -247,9 +248,7 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bo pixFmt, format.width, format.height, - format.pitch, - true, // streaming texture - useDMA + format.pitch )) { DEBUG_ERROR("Failed to setup the desktop texture"); @@ -272,13 +271,6 @@ bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dm return false; } - enum EGL_TexStatus status; - if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK) - { - if (status != EGL_TEX_STATUS_NOTREADY) - DEBUG_ERROR("Failed to process the desktop texture"); - } - return true; } @@ -289,6 +281,13 @@ bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, if (!desktop->shader) return false; + enum EGL_TexStatus status; + if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK) + { + if (status != EGL_TEX_STATUS_NOTREADY) + DEBUG_ERROR("Failed to process the desktop texture"); + } + int scaleAlgo = EGL_SCALE_NEAREST; switch (desktop->scaleAlgo) diff --git a/client/renderers/EGL/desktop.h b/client/renderers/EGL/desktop.h index 0e6e8fd5..8fbf0dff 100644 --- a/client/renderers/EGL/desktop.h +++ b/client/renderers/EGL/desktop.h @@ -36,10 +36,10 @@ enum EGL_DesktopScaleType struct Option; bool egl_desktop_scale_validate(struct Option * opt, const char ** error); -bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display); +bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display, bool useDMA); void egl_desktop_free(EGL_Desktop ** desktop); -bool egl_desktop_setup (EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA); +bool egl_desktop_setup (EGL_Desktop * desktop, const LG_RendererFormat format); bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd); bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType, diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 3ca9fd12..2b1b4e04 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -464,7 +464,7 @@ static bool egl_on_mouse_event(void * opaque, const bool visible, const int x, c return true; } -static bool egl_on_frame_format(void * opaque, const LG_RendererFormat format, bool useDMA) +static bool egl_on_frame_format(void * opaque, const LG_RendererFormat format) { struct Inst * this = (struct Inst *)opaque; memcpy(&this->format, &format, sizeof(LG_RendererFormat)); @@ -494,7 +494,7 @@ static bool egl_on_frame_format(void * opaque, const LG_RendererFormat format, b egl_update_scale_type(this); egl_damage_setup(this->damage, format.width, format.height); - return egl_desktop_setup(this->desktop, format, useDMA); + return egl_desktop_setup(this->desktop, format); } static bool egl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd, @@ -603,7 +603,7 @@ static void debugCallback(GLenum source, GLenum type, GLuint id, DEBUG_PRINT(level, "GL message (source: %s, type: %s): %s", sourceName, typeName, message); } -static bool egl_render_startup(void * opaque) +static bool egl_render_startup(void * opaque, bool useDMA) { struct Inst * this = (struct Inst *)opaque; @@ -767,7 +767,7 @@ static bool egl_render_startup(void * opaque) eglSwapInterval(this->display, this->opt.vsync ? 1 : 0); - if (!egl_desktop_init(&this->desktop, this->display)) + if (!egl_desktop_init(&this->desktop, this->display, useDMA)) { DEBUG_ERROR("Failed to initialize the desktop"); return false; diff --git a/client/renderers/EGL/model.c b/client/renderers/EGL/model.c index 0f58c621..6d6d5a68 100644 --- a/client/renderers/EGL/model.c +++ b/client/renderers/EGL/model.c @@ -221,6 +221,5 @@ 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); + egl_shader_associate_textures(model->shader, 1); } diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index cecb1fa7..862c143d 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -19,580 +19,116 @@ */ #include "texture.h" -#include "common/debug.h" + +#include +#include +#include "shader.h" #include "common/framebuffer.h" -#include "egl_dynprocs.h" -#include "egldebug.h" -#include -#include -#include -#include +#include +#include -/** - * the following comes from drm_fourcc.h and is included here to avoid the - * external dependency for the few simple defines we need - */ -#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ - ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) -#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') -#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') -#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') -#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') +#include "texture_buffer.h" -/* this must be a multiple of 2 */ -#define BUFFER_COUNT 4 +extern const EGL_TextureOps EGL_TextureBuffer; +extern const EGL_TextureOps EGL_TextureBufferStream; +extern const EGL_TextureOps EGL_TextureFrameBuffer; +extern const EGL_TextureOps EGL_TextureDMABUF; -struct Buffer +bool egl_texture_init(EGL_Texture ** texture, EGLDisplay * display, + EGL_TexType type, bool streaming) { - bool hasPBO; - GLuint pbo; - void * map; - GLsync sync; -}; + const EGL_TextureOps * ops; -struct BufferState -{ - _Atomic(uint8_t) w, u, s, d; -}; - -struct EGL_Texture -{ - EGLDisplay * display; - - enum EGL_PixelFormat pixFmt; - size_t bpp; - bool streaming; - bool dma; - bool ready; - - GLuint sampler; - size_t width, height, stride, pitch; - GLenum intFormat; - GLenum format; - GLenum dataType; - unsigned int fourcc; - size_t pboBufferSize; - - struct BufferState state; - int bufferCount; - GLuint tex[BUFFER_COUNT]; - struct Buffer buf[BUFFER_COUNT]; - - size_t dmaImageCount; - size_t dmaImageUsed; - struct + switch(type) { - int fd; - EGLImage image; - } - * dmaImages; - - GLuint dmaFBO; - GLuint dmaTex; -}; - -bool egl_texture_init(EGL_Texture ** texture, EGLDisplay * display) -{ - *texture = (EGL_Texture *)malloc(sizeof(EGL_Texture)); - if (!*texture) - { - DEBUG_ERROR("Failed to malloc EGL_Texture"); - return false; - } - - memset(*texture, 0, sizeof(EGL_Texture)); - (*texture)->display = display; - return true; -} - -void egl_texture_free(EGL_Texture ** texture) -{ - if (!*texture) - return; - - glDeleteSamplers(1, &(*texture)->sampler); - - for(int i = 0; i < (*texture)->bufferCount; ++i) - { - struct Buffer * b = &(*texture)->buf[i]; - if (b->hasPBO) - { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, b->pbo); - if ((*texture)->buf[i].map) - { - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - (*texture)->buf[i].map = NULL; - } - glDeleteBuffers(1, &b->pbo); - if (b->sync) - glDeleteSync(b->sync); - } - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glDeleteTextures((*texture)->bufferCount, (*texture)->tex); - - for (size_t i = 0; i < (*texture)->dmaImageUsed; ++i) - eglDestroyImage((*texture)->display, (*texture)->dmaImages[i].image); - free((*texture)->dmaImages); - - free(*texture); - *texture = NULL; -} - -static bool egl_texture_map(EGL_Texture * texture, uint8_t i) -{ - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->buf[i].pbo); - texture->buf[i].map = glMapBufferRange( - GL_PIXEL_UNPACK_BUFFER, - 0, - texture->pboBufferSize, - GL_MAP_WRITE_BIT | - GL_MAP_UNSYNCHRONIZED_BIT | - GL_MAP_INVALIDATE_BUFFER_BIT | - GL_MAP_PERSISTENT_BIT_EXT | - GL_MAP_COHERENT_BIT_EXT - ); - - if (!texture->buf[i].map) - { - DEBUG_GL_ERROR("glMapBufferRange failed for %d of %lu bytes", i, - texture->pboBufferSize); - } - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - return texture->buf[i].map; -} - -static void egl_texture_unmap(EGL_Texture * texture, uint8_t i) -{ - if (!texture->buf[i].map) - return; - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->buf[i].pbo); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - texture->buf[i].map = NULL; -} - -bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t stride, bool streaming, bool useDMA) -{ - if (texture->streaming && !useDMA) - { - for(int i = 0; i < texture->bufferCount; ++i) - { - egl_texture_unmap(texture, i); - if (texture->buf[i].hasPBO) - { - glDeleteBuffers(1, &texture->buf[i].pbo); - texture->buf[i].hasPBO = false; - } - } - } - - texture->pixFmt = pixFmt; - texture->width = width; - texture->height = height; - texture->stride = stride; - texture->streaming = streaming; - texture->bufferCount = streaming ? BUFFER_COUNT : 1; - texture->dma = useDMA; - texture->ready = false; - - atomic_store_explicit(&texture->state.w, 0, memory_order_relaxed); - atomic_store_explicit(&texture->state.u, 0, memory_order_relaxed); - atomic_store_explicit(&texture->state.s, 0, memory_order_relaxed); - atomic_store_explicit(&texture->state.d, 0, memory_order_relaxed); - - switch(pixFmt) - { - case EGL_PF_BGRA: - texture->bpp = 4; - texture->format = GL_BGRA_EXT; - texture->intFormat = GL_BGRA_EXT; - texture->dataType = GL_UNSIGNED_BYTE; - texture->fourcc = DRM_FORMAT_ARGB8888; - texture->pboBufferSize = height * stride; + case EGL_TEXTYPE_BUFFER: + ops = streaming ? &EGL_TextureBufferStream : &EGL_TextureBuffer; break; - case EGL_PF_RGBA: - texture->bpp = 4; - texture->format = GL_RGBA; - texture->intFormat = GL_RGBA; - texture->dataType = GL_UNSIGNED_BYTE; - texture->fourcc = DRM_FORMAT_ABGR8888; - texture->pboBufferSize = height * stride; + case EGL_TEXTYPE_FRAMEBUFFER: + assert(streaming); + ops = &EGL_TextureFrameBuffer; break; - case EGL_PF_RGBA10: - texture->bpp = 4; - texture->format = GL_RGBA; - texture->intFormat = GL_RGB10_A2; - texture->dataType = GL_UNSIGNED_INT_2_10_10_10_REV; - texture->fourcc = DRM_FORMAT_BGRA1010102; - texture->pboBufferSize = height * stride; - break; - - case EGL_PF_RGBA16F: - texture->bpp = 8; - texture->format = GL_RGBA; - texture->intFormat = GL_RGBA16F; - texture->dataType = GL_HALF_FLOAT; - texture->fourcc = DRM_FORMAT_ABGR16161616F; - texture->pboBufferSize = height * stride; + case EGL_TEXTYPE_DMABUF: + assert(streaming); + ops = &EGL_TextureDMABUF; break; default: - DEBUG_ERROR("Unsupported pixel format"); return false; } - texture->pitch = stride / texture->bpp; - - if (texture->tex[0]) - glDeleteTextures(texture->bufferCount, texture->tex); - glGenTextures(texture->bufferCount, texture->tex); - - if (!texture->sampler) - { - glGenSamplers(1, &texture->sampler); - glSamplerParameteri(texture->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(texture->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(texture->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); - glSamplerParameteri(texture->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); - } - - if (useDMA) - { - if (texture->dmaFBO) - glDeleteFramebuffers(1, &texture->dmaFBO); - if (texture->dmaTex) - glDeleteTextures(1, &texture->dmaTex); - glGenFramebuffers(1, &texture->dmaFBO); - glGenTextures(1, &texture->dmaTex); - - glBindTexture(GL_TEXTURE_2D, texture->tex[0]); - glTexImage2D(GL_TEXTURE_2D, 0, texture->intFormat, texture->width, - texture->height, 0, texture->format, texture->dataType, NULL); - glBindTexture(GL_TEXTURE_2D, 0); - - for (size_t i = 0; i < texture->dmaImageUsed; ++i) - eglDestroyImage(texture->display, texture->dmaImages[i].image); - texture->dmaImageUsed = 0; - return true; - } - - for(int i = 0; i < texture->bufferCount; ++i) - { - glBindTexture(GL_TEXTURE_2D, texture->tex[i]); - glTexImage2D(GL_TEXTURE_2D, 0, texture->intFormat, texture->width, - texture->height, 0, texture->format, texture->dataType, NULL); - } - glBindTexture(GL_TEXTURE_2D, 0); - - if (!streaming) - return true; - - for(int i = 0; i < texture->bufferCount; ++i) - { - glGenBuffers(1, &texture->buf[i].pbo); - texture->buf[i].hasPBO = true; - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->buf[i].pbo); - glBufferStorageEXT( - GL_PIXEL_UNPACK_BUFFER, - texture->pboBufferSize, - NULL, - GL_MAP_WRITE_BIT | - GL_MAP_PERSISTENT_BIT_EXT | - GL_MAP_COHERENT_BIT_EXT - ); - - if (!egl_texture_map(texture, i)) - return false; - } + if (!ops->init(texture, display)) + return false; + (*texture)->ops = ops; return true; } -static void egl_warn_slow(void) +void egl_texture_free(EGL_Texture ** tex) { - static bool warnDone = false; - if (!warnDone) + (*tex)->ops->free(*tex); + *tex = NULL; +} + +bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, + size_t width, size_t height, size_t stride) +{ + const struct EGL_TexSetup setup = { - warnDone = true; - DEBUG_BREAK(); - DEBUG_WARN("The guest is providing updates faster then your computer can display them"); - DEBUG_WARN("This is a hardware limitation, expect microstutters & frame skips"); - DEBUG_BREAK(); - } + .pixFmt = pixFmt, + .width = width, + .height = height, + .stride = stride + }; + texture->size = height * stride; + return texture->ops->setup(texture, &setup); } bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer) { - if (texture->streaming) + const struct EGL_TexUpdate update = { - const uint8_t sw = - atomic_load_explicit(&texture->state.w, memory_order_acquire); - - if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1)) - { - egl_warn_slow(); - return true; - } - - const uint8_t b = sw % BUFFER_COUNT; - memcpy(texture->buf[b].map, buffer, texture->pboBufferSize); - atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release); - } - else - { - glBindTexture(GL_TEXTURE_2D, texture->tex[0]); - glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->pitch); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->width, texture->height, - texture->format, texture->dataType, buffer); - glBindTexture(GL_TEXTURE_2D, 0); - } - return true; + .type = EGL_TEXTYPE_BUFFER, + .buffer = buffer + }; + return texture->ops->update(texture, &update); } -bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * frame) +bool egl_texture_update_from_frame(EGL_Texture * texture, + const FrameBuffer * frame) { - if (!texture->streaming) - return false; - - const uint8_t sw = - atomic_load_explicit(&texture->state.w, memory_order_acquire); - - if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1)) + const struct EGL_TexUpdate update = { - egl_warn_slow(); - return true; - } - - const uint8_t b = sw % BUFFER_COUNT; - - framebuffer_read( - frame, - texture->buf[b].map, - texture->stride, - texture->height, - texture->width, - texture->bpp, - texture->stride - ); - - atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release); - - return true; + .type = EGL_TEXTYPE_FRAMEBUFFER, + .frame = frame + }; + return texture->ops->update(texture, &update); } -bool egl_texture_update_from_dma(EGL_Texture * texture, const FrameBuffer * frame, const int dmaFd) +bool egl_texture_update_from_dma(EGL_Texture * texture, + const FrameBuffer * frame, const int dmaFd) { - if (!texture->streaming) - return false; - - const uint8_t sw = - atomic_load_explicit(&texture->state.w, memory_order_acquire); - - if (atomic_load_explicit(&texture->state.u, memory_order_acquire) == (uint8_t)(sw + 1)) + const struct EGL_TexUpdate update = { - egl_warn_slow(); - return true; - } - - EGLImage image = EGL_NO_IMAGE; - - for (int i = 0; i < texture->dmaImageUsed; ++i) - { - if (texture->dmaImages[i].fd == dmaFd) - { - image = texture->dmaImages[i].image; - break; - } - } - - if (image == EGL_NO_IMAGE) - { - EGLAttrib const attribs[] = - { - EGL_WIDTH , texture->width, - EGL_HEIGHT , texture->height, - EGL_LINUX_DRM_FOURCC_EXT , texture->fourcc, - EGL_DMA_BUF_PLANE0_FD_EXT , dmaFd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0, - EGL_DMA_BUF_PLANE0_PITCH_EXT , texture->stride, - EGL_NONE , EGL_NONE - }; - - /* create the image backed by the dma buffer */ - image = eglCreateImage( - texture->display, - EGL_NO_CONTEXT, - EGL_LINUX_DMA_BUF_EXT, - (EGLClientBuffer)NULL, - attribs - ); - - if (image == EGL_NO_IMAGE) - { - DEBUG_EGL_ERROR("Failed to create ELGImage for DMA transfer"); - return false; - } - - if (texture->dmaImageUsed == texture->dmaImageCount) - { - size_t newCount = texture->dmaImageCount * 2 + 2; - void * new = realloc(texture->dmaImages, newCount * sizeof *texture->dmaImages); - if (!new) - { - DEBUG_ERROR("Failed to allocate memory"); - eglDestroyImage(texture->display, image); - return false; - } - texture->dmaImageCount = newCount; - texture->dmaImages = new; - } - - const size_t index = texture->dmaImageUsed++; - texture->dmaImages[index].fd = dmaFd; - texture->dmaImages[index].image = image; - } + .type = EGL_TEXTYPE_DMABUF, + .dmaFD = dmaFd + }; /* wait for completion */ - framebuffer_wait(frame, texture->height * texture->stride); + framebuffer_wait(frame, texture->size); - glBindTexture(GL_TEXTURE_2D, texture->dmaTex); - g_egl_dynProcs.glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); - - glBindFramebuffer(GL_FRAMEBUFFER, texture->dmaFBO); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture->dmaTex, 0); - - glBindTexture(GL_TEXTURE_2D, texture->tex[0]); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, texture->width, texture->height); - - GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glFlush(); - - switch (glClientWaitSync(fence, 0, 10000000)) // 10ms - { - case GL_ALREADY_SIGNALED: - case GL_CONDITION_SATISFIED: - break; - - case GL_TIMEOUT_EXPIRED: - egl_warn_slow(); - break; - - case GL_WAIT_FAILED: - case GL_INVALID_VALUE: - DEBUG_GL_ERROR("glClientWaitSync failed"); - } - - glDeleteSync(fence); - atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release); - return true; + return texture->ops->update(texture, &update); } enum EGL_TexStatus egl_texture_process(EGL_Texture * texture) { - if (!texture->streaming) - return EGL_TEX_STATUS_OK; - - const uint8_t su = - atomic_load_explicit(&texture->state.u, memory_order_acquire); - - const uint8_t nextu = su + 1; - if ( - su == atomic_load_explicit(&texture->state.w, memory_order_acquire) || - nextu == atomic_load_explicit(&texture->state.s, memory_order_acquire) || - nextu == atomic_load_explicit(&texture->state.d, memory_order_acquire)) - return texture->ready ? EGL_TEX_STATUS_OK : EGL_TEX_STATUS_NOTREADY; - - const uint8_t b = su % BUFFER_COUNT; - - /* update the texture */ - if (!texture->dma) - { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->buf[b].pbo); - glBindTexture(GL_TEXTURE_2D, texture->tex[b]); - glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->pitch); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->width, texture->height, - texture->format, texture->dataType, (const void *)0); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - /* create a fence to prevent usage before the update is complete */ - texture->buf[b].sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - /* we must flush to ensure the sync is in the command buffer */ - glFlush(); - } - - texture->ready = true; - atomic_fetch_add_explicit(&texture->state.u, 1, memory_order_release); - - return EGL_TEX_STATUS_OK; + return texture->ops->process(texture); } enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture) { - uint8_t ss = atomic_load_explicit(&texture->state.s, memory_order_acquire); - uint8_t sd = atomic_load_explicit(&texture->state.d, memory_order_acquire); - GLuint tex = texture->tex[0]; - - if (texture->streaming) - { - if (!texture->ready) - return EGL_TEX_STATUS_NOTREADY; - - const uint8_t b = ss % BUFFER_COUNT; - if (texture->dma) - { - ss = atomic_fetch_add_explicit(&texture->state.s, 1, - memory_order_release) + 1; - } - else if (texture->buf[b].sync != 0) - { - switch(glClientWaitSync(texture->buf[b].sync, 0, 20000000)) // 20ms - { - case GL_ALREADY_SIGNALED: - case GL_CONDITION_SATISFIED: - glDeleteSync(texture->buf[b].sync); - texture->buf[b].sync = 0; - - ss = atomic_fetch_add_explicit(&texture->state.s, 1, - memory_order_release) + 1; - break; - - case GL_TIMEOUT_EXPIRED: - break; - - case GL_WAIT_FAILED: - case GL_INVALID_VALUE: - glDeleteSync(texture->buf[b].sync); - texture->buf[b].sync = 0; - DEBUG_GL_ERROR("glClientWaitSync failed"); - return EGL_TEX_STATUS_ERROR; - } - } - - if (ss != sd && ss != (uint8_t)(sd + 1)) - sd = atomic_fetch_add_explicit(&texture->state.d, 1, - memory_order_release) + 1; - - if (!texture->dma) - tex = texture->tex[sd % BUFFER_COUNT]; - } - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, tex); - glBindSampler(0, texture->sampler); - - return EGL_TEX_STATUS_OK; -} - -int egl_texture_count(EGL_Texture * texture) -{ - return 1; + return texture->ops->bind(texture); } diff --git a/client/renderers/EGL/texture.h b/client/renderers/EGL/texture.h index c9680030..9e3aad1a 100644 --- a/client/renderers/EGL/texture.h +++ b/client/renderers/EGL/texture.h @@ -24,34 +24,121 @@ #include "shader.h" #include "common/framebuffer.h" +#include "util.h" + #include #include -typedef struct EGL_Texture EGL_Texture; +struct EGL_TextureOps; -enum EGL_PixelFormat +typedef struct EGL_Texture +{ + const struct EGL_TextureOps * ops; + + // needed for dmabuf + size_t size; +} +EGL_Texture; + +typedef enum EGL_TexType +{ + EGL_TEXTYPE_BUFFER, + EGL_TEXTYPE_FRAMEBUFFER, + EGL_TEXTYPE_DMABUF +} +EGL_TexType; + +typedef enum EGL_PixelFormat { EGL_PF_RGBA, EGL_PF_BGRA, EGL_PF_RGBA10, - EGL_PF_RGBA16F, - EGL_PF_YUV420 -}; + EGL_PF_RGBA16F +} +EGL_PixelFormat; -enum EGL_TexStatus +typedef enum EGL_TexStatus { EGL_TEX_STATUS_NOTREADY, EGL_TEX_STATUS_OK, EGL_TEX_STATUS_ERROR -}; +} +EGL_TexStatus; -bool egl_texture_init(EGL_Texture ** texture, EGLDisplay * display); +typedef struct EGL_TexSetup +{ + /* the pixel format of the texture */ + EGL_PixelFormat pixFmt; + + /* the width of the texture in pixels */ + size_t width; + + /* the height of the texture in pixels */ + size_t height; + + /* the stide of the texture in bytes */ + size_t stride; +} +EGL_TexSetup; + +typedef struct EGL_TexUpdate +{ + /* the type of this update */ + EGL_TexType type; + + union + { + /* EGL_TEXTURE_BUFFER */ + const uint8_t * buffer; + + /* EGL_TEXTURE_FRAMEBUFFER */ + const FrameBuffer * frame; + + /* EGL_TEXTURE_DMABUF */ + int dmaFD; + }; +} +EGL_TexUpdate; + +typedef struct EGL_TextureOps +{ + /* allocate & initialize an EGL_Texture */ + bool (*init)(EGL_Texture ** texture, EGLDisplay * display); + + /* free the EGL_Texture */ + void (*free)(EGL_Texture * texture); + + /* setup/reconfigure the texture format */ + bool (*setup)(EGL_Texture * texture, const EGL_TexSetup * setup); + + /* update the texture */ + bool (*update)(EGL_Texture * texture, const EGL_TexUpdate * update); + + /* called from a background job to prepare the texture for use before bind */ + enum EGL_TexStatus (*process)(EGL_Texture * texture); + + /* bind the texture for use */ + enum EGL_TexStatus (*bind)(EGL_Texture * texture); +} +EGL_TextureOps; + +bool egl_texture_init(EGL_Texture ** texture, EGLDisplay * display, + EGL_TexType type, bool streaming); void egl_texture_free(EGL_Texture ** tex); -bool egl_texture_setup (EGL_Texture * texture, enum EGL_PixelFormat pixfmt, size_t width, size_t height, size_t stride, bool streaming, bool useDMA); -bool egl_texture_update (EGL_Texture * texture, const uint8_t * buffer); -bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * frame); -bool egl_texture_update_from_dma (EGL_Texture * texture, const FrameBuffer * frmame, const int dmaFd); +bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, + size_t width, size_t height, size_t stride); + +bool egl_texture_update (EGL_Texture * texture, const uint8_t * buffer); + +bool egl_texture_update_from_frame(EGL_Texture * texture, + const FrameBuffer * frame); + +bool egl_texture_update_from_dma(EGL_Texture * texture, + const FrameBuffer * frame, const int dmaFd); + enum EGL_TexStatus egl_texture_process(EGL_Texture * texture); -enum EGL_TexStatus egl_texture_bind (EGL_Texture * texture); -int egl_texture_count (EGL_Texture * texture); + +enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture); + +int egl_texture_count(EGL_Texture * texture); diff --git a/client/renderers/EGL/texture_buffer.c b/client/renderers/EGL/texture_buffer.c new file mode 100644 index 00000000..2e75cf27 --- /dev/null +++ b/client/renderers/EGL/texture_buffer.c @@ -0,0 +1,278 @@ +/** + * 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 "texture_buffer.h" + +#include "egldebug.h" + +#include +#include + +// forwards +extern const EGL_TextureOps EGL_TextureBuffer; +extern const EGL_TextureOps EGL_TextureBufferStream; + +// internal functions + +static void eglTexBuffer_cleanup(TextureBuffer * this) +{ + eglTexUtilFreeBuffers(this->buf, this->texCount); + + if (this->tex[0]) + glDeleteTextures(this->texCount, this->tex); + + if (this->sampler) + glDeleteSamplers(1, &this->sampler); +} + +// common functions + +bool eglTexBuffer_init(EGL_Texture ** texture_, EGLDisplay * display) +{ + TextureBuffer * this = (TextureBuffer *)calloc(1, sizeof(*this)); + if (!this) + { + DEBUG_ERROR("Failed to malloc TexB"); + return false; + } + *texture_ = &this->base; + + this->display = display; + this->texCount = 1; + return true; +} + +void eglTexBuffer_free(EGL_Texture * texture_) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + + eglTexBuffer_cleanup(this); + LG_LOCK_FREE(this->copyLock); + free(this); +} + +bool eglTexBuffer_setup(EGL_Texture * texture_, const EGL_TexSetup * setup) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + + eglTexBuffer_cleanup(this); + + if (!eglTexUtilGetFormat(setup, &this->format)) + return false; + + glGenSamplers(1, &this->sampler); + glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); + glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + + glGenTextures(this->texCount, this->tex); + for(int i = 0; i < this->texCount; ++i) + { + glBindTexture(GL_TEXTURE_2D, this->tex[i]); + glTexImage2D(GL_TEXTURE_2D, + 0, + this->format.intFormat, + this->format.width, + this->format.height, + 0, + this->format.format, + this->format.dataType, + NULL); + } + + glBindTexture(GL_TEXTURE_2D, 0); + this->rIndex = -1; + + return true; +} + +static bool eglTexBuffer_update(EGL_Texture * texture_, const EGL_TexUpdate * update) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + assert(update->type == EGL_TEXTYPE_BUFFER); + + glBindTexture(GL_TEXTURE_2D, this->tex[0]); + glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.pitch); + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, + this->format.width, + this->format.height, + this->format.format, + this->format.dataType, + update->buffer); + glBindTexture(GL_TEXTURE_2D, 0); + + return true; +} + +EGL_TexStatus eglTexBuffer_process(EGL_Texture * texture_) +{ + return EGL_TEX_STATUS_OK; +} + +EGL_TexStatus eglTexBuffer_bind(EGL_Texture * texture_) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this->tex[0]); + glBindSampler(0, this->sampler); + + return true; +} + +// streaming functions + +bool eglTexBuffer_stream_init(EGL_Texture ** texture_, EGLDisplay * display) +{ + if (!eglTexBuffer_init(texture_, display)) + return false; + + TextureBuffer * this = UPCAST(TextureBuffer, *texture_); + + this->base.ops = &EGL_TextureBufferStream; + this->texCount = 2; + LG_LOCK_INIT(this->copyLock); + return true; +} + +bool eglTexBuffer_stream_setup(EGL_Texture * texture_, const EGL_TexSetup * setup) +{ + if (!eglTexBuffer_setup(texture_, setup)) + return false; + + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + return eglTexUtilGenBuffers(&this->format, this->buf, this->texCount); +} + +static bool eglTexBuffer_stream_update(EGL_Texture * texture_, + const EGL_TexUpdate * update) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + assert(update->type == EGL_TEXTYPE_BUFFER); + + LG_LOCK(this->copyLock); + memcpy(this->buf[this->bufIndex].map, update->buffer, + this->format.bufferSize); + this->buf[this->bufIndex].updated = true; + LG_UNLOCK(this->copyLock); + + return true; +} + +EGL_TexStatus eglTexBuffer_stream_process(EGL_Texture * texture_) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + + LG_LOCK(this->copyLock); + + GLuint tex = this->tex[this->bufIndex]; + EGL_TexBuffer * buffer = &this->buf[this->bufIndex]; + + if (buffer->updated && buffer->sync == 0) + { + this->rIndex = this->bufIndex; + if (++this->bufIndex == this->texCount) + this->bufIndex = 0; + } + + LG_UNLOCK(this->copyLock); + + if (buffer->updated) + { + buffer->updated = false; + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + glBindTexture(GL_TEXTURE_2D, tex); + glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.pitch); + glTexSubImage2D(GL_TEXTURE_2D, + 0, 0, 0, + this->format.width, + this->format.height, + this->format.format, + this->format.dataType, + (const void *)0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + buffer->sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + } + + return EGL_TEX_STATUS_OK; +} + +EGL_TexStatus eglTexBuffer_stream_bind(EGL_Texture * texture_) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + + if (this->rIndex == -1) + return EGL_TEX_STATUS_NOTREADY; + + EGL_TexBuffer * buffer = &this->buf[this->rIndex]; + if (buffer->sync) + { + switch(glClientWaitSync(buffer->sync, 0, 20000000)) // 20ms + { + case GL_ALREADY_SIGNALED: + case GL_CONDITION_SATISFIED: + glDeleteSync(buffer->sync); + buffer->sync = 0; + break; + + case GL_TIMEOUT_EXPIRED: + return EGL_TEX_STATUS_NOTREADY; + + case GL_WAIT_FAILED: + case GL_INVALID_VALUE: + glDeleteSync(buffer->sync); + buffer->sync = 0; + DEBUG_GL_ERROR("glClientWaitSync failed"); + return EGL_TEX_STATUS_ERROR; + } + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this->tex[this->rIndex]); + glBindSampler(0, this->sampler); + + return EGL_TEX_STATUS_OK; +} + +const EGL_TextureOps EGL_TextureBuffer = +{ + .init = eglTexBuffer_init, + .free = eglTexBuffer_free, + .setup = eglTexBuffer_setup, + .update = eglTexBuffer_update, + .process = eglTexBuffer_process, + .bind = eglTexBuffer_bind +}; + +const EGL_TextureOps EGL_TextureBufferStream = +{ + .init = eglTexBuffer_stream_init, + .free = eglTexBuffer_free, + .setup = eglTexBuffer_stream_setup, + .update = eglTexBuffer_stream_update, + .process = eglTexBuffer_stream_process, + .bind = eglTexBuffer_stream_bind +}; diff --git a/client/renderers/EGL/texture_buffer.h b/client/renderers/EGL/texture_buffer.h new file mode 100644 index 00000000..681e0465 --- /dev/null +++ b/client/renderers/EGL/texture_buffer.h @@ -0,0 +1,53 @@ +/** + * 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 + */ + +#pragma once + +#include "texture.h" +#include "texture_util.h" +#include "common/locking.h" + +typedef struct TextureBuffer +{ + EGL_Texture base; + + EGLDisplay display; + EGL_TexFormat format; + int texCount; + GLuint tex[2]; + GLuint sampler; + EGL_TexBuffer buf[2]; + LG_Lock copyLock; + int bufIndex; + int rIndex; +} +TextureBuffer; + +bool eglTexBuffer_init(EGL_Texture ** texture_, EGLDisplay * display); +void eglTexBuffer_free(EGL_Texture * texture_); +bool eglTexBuffer_setup(EGL_Texture * texture_, const EGL_TexSetup * setup); +EGL_TexStatus eglTexBuffer_process(EGL_Texture * texture_); +EGL_TexStatus eglTexBuffer_bind(EGL_Texture * texture_); + +bool eglTexBuffer_stream_init(EGL_Texture ** texture_, EGLDisplay * display); +bool eglTexBuffer_stream_setup(EGL_Texture * texture_, + const EGL_TexSetup * setup); +EGL_TexStatus eglTexBuffer_stream_process(EGL_Texture * texture_); +EGL_TexStatus eglTexBuffer_stream_bind(EGL_Texture * texture_); diff --git a/client/renderers/EGL/texture_dmabuf.c b/client/renderers/EGL/texture_dmabuf.c new file mode 100644 index 00000000..e65d2222 --- /dev/null +++ b/client/renderers/EGL/texture_dmabuf.c @@ -0,0 +1,88 @@ +/** + * 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 "texture.h" + +typedef struct TexDMABUF +{ + EGL_Texture base; +} +TexDMABUF; + +EGL_TextureOps EGL_TextureDMABUF; + +static bool eglTexDMABUF_init(EGL_Texture ** texture_, EGLDisplay * display) +{ + TexDMABUF * texture = (TexDMABUF *)calloc(sizeof(*texture), 1); + *texture_ = &texture->base; + + return true; +} + +static void eglTexDMABUF_free(EGL_Texture * texture_) +{ + TexDMABUF * texture = UPCAST(TexDMABUF, texture_); + + free(texture); +} + +static bool eglTexDMABUF_setup(EGL_Texture * texture_, + const EGL_TexSetup * setup) +{ + TexDMABUF * texture = UPCAST(TexDMABUF, texture_); + (void)texture; + + return false; +} + +static bool eglTexDMABUF_update(EGL_Texture * texture_, + const EGL_TexUpdate * update) +{ + TexDMABUF * texture = UPCAST(TexDMABUF, texture_); + (void)texture; + + return false; +} + +static EGL_TexStatus eglTexDMABUF_process(EGL_Texture * texture_) +{ + TexDMABUF * texture = UPCAST(TexDMABUF, texture_); + (void)texture; + + return EGL_TEX_STATUS_ERROR; +} + +static EGL_TexStatus eglTexDMABUF_bind(EGL_Texture * texture_) +{ + TexDMABUF * texture = UPCAST(TexDMABUF, texture_); + (void)texture; + + return EGL_TEX_STATUS_ERROR; +} + +EGL_TextureOps EGL_TextureDMABUF = +{ + .init = eglTexDMABUF_init, + .free = eglTexDMABUF_free, + .setup = eglTexDMABUF_setup, + .update = eglTexDMABUF_update, + .process = eglTexDMABUF_process, + .bind = eglTexDMABUF_bind +}; diff --git a/client/renderers/EGL/texture_framebuffer.c b/client/renderers/EGL/texture_framebuffer.c new file mode 100644 index 00000000..cf26db93 --- /dev/null +++ b/client/renderers/EGL/texture_framebuffer.c @@ -0,0 +1,59 @@ +/** + * 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 "texture.h" + +#include + +#include "texture_buffer.h" +#include "common/debug.h" + +static bool eglTexFB_update(EGL_Texture * texture_, const EGL_TexUpdate * update) +{ + TextureBuffer * this = UPCAST(TextureBuffer, texture_); + assert(update->type == EGL_TEXTYPE_FRAMEBUFFER); + + LG_LOCK(this->copyLock); + + framebuffer_read( + update->frame, + this->buf[this->bufIndex].map, + this->format.stride, + this->format.height, + this->format.width, + this->format.bpp, + this->format.stride + ); + + this->buf[this->bufIndex].updated = true; + LG_UNLOCK(this->copyLock); + + return true; +} + +EGL_TextureOps EGL_TextureFrameBuffer = +{ + .init = eglTexBuffer_stream_init, + .free = eglTexBuffer_free, + .setup = eglTexBuffer_stream_setup, + .update = eglTexFB_update, + .process = eglTexBuffer_stream_process, + .bind = eglTexBuffer_stream_bind +}; diff --git a/client/renderers/EGL/texture_util.c b/client/renderers/EGL/texture_util.c new file mode 100644 index 00000000..0177fcb5 --- /dev/null +++ b/client/renderers/EGL/texture_util.c @@ -0,0 +1,166 @@ +/** + * 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 "texture_util.h" + +#include +#include +#include + +#include "egldebug.h" + +/** + * the following comes from drm_fourcc.h and is included here to avoid the + * external dependency for the few simple defines we need + */ +#define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ + ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) +#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') +#define DRM_FORMAT_ABGR8888 fourcc_code('A', 'B', '2', '4') +#define DRM_FORMAT_BGRA1010102 fourcc_code('B', 'A', '3', '0') +#define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') + +bool eglTexUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt) +{ + switch(setup->pixFmt) + { + case EGL_PF_BGRA: + fmt->bpp = 4; + fmt->format = GL_BGRA_EXT; + fmt->intFormat = GL_BGRA_EXT; + fmt->dataType = GL_UNSIGNED_BYTE; + fmt->fourcc = DRM_FORMAT_ARGB8888; + break; + + case EGL_PF_RGBA: + fmt->bpp = 4; + fmt->format = GL_RGBA; + fmt->intFormat = GL_RGBA; + fmt->dataType = GL_UNSIGNED_BYTE; + fmt->fourcc = DRM_FORMAT_ABGR8888; + break; + + case EGL_PF_RGBA10: + fmt->bpp = 4; + fmt->format = GL_RGBA; + fmt->intFormat = GL_RGB10_A2; + fmt->dataType = GL_UNSIGNED_INT_2_10_10_10_REV; + fmt->fourcc = DRM_FORMAT_BGRA1010102; + break; + + case EGL_PF_RGBA16F: + fmt->bpp = 8; + fmt->format = GL_RGBA; + fmt->intFormat = GL_RGBA16F; + fmt->dataType = GL_HALF_FLOAT; + fmt->fourcc = DRM_FORMAT_ABGR16161616F; + break; + + default: + DEBUG_ERROR("Unsupported pixel format"); + return false; + } + + fmt->width = setup->width; + fmt->height = setup->height; + fmt->stride = setup->stride; + fmt->pitch = setup->stride / fmt->bpp; + fmt->bufferSize = setup->height * setup->stride; + + return true; +} + +bool eglTexUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers, + int count) +{ + for(int i = 0; i < count; ++i) + { + EGL_TexBuffer *buffer = &buffers[i]; + + buffer->size = fmt->bufferSize; + glGenBuffers(1, &buffer->pbo); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + glBufferStorageEXT( + GL_PIXEL_UNPACK_BUFFER, + fmt->bufferSize, + NULL, + GL_MAP_WRITE_BIT | + GL_MAP_PERSISTENT_BIT_EXT | + GL_MAP_COHERENT_BIT_EXT + ); + + if (!eglTexUtilMapBuffer(buffer)) + return false; + } + + return true; +} + +void eglTexUtilFreeBuffers(EGL_TexBuffer * buffers, int count) +{ + for(int i = 0; i < count; ++i) + { + EGL_TexBuffer *buffer = &buffers[i]; + + if (!buffer->pbo) + continue; + + eglTexUtilUnmapBuffer(buffer); + glDeleteBuffers(1, &buffer->pbo); + if (buffer->sync) + { + glDeleteSync(buffer->sync); + buffer->sync = 0; + } + + buffer->pbo = 0; + } +} + +bool eglTexUtilMapBuffer(EGL_TexBuffer * buffer) +{ + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + buffer->map = glMapBufferRange( + GL_PIXEL_UNPACK_BUFFER, + 0, + buffer->size, + GL_MAP_WRITE_BIT | + GL_MAP_UNSYNCHRONIZED_BIT | + GL_MAP_INVALIDATE_BUFFER_BIT | + GL_MAP_PERSISTENT_BIT_EXT | + GL_MAP_COHERENT_BIT_EXT); + + if (!buffer->map) + DEBUG_GL_ERROR("glMapBufferRange failed of %lu bytes", buffer->size); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + return buffer->map; +} + +void eglTexUtilUnmapBuffer(EGL_TexBuffer * buffer) +{ + if (!buffer->map) + return; + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + buffer->map = NULL; +} diff --git a/client/renderers/EGL/texture_util.h b/client/renderers/EGL/texture_util.h new file mode 100644 index 00000000..17cc5abc --- /dev/null +++ b/client/renderers/EGL/texture_util.h @@ -0,0 +1,54 @@ +/** + * 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 + */ + +#pragma once + +#include "texture.h" + +typedef struct EGL_TexFormat +{ + size_t bpp; + GLenum format; + GLenum intFormat; + GLenum dataType; + unsigned int fourcc; + size_t bufferSize; + + size_t width, height; + size_t stride, pitch; +} +EGL_TexFormat; + +typedef struct EGL_TexBuffer +{ + size_t size; + GLuint pbo; + void * map; + GLsync sync; + bool updated; +} +EGL_TexBuffer; + +bool eglTexUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt); +bool eglTexUtilGenBuffers(const EGL_TexFormat * fmt, EGL_TexBuffer * buffers, + int count); +void eglTexUtilFreeBuffers(EGL_TexBuffer * buffers, int count); +bool eglTexUtilMapBuffer(EGL_TexBuffer * buffer); +void eglTexUtilUnmapBuffer(EGL_TexBuffer * buffer); diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index 08f70071..b409c671 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -357,7 +357,7 @@ bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const return false; } -bool opengl_on_frame_format(void * opaque, const LG_RendererFormat format, bool useDMA) +bool opengl_on_frame_format(void * opaque, const LG_RendererFormat format) { struct Inst * this = (struct Inst *)opaque; @@ -393,7 +393,7 @@ bool opengl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd, return true; } -bool opengl_render_startup(void * opaque) +bool opengl_render_startup(void * opaque, bool useDMA) { struct Inst * this = (struct Inst *)opaque; diff --git a/client/src/main.c b/client/src/main.c index ad5be375..130592df 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -140,7 +140,7 @@ static bool fpsTimerFn(void * unused) static int renderThread(void * unused) { - if (!g_state.lgr->render_startup(g_state.lgrData)) + if (!g_state.lgr->render_startup(g_state.lgrData, g_state.useDMA)) { g_state.state = APP_STATE_SHUTDOWN; @@ -456,13 +456,7 @@ int main_frameThread(void * unused) LG_RendererFormat lgrFormat; struct DMAFrameInfo dmaInfo[LGMP_Q_FRAME_LEN] = {0}; - const bool useDMA = - g_params.allowDMA && - ivshmemHasDMA(&g_state.shm) && - g_state.lgr->supports && - g_state.lgr->supports(g_state.lgrData, LG_SUPPORTS_DMABUF); - - if (useDMA) + if (g_state.useDMA) DEBUG_INFO("Using DMA buffer support"); lgWaitEvent(e_startup, TIMEOUT_INFINITE); @@ -607,7 +601,7 @@ int main_frameThread(void * unused) frame->rotation); LG_LOCK(g_state.lgrLock); - if (!g_state.lgr->on_frame_format(g_state.lgrData, lgrFormat, useDMA)) + if (!g_state.lgr->on_frame_format(g_state.lgrData, lgrFormat)) { DEBUG_ERROR("renderer failed to configure format"); g_state.state = APP_STATE_SHUTDOWN; @@ -625,7 +619,7 @@ int main_frameThread(void * unused) core_updatePositionInfo(); } - if (useDMA) + if (g_state.useDMA) { /* find the existing dma buffer if it exists */ for(int i = 0; i < sizeof(dmaInfo) / sizeof(struct DMAFrameInfo); ++i) @@ -674,7 +668,7 @@ int main_frameThread(void * unused) } FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)frame) + frame->offset); - if (!g_state.lgr->on_frame(g_state.lgrData, fb, useDMA ? dma->fd : -1, + if (!g_state.lgr->on_frame(g_state.lgrData, fb, g_state.useDMA ? dma->fd : -1, frame->damageRects, frame->damageRectsCount)) { lgmpClientMessageDone(queue); @@ -719,7 +713,7 @@ int main_frameThread(void * unused) lgmpClientUnsubscribe(&queue); g_state.lgr->on_restart(g_state.lgrData); - if (useDMA) + if (g_state.useDMA) { for(int i = 0; i < sizeof(dmaInfo) / sizeof(struct DMAFrameInfo); ++i) if (dmaInfo[i].fd >= 0) @@ -931,6 +925,12 @@ static int lg_run(void) } } + g_state.useDMA = + g_params.allowDMA && + ivshmemHasDMA(&g_state.shm) && + g_state.lgr->supports && + g_state.lgr->supports(g_state.lgrData, LG_SUPPORTS_DMABUF); + if (!g_state.lgr) { DEBUG_INFO("Unable to find a suitable renderer"); diff --git a/client/src/main.h b/client/src/main.h index 9d280fa5..a48b5a47 100644 --- a/client/src/main.h +++ b/client/src/main.h @@ -95,6 +95,7 @@ struct AppState void * lgrData; atomic_int lgrResize; LG_Lock lgrLock; + bool useDMA; bool cbAvailable; SpiceDataType cbType;