mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-22 13:33:40 +00:00
[client/host] added new asyncronous memory copy
This changes the method of the memory copy from the host application to the guest. Instead of performing a full copy from the capture device into shared memory, and then flagging the new frame, we instead set a write pointer, flag the client that there is a new frame and then copy in chunks of 1024 bytes until the entire frame is copied. The client upon seeing the new frame flag begins to poll at high frequency the write pointer and upon each update copies as much as it can into the texture. This should improve latency but also slightly increase CPU usage on the client due to the high frequency polling.
This commit is contained in:
parent
6d2c464436
commit
bca54ab1f6
@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
typedef enum CaptureResult
|
||||
{
|
||||
@ -55,7 +56,6 @@ typedef struct CaptureFrame
|
||||
unsigned int pitch;
|
||||
unsigned int stride;
|
||||
CaptureFormat format;
|
||||
void * data;
|
||||
}
|
||||
CaptureFrame;
|
||||
|
||||
@ -84,7 +84,8 @@ typedef struct CaptureInterface
|
||||
unsigned int (*getMaxFrameSize)();
|
||||
|
||||
CaptureResult (*capture )();
|
||||
CaptureResult (*getFrame )(CaptureFrame * frame );
|
||||
CaptureResult (*waitFrame )(CaptureFrame * frame );
|
||||
CaptureResult (*getFrame )(FrameBuffer frame );
|
||||
CaptureResult (*getPointer)(CapturePointer * pointer);
|
||||
}
|
||||
CaptureInterface;
|
@ -766,7 +766,7 @@ static CaptureResult dxgi_capture()
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult dxgi_getFrame(CaptureFrame * frame)
|
||||
static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
|
||||
{
|
||||
assert(this);
|
||||
assert(this->initialized);
|
||||
@ -778,7 +778,6 @@ static CaptureResult dxgi_getFrame(CaptureFrame * frame)
|
||||
if (this->stop)
|
||||
return CAPTURE_RESULT_REINIT;
|
||||
|
||||
// only reset the event if we used the texture
|
||||
os_resetEvent(tex->mapped);
|
||||
|
||||
frame->width = this->width;
|
||||
@ -787,7 +786,16 @@ static CaptureResult dxgi_getFrame(CaptureFrame * frame)
|
||||
frame->stride = this->stride;
|
||||
frame->format = this->format;
|
||||
|
||||
memcpy(frame->data, tex->map.pData, this->pitch * this->height);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult dxgi_getFrame(FrameBuffer frame)
|
||||
{
|
||||
assert(this);
|
||||
assert(this->initialized);
|
||||
|
||||
Texture * tex = &this->texture[this->texRIndex];
|
||||
framebuffer_write(frame, tex->map.pData, this->pitch * this->height);
|
||||
os_signalEvent(tex->free);
|
||||
|
||||
if (++this->texRIndex == this->maxTextures)
|
||||
@ -867,6 +875,7 @@ struct CaptureInterface Capture_DXGI =
|
||||
.free = dxgi_free,
|
||||
.getMaxFrameSize = dxgi_getMaxFrameSize,
|
||||
.capture = dxgi_capture,
|
||||
.waitFrame = dxgi_waitFrame,
|
||||
.getFrame = dxgi_getFrame,
|
||||
.getPointer = dxgi_getPointer
|
||||
};
|
@ -23,6 +23,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "windows/debug.h"
|
||||
#include "windows/mousehook.h"
|
||||
#include "common/option.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
@ -236,7 +237,7 @@ static CaptureResult nvfbc_capture()
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
|
||||
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame)
|
||||
{
|
||||
if (!os_waitEvent(this->frameEvent, 1000))
|
||||
return CAPTURE_RESULT_TIMEOUT;
|
||||
@ -266,7 +267,16 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
|
||||
#endif
|
||||
|
||||
frame->format = this->grabInfo.bIsHDR ? CAPTURE_FMT_RGBA10 : CAPTURE_FMT_BGRA;
|
||||
memcpy(frame->data, this->frameBuffer, frame->pitch * frame->height);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
static CaptureResult nvfbc_getFrame(FrameBuffer frame)
|
||||
{
|
||||
framebuffer_write(
|
||||
frame,
|
||||
this->frameBuffer,
|
||||
this->grabInfo.dwHeight * this->grabInfo.dwBufferWidth * 4
|
||||
);
|
||||
return CAPTURE_RESULT_OK;
|
||||
}
|
||||
|
||||
@ -310,6 +320,7 @@ struct CaptureInterface Capture_NVFBC =
|
||||
.free = nvfbc_free,
|
||||
.getMaxFrameSize = nvfbc_getMaxFrameSize,
|
||||
.capture = nvfbc_capture,
|
||||
.waitFrame = nvfbc_waitFrame,
|
||||
.getFrame = nvfbc_getFrame,
|
||||
.getPointer = nvfbc_getPointer
|
||||
};
|
@ -49,7 +49,7 @@ struct app
|
||||
|
||||
uint8_t * frames;
|
||||
unsigned int frameSize;
|
||||
uint8_t * frame[MAX_FRAMES];
|
||||
FrameBuffer frame[MAX_FRAMES];
|
||||
unsigned int frameOffset[MAX_FRAMES];
|
||||
|
||||
bool running;
|
||||
@ -168,9 +168,7 @@ static int frameThread(void * opaque)
|
||||
|
||||
while(app.running)
|
||||
{
|
||||
frame.data = app.frame[frameIndex];
|
||||
|
||||
switch(app.iface->getFrame(&frame))
|
||||
switch(app.iface->waitFrame(&frame))
|
||||
{
|
||||
case CAPTURE_RESULT_OK:
|
||||
break;
|
||||
@ -226,7 +224,9 @@ static int frameThread(void * opaque)
|
||||
fi->dataPos = app.frameOffset[frameIndex];
|
||||
frameValid = true;
|
||||
|
||||
framebuffer_prepare(app.frame[frameIndex]);
|
||||
INTERLOCKED_OR8(&fi->flags, KVMFR_FRAME_FLAG_UPDATE);
|
||||
app.iface->getFrame(app.frame[frameIndex]);
|
||||
|
||||
if (++frameIndex == MAX_FRAMES)
|
||||
frameIndex = 0;
|
||||
@ -369,8 +369,8 @@ int app_main(int argc, char * argv[])
|
||||
|
||||
for (int i = 0; i < MAX_FRAMES; ++i)
|
||||
{
|
||||
app.frame [i] = app.frames + i * app.frameSize;
|
||||
app.frameOffset[i] = app.frame[i] - shmemMap;
|
||||
app.frame [i] = (FrameBuffer)(app.frames + i * app.frameSize);
|
||||
app.frameOffset[i] = (uint8_t *)app.frame[i] - shmemMap;
|
||||
DEBUG_INFO("Frame %d : 0x%" PRIXPTR " (0x%08x)", i, (uintptr_t)app.frame[i], app.frameOffset[i]);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "app.h"
|
||||
#include "common/KVMFR.h"
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#define IS_LG_RENDERER_VALID(x) \
|
||||
((x)->get_name && \
|
||||
@ -89,7 +90,7 @@ typedef void (* LG_RendererDeInitialize)(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_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const uint8_t * data);
|
||||
typedef bool (* LG_RendererOnFrameEvent)(void * opaque, const LG_RendererFormat format, const FrameBuffer frame);
|
||||
typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag);
|
||||
typedef bool (* LG_RendererRender )(void * opaque, SDL_Window *window);
|
||||
typedef void (* LG_RendererUpdateFPS )(void * opaque, const float avgUPS, const float avgFPS);
|
||||
|
@ -60,7 +60,7 @@ struct EGL_Desktop
|
||||
enum EGL_PixelFormat pixFmt;
|
||||
unsigned int width, height;
|
||||
unsigned int pitch;
|
||||
const uint8_t * data;
|
||||
FrameBuffer frame;
|
||||
bool update;
|
||||
|
||||
// night vision
|
||||
@ -181,7 +181,7 @@ void egl_desktop_free(EGL_Desktop ** desktop)
|
||||
*desktop = NULL;
|
||||
}
|
||||
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer frame)
|
||||
{
|
||||
if (sourceChanged)
|
||||
{
|
||||
@ -217,7 +217,7 @@ bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged,
|
||||
desktop->width = format.width;
|
||||
desktop->height = format.height;
|
||||
desktop->pitch = format.pitch;
|
||||
desktop->data = data;
|
||||
desktop->frame = frame;
|
||||
desktop->update = true;
|
||||
|
||||
/* defer the actual update as the format has changed and we need to issue GL commands first */
|
||||
@ -226,7 +226,7 @@ bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged,
|
||||
}
|
||||
|
||||
/* update the texture now */
|
||||
return egl_texture_update(desktop->texture, data);
|
||||
return egl_texture_update_from_frame(desktop->texture, frame);
|
||||
}
|
||||
|
||||
void egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged)
|
||||
@ -253,7 +253,7 @@ void egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged)
|
||||
if (desktop->update)
|
||||
{
|
||||
desktop->update = false;
|
||||
egl_texture_update(desktop->texture, desktop->data);
|
||||
egl_texture_update_from_frame(desktop->texture, desktop->frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,6 @@ typedef struct EGL_Desktop EGL_Desktop;
|
||||
bool egl_desktop_init(EGL_Desktop ** desktop);
|
||||
void egl_desktop_free(EGL_Desktop ** desktop);
|
||||
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const uint8_t * data);
|
||||
bool egl_desktop_prepare_update(EGL_Desktop * desktop, const bool sourceChanged, const LG_RendererFormat format, const FrameBuffer frame);
|
||||
void egl_desktop_perform_update(EGL_Desktop * desktop, const bool sourceChanged);
|
||||
bool egl_desktop_render(EGL_Desktop * desktop, const float x, const float y, const float scaleX, const float scaleY, const bool nearest);
|
@ -296,7 +296,7 @@ bool egl_on_mouse_event(void * opaque, const bool visible, const int x, const in
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer frame)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
this->sourceChanged = (
|
||||
@ -312,7 +312,7 @@ bool egl_on_frame_event(void * opaque, const LG_RendererFormat format, const uin
|
||||
|
||||
this->useNearest = this->width < format.width || this->height < format.height;
|
||||
|
||||
if (!egl_desktop_prepare_update(this->desktop, this->sourceChanged, format, data))
|
||||
if (!egl_desktop_prepare_update(this->desktop, this->sourceChanged, format, frame))
|
||||
{
|
||||
DEBUG_INFO("Failed to prepare to update the desktop");
|
||||
return false;
|
||||
|
@ -20,6 +20,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "texture.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/locking.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
@ -278,6 +279,24 @@ bool egl_texture_update(EGL_Texture * texture, const uint8_t * buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer frame)
|
||||
{
|
||||
if (!texture->streaming)
|
||||
return false;
|
||||
|
||||
if (texture->pboCount == 2)
|
||||
return true;
|
||||
|
||||
framebuffer_read(frame, texture->pboMap[texture->pboWIndex], texture->pboBufferSize);
|
||||
texture->pboSync[texture->pboWIndex] = 0;
|
||||
|
||||
if (++texture->pboWIndex == 2)
|
||||
texture->pboWIndex = 0;
|
||||
INTERLOCKED_INC(&texture->pboCount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum EGL_TexStatus egl_texture_process(EGL_Texture * texture)
|
||||
{
|
||||
if (!texture->streaming)
|
||||
|
@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "shader.h"
|
||||
#include "common/framebuffer.h"
|
||||
|
||||
#include <GL/gl.h>
|
||||
|
||||
@ -46,6 +47,7 @@ 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_update (EGL_Texture * texture, const uint8_t * buffer);
|
||||
bool egl_texture_update_from_frame(EGL_Texture * texture, const FrameBuffer frame);
|
||||
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);
|
@ -32,8 +32,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/option.h"
|
||||
#include "common/framebuffer.h"
|
||||
#include "utils.h"
|
||||
#include "lg-decoders.h"
|
||||
#include "dynamic/fonts.h"
|
||||
#include "ll.h"
|
||||
|
||||
@ -122,8 +122,8 @@ struct Inst
|
||||
GLuint vboFormat;
|
||||
GLuint dataFormat;
|
||||
size_t texSize;
|
||||
const LG_Decoder* decoder;
|
||||
void * decoderData;
|
||||
size_t texPos;
|
||||
FrameBuffer frame;
|
||||
|
||||
uint64_t drawStart;
|
||||
bool hasBuffers;
|
||||
@ -140,7 +140,6 @@ struct Inst
|
||||
bool hasTextures, hasFrames;
|
||||
GLuint frames[BUFFER_COUNT];
|
||||
GLsync fences[BUFFER_COUNT];
|
||||
void * decoderFrames[BUFFER_COUNT];
|
||||
GLuint textures[TEXTURE_COUNT];
|
||||
struct ll * alerts;
|
||||
int alertList;
|
||||
@ -362,7 +361,7 @@ bool opengl_on_mouse_event(void * opaque, const bool visible, const int x, const
|
||||
return false;
|
||||
}
|
||||
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const uint8_t * data)
|
||||
bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const FrameBuffer frame)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
if (!this)
|
||||
@ -394,12 +393,7 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const
|
||||
LG_UNLOCK(this->formatLock);
|
||||
|
||||
LG_LOCK(this->syncLock);
|
||||
if (!this->decoder->decode(this->decoderData, data, format.pitch))
|
||||
{
|
||||
DEBUG_ERROR("decode returned failure");
|
||||
LG_UNLOCK(this->syncLock);
|
||||
return false;
|
||||
}
|
||||
this->frame = frame;
|
||||
this->frameUpdate = true;
|
||||
LG_UNLOCK(this->syncLock);
|
||||
|
||||
@ -849,13 +843,21 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
switch(this->format.type)
|
||||
{
|
||||
case FRAME_TYPE_BGRA:
|
||||
case FRAME_TYPE_RGBA:
|
||||
case FRAME_TYPE_RGBA10:
|
||||
this->decoder = &LGD_NULL;
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_YUV420:
|
||||
this->decoder = &LGD_YUV420;
|
||||
case FRAME_TYPE_RGBA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case FRAME_TYPE_RGBA10:
|
||||
this->intFormat = GL_RGB10_A2;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -863,128 +865,73 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_INFO("Using decoder: %s", this->decoder->name);
|
||||
|
||||
if (!this->decoder->create(&this->decoderData))
|
||||
{
|
||||
DEBUG_ERROR("Failed to create the decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->decoder->initialize(
|
||||
this->decoderData,
|
||||
this->format,
|
||||
window))
|
||||
{
|
||||
DEBUG_ERROR("Failed to initialize decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch(this->decoder->get_out_format(this->decoderData))
|
||||
{
|
||||
case LG_OUTPUT_BGRA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_RGBA:
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_RGBA10:
|
||||
this->intFormat = GL_RGB10_A2;
|
||||
this->vboFormat = GL_RGBA;
|
||||
this->dataFormat = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
break;
|
||||
|
||||
case LG_OUTPUT_YUV420:
|
||||
// fixme
|
||||
this->intFormat = GL_RGBA8;
|
||||
this->vboFormat = GL_BGRA;
|
||||
this->dataFormat = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_ERROR("Format not supported");
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate the texture size in bytes
|
||||
this->texSize =
|
||||
this->format.height *
|
||||
this->decoder->get_frame_pitch(this->decoderData);
|
||||
this->texSize = this->format.height * this->format.pitch;
|
||||
this->texPos = 0;
|
||||
|
||||
// generate the pixel unpack buffers if the decoder isn't going to do it for us
|
||||
if (!this->decoder->has_gl)
|
||||
glGenBuffers(BUFFER_COUNT, this->vboID);
|
||||
if (check_gl_error("glGenBuffers"))
|
||||
{
|
||||
glGenBuffers(BUFFER_COUNT, this->vboID);
|
||||
if (check_gl_error("glGenBuffers"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
this->hasBuffers = true;
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
this->hasBuffers = true;
|
||||
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
const int pagesize = getpagesize();
|
||||
this->texPixels[0] = memalign(pagesize, this->texSize * BUFFER_COUNT);
|
||||
memset(this->texPixels[0], 0, this->texSize * BUFFER_COUNT);
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[0] + this->texSize;
|
||||
if (this->amdPinnedMemSupport)
|
||||
{
|
||||
const int pagesize = getpagesize();
|
||||
this->texPixels[0] = memalign(pagesize, this->texSize * BUFFER_COUNT);
|
||||
memset(this->texPixels[0], 0, this->texSize * BUFFER_COUNT);
|
||||
for(int i = 1; i < BUFFER_COUNT; ++i)
|
||||
this->texPixels[i] = this->texPixels[0] + this->texSize;
|
||||
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
|
||||
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]);
|
||||
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
glBufferData(
|
||||
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
|
||||
this->texSize,
|
||||
this->texPixels[i],
|
||||
GL_STREAM_DRAW);
|
||||
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
glBufferData(
|
||||
GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD,
|
||||
this->texSize,
|
||||
this->texPixels[i],
|
||||
GL_STREAM_DRAW);
|
||||
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBufferData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize,
|
||||
NULL,
|
||||
GL_STREAM_DRAW
|
||||
);
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]);
|
||||
if (check_gl_error("glBindBuffer"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
|
||||
glBufferData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texSize,
|
||||
NULL,
|
||||
GL_STREAM_DRAW
|
||||
);
|
||||
if (check_gl_error("glBufferData"))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
// create the frame textures
|
||||
@ -1023,26 +970,11 @@ static bool configure(struct Inst * this, SDL_Window *window)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
if (!this->decoder->init_gl_texture(
|
||||
this->decoderData,
|
||||
GL_TEXTURE_2D,
|
||||
this->frames[i],
|
||||
&this->decoderFrames[i]))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// configure the texture
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
}
|
||||
// configure the texture
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
|
||||
// create the display lists
|
||||
glNewList(this->texList + i, GL_COMPILE);
|
||||
@ -1082,19 +1014,6 @@ static void deconfigure(struct Inst * this)
|
||||
|
||||
if (this->hasFrames)
|
||||
{
|
||||
if (this->decoder->has_gl)
|
||||
{
|
||||
for(int i = 0; i < BUFFER_COUNT; ++i)
|
||||
{
|
||||
if (this->decoderFrames[i])
|
||||
this->decoder->free_gl_texture(
|
||||
this->decoderData,
|
||||
this->decoderFrames[i]
|
||||
);
|
||||
this->decoderFrames[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteTextures(BUFFER_COUNT, this->frames);
|
||||
this->hasFrames = false;
|
||||
}
|
||||
@ -1121,12 +1040,6 @@ static void deconfigure(struct Inst * this)
|
||||
}
|
||||
}
|
||||
|
||||
if (this->decoderData)
|
||||
{
|
||||
this->decoder->destroy(this->decoderData);
|
||||
this->decoderData = NULL;
|
||||
}
|
||||
|
||||
this->configured = false;
|
||||
}
|
||||
|
||||
@ -1277,6 +1190,23 @@ static void update_mouse_shape(struct Inst * this, bool * newShape)
|
||||
LG_UNLOCK(this->mouseLock);
|
||||
}
|
||||
|
||||
static bool opengl_buffer_fn(void * opaque, const void * data, size_t size)
|
||||
{
|
||||
struct Inst * this = (struct Inst *)opaque;
|
||||
|
||||
// update the buffer, this performs a DMA transfer if possible
|
||||
glBufferSubData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
this->texPos,
|
||||
size,
|
||||
data
|
||||
);
|
||||
check_gl_error("glBufferSubData");
|
||||
|
||||
this->texPos += size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool draw_frame(struct Inst * this)
|
||||
{
|
||||
LG_LOCK(this->syncLock);
|
||||
@ -1293,96 +1223,70 @@ static bool draw_frame(struct Inst * this)
|
||||
LG_UNLOCK(this->syncLock);
|
||||
|
||||
LG_LOCK(this->formatLock);
|
||||
if (this->decoder->has_gl)
|
||||
if (glIsSync(this->fences[this->texIndex]))
|
||||
{
|
||||
if (!this->decoder->update_gl_texture(
|
||||
this->decoderData,
|
||||
this->decoderFrames[this->texIndex]
|
||||
))
|
||||
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
DEBUG_ERROR("Failed to update the texture from the decoder");
|
||||
return false;
|
||||
case GL_ALREADY_SIGNALED:
|
||||
break;
|
||||
|
||||
case GL_CONDITION_SATISFIED:
|
||||
DEBUG_WARN("Had to wait for the sync");
|
||||
break;
|
||||
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
DEBUG_WARN("Timeout expired, DMA transfers are too slow!");
|
||||
break;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
|
||||
break;
|
||||
}
|
||||
|
||||
glDeleteSync(this->fences[this->texIndex]);
|
||||
this->fences[this->texIndex] = NULL;
|
||||
}
|
||||
else
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride);
|
||||
|
||||
this->texPos = 0;
|
||||
framebuffer_read_fn(
|
||||
this->frame,
|
||||
opengl_buffer_fn,
|
||||
this->format.height * this->format.stride,
|
||||
this
|
||||
);
|
||||
|
||||
// update the texture
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
this->format.width ,
|
||||
this->format.height,
|
||||
this->vboFormat,
|
||||
this->dataFormat,
|
||||
(void*)0
|
||||
);
|
||||
if (check_gl_error("glTexSubImage2D"))
|
||||
{
|
||||
if (glIsSync(this->fences[this->texIndex]))
|
||||
{
|
||||
switch(glClientWaitSync(this->fences[this->texIndex], 0, GL_TIMEOUT_IGNORED))
|
||||
{
|
||||
case GL_ALREADY_SIGNALED:
|
||||
break;
|
||||
|
||||
case GL_CONDITION_SATISFIED:
|
||||
DEBUG_WARN("Had to wait for the sync");
|
||||
break;
|
||||
|
||||
case GL_TIMEOUT_EXPIRED:
|
||||
DEBUG_WARN("Timeout expired, DMA transfers are too slow!");
|
||||
break;
|
||||
|
||||
case GL_WAIT_FAILED:
|
||||
DEBUG_ERROR("Wait failed %s", gluErrorString(glGetError()));
|
||||
break;
|
||||
}
|
||||
|
||||
glDeleteSync(this->fences[this->texIndex]);
|
||||
this->fences[this->texIndex] = NULL;
|
||||
}
|
||||
|
||||
const uint8_t * data = this->decoder->get_buffer(this->decoderData);
|
||||
if (!data)
|
||||
{
|
||||
LG_UNLOCK(this->formatLock);
|
||||
DEBUG_ERROR("Failed to get the buffer from the decoder");
|
||||
return false;
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT , 4);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH ,
|
||||
this->decoder->get_frame_stride(this->decoderData)
|
||||
DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu",
|
||||
this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize
|
||||
);
|
||||
|
||||
// update the buffer, this performs a DMA transfer if possible
|
||||
glBufferSubData(
|
||||
GL_PIXEL_UNPACK_BUFFER,
|
||||
0,
|
||||
this->texSize,
|
||||
data
|
||||
);
|
||||
check_gl_error("glBufferSubData");
|
||||
|
||||
// update the texture
|
||||
glTexSubImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
this->format.width ,
|
||||
this->format.height,
|
||||
this->vboFormat,
|
||||
this->dataFormat,
|
||||
(void*)0
|
||||
);
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
// set a fence so we don't overwrite a buffer in use
|
||||
this->fences[this->texIndex] =
|
||||
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
// set a fence so we don't overwrite a buffer in use
|
||||
this->fences[this->texIndex] =
|
||||
glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
// unbind the buffer
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
|
||||
const bool mipmap = this->opt.mipmap && (
|
||||
(this->format.width > this->destRect.w) ||
|
||||
(this->format.height > this->destRect.h));
|
||||
|
@ -377,8 +377,8 @@ static int frameThread(void * unused)
|
||||
updatePositionInfo();
|
||||
}
|
||||
|
||||
const uint8_t * data = (const uint8_t *)state.shm + header.dataPos;
|
||||
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, data))
|
||||
FrameBuffer frame = (FrameBuffer)((uint8_t *)state.shm + header.dataPos);
|
||||
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, frame))
|
||||
{
|
||||
DEBUG_ERROR("renderer on frame event returned failure");
|
||||
break;
|
||||
|
@ -13,6 +13,7 @@ set(COMMON_SOURCES
|
||||
src/stringutils.c
|
||||
src/stringlist.c
|
||||
src/option.c
|
||||
src/framebuffer.c
|
||||
)
|
||||
|
||||
set(LINUX_SOURCES
|
||||
|
@ -49,7 +49,7 @@ CursorType;
|
||||
|
||||
typedef struct KVMFRCursor
|
||||
{
|
||||
uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
volatile uint8_t flags; // KVMFR_CURSOR_FLAGS
|
||||
int16_t x, y; // cursor x & y position
|
||||
|
||||
uint32_t version; // shape version
|
||||
@ -65,7 +65,7 @@ KVMFRCursor;
|
||||
|
||||
typedef struct KVMFRFrame
|
||||
{
|
||||
uint8_t flags; // KVMFR_FRAME_FLAGS
|
||||
volatile uint8_t flags; // KVMFR_FRAME_FLAGS
|
||||
FrameType type; // the frame data type
|
||||
uint32_t width; // the width
|
||||
uint32_t height; // the height
|
||||
@ -83,7 +83,7 @@ typedef struct KVMFRHeader
|
||||
{
|
||||
char magic[sizeof(KVMFR_HEADER_MAGIC)];
|
||||
uint32_t version; // version of this structure
|
||||
uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
volatile uint8_t flags; // KVMFR_HEADER_FLAGS
|
||||
KVMFRFrame frame; // the frame information
|
||||
KVMFRCursor cursor; // the cursor information
|
||||
}
|
||||
|
48
common/include/common/framebuffer.h
Normal file
48
common/include/common/framebuffer.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct stFrameBuffer * FrameBuffer;
|
||||
|
||||
typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size);
|
||||
|
||||
/**
|
||||
* Read data from the KVMFRFrame into the dst buffer
|
||||
*/
|
||||
bool framebuffer_read(const FrameBuffer frame, void * dst, size_t size);
|
||||
|
||||
/**
|
||||
* Read data from the KVMFRFrame using a callback
|
||||
*/
|
||||
bool framebuffer_read_fn(const FrameBuffer frame, FrameBufferReadFn fn, size_t size, void * opaque);
|
||||
|
||||
/**
|
||||
* Prepare the framebuffer for writing
|
||||
*/
|
||||
void framebuffer_prepare(const FrameBuffer frame);
|
||||
|
||||
/**
|
||||
* Write data from the src buffer into the KVMFRFrame
|
||||
*/
|
||||
bool framebuffer_write(const FrameBuffer frame, const void * src, size_t size);
|
85
common/src/framebuffer.c
Normal file
85
common/src/framebuffer.c
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
KVMGFX Client - A KVM Client for VGA Passthrough
|
||||
Copyright (C) 2017-2019 Geoffrey McRae <geoff@hostfission.com>
|
||||
https://looking-glass.hostfission.com
|
||||
|
||||
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 "common/framebuffer.h"
|
||||
#include "common/debug.h"
|
||||
|
||||
#include <string.h>
|
||||
#define FB_CHUNK_SIZE 1024
|
||||
|
||||
struct stFrameBuffer
|
||||
{
|
||||
uint64_t wp;
|
||||
uint8_t data[0];
|
||||
};
|
||||
|
||||
bool framebuffer_read(const FrameBuffer frame, void * dst, size_t size)
|
||||
{
|
||||
uint64_t rp = 0;
|
||||
while(rp < size)
|
||||
{
|
||||
/* spinlock */
|
||||
while(rp == frame->wp) { }
|
||||
|
||||
/* copy what we can */
|
||||
uint64_t avail = frame->wp - rp;
|
||||
memcpy(dst, frame->data + rp, avail);
|
||||
rp += avail;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool framebuffer_read_fn(const FrameBuffer frame, FrameBufferReadFn fn, size_t size, void * opaque)
|
||||
{
|
||||
uint64_t rp = 0;
|
||||
while(rp < size)
|
||||
{
|
||||
/* spinlock */
|
||||
while(rp == frame->wp) { }
|
||||
|
||||
/* copy what we can */
|
||||
uint64_t avail = frame->wp - rp;
|
||||
if (!fn(opaque, frame->data + rp, avail))
|
||||
return false;
|
||||
rp += avail;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the framebuffer for writing
|
||||
*/
|
||||
void framebuffer_prepare(const FrameBuffer frame)
|
||||
{
|
||||
frame->wp = 0;
|
||||
}
|
||||
|
||||
bool framebuffer_write(FrameBuffer frame, const void * src, size_t size)
|
||||
{
|
||||
/* copy in chunks */
|
||||
while(size)
|
||||
{
|
||||
size_t copy = size < FB_CHUNK_SIZE ? FB_CHUNK_SIZE : size;
|
||||
memcpy(frame->data + frame->wp, src, copy);
|
||||
__sync_fetch_and_add(&frame->wp, copy);
|
||||
size -= copy;
|
||||
}
|
||||
return true;
|
||||
}
|
Loading…
Reference in New Issue
Block a user