mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-22 13:37:22 +00:00
[client] redesign of the renderer archiceture for mt support
This is the first of two commits that completely turn the rendering code on it's head. This change set decouples the guest's capture rate from the host's render rate for both cursor and frame updates. This helps prevent the host application from stalling when waiting for frame draws when all it want's to do is send cursor updates. * Breaks OpenGL-Basic for now
This commit is contained in:
parent
a70adb2568
commit
695822bd6d
@ -18,8 +18,8 @@ OBJS = main.o \
|
||||
lg-renderer.o \
|
||||
spice/spice.o \
|
||||
ivshmem/ivshmem.o \
|
||||
renderers/opengl.o \
|
||||
renderers/opengl-basic.o
|
||||
renderers/opengl.o
|
||||
# renderers/opengl-basic.o
|
||||
|
||||
BUILD_OBJS = $(foreach obj,$(OBJS),$(BUILD)/$(obj))
|
||||
|
||||
|
@ -28,10 +28,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
((x)->get_name && \
|
||||
(x)->create && \
|
||||
(x)->initialize && \
|
||||
(x)->configure && \
|
||||
(x)->deconfigure && \
|
||||
(x)->deinitialize && \
|
||||
(x)->is_compatible && \
|
||||
(x)->on_resize && \
|
||||
(x)->on_mouse_shape && \
|
||||
(x)->on_mouse_event && \
|
||||
@ -96,15 +93,12 @@ LG_RendererCursor;
|
||||
typedef const char * (* LG_RendererGetName )();
|
||||
typedef bool (* LG_RendererCreate )(void ** opaque, const LG_RendererParams params);
|
||||
typedef bool (* LG_RendererInitialize )(void * opaque, Uint32 * sdlFlags);
|
||||
typedef bool (* LG_RendererConfigure )(void * opaque, SDL_Window *window, const LG_RendererFormat format);
|
||||
typedef void (* LG_RendererDeConfigure )(void * opaque);
|
||||
typedef void (* LG_RendererDeInitialize)(void * opaque);
|
||||
typedef bool (* LG_RendererIsCompatible)(void * opaque, const LG_RendererFormat format);
|
||||
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_RendererOnFrameEvent)(void * opaque, const uint8_t * data);
|
||||
typedef bool (* LG_RendererRender )(void * opaque);
|
||||
typedef bool (* LG_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const uint8_t * data);
|
||||
typedef bool (* LG_RendererRender )(void * opaque, SDL_Window *window);
|
||||
|
||||
typedef struct LG_Renderer
|
||||
{
|
||||
@ -113,10 +107,7 @@ typedef struct LG_Renderer
|
||||
LG_RendererOptions options;
|
||||
unsigned int option_count;
|
||||
LG_RendererInitialize initialize;
|
||||
LG_RendererConfigure configure;
|
||||
LG_RendererDeConfigure deconfigure;
|
||||
LG_RendererDeInitialize deinitialize;
|
||||
LG_RendererIsCompatible is_compatible;
|
||||
LG_RendererOnResize on_resize;
|
||||
LG_RendererOnMouseShape on_mouse_shape;
|
||||
LG_RendererOnMouseEvent on_mouse_event;
|
||||
|
@ -21,12 +21,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "lg-renderer.h"
|
||||
|
||||
extern const LG_Renderer LGR_OpenGL;
|
||||
extern const LG_Renderer LGR_OpenGLBasic;
|
||||
//extern const LG_Renderer LGR_OpenGLBasic;
|
||||
|
||||
const LG_Renderer * LG_Renderers[] =
|
||||
{
|
||||
&LGR_OpenGL,
|
||||
&LGR_OpenGLBasic,
|
||||
// &LGR_OpenGLBasic,
|
||||
NULL // end of array sentinal
|
||||
};
|
||||
|
||||
|
301
client/main.c
301
client/main.c
@ -119,7 +119,7 @@ struct AppParams params =
|
||||
.forceRenderer = false
|
||||
};
|
||||
|
||||
inline void updatePositionInfo()
|
||||
static inline void updatePositionInfo()
|
||||
{
|
||||
int w, h;
|
||||
SDL_GetWindowSize(state.window, &w, &h);
|
||||
@ -161,58 +161,123 @@ inline void updatePositionInfo()
|
||||
state.lgr->on_resize(state.lgrData, w, h, state.dstRect);
|
||||
}
|
||||
|
||||
int renderThread(void * unused)
|
||||
void mainThread()
|
||||
{
|
||||
while(state.running)
|
||||
{
|
||||
nsleep(1000);
|
||||
if (state.started)
|
||||
if (!state.lgr->render(state.lgrData, state.window))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int cursorThread(void * unused)
|
||||
{
|
||||
bool error = false;
|
||||
struct KVMFRHeader header;
|
||||
|
||||
LG_RendererCursor cursorType = LG_CURSOR_COLOR;
|
||||
struct KVMFRFrame cursor = {};
|
||||
size_t cursorDataSize = 0;
|
||||
uint8_t * cursorData = NULL;
|
||||
|
||||
volatile uint32_t * updateCount = &state.shm->updateCount;
|
||||
uint32_t version = 0;
|
||||
|
||||
memset(&header, 0, sizeof(struct KVMFRHeader));
|
||||
|
||||
while(state.running)
|
||||
{
|
||||
// poll until we have a new frame, or we time out
|
||||
while(header.updateCount == *updateCount && state.running)
|
||||
{
|
||||
const struct timespec s =
|
||||
{
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 1000
|
||||
};
|
||||
nanosleep(&s, NULL);
|
||||
}
|
||||
|
||||
if (!state.running)
|
||||
break;
|
||||
|
||||
// we must take a copy of the header, both to let the guest advance and to
|
||||
// prevent the contained arguments being abused to overflow buffers
|
||||
memcpy(&header, state.shm, sizeof(struct KVMFRHeader));
|
||||
__sync_or_and_fetch(&state.shm->flags, KVMFR_HEADER_FLAG_READY);
|
||||
memcpy(&header, (KVMFRHeader *)state.shm, sizeof(struct KVMFRHeader));
|
||||
__sync_and_and_fetch(&state.shm->flags, ~KVMFR_HEADER_FLAG_CURSOR);
|
||||
|
||||
// check the header's magic and version are valid
|
||||
if (memcmp(header.magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC)) != 0)
|
||||
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE &&
|
||||
header.detail.cursor.version != version)
|
||||
{
|
||||
version = header.detail.cursor.version;
|
||||
|
||||
bool bad = false;
|
||||
switch(header.detail.cursor.type)
|
||||
{
|
||||
case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break;
|
||||
case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break;
|
||||
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
|
||||
default:
|
||||
DEBUG_ERROR("Invalid cursor type");
|
||||
bad = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (bad)
|
||||
break;
|
||||
|
||||
// check the data position is sane
|
||||
const uint64_t dataSize = header.detail.frame.height * header.detail.frame.pitch;
|
||||
if (header.detail.cursor.dataPos + dataSize > state.shmSize)
|
||||
{
|
||||
DEBUG_ERROR("The guest sent an invalid mouse dataPos");
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.detail.cursor.dataPos;
|
||||
if (!state.lgr->on_mouse_shape(
|
||||
state.lgrData,
|
||||
cursorType,
|
||||
header.detail.cursor.width,
|
||||
header.detail.cursor.height,
|
||||
header.detail.cursor.pitch,
|
||||
data)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to update mouse shape");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_POS)
|
||||
{
|
||||
state.cursor.x = header.detail.cursor.x;
|
||||
state.cursor.y = header.detail.cursor.y;
|
||||
state.cursorVisible = header.detail.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
state.haveCursorPos = true;
|
||||
|
||||
state.lgr->on_mouse_event
|
||||
(
|
||||
state.lgrData,
|
||||
state.cursorVisible,
|
||||
state.cursor.x,
|
||||
state.cursor.y
|
||||
);
|
||||
}
|
||||
|
||||
// poll until we have cursor data
|
||||
while(((state.shm->flags & KVMFR_HEADER_FLAG_CURSOR) == 0) && state.running)
|
||||
{
|
||||
usleep(1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header.version != KVMFR_HEADER_VERSION)
|
||||
{
|
||||
DEBUG_ERROR("KVMFR version missmatch, expected %u but got %u", KVMFR_HEADER_VERSION, header.version);
|
||||
DEBUG_ERROR("This is not a bug, ensure you have the right version of looking-glass-host.exe on the guest");
|
||||
break;
|
||||
}
|
||||
|
||||
// if we have a frame
|
||||
if (header.flags & KVMFR_HEADER_FLAG_FRAME)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int frameThread(void * unused)
|
||||
{
|
||||
bool error = false;
|
||||
struct KVMFRHeader header;
|
||||
|
||||
memset(&header, 0, sizeof(struct KVMFRHeader));
|
||||
|
||||
while(state.running)
|
||||
{
|
||||
// poll until we have a new frame
|
||||
if(!(state.shm->flags & KVMFR_HEADER_FLAG_FRAME))
|
||||
{
|
||||
nsleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
// we must take a copy of the header, both to let the guest advance and to
|
||||
// prevent the contained arguments being abused to overflow buffers
|
||||
memcpy(&header, (KVMFRHeader *)state.shm, sizeof(struct KVMFRHeader));
|
||||
__sync_and_and_fetch(&state.shm->flags, ~KVMFR_HEADER_FLAG_FRAME);
|
||||
|
||||
// sainty check of the frame format
|
||||
if (
|
||||
header.detail.frame.type >= FRAME_TYPE_MAX ||
|
||||
@ -262,154 +327,28 @@ int renderThread(void * unused)
|
||||
break;
|
||||
}
|
||||
|
||||
// check if the renderer needs reconfiguration
|
||||
if (!state.lgr->is_compatible(state.lgrData, lgrFormat))
|
||||
if (header.detail.frame.width != state.srcSize.x || header.detail.frame.height != state.srcSize.y)
|
||||
{
|
||||
DEBUG_INFO("Data Format: w=%u, h=%u, s=%u, p=%u, bpp=%u",
|
||||
lgrFormat.width, lgrFormat.height, lgrFormat.stride, lgrFormat.pitch, lgrFormat.bpp);
|
||||
|
||||
state.lgr->deconfigure(state.lgrData);
|
||||
if (!state.lgr->configure(state.lgrData, state.window, lgrFormat))
|
||||
{
|
||||
DEBUG_ERROR("Failed to reconfigure %s", state.lgr->get_name());
|
||||
break;
|
||||
}
|
||||
|
||||
state.srcSize.x = header.detail.frame.width;
|
||||
state.srcSize.y = header.detail.frame.height;
|
||||
if (params.autoResize)
|
||||
SDL_SetWindowSize(state.window, header.detail.frame.width, header.detail.frame.height);
|
||||
|
||||
state.started = true;
|
||||
updatePositionInfo();
|
||||
|
||||
// if we have saved shape info, send it now
|
||||
if (cursorData)
|
||||
{
|
||||
if (!state.lgr->on_mouse_shape(
|
||||
state.lgrData,
|
||||
cursorType,
|
||||
cursor.width,
|
||||
cursor.height,
|
||||
cursor.pitch,
|
||||
cursorData
|
||||
))
|
||||
{
|
||||
DEBUG_ERROR("Failed to update mouse shape");
|
||||
break;
|
||||
}
|
||||
|
||||
free(cursorData);
|
||||
cursorData = NULL;
|
||||
cursorDataSize = 0;
|
||||
}
|
||||
|
||||
// if we have a cursor position, send it now
|
||||
if (state.haveCursorPos)
|
||||
{
|
||||
state.lgr->on_mouse_event
|
||||
(
|
||||
state.lgrData,
|
||||
state.cursorVisible,
|
||||
state.cursor.x,
|
||||
state.cursor.y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos;
|
||||
if (!state.lgr->on_frame_event(state.lgrData, data))
|
||||
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, data))
|
||||
{
|
||||
DEBUG_ERROR("Failed to render the frame");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have cursor data
|
||||
if (header.flags & KVMFR_HEADER_FLAG_CURSOR)
|
||||
{
|
||||
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_POS)
|
||||
{
|
||||
state.cursor.x = header.detail.cursor.x;
|
||||
state.cursor.y = header.detail.cursor.y;
|
||||
state.cursorVisible = header.detail.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE;
|
||||
state.haveCursorPos = true;
|
||||
}
|
||||
|
||||
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE)
|
||||
{
|
||||
if (state.lgr)
|
||||
{
|
||||
bool bad = false;
|
||||
switch(header.detail.cursor.type)
|
||||
{
|
||||
case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break;
|
||||
case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break;
|
||||
case CURSOR_TYPE_MASKED_COLOR: cursorType = LG_CURSOR_MASKED_COLOR; break;
|
||||
default:
|
||||
DEBUG_ERROR("Invalid cursor type");
|
||||
bad = true;
|
||||
DEBUG_ERROR("renderer on frame event returned failure");
|
||||
break;
|
||||
}
|
||||
|
||||
if (bad)
|
||||
break;
|
||||
|
||||
// check the data position is sane
|
||||
const uint64_t dataSize = header.detail.frame.height * header.detail.frame.pitch;
|
||||
if (header.detail.frame.dataPos + dataSize > state.shmSize)
|
||||
{
|
||||
DEBUG_ERROR("The guest sent an invalid mouse dataPos");
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos;
|
||||
if (!state.started)
|
||||
{
|
||||
// save off the cursor data so we can tell the renderer when it's ready
|
||||
if (cursorDataSize < dataSize)
|
||||
{
|
||||
if (cursorData) free(cursorData);
|
||||
cursorData = (uint8_t *)malloc(dataSize);
|
||||
cursorDataSize = dataSize;
|
||||
}
|
||||
memcpy(&cursor, &header.detail.frame, sizeof(struct KVMFRFrame));
|
||||
memcpy(cursorData, data, dataSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!state.lgr->on_mouse_shape(
|
||||
state.lgrData,
|
||||
cursorType,
|
||||
header.detail.frame.width,
|
||||
header.detail.frame.height,
|
||||
header.detail.frame.pitch,
|
||||
data)
|
||||
)
|
||||
{
|
||||
DEBUG_ERROR("Failed to update mouse shape");
|
||||
break;
|
||||
DEBUG_INFO("feed started");
|
||||
state.started = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (state.lgr && state.started)
|
||||
{
|
||||
state.lgr->on_mouse_event(
|
||||
state.lgrData,
|
||||
state.cursorVisible,
|
||||
state.cursor.x,
|
||||
state.cursor.y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.started && state.lgr)
|
||||
state.lgr->render(state.lgrData);
|
||||
}
|
||||
|
||||
if (cursorData)
|
||||
free(cursorData);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -906,7 +845,7 @@ int run()
|
||||
|
||||
if (!(t_event = SDL_CreateThread(eventThread, "eventThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("gpu create thread failed");
|
||||
DEBUG_ERROR("event create thread failed");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -919,7 +858,33 @@ int run()
|
||||
usleep(1000);
|
||||
DEBUG_INFO("Host ready, starting session");
|
||||
|
||||
renderThread(NULL);
|
||||
// check the header's magic and version are valid
|
||||
if (memcmp(state.shm->magic, KVMFR_HEADER_MAGIC, sizeof(KVMFR_HEADER_MAGIC)) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Invalid header magic, is the host running?");
|
||||
break;
|
||||
}
|
||||
|
||||
if (state.shm->version != KVMFR_HEADER_VERSION)
|
||||
{
|
||||
DEBUG_ERROR("KVMFR version missmatch, expected %u but got %u", KVMFR_HEADER_VERSION, state.shm->version);
|
||||
DEBUG_ERROR("This is not a bug, ensure you have the right version of looking-glass-host.exe on the guest");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(t_event = SDL_CreateThread(cursorThread, "cursorThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("cursor create thread failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(t_event = SDL_CreateThread(frameThread, "frameThread", NULL)))
|
||||
{
|
||||
DEBUG_ERROR("frame create thread failed");
|
||||
break;
|
||||
}
|
||||
|
||||
mainThread();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "memcpySSE.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define BUFFER_COUNT 1
|
||||
#define BUFFER_COUNT 2
|
||||
|
||||
#define FRAME_TEXTURE 0
|
||||
#define FPS_TEXTURE 1
|
||||
@ -48,7 +48,6 @@ struct Options
|
||||
bool mipmap;
|
||||
bool vsync;
|
||||
bool preventBuffer;
|
||||
bool splitMouse;
|
||||
};
|
||||
|
||||
static struct Options defaultOptions =
|
||||
@ -56,16 +55,15 @@ static struct Options defaultOptions =
|
||||
.mipmap = true,
|
||||
.vsync = true,
|
||||
.preventBuffer = true,
|
||||
.splitMouse = false
|
||||
};
|
||||
|
||||
struct LGR_OpenGL
|
||||
struct Inst
|
||||
{
|
||||
LG_RendererParams params;
|
||||
struct Options opt;
|
||||
|
||||
bool configured;
|
||||
SDL_Window * sdlWindow;
|
||||
bool reconfigure;
|
||||
SDL_GLContext glContext;
|
||||
bool doneInfo;
|
||||
|
||||
@ -73,6 +71,7 @@ struct LGR_OpenGL
|
||||
bool resizeWindow;
|
||||
bool frameUpdate;
|
||||
|
||||
volatile int formatLock;
|
||||
LG_RendererFormat format;
|
||||
GLuint intFormat;
|
||||
GLuint vboFormat;
|
||||
@ -82,7 +81,8 @@ struct LGR_OpenGL
|
||||
bool hasBuffers;
|
||||
GLuint vboID[1];
|
||||
uint8_t * texPixels[BUFFER_COUNT];
|
||||
int texIndex;
|
||||
volatile int syncLock;
|
||||
int texIndex, wTexIndex;
|
||||
int texList;
|
||||
int fpsList;
|
||||
int mouseList;
|
||||
@ -99,6 +99,14 @@ struct LGR_OpenGL
|
||||
uint64_t renderCount;
|
||||
SDL_Rect fpsRect;
|
||||
|
||||
volatile int mouseLock;
|
||||
LG_RendererCursor mouseCursor;
|
||||
int mouseWidth;
|
||||
int mouseHeight;
|
||||
int mousePitch;
|
||||
uint8_t * mouseData;
|
||||
size_t mouseDataSize;
|
||||
|
||||
bool mouseUpdate;
|
||||
bool newShape;
|
||||
uint64_t lastMouseDraw;
|
||||
@ -107,43 +115,41 @@ struct LGR_OpenGL
|
||||
SDL_Rect mousePos;
|
||||
};
|
||||
|
||||
bool lgr_opengl_check_error(const char * name)
|
||||
{
|
||||
GLenum error = glGetError();
|
||||
if (error == GL_NO_ERROR)
|
||||
return false;
|
||||
static bool _check_gl_error(unsigned int line, const char * name);
|
||||
#define check_gl_error(name) _check_gl_error(__LINE__, name)
|
||||
|
||||
const GLubyte * errStr = gluErrorString(error);
|
||||
DEBUG_ERROR("%s = %d (%s)", name, error, errStr);
|
||||
return true;
|
||||
}
|
||||
static void deconfigure(struct Inst * this);
|
||||
static bool configure(struct Inst * this, SDL_Window *window);
|
||||
static void update_mouse_shape(struct Inst * this, bool * newShape);
|
||||
static bool draw_frame(struct Inst * this, bool * frameUpdate);
|
||||
static void draw_mouse(struct Inst * this);
|
||||
|
||||
const char * lgr_opengl_get_name()
|
||||
const char * opengl_get_name()
|
||||
{
|
||||
return "OpenGL";
|
||||
}
|
||||
|
||||
bool lgr_opengl_create(void ** opaque, const LG_RendererParams params)
|
||||
bool opengl_create(void ** opaque, const LG_RendererParams params)
|
||||
{
|
||||
// create our local storage
|
||||
*opaque = malloc(sizeof(struct LGR_OpenGL));
|
||||
*opaque = malloc(sizeof(struct Inst));
|
||||
if (!*opaque)
|
||||
{
|
||||
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct LGR_OpenGL));
|
||||
DEBUG_INFO("Failed to allocate %lu bytes", sizeof(struct Inst));
|
||||
return false;
|
||||
}
|
||||
memset(*opaque, 0, sizeof(struct LGR_OpenGL));
|
||||
memset(*opaque, 0, sizeof(struct Inst));
|
||||
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)*opaque;
|
||||
struct Inst * this = (struct Inst *)*opaque;
|
||||
memcpy(&this->params, ¶ms , sizeof(LG_RendererParams));
|
||||
memcpy(&this->opt , &defaultOptions, sizeof(struct Options ));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lgr_opengl_initialize(void * opaque, Uint32 * sdlFlags)
|
||||
bool opengl_initialize(void * opaque, Uint32 * sdlFlags)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return false;
|
||||
|
||||
@ -165,23 +171,280 @@ bool lgr_opengl_initialize(void * opaque, Uint32 * sdlFlags)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFormat format)
|
||||
void opengl_deinitialize(void * opaque)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
deconfigure(this);
|
||||
if (this->mouseData)
|
||||
free(this->mouseData);
|
||||
|
||||
free(this);
|
||||
}
|
||||
|
||||
void opengl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->window.x = width;
|
||||
this->window.y = height;
|
||||
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
|
||||
|
||||
this->resizeWindow = true;
|
||||
}
|
||||
|
||||
bool opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return false;
|
||||
|
||||
if (this->configured)
|
||||
while(__sync_lock_test_and_set(&this->mouseLock, 1));
|
||||
this->mouseCursor = cursor;
|
||||
this->mouseWidth = width;
|
||||
this->mouseHeight = height;
|
||||
this->mousePitch = pitch;
|
||||
|
||||
const size_t size = height * pitch;
|
||||
if (size > this->mouseDataSize)
|
||||
{
|
||||
DEBUG_ERROR("Renderer already configured, call deconfigure first");
|
||||
if (this->mouseData)
|
||||
free(this->mouseData);
|
||||
this->mouseData = (uint8_t *)malloc(size);
|
||||
this->mouseDataSize = size;
|
||||
}
|
||||
|
||||
memcpy(this->mouseData, data, size);
|
||||
this->newShape = true;
|
||||
__sync_lock_release(&this->mouseLock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return false;
|
||||
|
||||
if (this->mousePos.x == x && this->mousePos.y == y && this->mouseVisible == visible)
|
||||
return true;
|
||||
|
||||
this->mouseVisible = visible;
|
||||
this->mousePos.x = x;
|
||||
this->mousePos.y = y;
|
||||
this->mouseUpdate = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Invalid opaque pointer");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->sdlWindow = window;
|
||||
while(__sync_lock_test_and_set(&this->formatLock, 1));
|
||||
if (this->reconfigure)
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this->configured || memcmp(&this->format, &format, sizeof(LG_RendererFormat)) != 0)
|
||||
{
|
||||
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
|
||||
this->reconfigure = true;
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return true;
|
||||
}
|
||||
__sync_lock_release(&this->formatLock);
|
||||
|
||||
// lock, perform the update, then unlock
|
||||
while(__sync_lock_test_and_set(&this->syncLock, 1));
|
||||
memcpySSE(this->texPixels[this->wTexIndex], data, this->texSize);
|
||||
this->frameUpdate = true;
|
||||
__sync_lock_release(&this->syncLock);
|
||||
|
||||
++this->frameCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool opengl_render(void * opaque, SDL_Window * window)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return false;
|
||||
|
||||
if (!configure(this, window))
|
||||
return true;
|
||||
|
||||
if (this->resizeWindow)
|
||||
{
|
||||
// setup the projection matrix
|
||||
glViewport(0, 0, this->window.x, this->window.y);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, this->window.x, this->window.y, 0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
|
||||
glScalef(
|
||||
(float)this->destRect.w / (float)this->format.width,
|
||||
(float)this->destRect.h / (float)this->format.height,
|
||||
1.0f
|
||||
);
|
||||
|
||||
// update the scissor rect to prevent drawing outside of the frame
|
||||
glScissor(
|
||||
this->destRect.x,
|
||||
this->destRect.y,
|
||||
this->destRect.w,
|
||||
this->destRect.h
|
||||
);
|
||||
|
||||
this->resizeWindow = false;
|
||||
}
|
||||
|
||||
bool frameUpdate;
|
||||
if (!draw_frame(this, &frameUpdate))
|
||||
return false;
|
||||
|
||||
bool newShape;
|
||||
update_mouse_shape(this, &newShape);
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
glCallList(this->texList + this->texIndex);
|
||||
draw_mouse(this);
|
||||
if (this->fpsTexture)
|
||||
glCallList(this->fpsList);
|
||||
|
||||
if (this->opt.preventBuffer)
|
||||
{
|
||||
unsigned int before, after;
|
||||
glXGetVideoSyncSGI(&before);
|
||||
SDL_GL_SwapWindow(window);
|
||||
|
||||
// wait for the swap to happen to ensure we dont buffer frames //
|
||||
glXGetVideoSyncSGI(&after);
|
||||
if (before == after)
|
||||
glXWaitVideoSyncSGI(1, 0, &before);
|
||||
}
|
||||
else
|
||||
SDL_GL_SwapWindow(window);
|
||||
|
||||
const uint64_t t = nanotime();
|
||||
this->renderTime += t - this->lastFrameTime;
|
||||
this->lastFrameTime = t;
|
||||
++this->renderCount;
|
||||
|
||||
this->mouseUpdate = false;
|
||||
this->lastMouseDraw = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_opt_mipmap(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.mipmap = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_vsync(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.vsync = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_prevent_buffer(void * opaque, const char *value)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.preventBuffer = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static LG_RendererOpt opengl_options[] =
|
||||
{
|
||||
{
|
||||
.name = "mipmap",
|
||||
.desc = "Enable or disable mipmapping [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_mipmap
|
||||
},
|
||||
{
|
||||
.name = "vsync",
|
||||
.desc ="Enable or disable vsync [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_vsync
|
||||
},
|
||||
{
|
||||
.name = "preventBuffer",
|
||||
.desc = "Prevent the driver from buffering frames [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_prevent_buffer
|
||||
}
|
||||
};
|
||||
|
||||
const LG_Renderer LGR_OpenGL =
|
||||
{
|
||||
.get_name = opengl_get_name,
|
||||
.options = opengl_options,
|
||||
.option_count = LGR_OPTION_COUNT(opengl_options),
|
||||
.create = opengl_create,
|
||||
.initialize = opengl_initialize,
|
||||
.deinitialize = opengl_deinitialize,
|
||||
.on_resize = opengl_on_resize,
|
||||
.on_mouse_shape = opengl_on_mouse_shape,
|
||||
.on_mouse_event = opengl_on_mouse_event,
|
||||
.on_frame_event = opengl_on_frame_event,
|
||||
.render = opengl_render
|
||||
};
|
||||
|
||||
static bool _check_gl_error(unsigned int line, const char * name)
|
||||
{
|
||||
GLenum error = glGetError();
|
||||
if (error == GL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
const GLubyte * errStr = gluErrorString(error);
|
||||
DEBUG_ERROR("%d: %s = %d (%s)", line, name, error, errStr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool configure(struct Inst * this, SDL_Window *window)
|
||||
{
|
||||
while(__sync_lock_test_and_set(&this->formatLock, 1));
|
||||
if (!this->reconfigure)
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return this->configured;
|
||||
}
|
||||
|
||||
if (this->configured)
|
||||
deconfigure(this);
|
||||
|
||||
this->glContext = SDL_GL_CreateContext(window);
|
||||
if (!this->glContext)
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the OpenGL context");
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -193,12 +456,6 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
this->doneInfo = true;
|
||||
}
|
||||
|
||||
if (SDL_GL_MakeCurrent(window, this->glContext) != 0)
|
||||
{
|
||||
DEBUG_ERROR("Failed to make the GL context current");
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(this->opt.vsync ? 1 : 0);
|
||||
|
||||
// check if the GPU supports GL_ARB_buffer_storage first
|
||||
@ -207,11 +464,12 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
if (!gluCheckExtension((const GLubyte *)"GL_ARB_buffer_storage", extensions))
|
||||
{
|
||||
DEBUG_INFO("The GPU doesn't support GL_ARB_buffer_storage");
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// assume 24 and 32 bit formats are RGB and RGBA
|
||||
switch(format.bpp)
|
||||
switch(this->format.bpp)
|
||||
{
|
||||
case 24:
|
||||
this->intFormat = GL_RGB8;
|
||||
@ -224,12 +482,13 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_INFO("%d bpp not supported", format.bpp);
|
||||
DEBUG_INFO("%d bpp not supported", this->format.bpp);
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate the texture size in bytes
|
||||
this->texSize = format.height * format.pitch;
|
||||
this->texSize = this->format.height * this->format.pitch;
|
||||
|
||||
// generate lists for drawing
|
||||
this->texList = glGenLists(BUFFER_COUNT);
|
||||
@ -238,13 +497,19 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
|
||||
// generate the pixel unpack buffers
|
||||
glGenBuffers(1, this->vboID);
|
||||
if (lgr_opengl_check_error("glGenBuffers"))
|
||||
if (check_gl_error("glGenBuffers"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
this->hasBuffers = true;
|
||||
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0]);
|
||||
if (lgr_opengl_check_error("glBindBuffer"))
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBufferStorage(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
@ -253,8 +518,11 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
GL_MAP_WRITE_BIT |
|
||||
GL_MAP_PERSISTENT_BIT
|
||||
);
|
||||
if (lgr_opengl_check_error("glBufferStorage"))
|
||||
if (check_gl_error("glBufferStorage"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->texPixels[0] = glMapBufferRange(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
@ -265,36 +533,48 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
GL_MAP_FLUSH_EXPLICIT_BIT
|
||||
);
|
||||
|
||||
if (lgr_opengl_check_error("glMapBufferRange"))
|
||||
if (check_gl_error("glMapBufferRange"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[i-1] + this->texSize;
|
||||
|
||||
// create the textures
|
||||
glGenTextures(TEXTURE_COUNT, this->textures);
|
||||
if (lgr_opengl_check_error("glGenTextures"))
|
||||
if (check_gl_error("glGenTextures"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
this->hasTextures = true;
|
||||
|
||||
// create the frame texture
|
||||
glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]);
|
||||
if (lgr_opengl_check_error("glBindTexture"))
|
||||
if (check_gl_error("glBindTexture"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
this->intFormat,
|
||||
format.width,
|
||||
format.height * BUFFER_COUNT,
|
||||
this->format.width,
|
||||
this->format.height * BUFFER_COUNT,
|
||||
0,
|
||||
this->vboFormat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
(void*)0
|
||||
);
|
||||
if (lgr_opengl_check_error("glTexImage2D"))
|
||||
if (check_gl_error("glTexImage2D"))
|
||||
{
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// configure the texture
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
@ -313,9 +593,9 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f, ts); glVertex2i(0 , 0 );
|
||||
glTexCoord2f(1.0f, ts); glVertex2i(format.width, 0 );
|
||||
glTexCoord2f(0.0f, te); glVertex2i(0 , format.height);
|
||||
glTexCoord2f(1.0f, te); glVertex2i(format.width, format.height);
|
||||
glTexCoord2f(1.0f, ts); glVertex2i(this->format.width, 0 );
|
||||
glTexCoord2f(0.0f, te); glVertex2i(0 , this->format.height);
|
||||
glTexCoord2f(1.0f, te); glVertex2i(this->format.width, this->format.height);
|
||||
glEnd();
|
||||
glEndList();
|
||||
}
|
||||
@ -333,16 +613,16 @@ bool lgr_opengl_configure(void * opaque, SDL_Window *window, const LG_RendererFo
|
||||
this->drawStart = nanotime();
|
||||
glXGetVideoSyncSGI(&this->gpuFrameCount);
|
||||
|
||||
// copy the format into the local storage
|
||||
memcpy(&this->format, &format, sizeof(LG_RendererFormat));
|
||||
this->configured = true;
|
||||
this->reconfigure = false;
|
||||
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
void lgr_opengl_deconfigure(void * opaque)
|
||||
static void deconfigure(struct Inst * this)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
if (!this->configured)
|
||||
return;
|
||||
|
||||
if (this->hasTextures)
|
||||
@ -366,45 +646,21 @@ void lgr_opengl_deconfigure(void * opaque)
|
||||
this->configured = false;
|
||||
}
|
||||
|
||||
void lgr_opengl_deinitialize(void * opaque)
|
||||
static void update_mouse_shape(struct Inst * this, bool * newShape)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
while(__sync_lock_test_and_set(&this->mouseLock, 1));
|
||||
*newShape = this->newShape;
|
||||
if (!this->newShape)
|
||||
{
|
||||
__sync_lock_release(&this->mouseLock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->configured)
|
||||
lgr_opengl_deconfigure(opaque);
|
||||
|
||||
free(this);
|
||||
}
|
||||
|
||||
bool lgr_opengl_is_compatible(void * opaque, const LG_RendererFormat format)
|
||||
{
|
||||
const struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
return (memcmp(&this->format, &format, sizeof(LG_RendererFormat)) == 0);
|
||||
}
|
||||
|
||||
void lgr_opengl_on_resize(void * opaque, const int width, const int height, const LG_RendererRect destRect)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
return;
|
||||
|
||||
this->window.x = width;
|
||||
this->window.y = height;
|
||||
memcpy(&this->destRect, &destRect, sizeof(LG_RendererRect));
|
||||
|
||||
this->resizeWindow = true;
|
||||
}
|
||||
|
||||
bool lgr_opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
const LG_RendererCursor cursor = this->mouseCursor;
|
||||
const int width = this->mouseWidth;
|
||||
const int height = this->mouseHeight;
|
||||
const int pitch = this->mousePitch;
|
||||
const uint8_t * data = this->mouseData;
|
||||
|
||||
this->mouseType = cursor;
|
||||
switch(cursor)
|
||||
@ -522,41 +778,26 @@ bool lgr_opengl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, co
|
||||
}
|
||||
|
||||
this->mouseUpdate = true;
|
||||
this->newShape = true;
|
||||
return true;
|
||||
__sync_lock_release(&this->mouseLock);
|
||||
}
|
||||
|
||||
bool lgr_opengl_on_mouse_event(void * opaque, const bool visible, const int x, const int y)
|
||||
static bool draw_frame(struct Inst * this, bool * frameUpdate)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
if (this->mousePos.x == x && this->mousePos.y == y && this->mouseVisible == visible)
|
||||
while(__sync_lock_test_and_set(&this->syncLock, 1));
|
||||
*frameUpdate = this->frameUpdate;
|
||||
if (!this->frameUpdate)
|
||||
{
|
||||
__sync_lock_release(&this->syncLock);
|
||||
return true;
|
||||
|
||||
this->mouseVisible = visible;
|
||||
this->mousePos.x = x;
|
||||
this->mousePos.y = y;
|
||||
this->mouseUpdate = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
{
|
||||
DEBUG_ERROR("Invalid opaque pointer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->configured)
|
||||
{
|
||||
DEBUG_ERROR("Not configured");
|
||||
return false;
|
||||
}
|
||||
this->texIndex = this->wTexIndex;
|
||||
if (++this->wTexIndex == BUFFER_COUNT)
|
||||
this->wTexIndex = 0;
|
||||
this->frameUpdate = false;
|
||||
__sync_lock_release(&this->syncLock);
|
||||
|
||||
while(__sync_lock_test_and_set(&this->formatLock, 1));
|
||||
if (this->params.showFPS && this->renderTime > 1e9)
|
||||
{
|
||||
char str[128];
|
||||
@ -568,6 +809,7 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
if (!(textSurface = TTF_RenderText_Blended(this->params.font, str, color)))
|
||||
{
|
||||
DEBUG_ERROR("Failed to render text");
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -636,7 +878,6 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride );
|
||||
|
||||
// copy the buffer to the texture
|
||||
memcpySSE(this->texPixels[this->texIndex], data, this->texSize);
|
||||
glFlushMappedBufferRange(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize * this->texIndex,
|
||||
@ -655,7 +896,10 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
GL_UNSIGNED_BYTE,
|
||||
(void*)(this->texIndex * this->texSize)
|
||||
);
|
||||
lgr_opengl_check_error("glTexSubImage2D");
|
||||
if (check_gl_error("glTexSubImage2D"))
|
||||
DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
|
||||
this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
|
||||
);
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
@ -677,16 +921,11 @@ bool lgr_opengl_on_frame_event(void * opaque, const uint8_t * data)
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (++this->texIndex == BUFFER_COUNT)
|
||||
this->texIndex = 0;
|
||||
|
||||
++this->frameCount;
|
||||
this->frameUpdate = true;
|
||||
__sync_lock_release(&this->formatLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void lgr_opengl_draw_mouse(struct LGR_OpenGL * this)
|
||||
static void draw_mouse(struct Inst * this)
|
||||
{
|
||||
if (!this->mouseVisible)
|
||||
return;
|
||||
@ -696,183 +935,3 @@ static inline void lgr_opengl_draw_mouse(struct LGR_OpenGL * this)
|
||||
glCallList(this->mouseList);
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
bool lgr_opengl_render(void * opaque)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this || !this->configured)
|
||||
return false;
|
||||
|
||||
if (this->resizeWindow)
|
||||
{
|
||||
// setup the projection matrix
|
||||
glViewport(0, 0, this->window.x, this->window.y);
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluOrtho2D(0, this->window.x, this->window.y, 0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
glTranslatef(this->destRect.x, this->destRect.y, 0.0f);
|
||||
glScalef(
|
||||
(float)this->destRect.w / (float)this->format.width,
|
||||
(float)this->destRect.h / (float)this->format.height,
|
||||
1.0f
|
||||
);
|
||||
|
||||
// update the scissor rect to prevent drawing outside of the frame
|
||||
glScissor(
|
||||
this->destRect.x,
|
||||
this->destRect.y,
|
||||
this->destRect.w,
|
||||
this->destRect.h
|
||||
);
|
||||
|
||||
this->resizeWindow = false;
|
||||
}
|
||||
|
||||
if (this->opt.splitMouse)
|
||||
{
|
||||
if (!this->frameUpdate)
|
||||
{
|
||||
if (!this->mouseUpdate)
|
||||
return true;
|
||||
|
||||
if (!this->newShape)
|
||||
{
|
||||
// don't update the mouse too fast
|
||||
const uint64_t delta = nanotime() - this->lastMouseDraw;
|
||||
if (delta < 5e6)
|
||||
return true;
|
||||
}
|
||||
this->newShape = false;
|
||||
|
||||
glDrawBuffer(GL_FRONT);
|
||||
glCallList(this->texList + this->texIndex);
|
||||
lgr_opengl_draw_mouse(this);
|
||||
if (this->fpsTexture)
|
||||
glCallList(this->fpsList);
|
||||
glDrawBuffer(GL_BACK);
|
||||
glFlush();
|
||||
|
||||
this->mouseUpdate = false;
|
||||
this->lastMouseDraw = nanotime();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
||||
glCallList(this->texList + this->texIndex);
|
||||
lgr_opengl_draw_mouse(this);
|
||||
if (this->fpsTexture)
|
||||
glCallList(this->fpsList);
|
||||
|
||||
if (this->opt.preventBuffer)
|
||||
{
|
||||
unsigned int before, after;
|
||||
glXGetVideoSyncSGI(&before);
|
||||
SDL_GL_SwapWindow(this->sdlWindow);
|
||||
|
||||
// wait for the swap to happen to ensure we dont buffer frames //
|
||||
glXGetVideoSyncSGI(&after);
|
||||
if (before == after)
|
||||
glXWaitVideoSyncSGI(1, 0, &before);
|
||||
}
|
||||
else
|
||||
SDL_GL_SwapWindow(this->sdlWindow);
|
||||
|
||||
const uint64_t t = nanotime();
|
||||
this->renderTime += t - this->lastFrameTime;
|
||||
this->lastFrameTime = t;
|
||||
++this->renderCount;
|
||||
|
||||
this->frameUpdate = false;
|
||||
this->mouseUpdate = false;
|
||||
this->lastMouseDraw = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_opt_mipmap(void * opaque, const char *value)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.mipmap = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_vsync(void * opaque, const char *value)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.vsync = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_prevent_buffer(void * opaque, const char *value)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.preventBuffer = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static void handle_opt_split_mouse(void * opaque, const char *value)
|
||||
{
|
||||
struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque;
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
this->opt.splitMouse = LG_RendererValueToBool(value);
|
||||
}
|
||||
|
||||
static LG_RendererOpt lgr_opengl_options[] =
|
||||
{
|
||||
{
|
||||
.name = "mipmap",
|
||||
.desc = "Enable or disable mipmapping [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_mipmap
|
||||
},
|
||||
{
|
||||
.name = "vsync",
|
||||
.desc ="Enable or disable vsync [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_vsync
|
||||
},
|
||||
{
|
||||
.name = "preventBuffer",
|
||||
.desc = "Prevent the driver from buffering frames [default: enabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_prevent_buffer
|
||||
},
|
||||
{
|
||||
.name = "splitMouse",
|
||||
.desc = "Draw mouse updates directly to the front buffer [default: disabled]",
|
||||
.validator = LG_RendererValidatorBool,
|
||||
.handler = handle_opt_split_mouse
|
||||
}
|
||||
};
|
||||
|
||||
const LG_Renderer LGR_OpenGL =
|
||||
{
|
||||
.get_name = lgr_opengl_get_name,
|
||||
.options = lgr_opengl_options,
|
||||
.option_count = LGR_OPTION_COUNT(lgr_opengl_options),
|
||||
.create = lgr_opengl_create,
|
||||
.initialize = lgr_opengl_initialize,
|
||||
.configure = lgr_opengl_configure,
|
||||
.deconfigure = lgr_opengl_deconfigure,
|
||||
.deinitialize = lgr_opengl_deinitialize,
|
||||
.is_compatible = lgr_opengl_is_compatible,
|
||||
.on_resize = lgr_opengl_on_resize,
|
||||
.on_mouse_shape = lgr_opengl_on_mouse_shape,
|
||||
.on_mouse_event = lgr_opengl_on_mouse_event,
|
||||
.on_frame_event = lgr_opengl_on_frame_event,
|
||||
.render = lgr_opengl_render
|
||||
};
|
@ -35,3 +35,13 @@ static inline uint64_t nanotime()
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &time);
|
||||
return ((uint64_t)time.tv_sec * 1e9) + time.tv_nsec;
|
||||
}
|
||||
|
||||
static inline void nsleep(uint64_t ns)
|
||||
{
|
||||
const struct timespec ts =
|
||||
{
|
||||
.tv_sec = ns / 1e9,
|
||||
.tv_nsec = ns - ((ns / 1e9) * 1e9)
|
||||
};
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdint.h>
|
||||
|
||||
#define KVMFR_HEADER_MAGIC "[[KVMFR]]"
|
||||
#define KVMFR_HEADER_VERSION 4
|
||||
#define KVMFR_HEADER_VERSION 5
|
||||
|
||||
typedef enum FrameType
|
||||
{
|
||||
@ -48,7 +48,13 @@ typedef struct KVMFRCursor
|
||||
{
|
||||
uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
int16_t x, y; // cursor x & y position
|
||||
|
||||
uint32_t version; // shape version
|
||||
CursorType type; // shape buffer data type
|
||||
uint32_t width; // shape width
|
||||
uint32_t height; // shape height
|
||||
uint32_t pitch; // shape row pitch (stride in bytes)
|
||||
uint64_t dataPos; // offset to the shape data
|
||||
}
|
||||
KVMFRCursor;
|
||||
|
||||
@ -79,7 +85,6 @@ typedef struct KVMFRHeader
|
||||
{
|
||||
char magic[sizeof(KVMFR_HEADER_MAGIC)];
|
||||
uint32_t version; // version of this structure
|
||||
uint32_t updateCount; // updated each change
|
||||
uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
KVMFRDetail detail; // details
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user