mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-10 08:38:20 +00:00
[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:
parent
e23144aecd
commit
13d9c84dc9
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
278
client/renderers/EGL/texture_buffer.c
Normal file
278
client/renderers/EGL/texture_buffer.c
Normal 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
|
||||
};
|
53
client/renderers/EGL/texture_buffer.h
Normal file
53
client/renderers/EGL/texture_buffer.h
Normal 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_);
|
88
client/renderers/EGL/texture_dmabuf.c
Normal file
88
client/renderers/EGL/texture_dmabuf.c
Normal 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
|
||||
};
|
59
client/renderers/EGL/texture_framebuffer.c
Normal file
59
client/renderers/EGL/texture_framebuffer.c
Normal 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
|
||||
};
|
166
client/renderers/EGL/texture_util.c
Normal file
166
client/renderers/EGL/texture_util.c
Normal 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;
|
||||
}
|
54
client/renderers/EGL/texture_util.h
Normal file
54
client/renderers/EGL/texture_util.h
Normal 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);
|
@ -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;
|
||||
|
||||
|
@ -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");
|
||||
|
@ -95,6 +95,7 @@ struct AppState
|
||||
void * lgrData;
|
||||
atomic_int lgrResize;
|
||||
LG_Lock lgrLock;
|
||||
bool useDMA;
|
||||
|
||||
bool cbAvailable;
|
||||
SpiceDataType cbType;
|
||||
|
Loading…
Reference in New Issue
Block a user