[client] egl: added DMA texture support for direct upload

Note: This only works with the KVMFR kernel module in a VM->VM
configuration. If this causes issues it can be disabled with the new
option `app:allowDMA`
This commit is contained in:
Geoffrey McRae 2020-10-30 02:32:25 +11:00
parent 0bf73d862d
commit 4f9544d61d
14 changed files with 190 additions and 71 deletions

View file

@ -100,7 +100,7 @@ typedef void (* LG_RendererOnRestart )(void * opaque);
typedef void (* LG_RendererOnResize )(void * opaque, const int width, const int height, const LG_RendererRect destRect);
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);
typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format, bool useDMA);
typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD);
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
typedef bool (* LG_RendererRender )(void * opaque, SDL_Window *window);

View file

@ -72,7 +72,7 @@ bool egl_alert_init(EGL_Alert ** alert, const LG_Font * font, LG_FontObj fontObj
(*alert)->fontObj = fontObj;
LG_LOCK_INIT((*alert)->lock);
if (!egl_texture_init(&(*alert)->texture))
if (!egl_texture_init(&(*alert)->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the alert texture");
return false;
@ -175,6 +175,7 @@ void egl_alert_render(EGL_Alert * alert, const float scaleX, const float scaleY)
alert->bmp->width ,
alert->bmp->height,
alert->bmp->width * alert->bmp->bpp,
false,
false
);

View file

@ -76,13 +76,13 @@ bool egl_cursor_init(EGL_Cursor ** cursor)
memset(*cursor, 0, sizeof(EGL_Cursor));
LG_LOCK_INIT((*cursor)->lock);
if (!egl_texture_init(&(*cursor)->texture))
if (!egl_texture_init(&(*cursor)->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
}
if (!egl_texture_init(&(*cursor)->textureMono))
if (!egl_texture_init(&(*cursor)->textureMono, NULL))
{
DEBUG_ERROR("Failed to initialize the cursor mono texture");
return false;
@ -214,7 +214,7 @@ void egl_cursor_render(EGL_Cursor * cursor)
case LG_CURSOR_COLOR:
{
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false);
egl_texture_setup(cursor->texture, EGL_PF_BGRA, cursor->width, cursor->height, cursor->stride, false, false);
egl_texture_update(cursor->texture, data);
egl_model_set_texture(cursor->model, cursor->texture);
break;
@ -238,8 +238,8 @@ void egl_cursor_render(EGL_Cursor * cursor)
xor[y * cursor->width + x] = xorMask;
}
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false);
egl_texture_setup (cursor->texture , EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
egl_texture_setup (cursor->textureMono, EGL_PF_BGRA, cursor->width, cursor->height, cursor->width * 4, false, false);
egl_texture_update(cursor->texture , (uint8_t *)and);
egl_texture_update(cursor->textureMono, (uint8_t *)xor);
break;

View file

@ -47,7 +47,7 @@ struct DesktopShader
struct EGL_Desktop
{
void * egl;
EGLDisplay * display;
EGL_Texture * texture;
struct DesktopShader * shader; // the active shader
@ -94,7 +94,7 @@ static bool egl_init_desktop_shader(
return true;
}
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop)
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display)
{
*desktop = (EGL_Desktop *)malloc(sizeof(EGL_Desktop));
if (!*desktop)
@ -104,8 +104,9 @@ bool egl_desktop_init(void * egl, EGL_Desktop ** desktop)
}
memset(*desktop, 0, sizeof(EGL_Desktop));
(*desktop)->display = display;
if (!egl_texture_init(&(*desktop)->texture))
if (!egl_texture_init(&(*desktop)->texture, display))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
@ -138,7 +139,6 @@ bool egl_desktop_init(void * egl, EGL_Desktop ** desktop)
egl_model_set_default((*desktop)->model);
egl_model_set_texture((*desktop)->model, (*desktop)->texture);
(*desktop)->egl = egl;
(*desktop)->kbNV = app_register_keybind(SDL_SCANCODE_N, egl_desktop_toggle_nv, *desktop);
(*desktop)->nvMax = option_get_int("egl", "nvGainMax");
@ -175,7 +175,7 @@ void egl_desktop_free(EGL_Desktop ** desktop)
*desktop = NULL;
}
bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format)
bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA)
{
enum EGL_PixelFormat pixFmt;
switch(format.type)
@ -219,7 +219,8 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format)
format.width,
format.height,
format.pitch,
true // streaming texture
true, // streaming texture
useDMA
))
{
DEBUG_ERROR("Failed to setup the desktop texture");
@ -229,10 +230,18 @@ bool egl_desktop_setup(EGL_Desktop * desktop, const LG_RendererFormat format)
return true;
}
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame)
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd)
{
if (!egl_texture_update_from_frame(desktop->texture, frame))
return false;
if (dmaFd >= 0)
{
if (!egl_texture_update_from_dma(desktop->texture, frame, dmaFd))
return false;
}
else
{
if (!egl_texture_update_from_frame(desktop->texture, frame))
return false;
}
enum EGL_TexStatus status;
if ((status = egl_texture_process(desktop->texture)) != EGL_TEX_STATUS_OK)

View file

@ -20,14 +20,15 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once
#include <stdbool.h>
#include <SDL2/SDL_egl.h>
#include "interface/renderer.h"
typedef struct EGL_Desktop EGL_Desktop;
bool egl_desktop_init(void * egl, EGL_Desktop ** desktop);
bool egl_desktop_init(EGL_Desktop ** desktop, EGLDisplay * display);
void egl_desktop_free(EGL_Desktop ** desktop);
bool egl_desktop_setup (EGL_Desktop * desktop, const LG_RendererFormat format);
bool egl_desktop_update(EGL_Desktop * desktop, const FrameBuffer * frame);
bool egl_desktop_setup (EGL_Desktop * desktop, const LG_RendererFormat format, bool useDMA);
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, const bool nearest);

View file

@ -52,6 +52,7 @@ struct Options
struct Inst
{
bool dmaSupport;
LG_RendererParams params;
struct Options opt;
@ -188,8 +189,8 @@ bool egl_initialize(void * opaque, Uint32 * sdlFlags)
DEBUG_INFO("Double buffering is %s", doubleBuffer ? "on" : "off");
*sdlFlags = SDL_WINDOW_OPENGL;
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER , doubleBuffer ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER , doubleBuffer ? 1 : 0);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK , SDL_GL_CONTEXT_PROFILE_CORE);
if (option_get_bool("egl", "multisample"))
{
@ -226,6 +227,20 @@ void egl_deinitialize(void * opaque)
free(this);
}
bool egl_supports(void * opaque, LG_RendererSupport flag)
{
struct Inst * this = (struct Inst *)opaque;
switch(flag)
{
case LG_SUPPORTS_DMABUF:
return this->dmaSupport;
default:
return false;
}
}
void egl_on_restart(void * opaque)
{
struct Inst * this = (struct Inst *)opaque;
@ -308,7 +323,7 @@ bool egl_on_mouse_event(void * opaque, const bool visible, const int x, const in
return true;
}
bool egl_on_frame_format(void * opaque, const LG_RendererFormat format)
bool egl_on_frame_format(void * opaque, const LG_RendererFormat format, bool useDMA)
{
struct Inst * this = (struct Inst *)opaque;
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
@ -335,14 +350,14 @@ bool egl_on_frame_format(void * opaque, const LG_RendererFormat format)
}
this->useNearest = this->width < format.width || this->height < format.height;
return egl_desktop_setup(this->desktop, format);
return egl_desktop_setup(this->desktop, format, useDMA);
}
bool egl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd)
{
struct Inst * this = (struct Inst *)opaque;
if (!egl_desktop_update(this->desktop, frame))
if (!egl_desktop_update(this->desktop, frame, dmaFd))
{
DEBUG_INFO("Failed to to update the desktop");
return false;
@ -399,12 +414,12 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
return false;
}
const char *client_exts = eglQueryString(NULL, EGL_EXTENSIONS);
DEBUG_INFO("Supported extensions: %s", client_exts);
bool useNative = false;
if (strstr(client_exts, "EGL_KHR_platform_base") != NULL)
useNative = true;
{
const char *client_exts = eglQueryString(NULL, EGL_EXTENSIONS);
if (strstr(client_exts, "EGL_KHR_platform_base") != NULL)
useNative = true;
}
DEBUG_INFO("use native: %s", useNative ? "true" : "false");
@ -451,7 +466,8 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
return false;
}
if (!eglInitialize(this->display, NULL, NULL))
int maj, min;
if (!eglInitialize(this->display, &maj, &min))
{
DEBUG_ERROR("Unable to initialize EGL");
return false;
@ -494,14 +510,21 @@ bool egl_render_startup(void * opaque, SDL_Window * window)
}
eglMakeCurrent(this->display, this->surface, this->surface, this->context);
const char *client_exts = eglQueryString(this->display, EGL_EXTENSIONS);
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
DEBUG_INFO("Renderer: %s", glGetString(GL_RENDERER));
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
DEBUG_INFO("EGL : %d.%d", maj, min);
DEBUG_INFO("Vendor : %s", glGetString(GL_VENDOR ));
DEBUG_INFO("Renderer : %s", glGetString(GL_RENDERER));
DEBUG_INFO("Version : %s", glGetString(GL_VERSION ));
DEBUG_INFO("EGL APIs : %s", eglQueryString(this->display, EGL_CLIENT_APIS));
DEBUG_INFO("Extensions: %s", client_exts);
if (strstr(client_exts, "EGL_EXT_image_dma_buf_import") != NULL)
this->dmaSupport = true;
eglSwapInterval(this->display, this->opt.vsync ? 1 : 0);
if (!egl_desktop_init(this, &this->desktop))
if (!egl_desktop_init(&this->desktop, this->display))
{
DEBUG_ERROR("Failed to initialize the desktop");
return false;
@ -617,6 +640,7 @@ struct LG_Renderer LGR_EGL =
.create = egl_create,
.initialize = egl_initialize,
.deinitialize = egl_deinitialize,
.supports = egl_supports,
.on_restart = egl_on_restart,
.on_resize = egl_on_resize,
.on_mouse_shape = egl_on_mouse_shape,

View file

@ -66,7 +66,7 @@ bool egl_fps_init(EGL_FPS ** fps, const LG_Font * font, LG_FontObj fontObj)
(*fps)->font = font;
(*fps)->fontObj = fontObj;
if (!egl_texture_init(&(*fps)->texture))
if (!egl_texture_init(&(*fps)->texture, NULL))
{
DEBUG_ERROR("Failed to initialize the fps texture");
return false;
@ -158,6 +158,7 @@ void egl_fps_update(EGL_FPS * fps, const float avgFPS, const float renderFPS)
bmp->width ,
bmp->height,
bmp->width * bmp->bpp,
false,
false
);
}

View file

@ -27,6 +27,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <string.h>
#include <stdio.h>
#include <stdatomic.h>
#include <libdrm/drm_fourcc.h>
#include <SDL2/SDL_egl.h>
@ -35,11 +36,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA
struct Tex
{
GLuint t[3];
bool hasPBO;
GLuint pbo;
void * map;
GLsync sync;
GLuint t[3];
bool hasPBO;
GLuint pbo;
void * map;
GLsync sync;
};
struct TexState
@ -49,10 +50,13 @@ struct TexState
struct EGL_Texture
{
EGLDisplay * display;
enum EGL_PixelFormat pixFmt;
size_t width, height, stride;
size_t bpp;
bool streaming;
bool dma;
bool ready;
int planeCount;
@ -69,7 +73,7 @@ struct EGL_Texture
struct Tex tex[TEXTURE_COUNT];
};
bool egl_texture_init(EGL_Texture ** texture)
bool egl_texture_init(EGL_Texture ** texture, EGLDisplay * display)
{
*texture = (EGL_Texture *)malloc(sizeof(EGL_Texture));
if (!*texture)
@ -79,6 +83,7 @@ bool egl_texture_init(EGL_Texture ** texture)
}
memset(*texture, 0, sizeof(EGL_Texture));
(*texture)->display = display;
return true;
}
@ -106,8 +111,8 @@ void egl_texture_free(EGL_Texture ** texture)
glDeleteSync(t->sync);
}
if ((*texture)->planeCount > 0)
glDeleteTextures((*texture)->planeCount, t->t);
if ((*texture)->planeCount > 0)
glDeleteTextures((*texture)->planeCount, t->t);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
@ -148,7 +153,7 @@ static void egl_texture_unmap(EGL_Texture * texture, uint8_t i)
texture->tex[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 egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_t width, size_t height, size_t stride, bool streaming, bool useDMA)
{
int planeCount;
@ -156,11 +161,14 @@ bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_
{
for(int i = 0; i < texture->textureCount; ++i)
{
egl_texture_unmap(texture, i);
if (texture->tex[i].hasPBO)
if (!useDMA)
{
glDeleteBuffers(1, &texture->tex[i].pbo);
texture->tex[i].hasPBO = false;
egl_texture_unmap(texture, i);
if (texture->tex[i].hasPBO)
{
glDeleteBuffers(1, &texture->tex[i].pbo);
texture->tex[i].hasPBO = false;
}
}
}
}
@ -292,7 +300,7 @@ bool egl_texture_setup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, size_
}
glBindTexture(GL_TEXTURE_2D, 0);
if (!streaming)
if (!streaming || useDMA)
return true;
for(int i = 0; i < texture->textureCount; ++i)
@ -394,6 +402,55 @@ bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer * fr
return true;
}
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))
{
egl_warn_slow();
return true;
}
const uint8_t t = sw % TEXTURE_COUNT;
EGLAttrib const attribs[] =
{
EGL_WIDTH , texture->width,
EGL_HEIGHT , texture->height,
EGL_LINUX_DRM_FOURCC_EXT , DRM_FORMAT_ARGB8888,
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 */
EGLImage image = eglCreateImage(
texture->display,
EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT,
(EGLClientBuffer)NULL,
attribs
);
/* bind the texture and initiate the transfer */
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[0]);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
/* wait for completion */
framebuffer_wait(frame, texture->height * texture->stride);
/* destroy the image to prevent future writes corrupting the display image */
eglDestroyImage(texture->display, image);
atomic_fetch_add_explicit(&texture->state.w, 1, memory_order_release);
return true;
}
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
{
if (!texture->streaming)
@ -409,24 +466,28 @@ enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
nextu == atomic_load_explicit(&texture->state.d, memory_order_acquire))
return texture->ready ? EGL_TEX_STATUS_OK : EGL_TEX_STATUS_NOTREADY;
/* update the texture */
const uint8_t t = su % TEXTURE_COUNT;
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[t].pbo);
for(int p = 0; p < texture->planeCount; ++p)
/* update the texture */
if (!texture->dma)
{
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[p]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
texture->format, texture->dataType, (const void *)texture->offsets[p]);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture->tex[t].pbo);
for(int p = 0; p < texture->planeCount; ++p)
{
glBindTexture(GL_TEXTURE_2D, texture->tex[t].t[p]);
glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->planes[p][2]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, texture->planes[p][0], texture->planes[p][1],
texture->format, texture->dataType, (const void *)texture->offsets[p]);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
/* create a fence to prevent usage before the update is complete */
texture->tex[t].sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
/* we must flush to ensure the sync is in the command buffer */
glFlush();
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
/* create a fence to prevent usage before the update is complete */
texture->tex[t].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);
@ -445,7 +506,12 @@ enum EGL_TexStatus egl_texture_bind(EGL_Texture * texture)
return EGL_TEX_STATUS_NOTREADY;
const uint8_t t = ss % TEXTURE_COUNT;
if (texture->tex[t].sync != 0)
if (texture->dma)
{
ss = atomic_fetch_add_explicit(&texture->state.s, 1,
memory_order_release) + 1;
}
else if (texture->tex[t].sync != 0)
{
switch(glClientWaitSync(texture->tex[t].sync, 0, 20000000)) // 20ms
{

View file

@ -23,6 +23,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "shader.h"
#include "common/framebuffer.h"
#include <SDL2/SDL_egl.h>
#include <GL/gl.h>
typedef struct EGL_Texture EGL_Texture;
@ -43,12 +44,13 @@ enum EGL_TexStatus
EGL_TEX_STATUS_ERROR
};
bool egl_texture_init(EGL_Texture ** tex);
bool egl_texture_init(EGL_Texture ** texture, EGLDisplay * display);
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 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);
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);

View file

@ -375,7 +375,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 opengl_on_frame_format(void * opaque, const LG_RendererFormat format, bool useDMA)
{
struct Inst * this = (struct Inst *)opaque;

View file

@ -84,6 +84,13 @@ static struct Option options[] =
.type = OPTION_TYPE_INT,
.value.x_int = 1000
},
{
.module = "app",
.name = "allowDMA",
.description = "Allow direct DMA transfers if possible (VM-VM only for now)",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true
},
// window options
{
@ -401,6 +408,7 @@ bool config_load(int argc, char * argv[])
// setup the application params for the basic types
params.cursorPollInterval = option_get_int ("app", "cursorPollInterval");
params.framePollInterval = option_get_int ("app", "framePollInterval" );
params.allowDMA = option_get_bool ("app", "allowDMA" );
params.windowTitle = option_get_string("win", "title" );
params.autoResize = option_get_bool ("win", "autoResize" );

View file

@ -375,7 +375,10 @@ static int frameThread(void * unused)
//FIXME: Should use LGMP_Q_FRAME_LEN
struct DMAFrameInfo dmaInfo[2] = {0};
const bool useDMA = ivshmemHasDMA(&state.shm) && state.lgr->supports &&
const bool useDMA =
params.allowDMA &&
ivshmemHasDMA(&state.shm) &&
state.lgr->supports &&
state.lgr->supports(state.lgrData, LG_SUPPORTS_DMABUF);
if (useDMA)
@ -479,7 +482,7 @@ static int frameThread(void * unused)
frame->width, frame->height,
frame->stride, frame->pitch);
if (!state.lgr->on_frame_format(state.lgrData, lgrFormat))
if (!state.lgr->on_frame_format(state.lgrData, lgrFormat, useDMA))
{
DEBUG_ERROR("renderer failed to configure format");
state.state = APP_STATE_SHUTDOWN;
@ -557,7 +560,6 @@ static int frameThread(void * unused)
atomic_fetch_add_explicit(&state.frameCount, 1, memory_order_relaxed);
lgSignalEvent(e_frame);
lgmpClientMessageDone(queue);
}

View file

@ -158,6 +158,7 @@ struct AppParams
unsigned int cursorPollInterval;
unsigned int framePollInterval;
bool allowDMA;
bool forceRenderer;
unsigned int forceRendererIndex;

View file

@ -39,7 +39,11 @@ const size_t FrameBufferStructSize = sizeof(FrameBuffer);
void framebuffer_wait(const FrameBuffer * frame, size_t size)
{
while(atomic_load_explicit(&frame->wp, memory_order_acquire) != size) {}
while(atomic_load_explicit(&frame->wp, memory_order_acquire) < size) {
while(frame->wp < size)
{
}
}
}
bool framebuffer_read(const FrameBuffer * frame, void * restrict dst,