diff --git a/client/lg-renderer.h b/client/lg-renderer.h index ee7feb75..a3b2dc9d 100644 --- a/client/lg-renderer.h +++ b/client/lg-renderer.h @@ -21,18 +21,24 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include +#include #define IS_LG_RENDERER_VALID(x) \ ((x)->get_name && \ (x)->initialize && \ (x)->deinitialize && \ (x)->is_compatible && \ + (x)->on_resize && \ (x)->render) typedef struct LG_RendererParams { - SDL_Window * window; - SDL_Renderer * renderer; + SDL_Window * window; + TTF_Font * font; + bool vsync; + bool showFPS; + int width; + int height; } LG_RendererParams; @@ -59,6 +65,7 @@ typedef const char * (* LG_RendererGetName )(); typedef bool (* LG_RendererInitialize )(void ** opaque, const LG_RendererParams params, const LG_RendererFormat format); 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); typedef bool (* LG_RendererRender )(void * opaque, const LG_RendererRect destRect, const uint8_t * data, bool resample); typedef struct LG_Renderer @@ -67,6 +74,7 @@ typedef struct LG_Renderer LG_RendererInitialize initialize; LG_RendererDeInitialize deinitialize; LG_RendererIsCompatible is_compatible; + LG_RendererOnResize on_resize; LG_RendererRender render; } LG_Renderer; \ No newline at end of file diff --git a/client/main.c b/client/main.c index 974bcc6a..104bd3f5 100644 --- a/client/main.c +++ b/client/main.c @@ -16,16 +16,12 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// limit the FPS when sync is turned off -#define FPS_LIMIT 240 - #include #include #include #include #include #include -#include #include #include #include @@ -34,12 +30,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include -#define GL_GLEXT_PROTOTYPES -#include -#include - #include "debug.h" -#include "memcpySSE.h" +#include "utils.h" #include "KVMFR.h" #include "ivshmem/ivshmem.h" #include "spice/spice.h" @@ -57,8 +49,10 @@ struct AppState LG_RendererRect dstRect; float scaleX, scaleY; + const LG_Renderer * lgr ; + void * lgrData; + SDL_Window * window; - SDL_Renderer * renderer; struct KVMFRHeader * shm; unsigned int shmSize; }; @@ -109,7 +103,7 @@ struct AppParams params = inline void updatePositionInfo() { int w, h; - SDL_GetRendererOutputSize(state.renderer, &w, &h); + SDL_GetWindowSize(state.window, &w, &h); if (params.keepAspect) { @@ -140,59 +134,17 @@ inline void updatePositionInfo() state.scaleX = (float)state.srcSize.y / (float)state.dstRect.h; state.scaleY = (float)state.srcSize.x / (float)state.dstRect.w; -} -inline uint64_t microtime() -{ - struct timeval time; - gettimeofday(&time, NULL); - return ((uint64_t)time.tv_sec * 1000000) + time.tv_usec; -} - -uint64_t detectPresentTime() -{ - glFinish(); - - // time 20 iterations and compute the average - const uint64_t start = microtime(); - for(int i = 0; i < 20; ++i) - { - SDL_RenderPresent(state.renderer); - glFinish(); - } - const uint64_t t = (microtime() - start) / 20; - - DEBUG_INFO("detected: %lu (%f Hz)", t, 1000000.0f / t); - if (t < 2000) - { - DEBUG_INFO("present time too low, setting framerate limit to %d Hz", FPS_LIMIT); - return ceil((1000000.0/(double)FPS_LIMIT)); - } - - return t; + if (state.lgr) + state.lgr->on_resize(state.lgrData, w, h); } int renderThread(void * unused) { bool error = false; struct KVMFRHeader header; - const LG_Renderer * lgr = NULL; - void * lgrData; - unsigned int frameCount = 0; - SDL_Texture * textTexture = NULL; - SDL_Rect textRect; - - const uint64_t presentTime = detectPresentTime(); - unsigned int lateCount = 0; - - int pollDelay = 0; - uint64_t drawStart = 0; - int drawTime = 0; - - uint64_t fpsStart = microtime(); - int fpsTime = 0; - - + int pollDelay = 0; + int lateCount = 0; volatile uint64_t * dataPos = &state.shm->dataPos; while(state.running) @@ -280,32 +232,39 @@ int renderThread(void * unused) } // check if we have a compatible renderer - if (!lgr || !lgr->is_compatible(lgrData, lgrFormat)) + if (!state.lgr || !state.lgr->is_compatible(state.lgrData, lgrFormat)) { + int width, height; + SDL_GetWindowSize(state.window, &width, &height); + LG_RendererParams lgrParams; - lgrParams.window = state.window; - lgrParams.renderer = state.renderer; + lgrParams.window = state.window; + lgrParams.font = state.font; + lgrParams.showFPS = params.showFPS; + lgrParams.vsync = params.vsync; + lgrParams.width = width; + lgrParams.height = height; DEBUG_INFO("Data Format: w=%u, h=%u, s=%u, p=%u, bpp=%u", lgrFormat.width, lgrFormat.height, lgrFormat.stride, lgrFormat.pitch, lgrFormat.bpp); // first try to reinitialize any existing renderer - if (lgr) + if (state.lgr) { - lgr->deinitialize(lgrData); - if (lgr->initialize(&lgrData, lgrParams, lgrFormat)) + state.lgr->deinitialize(state.lgrData); + if (state.lgr->initialize(&state.lgrData, lgrParams, lgrFormat)) { - DEBUG_INFO("Reinitialized %s", lgr->get_name()); + DEBUG_INFO("Reinitialized %s", state.lgr->get_name()); } else { - DEBUG_ERROR("Failed to reinitialize %s, trying other renderers", lgr->get_name()); - lgr->deinitialize(lgrData); - lgr = NULL; + DEBUG_ERROR("Failed to reinitialize %s, trying other renderers", state.lgr->get_name()); + state.lgr->deinitialize(state.lgrData); + state.lgr = NULL; } } - if (!lgr) + if (!state.lgr) { // probe for a a suitable renderer for(const LG_Renderer **r = &LG_Renderers[0]; *r; ++r) @@ -316,19 +275,19 @@ int renderThread(void * unused) continue; } - lgrData = NULL; - if (!(*r)->initialize(&lgrData, lgrParams, lgrFormat)) + state.lgrData = NULL; + if (!(*r)->initialize(&state.lgrData, lgrParams, lgrFormat)) { - (*r)->deinitialize(lgrData); + (*r)->deinitialize(state.lgrData); continue; } - lgr = *r; + state.lgr = *r; DEBUG_INFO("Initialized %s", (*r)->get_name()); break; } - if (!lgr) + if (!state.lgr) { DEBUG_INFO("Unable to find a suitable renderer"); return -1; @@ -342,8 +301,8 @@ int renderThread(void * unused) updatePositionInfo(); } - if (!lgr->render( - lgrData, + if (!state.lgr->render( + state.lgrData, state.dstRect, (uint8_t *)state.shm + header.dataPos, params.useMipmap @@ -352,106 +311,10 @@ int renderThread(void * unused) DEBUG_ERROR("Failed to render the frame"); break; } - - if (params.showFPS) - { - // for now render the frame counter here, we really should - // move this into the renderers though. - if (fpsTime > 1000000) - { - SDL_Surface *textSurface = NULL; - if (textTexture) - { - SDL_DestroyTexture(textTexture); - textTexture = NULL; - } - - char str[128]; - const float avgFPS = 1000.0f / (((float)fpsTime / frameCount) / 1000.0f); - snprintf(str, sizeof(str), "FPS: %8.4f (Frames: %d, Late: %d)", avgFPS, frameCount, lateCount); - SDL_Color color = {0xff, 0xff, 0xff}; - if (!(textSurface = TTF_RenderText_Blended(state.font, str, color))) - { - DEBUG_ERROR("Failed to render text"); - break; - } - - textRect.x = 5; - textRect.y = 5; - textRect.w = textSurface->w; - textRect.h = textSurface->h; - - textTexture = SDL_CreateTextureFromSurface(state.renderer, textSurface); - SDL_SetTextureBlendMode(textTexture, SDL_BLENDMODE_BLEND); - SDL_FreeSurface(textSurface); - - fpsTime = 0; - frameCount = 0; - lateCount = 0; - } - - if (textTexture) - { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glColor4f(0.0f, 0.0f, 1.0f, 0.5f); - glBegin(GL_TRIANGLE_STRIP); - glVertex2i(textRect.x , textRect.y ); - glVertex2i(textRect.x + textRect.w, textRect.y ); - glVertex2i(textRect.x , textRect.y + textRect.h); - glVertex2i(textRect.x + textRect.w, textRect.y + textRect.h); - glEnd(); - - - float tw, th; - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - SDL_GL_BindTexture(textTexture, &tw, &th); - glBegin(GL_TRIANGLE_STRIP); - glTexCoord2f(0.0f, 0.0f); glVertex2i(textRect.x , textRect.y ); - glTexCoord2f(tw , 0.0f); glVertex2i(textRect.x + textRect.w, textRect.y ); - glTexCoord2f(0.0f, th ); glVertex2i(textRect.x , textRect.y + textRect.h); - glTexCoord2f(tw , th ); glVertex2i(textRect.x + textRect.w, textRect.y + textRect.h); - glEnd(); - glDisable(GL_BLEND); - SDL_GL_UnbindTexture(textTexture); - } - } - - - SDL_RenderPresent(state.renderer); - - // sleep for the remainder of the presentation time - { - const uint64_t t = microtime(); - drawTime = t - drawStart; - if (drawTime < presentTime) - { - const uint64_t delta = presentTime - drawTime; - if (delta > 1000) - usleep(delta - 1000); - - if (!params.vsync) - { - // poll for the final microsecond - const uint64_t target = t + delta; - while(microtime() <= target) {} - } - } - - // ensure buffers are flushed - glFinish(); - drawStart = microtime(); - } - - ++frameCount; - const uint64_t t = microtime(); - fpsTime += t - fpsStart; - fpsStart = t; } - if (lgr) - lgr->deinitialize(lgrData); + if (state.lgr) + state.lgr->deinitialize(state.lgrData); return 0; } @@ -756,7 +619,8 @@ int run() params.w, params.h, ( - SDL_WINDOW_SHOWN | + SDL_WINDOW_SHOWN | + SDL_WINDOW_OPENGL | (params.allowResize ? SDL_WINDOW_RESIZABLE : 0) | (params.borderless ? SDL_WINDOW_BORDERLESS : 0) ) @@ -778,17 +642,6 @@ int run() SDL_ShowCursor(SDL_DISABLE); } - state.renderer = SDL_CreateRenderer(state.window, -1, - SDL_RENDERER_ACCELERATED | - (params.vsync ? SDL_RENDERER_PRESENTVSYNC : 0) - ); - - if (!state.renderer) - { - DEBUG_ERROR("failed to create renderer"); - return -1; - } - int shm_fd = 0; SDL_Thread *t_ivshmem = NULL; SDL_Thread *t_spice = NULL; @@ -866,9 +719,6 @@ int run() if (t_spice) SDL_WaitThread(t_spice, NULL); - if (state.renderer) - SDL_DestroyRenderer(state.renderer); - if (state.window) SDL_DestroyWindow(state.window); diff --git a/client/renderers/basic.c b/client/renderers/basic.c index f1046179..11f9153f 100644 --- a/client/renderers/basic.c +++ b/client/renderers/basic.c @@ -34,6 +34,17 @@ bool lgr_basic_initialize(void ** opaque, const LG_RendererParams params, const memset(*opaque, 0, sizeof(struct LGR_Basic)); struct LGR_Basic * this = (struct LGR_Basic *)*opaque; + this->renderer = SDL_CreateRenderer(params.window, -1, + SDL_RENDERER_ACCELERATED | + (params.vsync ? SDL_RENDERER_PRESENTVSYNC : 0) + ); + + if (!this->renderer) + { + DEBUG_ERROR("Failed to create renderer"); + return false; + } + Uint32 sdlFormat; switch(format.bpp) { @@ -55,7 +66,7 @@ bool lgr_basic_initialize(void ** opaque, const LG_RendererParams params, const // create the target texture this->texture = SDL_CreateTexture( - params.renderer, + this->renderer, sdlFormat, SDL_TEXTUREACCESS_STREAMING, format.width, @@ -70,7 +81,6 @@ bool lgr_basic_initialize(void ** opaque, const LG_RendererParams params, const memcpy(&this->format, &format, sizeof(LG_RendererFormat)); - this->renderer = params.renderer; this->dataWidth = this->format.width * (this->format.bpp / 8); this->initialized = true; return true; @@ -85,6 +95,9 @@ void lgr_basic_deinitialize(void * opaque) if (this->texture) SDL_DestroyTexture(this->texture); + if (this->renderer) + SDL_DestroyRenderer(this->renderer); + free(this); } diff --git a/client/renderers/opengl.c b/client/renderers/opengl.c index 2b8ddf93..bad03cd9 100644 --- a/client/renderers/opengl.c +++ b/client/renderers/opengl.c @@ -1,6 +1,9 @@ #include "lg-renderer.h" #include #include +#include + +#include #define GL_GLEXT_PROTOTYPES #include @@ -8,26 +11,45 @@ #include "debug.h" #include "memcpySSE.h" +#include "utils.h" + +// limit the FPS when sync is turned off +#define FPS_LIMIT 240 #define VBO_BUFFERS 2 struct LGR_OpenGL { + LG_RendererParams params; bool initialized; + SDL_GLContext glContext; + bool resizeWindow; + LG_RendererFormat format; GLuint intFormat; GLuint vboFormat; size_t texSize; + uint64_t presentTime; + uint64_t drawStart; bool hasBuffers; GLuint vboID[VBO_BUFFERS]; uint8_t * texPixels[VBO_BUFFERS]; int texIndex; bool hasTextures; - GLuint vboTex[VBO_BUFFERS]; + GLuint vboTex[VBO_BUFFERS + 1]; // extra texture for FPS + + bool fpsTexture; + uint64_t lastFrameTime; + uint64_t renderTime; + uint64_t frameCount; + SDL_Rect fpsRect; }; +uint64_t lgr_opengl_benchmark(); +void lgr_opengl_on_resize(void * opaque, const int width, const int height); + bool lgr_opengl_check_error(const char * name) { GLenum error = glGetError(); @@ -46,15 +68,6 @@ const char * lgr_opengl_get_name() bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const LG_RendererFormat format) { - // check if the GPU supports GL_ARB_buffer_storage first - // there is no advantage to this renderer if it is not present. - const GLubyte * extensions = glGetString(GL_EXTENSIONS); - if (!gluCheckExtension((const GLubyte *)"GL_ARB_buffer_storage", extensions)) - { - DEBUG_INFO("The GPU doesn't support GL_ARB_buffer_storage"); - return false; - } - // create our local storage *opaque = malloc(sizeof(struct LGR_OpenGL)); if (!*opaque) @@ -64,6 +77,38 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const } memset(*opaque, 0, sizeof(struct LGR_OpenGL)); struct LGR_OpenGL * this = (struct LGR_OpenGL *)*opaque; + memcpy(&this->params, ¶ms, sizeof(LG_RendererParams)); + + this->glContext = SDL_GL_CreateContext(params.window); + if (!this->glContext) + { + DEBUG_ERROR("Failed to create the OpenGL context"); + return false; + } + + if (SDL_GL_MakeCurrent(params.window, this->glContext) != 0) + { + DEBUG_ERROR("Failed to make the GL context current"); + return false; + } + + if (params.vsync) + { + // try for dynamic vsync first + if (!SDL_GL_SetSwapInterval(-1)) + SDL_GL_SetSwapInterval(1); + } + else + SDL_GL_SetSwapInterval(0); + + // check if the GPU supports GL_ARB_buffer_storage first + // there is no advantage to this renderer if it is not present. + const GLubyte * extensions = glGetString(GL_EXTENSIONS); + if (!gluCheckExtension((const GLubyte *)"GL_ARB_buffer_storage", extensions)) + { + DEBUG_INFO("The GPU doesn't support GL_ARB_buffer_storage"); + return false; + } // assume 24 and 32 bit formats are RGB and RGBA switch(format.bpp) @@ -118,7 +163,7 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const } // create the textures - glGenTextures(VBO_BUFFERS, this->vboTex); + glGenTextures(VBO_BUFFERS + (params.showFPS ? 1 : 0), this->vboTex); if (lgr_opengl_check_error("glGenTextures")) return false; this->hasTextures = true; @@ -146,6 +191,13 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const } glEnable(GL_TEXTURE_2D); + glEnable(GL_COLOR_MATERIAL); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendEquation(GL_FUNC_ADD); + + this->resizeWindow = true; + this->presentTime = lgr_opengl_benchmark(this); + this->drawStart = nanotime(); // copy the format into the local storage memcpy(&this->format, &format, sizeof(LG_RendererFormat)); @@ -153,6 +205,29 @@ bool lgr_opengl_initialize(void ** opaque, const LG_RendererParams params, const return true; } +uint64_t lgr_opengl_benchmark(struct LGR_OpenGL * this) +{ + glFinish(); + + // time 20 iterations and compute the average + const uint64_t start = nanotime(); + for(int i = 0; i < 20; ++i) + { + SDL_GL_SwapWindow(this->params.window); + glFinish(); + } + const uint64_t t = (nanotime() - start) / 20; + + DEBUG_INFO("detected: %lu (%f Hz)", t, 1e9f / t); + if (t < 2e6) + { + DEBUG_INFO("present time too low, setting framerate limit to %d Hz", FPS_LIMIT); + return ceil(((double)1e9/(double)FPS_LIMIT)); + } + + return t; +} + void lgr_opengl_deinitialize(void * opaque) { struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque; @@ -165,6 +240,9 @@ void lgr_opengl_deinitialize(void * opaque) if (this->hasBuffers) glDeleteBuffers(VBO_BUFFERS, this->vboID); + if (this->glContext) + SDL_GL_DeleteContext(this->glContext); + free(this); } @@ -177,13 +255,41 @@ bool lgr_opengl_is_compatible(void * opaque, const LG_RendererFormat format) return (memcmp(&this->format, &format, sizeof(LG_RendererFormat)) == 0); } +void lgr_opengl_on_resize(void * opaque, const int width, const int height) +{ + struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque; + if (!this || !this->initialized) + return; + + this->params.width = width; + this->params.height = height; + this->resizeWindow = true; +} + bool lgr_opengl_render(void * opaque, const LG_RendererRect destRect, const uint8_t * data, bool resample) { struct LGR_OpenGL * this = (struct LGR_OpenGL *)opaque; if (!this || !this->initialized) return false; - glClear(GL_COLOR_BUFFER_BIT); + if (SDL_GL_MakeCurrent(this->params.window, this->glContext) != 0) + { + DEBUG_ERROR("Failed to make the GL context current"); + return false; + } + + if (this->resizeWindow) + { + // setup the projection matrix + glViewport(0, 0, this->params.width, this->params.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0, this->params.width, this->params.height, 0); + glMatrixMode(GL_MODELVIEW); + this->resizeWindow = false; + + glClear(GL_COLOR_BUFFER_BIT); + } // copy the buffer to the texture memcpySSE(this->texPixels[this->texIndex], data, this->texSize); @@ -228,17 +334,123 @@ bool lgr_opengl_render(void * opaque, const LG_RendererRect destRect, const uint } // draw the screen - glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, this->vboTex[this->texIndex]); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(0.0f, 0.0f); glVertex2i(destRect.x , destRect.y ); glTexCoord2f(1.0f, 0.0f); glVertex2i(destRect.x + destRect.w, destRect.y ); glTexCoord2f(0.0f, 1.0f); glVertex2i(destRect.x , destRect.y + destRect.h); glTexCoord2f(1.0f, 1.0f); glVertex2i(destRect.x + destRect.w, destRect.y + destRect.h); glEnd(); - glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); + if (this->params.showFPS) + { + if (this->renderTime > 1e9) + { + char str[128]; + const float avgFPS = 1000.0f / (((float)this->renderTime / this->frameCount) / 1e6f); + snprintf(str, sizeof(str), "FPS: %8.4f", avgFPS); + SDL_Color color = {0xff, 0xff, 0xff}; + SDL_Surface *textSurface = NULL; + if (!(textSurface = TTF_RenderText_Blended(this->params.font, str, color))) + { + DEBUG_ERROR("Failed to render text"); + return false; + } + + glBindTexture(GL_TEXTURE_2D , this->vboTex[2]); + glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); + glPixelStorei(GL_UNPACK_ROW_LENGTH, textSurface->w ); + glTexImage2D( + GL_TEXTURE_2D, + 0, + textSurface->format->BytesPerPixel, + textSurface->w, + textSurface->h, + 0, + GL_BGRA, + GL_UNSIGNED_BYTE, + textSurface->pixels + ); + + 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_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBindTexture(GL_TEXTURE_2D, 0); + + this->fpsRect.x = 5; + this->fpsRect.y = 5; + this->fpsRect.w = textSurface->w; + this->fpsRect.h = textSurface->h; + + SDL_FreeSurface(textSurface); + + this->renderTime = 0; + this->frameCount = 0; + this->fpsTexture = true; + } + + if (this->fpsTexture) + { + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glColor4f(0.0f, 0.0f, 1.0f, 0.5f); + glBegin(GL_TRIANGLE_STRIP); + glVertex2i(this->fpsRect.x , this->fpsRect.y ); + glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y ); + glVertex2i(this->fpsRect.x , this->fpsRect.y + this->fpsRect.h); + glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y + this->fpsRect.h); + glEnd(); + glEnable(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, this->vboTex[2]); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f , 0.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y ); + glTexCoord2f(1.0f , 0.0f); glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y ); + glTexCoord2f(0.0f , 1.0f); glVertex2i(this->fpsRect.x , this->fpsRect.y + this->fpsRect.h); + glTexCoord2f(1.0f, 1.0f); glVertex2i(this->fpsRect.x + this->fpsRect.w, this->fpsRect.y + this->fpsRect.h); + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_BLEND); + } + } + glFlush(); + + // sleep for the remainder of the presentation time + { + const uint64_t t = nanotime(); + const uint64_t drawTime = t - this->drawStart; + if (drawTime < this->presentTime) + { + const uint64_t delta = (this->presentTime - drawTime); + if (delta > 1e5) + usleep((delta - 1e5) / 1e3); + + if (!this->params.vsync) + { + // poll for the final microsecond + const uint64_t target = t + delta; + while(nanotime() <= target) {} + } + } + + // ensure buffers are flushed + glFinish(); + this->drawStart = nanotime(); + } + + SDL_GL_SwapWindow(this->params.window); + ++this->frameCount; + + const uint64_t t = nanotime(); + this->renderTime += t - this->lastFrameTime; + this->lastFrameTime = t; + if (++this->texIndex == VBO_BUFFERS) this->texIndex = 0; @@ -251,5 +463,6 @@ const LG_Renderer LGR_OpenGL = .initialize = lgr_opengl_initialize, .deinitialize = lgr_opengl_deinitialize, .is_compatible = lgr_opengl_is_compatible, + .on_resize = lgr_opengl_on_resize, .render = lgr_opengl_render }; \ No newline at end of file diff --git a/client/utils.h b/client/utils.h new file mode 100644 index 00000000..30b0041a --- /dev/null +++ b/client/utils.h @@ -0,0 +1,36 @@ +/* +Looking Glass - KVM FrameRelay (KVMFR) Client +Copyright (C) 2017 Geoffrey McRae + +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 + +inline uint64_t microtime() +{ + struct timespec time; + clock_gettime(CLOCK_MONOTONIC_RAW, &time); + return ((uint64_t)time.tv_sec * 1000000) + (time.tv_nsec / 1000); +} + +inline uint64_t nanotime() +{ + struct timespec time; + clock_gettime(CLOCK_MONOTONIC_RAW, &time); + return ((uint64_t)time.tv_sec * 1e9) + time.tv_nsec; +} \ No newline at end of file