From 6cbfa6e734651959ed7f489e97a28e7f1ce51b24 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Tue, 24 May 2022 06:57:33 +1000 Subject: [PATCH] [client] egl: add spice display support --- client/renderers/OpenGL/opengl.c | 224 ++++++++++++++++++++++++++----- 1 file changed, 188 insertions(+), 36 deletions(-) diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index b8366bb0..1b3e0b90 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -42,7 +42,8 @@ #define FPS_TEXTURE 0 #define MOUSE_TEXTURE 1 -#define TEXTURE_COUNT 2 +#define SPICE_TEXTURE 2 +#define TEXTURE_COUNT 3 #define FADE_TIME 1000000 @@ -136,13 +137,17 @@ struct Inst int texWIndex, texRIndex; int texList; int mouseList; + int spiceList; LG_RendererRect destRect; + struct IntPoint spiceSize; + bool spiceShow; bool hasTextures, hasFrames; GLuint frames[BUFFER_COUNT]; GLsync fences[BUFFER_COUNT]; GLuint textures[TEXTURE_COUNT]; + bool start; bool waiting; uint64_t waitFadeTime; bool waitDone; @@ -174,7 +179,7 @@ enum ConfigStatus static void deconfigure(struct Inst * this); static enum ConfigStatus configure(struct Inst * this); -static void updateMouseShape(struct Inst * this, bool * newShape); +static void updateMouseShape(struct Inst * this); static bool drawFrame(struct Inst * this); static void drawMouse(struct Inst * this); static void renderWait(struct Inst * this); @@ -207,7 +212,6 @@ bool opengl_create(LG_Renderer ** renderer, const LG_RendererParams params, this->opt.preventBuffer = option_get_bool("opengl", "preventBuffer"); this->opt.amdPinnedMem = option_get_bool("opengl", "amdPinnedMem" ); - LG_LOCK_INIT(this->formatLock); LG_LOCK_INIT(this->frameLock ); LG_LOCK_INIT(this->mouseLock ); @@ -235,6 +239,7 @@ void opengl_deinitialize(LG_Renderer * renderer) glDeleteLists(this->texList , BUFFER_COUNT); glDeleteLists(this->mouseList, 1); + glDeleteLists(this->spiceList, 1); } deconfigure(this); @@ -266,6 +271,34 @@ void opengl_onRestart(LG_Renderer * renderer) struct Inst * this = UPCAST(struct Inst, renderer); this->waiting = true; + this->start = false; +} + +static void setupModelView(struct Inst * this) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if (!this->destRect.valid) + return; + + int fw, fh; + if (this->spiceShow) + { + fw = this->spiceSize.x; + fh = this->spiceSize.y; + } + else + { + fw = this->format.frameWidth; + fh = this->format.frameHeight; + } + glTranslatef(this->destRect.x, this->destRect.y, 0.0f); + glScalef( + (float)this->destRect.w / (float)fw, + (float)this->destRect.h / (float)fh, + 1.0f + ); } void opengl_onResize(LG_Renderer * renderer, const int width, const int height, const double scale, @@ -292,19 +325,6 @@ void opengl_onResize(LG_Renderer * renderer, const int width, const int height, glLoadIdentity(); glOrtho(0, this->window.x, this->window.y, 0, -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - if (this->destRect.valid) - { - glTranslatef(this->destRect.x, this->destRect.y, 0.0f); - glScalef( - (float)this->destRect.w / (float)this->format.frameWidth, - (float)this->destRect.h / (float)this->format.frameHeight, - 1.0f - ); - } - // this is needed to refresh the font atlas texture ImGui_ImplOpenGL2_Shutdown(); ImGui_ImplOpenGL2_Init(); @@ -381,18 +401,7 @@ bool opengl_onFrame(LG_Renderer * renderer, const FrameBuffer * frame, int dmaFd atomic_store_explicit(&this->frameUpdate, true, memory_order_release); LG_UNLOCK(this->frameLock); - if (this->waiting) - { - this->waiting = false; - if (!this->params.quickSplash) - this->waitFadeTime = microtime() + FADE_TIME; - else - { - glDisable(GL_MULTISAMPLE); - this->waitDone = true; - } - } - + this->start = true; return true; } @@ -456,6 +465,7 @@ bool opengl_renderStartup(LG_Renderer * renderer, bool useDMA) // generate lists for drawing this->texList = glGenLists(BUFFER_COUNT); this->mouseList = glGenLists(1); + this->spiceList = glGenLists(1); // create the overlay textures glGenTextures(TEXTURE_COUNT, this->textures); @@ -489,6 +499,20 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool { struct Inst * this = UPCAST(struct Inst, renderer); + setupModelView(this); + + if (this->start && this->waiting) + { + this->waiting = false; + if (!this->params.quickSplash) + this->waitFadeTime = microtime() + FADE_TIME; + else + { + glDisable(GL_MULTISAMPLE); + this->waitDone = true; + } + } + switch(configure(this)) { case CONFIG_STATUS_ERROR: @@ -508,10 +532,14 @@ bool opengl_render(LG_Renderer * renderer, LG_RendererRotate rotate, const bool renderWait(this); else { - bool newShape; - updateMouseShape(this, &newShape); - glCallList(this->texList + this->texRIndex); - drawMouse(this); + if (this->spiceShow) + glCallList(this->spiceList); + else + { + updateMouseShape(this); + glCallList(this->texList + this->texRIndex); + drawMouse(this); + } if (!this->waitDone) renderWait(this); @@ -569,6 +597,9 @@ static void renderWait(struct Inst * this) a = 1.0f; else { + if (this->waitDone) + return; + uint64_t t = microtime(); if (t > this->waitFadeTime) { @@ -626,12 +657,129 @@ static void renderWait(struct Inst * this) drawTorusArc(-14, 83, 5, 7, 10, M_PI , M_PI / 2.0f); drawTorusArc( 14, 83, 5, 7, 10, M_PI * 1.5f, M_PI / 2.0f); - //FIXME: draw the diagnoal marks on the circle + //FIXME: draw the diagonal marks on the circle glPopMatrix(); glDisable(GL_BLEND); } +static void opengl_spiceConfigure(LG_Renderer * renderer, int width, int height) +{ + struct Inst * this = UPCAST(struct Inst, renderer); + this->spiceSize.x = width; + this->spiceSize.y = height; + + glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]); + glTexImage2D + ( + GL_TEXTURE_2D, + 0 , + GL_RGBA, + width , + height , + 0 , + GL_BGRA, + GL_UNSIGNED_BYTE, + 0 + ); + + 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); + + // create the display lists + glNewList(this->spiceList, GL_COMPILE); + glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_TRIANGLE_STRIP); + glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); + glTexCoord2f(1.0f, 0.0f); glVertex2i(this->spiceSize.x, 0); + glTexCoord2f(0.0f, 1.0f); glVertex2i(0, this->spiceSize.y); + glTexCoord2f(1.0f, 1.0f); + glVertex2i(this->spiceSize.x, this->spiceSize.y); + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + glEndList(); +} + +static void opengl_spiceDrawFill(LG_Renderer * renderer, int x, int y, int width, + int height, uint32_t color) +{ + struct Inst * this = UPCAST(struct Inst, renderer); + + /* this is a fairly hacky way to do this, but since it's only for the fallback + * spice display it's not really an issue */ + + uint32_t line[width]; + for(int x = 0; x < width; ++x) + line[x] = color; + + glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]); + glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); + glPixelStorei(GL_UNPACK_ROW_LENGTH, width); + for(; y < height; ++y) + glTexSubImage2D + ( + GL_TEXTURE_2D, + 0 , + x , + y , + width , + 1 , + GL_BGRA, + GL_UNSIGNED_BYTE, + line + ); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static void opengl_spiceDrawBitmap(LG_Renderer * renderer, int x, int y, int width, + int height, int stride, uint8_t * data, bool topDown) +{ + struct Inst * this = UPCAST(struct Inst, renderer); + + if (!topDown) + { + // this is non-optimal but as spice is a fallback it's not too critical + uint8_t line[stride]; + for(int y = 0; y < height / 2; ++y) + { + uint8_t * top = data + y * stride; + uint8_t * btm = data + (height - y - 1) * stride; + memcpy(line, top , stride); + memcpy(top , btm , stride); + memcpy(btm , line, stride); + } + } + + glBindTexture(GL_TEXTURE_2D, this->textures[SPICE_TEXTURE]); + glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / 4); + glTexSubImage2D + ( + GL_TEXTURE_2D, + 0 , + x , + y , + width , + height , + GL_BGRA, + GL_UNSIGNED_BYTE, + data + ); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static void opengl_spiceShow(LG_Renderer * renderer, bool show) +{ + struct Inst * this = UPCAST(struct Inst, renderer); + this->spiceShow = show; + if (show) + this->start = true; +} + const LG_RendererOps LGR_OpenGL = { .getName = opengl_getName, @@ -648,7 +796,12 @@ const LG_RendererOps LGR_OpenGL = .onFrame = opengl_onFrame, .renderStartup = opengl_renderStartup, .needsRender = opengl_needsRender, - .render = opengl_render + .render = opengl_render, + + .spiceConfigure = opengl_spiceConfigure, + .spiceDrawFill = opengl_spiceDrawFill, + .spiceDrawBitmap = opengl_spiceDrawBitmap, + .spiceShow = opengl_spiceShow }; static bool _checkGLError(unsigned int line, const char * name) @@ -915,10 +1068,9 @@ static void deconfigure(struct Inst * this) this->configured = false; } -static void updateMouseShape(struct Inst * this, bool * newShape) +static void updateMouseShape(struct Inst * this) { LG_LOCK(this->mouseLock); - *newShape = this->newShape; if (!this->newShape) { LG_UNLOCK(this->mouseLock);