diff --git a/VERSION b/VERSION index 22dd0453..abc8c589 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -fetch-4-ge93bd7a3bf+1 \ No newline at end of file +fetch-6-g59013b2e3f+1 \ No newline at end of file diff --git a/c-host/include/interface/capture.h b/c-host/include/interface/capture.h index 535c04f9..daa6040b 100644 --- a/c-host/include/interface/capture.h +++ b/c-host/include/interface/capture.h @@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include +#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; \ No newline at end of file diff --git a/c-host/platform/Windows/capture/DXGI/src/dxgi.c b/c-host/platform/Windows/capture/DXGI/src/dxgi.c index 3aac2dde..01c9ac33 100644 --- a/c-host/platform/Windows/capture/DXGI/src/dxgi.c +++ b/c-host/platform/Windows/capture/DXGI/src/dxgi.c @@ -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 }; \ No newline at end of file diff --git a/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c b/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c index 8c76f138..cf0a1031 100644 --- a/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c +++ b/c-host/platform/Windows/capture/NVFBC/src/nvfbc.c @@ -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 #include #include @@ -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 }; \ No newline at end of file diff --git a/c-host/src/app.c b/c-host/src/app.c index 89c3e7a7..771a02a0 100644 --- a/c-host/src/app.c +++ b/c-host/src/app.c @@ -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]); } diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index d4ab9293..f5e80e4c 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -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); diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index b684cca9..8821705b 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -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); } } diff --git a/client/renderers/EGL/desktop.h b/client/renderers/EGL/desktop.h index 4738539a..09de74ef 100644 --- a/client/renderers/EGL/desktop.h +++ b/client/renderers/EGL/desktop.h @@ -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); \ No newline at end of file diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 27f0a1b3..42012d82 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -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; diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index 1c735663..98897631 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -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) diff --git a/client/renderers/EGL/texture.h b/client/renderers/EGL/texture.h index 8f204b6a..b8ee49e0 100644 --- a/client/renderers/EGL/texture.h +++ b/client/renderers/EGL/texture.h @@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include "shader.h" +#include "common/framebuffer.h" #include @@ -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); \ No newline at end of file diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index a1f1bde4..7768bd21 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -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)); diff --git a/client/src/main.c b/client/src/main.c index eee3ac04..7ca51d25 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -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; diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 86af720c..7b22d8ef 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -13,6 +13,7 @@ set(COMMON_SOURCES src/stringutils.c src/stringlist.c src/option.c + src/framebuffer.c ) set(LINUX_SOURCES diff --git a/common/include/common/KVMFR.h b/common/include/common/KVMFR.h index c4c3d194..286b567c 100644 --- a/common/include/common/KVMFR.h +++ b/common/include/common/KVMFR.h @@ -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 } diff --git a/common/include/common/framebuffer.h b/common/include/common/framebuffer.h new file mode 100644 index 00000000..9dcf15f6 --- /dev/null +++ b/common/include/common/framebuffer.h @@ -0,0 +1,48 @@ +/* +KVMGFX Client - A KVM Client for VGA Passthrough +Copyright (C) 2017-2019 Geoffrey McRae +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 +#include +#include + +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); \ No newline at end of file diff --git a/common/src/framebuffer.c b/common/src/framebuffer.c new file mode 100644 index 00000000..7d9a7da4 --- /dev/null +++ b/common/src/framebuffer.c @@ -0,0 +1,85 @@ +/* +KVMGFX Client - A KVM Client for VGA Passthrough +Copyright (C) 2017-2019 Geoffrey McRae +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 +#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; +} \ No newline at end of file