[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.
This commit is contained in:
Geoffrey McRae 2021-08-02 23:37:33 +10:00
parent e23144aecd
commit 13d9c84dc9
19 changed files with 910 additions and 583 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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,

View File

@ -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;

View File

@ -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);
}

View File

@ -19,580 +19,116 @@
*/
#include "texture.h"
#include "common/debug.h"
#include <stdbool.h>
#include <assert.h>
#include "shader.h"
#include "common/framebuffer.h"
#include "egl_dynprocs.h"
#include "egldebug.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdatomic.h>
#include <EGL/egl.h>
#include <EGL/eglext.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')
#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);
}

View File

@ -24,34 +24,121 @@
#include "shader.h"
#include "common/framebuffer.h"
#include "util.h"
#include <EGL/egl.h>
#include <EGL/eglext.h>
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);

View File

@ -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 <string.h>
#include <assert.h>
// 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
};

View File

@ -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_);

View File

@ -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
};

View File

@ -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 <assert.h>
#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
};

View File

@ -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 <EGL/egl.h>
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
#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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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");

View File

@ -95,6 +95,7 @@ struct AppState
void * lgrData;
atomic_int lgrResize;
LG_Lock lgrLock;
bool useDMA;
bool cbAvailable;
SpiceDataType cbType;