mirror of
				https://github.com/gnif/LookingGlass.git
				synced 2025-10-31 04:31:57 +00:00 
			
		
		
		
	[client] implement support for RGB24 packed data
This commit is contained in:
		| @@ -76,6 +76,8 @@ typedef struct LG_RendererFormat | ||||
|   bool              hdrPQ;        // if the HDR content is PQ mapped | ||||
|   unsigned int      screenWidth;  // actual width of the host | ||||
|   unsigned int      screenHeight; // actual height of the host | ||||
|   unsigned int      dataWidth;    // the width of the packed data | ||||
|   unsigned int      dataHeight;   // the height of the packed data | ||||
|   unsigned int      frameWidth;   // width of frame transmitted | ||||
|   unsigned int      frameHeight;  // height of frame transmitted | ||||
|   unsigned int      stride;  // scanline width (zero if compresed) | ||||
|   | ||||
| @@ -56,6 +56,7 @@ build_shaders( | ||||
|   shader/damage.vert | ||||
|   shader/damage.frag | ||||
|   shader/basic.vert | ||||
|   shader/convert_bgr_bgra.frag | ||||
|   shader/ffx_cas.frag | ||||
|   shader/ffx_fsr1_easu.frag | ||||
|   shader/ffx_fsr1_rcas.frag | ||||
| @@ -87,6 +88,7 @@ add_library(renderer_EGL STATIC | ||||
|   postprocess.c | ||||
|   ffx.c | ||||
|   filter.c | ||||
|   filter_bgr_bgra.c | ||||
|   filter_ffx_cas.c | ||||
|   filter_ffx_fsr1.c | ||||
|   filter_downscale.c | ||||
|   | ||||
| @@ -277,7 +277,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, | ||||
|           } | ||||
|  | ||||
|         egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA, | ||||
|             cursor->width, cursor->height, sizeof(xor[0])); | ||||
|             cursor->width, cursor->height, cursor->width, sizeof(xor[0])); | ||||
|         egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true); | ||||
|       } | ||||
|       // fall through | ||||
| @@ -285,7 +285,7 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, | ||||
|       case LG_CURSOR_COLOR: | ||||
|       { | ||||
|         egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA, | ||||
|             cursor->width, cursor->height, cursor->stride); | ||||
|             cursor->width, cursor->height, cursor->width, cursor->stride); | ||||
|         egl_textureUpdate(cursor->norm.texture, data, true); | ||||
|         break; | ||||
|       } | ||||
| @@ -311,9 +311,9 @@ struct CursorState egl_cursorRender(EGL_Cursor * cursor, | ||||
|         } | ||||
|  | ||||
|         egl_textureSetup(cursor->norm.texture, EGL_PF_BGRA, | ||||
|             cursor->width, cursor->height, sizeof(and[0])); | ||||
|             cursor->width, cursor->height, cursor->width, sizeof(and[0])); | ||||
|         egl_textureSetup(cursor->mono.texture, EGL_PF_BGRA, | ||||
|             cursor->width, cursor->height, sizeof(xor[0])); | ||||
|             cursor->width, cursor->height, cursor->width, sizeof(xor[0])); | ||||
|         egl_textureUpdate(cursor->norm.texture, (uint8_t *)and, true); | ||||
|         egl_textureUpdate(cursor->mono.texture, (uint8_t *)xor, true); | ||||
|         break; | ||||
|   | ||||
| @@ -119,8 +119,8 @@ static bool egl_initDesktopShader( | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   shader->uTransform    = egl_shaderGetUniform(shader->shader, "transform"   ); | ||||
|   shader->uDesktopSize  = egl_shaderGetUniform(shader->shader, "desktopSize" ); | ||||
|   shader->uTransform    = egl_shaderGetUniform(shader->shader, "transform"   ); | ||||
|   shader->uScaleAlgo    = egl_shaderGetUniform(shader->shader, "scaleAlgo"   ); | ||||
|   shader->uNVGain       = egl_shaderGetUniform(shader->shader, "nvGain"      ); | ||||
|   shader->uCBMode       = egl_shaderGetUniform(shader->shader, "cbMode"      ); | ||||
| @@ -206,6 +206,9 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display, | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   // this MUST be first | ||||
|   egl_postProcessAdd(desktop->pp, &egl_filterBGRtoBGRAOps); | ||||
|  | ||||
|   egl_postProcessAdd(desktop->pp, &egl_filterDownscaleOps); | ||||
|   egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps   ); | ||||
|   egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops  ); | ||||
| @@ -337,6 +340,10 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format) | ||||
|       pixFmt = EGL_PF_RGBA16F; | ||||
|       break; | ||||
|  | ||||
|     case FRAME_TYPE_BGR: | ||||
|       pixFmt = EGL_PF_BGR; | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       DEBUG_ERROR("Unsupported frame format"); | ||||
|       return false; | ||||
| @@ -350,8 +357,9 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format) | ||||
|   if (!egl_textureSetup( | ||||
|     desktop->texture, | ||||
|     pixFmt, | ||||
|     format.frameWidth, | ||||
|     format.frameHeight, | ||||
|     format.dataWidth, | ||||
|     format.dataHeight, | ||||
|     format.stride, | ||||
|     format.pitch | ||||
|   )) | ||||
|   { | ||||
| @@ -572,6 +580,7 @@ void egl_desktopSpiceConfigure(EGL_Desktop * desktop, int width, int height) | ||||
|     EGL_PF_BGRA, | ||||
|     width, | ||||
|     height, | ||||
|     width, | ||||
|     width * 4 | ||||
|   )) | ||||
|   { | ||||
| @@ -595,7 +604,7 @@ void egl_desktopSpiceDrawFill(EGL_Desktop * desktop, int x, int y, int width, | ||||
|  | ||||
|   for(; y < height; ++y) | ||||
|     egl_textureUpdateRect(desktop->spiceTexture, | ||||
|         x, y, width, 1, sizeof(line), (uint8_t *)line, false); | ||||
|         x, y, width, 1, width, sizeof(line), (uint8_t *)line, false); | ||||
|  | ||||
|   atomic_store(&desktop->processFrame, true); | ||||
| } | ||||
| @@ -604,7 +613,7 @@ void egl_desktopSpiceDrawBitmap(EGL_Desktop * desktop, int x, int y, int width, | ||||
|     int height, int stride, uint8_t * data, bool topDown) | ||||
| { | ||||
|   egl_textureUpdateRect(desktop->spiceTexture, | ||||
|       x, y, width, height, stride, data, topDown); | ||||
|       x, y, width, height, width, stride, data, topDown); | ||||
|   atomic_store(&desktop->processFrame, true); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -37,7 +37,8 @@ typedef enum EGL_PixelFormat | ||||
|   EGL_PF_RGBA, | ||||
|   EGL_PF_BGRA, | ||||
|   EGL_PF_RGBA10, | ||||
|   EGL_PF_RGBA16F | ||||
|   EGL_PF_RGBA16F, | ||||
|   EGL_PF_BGR | ||||
| } | ||||
| EGL_PixelFormat; | ||||
|  | ||||
| @@ -60,13 +61,17 @@ typedef struct EGL_TexSetup | ||||
|   /* the height of the texture in pixels */ | ||||
|   size_t height; | ||||
|  | ||||
|   /* the stide of the texture in bytes */ | ||||
|   /* the row length of the texture in pixels */ | ||||
|   size_t stride; | ||||
|  | ||||
|   /* the row length of the texture in bytes */ | ||||
|   size_t pitch; | ||||
| } | ||||
| EGL_TexSetup; | ||||
|  | ||||
| typedef enum EGL_FilterType | ||||
| { | ||||
|   EGL_FILTER_TYPE_INTERNAL, | ||||
|   EGL_FILTER_TYPE_EFFECT, | ||||
|   EGL_FILTER_TYPE_UPSCALE, | ||||
|   EGL_FILTER_TYPE_DOWNSCALE | ||||
|   | ||||
| @@ -72,7 +72,8 @@ typedef struct EGL_FilterOps | ||||
|    * useDMA will be true if the texture provided needs to use samplerExternalOES | ||||
|    */ | ||||
|   bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt, | ||||
|       unsigned int width, unsigned int height, bool useDMA); | ||||
|       unsigned int width, unsigned int height, | ||||
|       unsigned int desktopWidth, unsigned int desktopHeight, bool useDMA); | ||||
|  | ||||
|   /* set the output resolution hint for the filter | ||||
|    * this is optional and only a hint */ | ||||
| @@ -104,6 +105,12 @@ typedef struct EGL_Filter | ||||
| } | ||||
| EGL_Filter; | ||||
|  | ||||
| static inline void egl_filterEarlyInit(const EGL_FilterOps * ops) | ||||
| { | ||||
|   if (ops->earlyInit) | ||||
|     ops->earlyInit(); | ||||
| } | ||||
|  | ||||
| static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter) | ||||
| { | ||||
|   if (!ops->init(filter)) | ||||
| @@ -121,24 +128,30 @@ static inline void egl_filterFree(EGL_Filter ** filter) | ||||
|  | ||||
| static inline bool egl_filterImguiConfig(EGL_Filter * filter) | ||||
| { | ||||
|   return filter->ops.imguiConfig(filter); | ||||
|   if (filter->ops.imguiConfig) | ||||
|     return filter->ops.imguiConfig(filter); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| static inline void egl_filterSaveState(EGL_Filter * filter) | ||||
| { | ||||
|   filter->ops.saveState(filter); | ||||
|   if (filter->ops.saveState) | ||||
|     filter->ops.saveState(filter); | ||||
| } | ||||
|  | ||||
| static inline void egl_filterLoadState(EGL_Filter * filter) | ||||
| { | ||||
|   filter->ops.loadState(filter); | ||||
|   if (filter->ops.loadState) | ||||
|     filter->ops.loadState(filter); | ||||
| } | ||||
|  | ||||
| static inline bool egl_filterSetup(EGL_Filter * filter, | ||||
|     enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, | ||||
|     unsigned int desktopWidth, unsigned int desktopHeight, | ||||
|     bool useDMA) | ||||
| { | ||||
|   return filter->ops.setup(filter, pixFmt, width, height, useDMA); | ||||
|   return filter->ops.setup(filter, pixFmt, width, height, | ||||
|       desktopWidth, desktopHeight, useDMA); | ||||
| } | ||||
|  | ||||
| static inline void egl_filterSetOutputResHint(EGL_Filter * filter, | ||||
|   | ||||
							
								
								
									
										211
									
								
								client/renderers/EGL/filter_bgr_bgra.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								client/renderers/EGL/filter_bgr_bgra.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | ||||
| /** | ||||
|  * Looking Glass | ||||
|  * Copyright © 2017-2023 The Looking Glass Authors | ||||
|  * https://looking-glass.io | ||||
|  * | ||||
|  * 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 "filter.h" | ||||
| #include "framebuffer.h" | ||||
|  | ||||
| #include <math.h> | ||||
|  | ||||
| #include "common/array.h" | ||||
| #include "common/debug.h" | ||||
| #include "common/option.h" | ||||
| #include "cimgui.h" | ||||
|  | ||||
| #include "basic.vert.h" | ||||
| #include "convert_bgr_bgra.frag.h" | ||||
|  | ||||
|  | ||||
| typedef struct EGL_FilterBGRtoBGRA | ||||
| { | ||||
|   EGL_Filter base; | ||||
|  | ||||
|   bool enable; | ||||
|   int  useDMA; | ||||
|   unsigned int width, height; | ||||
|   unsigned int desktopWidth, desktopHeight; | ||||
|   bool prepared; | ||||
|  | ||||
|   EGL_Uniform       uOutputSize; | ||||
|  | ||||
|   EGL_Shader      * shader; | ||||
|   EGL_Framebuffer * fb; | ||||
|   GLuint            sampler[2]; | ||||
| } | ||||
| EGL_FilterBGRtoBGRA; | ||||
|  | ||||
| static bool egl_filterBGRtoBGRAInit(EGL_Filter ** filter) | ||||
| { | ||||
|   EGL_FilterBGRtoBGRA * this = calloc(1, sizeof(*this)); | ||||
|   if (!this) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to allocate ram"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   this->useDMA = -1; | ||||
|  | ||||
|   if (!egl_shaderInit(&this->shader)) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to initialize the shader"); | ||||
|     goto error_this; | ||||
|   } | ||||
|  | ||||
|   if (!egl_framebufferInit(&this->fb)) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to initialize the framebuffer"); | ||||
|     goto error_shader; | ||||
|   } | ||||
|  | ||||
|   glGenSamplers(ARRAY_LENGTH(this->sampler), this->sampler); | ||||
|   glSamplerParameteri(this->sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||||
|   glSamplerParameteri(this->sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||||
|   glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_S    , GL_CLAMP_TO_EDGE); | ||||
|   glSamplerParameteri(this->sampler[0], GL_TEXTURE_WRAP_T    , GL_CLAMP_TO_EDGE); | ||||
|   glSamplerParameteri(this->sampler[1], GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|   glSamplerParameteri(this->sampler[1], GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
|   glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_S    , GL_CLAMP_TO_EDGE); | ||||
|   glSamplerParameteri(this->sampler[1], GL_TEXTURE_WRAP_T    , GL_CLAMP_TO_EDGE); | ||||
|  | ||||
|   *filter = &this->base; | ||||
|   return true; | ||||
|  | ||||
| error_shader: | ||||
|   egl_shaderFree(&this->shader); | ||||
|  | ||||
| error_this: | ||||
|   free(this); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| static void egl_filterBGRtoBGRAFree(EGL_Filter * filter) | ||||
| { | ||||
|   EGL_FilterBGRtoBGRA * this = UPCAST(EGL_FilterBGRtoBGRA, filter); | ||||
|  | ||||
|   egl_shaderFree(&this->shader); | ||||
|   egl_framebufferFree(&this->fb); | ||||
|   glDeleteSamplers(ARRAY_LENGTH(this->sampler), this->sampler); | ||||
|   free(this); | ||||
| } | ||||
|  | ||||
| static bool egl_filterBGRtoBGRASetup(EGL_Filter * filter, | ||||
|     enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, | ||||
|     unsigned int desktopWidth, unsigned int desktopHeight, | ||||
|     bool useDMA) | ||||
| { | ||||
|   EGL_FilterBGRtoBGRA * this = UPCAST(EGL_FilterBGRtoBGRA, filter); | ||||
|  | ||||
|   if (pixFmt != EGL_PF_BGR) | ||||
|     return false; | ||||
|  | ||||
|   if (this->useDMA != useDMA) | ||||
|   { | ||||
|     if (!egl_shaderCompile(this->shader, | ||||
|           b_shader_basic_vert           , b_shader_basic_vert_size, | ||||
|           b_shader_convert_bgr_bgra_frag, b_shader_convert_bgr_bgra_frag_size, | ||||
|           useDMA) | ||||
|        ) | ||||
|     { | ||||
|       DEBUG_ERROR("Failed to compile the shader"); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     this->uOutputSize.type     = EGL_UNIFORM_TYPE_2F; | ||||
|     this->uOutputSize.location = | ||||
|       egl_shaderGetUniform(this->shader, "outputSize"); | ||||
|  | ||||
|     this->useDMA = useDMA; | ||||
|   } | ||||
|  | ||||
|   if (this->prepared && | ||||
|       this->width         == width        && | ||||
|       this->height        == height       && | ||||
|       this->desktopWidth  == desktopWidth && | ||||
|       this->desktopHeight == desktopHeight) | ||||
|     return true; | ||||
|  | ||||
|   if (!egl_framebufferSetup(this->fb, pixFmt, desktopWidth, desktopHeight)) | ||||
|     return false; | ||||
|  | ||||
|   this->width         = width; | ||||
|   this->height        = height; | ||||
|   this->desktopWidth  = desktopWidth; | ||||
|   this->desktopHeight = desktopHeight; | ||||
|   this->prepared      = false; | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static void egl_filterBGRtoBGRAGetOutputRes(EGL_Filter * filter, | ||||
|     unsigned int *width, unsigned int *height) | ||||
| { | ||||
|   EGL_FilterBGRtoBGRA * this = UPCAST(EGL_FilterBGRtoBGRA, filter); | ||||
|   *width  = this->desktopWidth; | ||||
|   *height = this->desktopHeight; | ||||
| } | ||||
|  | ||||
| static bool egl_filterBGRtoBGRAPrepare(EGL_Filter * filter) | ||||
| { | ||||
|   EGL_FilterBGRtoBGRA * this = UPCAST(EGL_FilterBGRtoBGRA, filter); | ||||
|  | ||||
|   if (this->prepared) | ||||
|     return true; | ||||
|  | ||||
|   this->uOutputSize.f[0] = this->desktopWidth; | ||||
|   this->uOutputSize.f[1] = this->desktopHeight; | ||||
|   egl_shaderSetUniforms(this->shader, &this->uOutputSize, 1); | ||||
|  | ||||
|   this->prepared = true; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static EGL_Texture * egl_filterBGRtoBGRARun(EGL_Filter * filter, | ||||
|     EGL_FilterRects * rects, EGL_Texture * texture) | ||||
| { | ||||
|   EGL_FilterBGRtoBGRA * this = UPCAST(EGL_FilterBGRtoBGRA, filter); | ||||
|  | ||||
|   egl_framebufferBind(this->fb); | ||||
|  | ||||
|   glActiveTexture(GL_TEXTURE0); | ||||
|   egl_textureBind(texture); | ||||
|  | ||||
|   glBindSampler(0, this->sampler[0]); | ||||
|  | ||||
|   egl_shaderUse(this->shader); | ||||
|   egl_filterRectsRender(this->shader, rects); | ||||
|  | ||||
|   return egl_framebufferGetTexture(this->fb); | ||||
| } | ||||
|  | ||||
| EGL_FilterOps egl_filterBGRtoBGRAOps = | ||||
| { | ||||
|   .id           = "bgrtobgra", | ||||
|   .name         = "BGRtoBGRA", | ||||
|   .type         = EGL_FILTER_TYPE_INTERNAL, | ||||
|   .earlyInit    = NULL, | ||||
|   .init         = egl_filterBGRtoBGRAInit, | ||||
|   .free         = egl_filterBGRtoBGRAFree, | ||||
|   .imguiConfig  = NULL, | ||||
|   .saveState    = NULL, | ||||
|   .loadState    = NULL, | ||||
|   .setup        = egl_filterBGRtoBGRASetup, | ||||
|   .getOutputRes = egl_filterBGRtoBGRAGetOutputRes, | ||||
|   .prepare      = egl_filterBGRtoBGRAPrepare, | ||||
|   .run          = egl_filterBGRtoBGRARun | ||||
| }; | ||||
| @@ -299,6 +299,7 @@ static bool egl_filterDownscaleImguiConfig(EGL_Filter * filter) | ||||
|  | ||||
| static bool egl_filterDownscaleSetup(EGL_Filter * filter, | ||||
|     enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, | ||||
|     unsigned int desktopWidth, unsigned int desktopHeight, | ||||
|     bool useDMA) | ||||
| { | ||||
|   EGL_FilterDownscale * this = UPCAST(EGL_FilterDownscale, filter); | ||||
|   | ||||
| @@ -209,6 +209,7 @@ static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter) | ||||
|  | ||||
| static bool egl_filterFFXCASSetup(EGL_Filter * filter, | ||||
|     enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, | ||||
|     unsigned int desktopWidth, unsigned int desktopHeight, | ||||
|     bool useDMA) | ||||
| { | ||||
|   EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter); | ||||
|   | ||||
| @@ -323,6 +323,7 @@ static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter, | ||||
|  | ||||
| static bool egl_filterFFXFSR1Setup(EGL_Filter * filter, | ||||
|     enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height, | ||||
|     unsigned int desktopWidth, unsigned int desktopHeight, | ||||
|     bool useDMA) | ||||
| { | ||||
|   EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter); | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| extern EGL_FilterOps egl_filterBGRtoBGRAOps; | ||||
| extern EGL_FilterOps egl_filterDownscaleOps; | ||||
| extern EGL_FilterOps egl_filterFFXCASOps; | ||||
| extern EGL_FilterOps egl_filterFFXFSR1Ops; | ||||
|   | ||||
| @@ -64,7 +64,7 @@ void egl_framebufferFree(EGL_Framebuffer ** fb) | ||||
| bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt, | ||||
|     unsigned int width, unsigned int height) | ||||
| { | ||||
|   if (!egl_textureSetup(this->tex, pixFmt, width, height, 0)) | ||||
|   if (!egl_textureSetup(this->tex, pixFmt, width, height, 0, 0)) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to setup the texture"); | ||||
|     return false; | ||||
|   | ||||
| @@ -48,7 +48,7 @@ static const EGL_FilterOps * EGL_Filters[] = | ||||
|  | ||||
| struct EGL_PostProcess | ||||
| { | ||||
|   Vector filters; | ||||
|   Vector filters, internalFilters; | ||||
|   EGL_Texture * output; | ||||
|   unsigned int outputX, outputY; | ||||
|   _Atomic(bool) modified; | ||||
| @@ -85,7 +85,7 @@ void egl_postProcessEarlyInit(void) | ||||
|   option_register(options); | ||||
|  | ||||
|   for (int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i) | ||||
|     EGL_Filters[i]->earlyInit(); | ||||
|     egl_filterEarlyInit(EGL_Filters[i]); | ||||
| } | ||||
|  | ||||
| static void loadPreset(struct EGL_PostProcess * this, const char * name); | ||||
| @@ -464,7 +464,8 @@ static void configUI(void * opaque, int * id) | ||||
|   static size_t mouseIdx = -1; | ||||
|   static bool   moving   = false; | ||||
|   static size_t moveIdx  = 0; | ||||
|   bool doMove = false; | ||||
|  | ||||
|   bool   doMove = false; | ||||
|  | ||||
|   ImVec2 window, pos; | ||||
|   igGetWindowPos(&window); | ||||
| @@ -518,9 +519,16 @@ static void configUI(void * opaque, int * id) | ||||
|   { | ||||
|     EGL_Filter * tmp = filters[moveIdx]; | ||||
|     if (mouseIdx > moveIdx) // moving down | ||||
|       memmove(filters + moveIdx, filters + moveIdx + 1, (mouseIdx - moveIdx) * sizeof(EGL_Filter *)); | ||||
|       memmove( | ||||
|           filters + moveIdx, | ||||
|           filters + moveIdx + 1, | ||||
|           (mouseIdx - moveIdx) * sizeof(EGL_Filter *)); | ||||
|     else // moving up | ||||
|       memmove(filters + mouseIdx + 1, filters + mouseIdx, (moveIdx - mouseIdx) * sizeof(EGL_Filter *)); | ||||
|       memmove( | ||||
|           filters + mouseIdx + 1, | ||||
|           filters + mouseIdx, | ||||
|           (moveIdx - mouseIdx) * sizeof(EGL_Filter *)); | ||||
|  | ||||
|     filters[mouseIdx] = tmp; | ||||
|   } | ||||
|  | ||||
| @@ -540,16 +548,24 @@ bool egl_postProcessInit(EGL_PostProcess ** pp) | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (!vector_create(&this->filters, sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters))) | ||||
|   if (!vector_create(&this->filters, | ||||
|         sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters))) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to allocate the filter list"); | ||||
|     goto error_this; | ||||
|   } | ||||
|  | ||||
|   if (!vector_create(&this->internalFilters, | ||||
|         sizeof(EGL_Filter *), ARRAY_LENGTH(EGL_Filters))) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to allocate the filter list"); | ||||
|     goto error_filters; | ||||
|   } | ||||
|  | ||||
|   if (!egl_desktopRectsInit(&this->rects, 1)) | ||||
|   { | ||||
|     DEBUG_ERROR("Failed to initialize the desktop rects"); | ||||
|     goto error_filters; | ||||
|     goto error_internal; | ||||
|   } | ||||
|  | ||||
|   loadPresetList(this); | ||||
| @@ -559,6 +575,9 @@ bool egl_postProcessInit(EGL_PostProcess ** pp) | ||||
|   *pp = this; | ||||
|   return true; | ||||
|  | ||||
| error_internal: | ||||
|   vector_destroy(&this->internalFilters); | ||||
|  | ||||
| error_filters: | ||||
|   vector_destroy(&this->filters); | ||||
|  | ||||
| @@ -579,6 +598,10 @@ void egl_postProcessFree(EGL_PostProcess ** pp) | ||||
|     egl_filterFree(filter); | ||||
|   vector_destroy(&this->filters); | ||||
|  | ||||
|   vector_forEachRef(filter, &this->internalFilters) | ||||
|     egl_filterFree(filter); | ||||
|   vector_destroy(&this->internalFilters); | ||||
|  | ||||
|   free(this->presetDir); | ||||
|   if (this->presets) | ||||
|     stringlist_free(&this->presets); | ||||
| @@ -595,7 +618,10 @@ bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops) | ||||
|   if (!egl_filterInit(ops, &filter)) | ||||
|     return false; | ||||
|  | ||||
|   vector_push(&this->filters, &filter); | ||||
|   if (ops->type == EGL_FILTER_TYPE_INTERNAL) | ||||
|     vector_push(&this->internalFilters, &filter); | ||||
|   else | ||||
|     vector_push(&this->filters, &filter); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| @@ -638,25 +664,35 @@ bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex, | ||||
|  | ||||
|   EGL_Filter * filter; | ||||
|   EGL_Texture * texture = tex; | ||||
|   vector_forEach(filter, &this->filters) | ||||
|  | ||||
|   const Vector * lists[] = | ||||
|   { | ||||
|     egl_filterSetOutputResHint(filter, targetX, targetY); | ||||
|     &this->internalFilters, | ||||
|     &this->filters, | ||||
|     NULL | ||||
|   }; | ||||
|  | ||||
|     if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY, useDMA) || | ||||
|         !egl_filterPrepare(filter)) | ||||
|       continue; | ||||
|   for(const Vector ** filters = lists; *filters; ++filters) | ||||
|     vector_forEach(filter, *filters) | ||||
|     { | ||||
|       egl_filterSetOutputResHint(filter, targetX, targetY); | ||||
|  | ||||
|     texture = egl_filterRun(filter, &filterRects, texture); | ||||
|     egl_filterGetOutputRes(filter, &sizeX, &sizeY); | ||||
|       if (!egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY, | ||||
|             desktopWidth, desktopHeight, useDMA) || | ||||
|           !egl_filterPrepare(filter)) | ||||
|         continue; | ||||
|  | ||||
|     if (lastFilter) | ||||
|       egl_filterRelease(lastFilter); | ||||
|       texture = egl_filterRun(filter, &filterRects, texture); | ||||
|       egl_filterGetOutputRes(filter, &sizeX, &sizeY); | ||||
|  | ||||
|     lastFilter = filter; | ||||
|       if (lastFilter) | ||||
|         egl_filterRelease(lastFilter); | ||||
|  | ||||
|     // the first filter to run will convert to a normal texture | ||||
|     useDMA = false; | ||||
|   } | ||||
|       lastFilter = filter; | ||||
|  | ||||
|       // the first filter to run will convert to a normal texture | ||||
|       useDMA = false; | ||||
|     } | ||||
|  | ||||
|   this->output  = texture; | ||||
|   this->outputX = sizeX; | ||||
|   | ||||
							
								
								
									
										35
									
								
								client/renderers/EGL/shader/convert_bgr_bgra.frag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								client/renderers/EGL/shader/convert_bgr_bgra.frag
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| #version 300 es | ||||
| #extension GL_OES_EGL_image_external_essl3 : enable | ||||
|  | ||||
| precision highp float; | ||||
|  | ||||
| in  vec2  fragCoord; | ||||
| out vec4  fragColor; | ||||
|  | ||||
| uniform sampler2D sampler1; | ||||
| uniform vec2      outputSize; | ||||
|  | ||||
| void main() | ||||
| { | ||||
|   uvec2 inputSize   = uvec2(textureSize(sampler1, 0)); | ||||
|   uvec2 pos         = uvec2(fragCoord * outputSize); | ||||
|   uint  outputWidth = uint(outputSize.x); | ||||
|  | ||||
|   uint output_idx = pos.y * outputWidth + pos.x; | ||||
|  | ||||
|   uint fst = output_idx * 3u / 4u; | ||||
|   vec4 color_0 = texelFetch(sampler1, ivec2(fst % inputSize.x, fst / inputSize.x), 0); | ||||
|  | ||||
|   uint snd = (output_idx * 3u + 1u) / 4u; | ||||
|   vec4 color_1 = texelFetch(sampler1, ivec2(snd % inputSize.x, snd / inputSize.x), 0); | ||||
|  | ||||
|   uint trd = (output_idx * 3u + 2u) / 4u; | ||||
|   vec4 color_2 = texelFetch(sampler1, ivec2(trd % inputSize.x, trd / inputSize.x), 0); | ||||
|  | ||||
|   fragColor.bgra = vec4( | ||||
|     color_0.barg[output_idx % 4u], | ||||
|     color_1.gbar[output_idx % 4u], | ||||
|     color_2.rgba[output_idx % 4u], | ||||
|     1.0 | ||||
|   ); | ||||
| } | ||||
| @@ -94,14 +94,15 @@ void egl_textureFree(EGL_Texture ** tex) | ||||
| } | ||||
|  | ||||
| bool egl_textureSetup(EGL_Texture * this, enum EGL_PixelFormat pixFmt, | ||||
|     size_t width, size_t height, size_t stride) | ||||
|     size_t width, size_t height, size_t stride, size_t pitch) | ||||
| { | ||||
|   const struct EGL_TexSetup setup = | ||||
|   { | ||||
|     .pixFmt = pixFmt, | ||||
|     .width  = width, | ||||
|     .height = height, | ||||
|     .stride = stride | ||||
|     .pixFmt  = pixFmt, | ||||
|     .width   = width, | ||||
|     .height  = height, | ||||
|     .stride  = stride, | ||||
|     .pitch   = pitch, | ||||
|   }; | ||||
|  | ||||
|   if (!egl_texUtilGetFormat(&setup, &this->format)) | ||||
| @@ -129,7 +130,7 @@ bool egl_textureUpdate(EGL_Texture * this, const uint8_t * buffer, bool topDown) | ||||
| } | ||||
|  | ||||
| bool egl_textureUpdateRect(EGL_Texture * this, | ||||
|     int x, int y, int width, int height, int stride, | ||||
|     int x, int y, int width, int height, int stride, int pitch, | ||||
|     const uint8_t * buffer, bool topDown) | ||||
| { | ||||
|   x      = clamp(x     , 0, this->format.width     ); | ||||
| @@ -147,8 +148,8 @@ bool egl_textureUpdateRect(EGL_Texture * this, | ||||
|     .y       = y, | ||||
|     .width   = width, | ||||
|     .height  = height, | ||||
|     .pitch   = stride / this->format.bpp, | ||||
|     .stride  = stride, | ||||
|     .pitch   = pitch, | ||||
|     .topDown = topDown, | ||||
|     .buffer  = buffer | ||||
|   }; | ||||
| @@ -193,7 +194,7 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this, | ||||
|   }; | ||||
|  | ||||
|   /* wait for completion */ | ||||
|   framebuffer_wait(frame, this->format.bufferSize); | ||||
|   framebuffer_wait(frame, this->format.dataSize); | ||||
|  | ||||
|   return this->ops.update(this, &update); | ||||
| } | ||||
|   | ||||
| @@ -44,8 +44,8 @@ typedef struct EGL_TexUpdate | ||||
|  | ||||
|   int x, y, width, height; | ||||
|  | ||||
|   //pitch  = row length in pixels | ||||
|   //stride = row length in bytes | ||||
|   //pitch     = row length in pixels | ||||
|   //stride    = row length in bytes | ||||
|   int pitch, stride; | ||||
|  | ||||
|   union | ||||
| @@ -113,13 +113,13 @@ bool egl_textureInit(EGL_Texture ** texture, EGLDisplay * display, | ||||
| void egl_textureFree(EGL_Texture ** tex); | ||||
|  | ||||
| bool egl_textureSetup(EGL_Texture * texture, enum EGL_PixelFormat pixFmt, | ||||
|     size_t width, size_t height, size_t stride); | ||||
|     size_t width, size_t height, size_t stride, size_t pitch); | ||||
|  | ||||
| bool egl_textureUpdate(EGL_Texture * texture, const uint8_t * buffer, | ||||
|     bool topDown); | ||||
|  | ||||
| bool egl_textureUpdateRect(EGL_Texture * texture, | ||||
|     int x, int y, int width, int height, int stride, | ||||
|     int x, int y, int width, int height, int stride, int pitch, | ||||
|     const uint8_t * buffer, bool topDown); | ||||
|  | ||||
| bool egl_textureUpdateFromFrame(EGL_Texture * texture, | ||||
|   | ||||
| @@ -112,7 +112,7 @@ static bool egl_texBufferUpdate(EGL_Texture * texture, const EGL_TexUpdate * upd | ||||
|   DEBUG_ASSERT(update->type == EGL_TEXTYPE_BUFFER); | ||||
|  | ||||
|   glBindTexture(GL_TEXTURE_2D, this->tex[0]); | ||||
|   glPixelStorei(GL_UNPACK_ROW_LENGTH, update->pitch); | ||||
|   glPixelStorei(GL_UNPACK_ROW_LENGTH, update->stride); | ||||
|   glTexSubImage2D(GL_TEXTURE_2D, | ||||
|       0, | ||||
|       update->x, | ||||
| @@ -187,7 +187,7 @@ static bool egl_texBufferStreamUpdate(EGL_Texture * texture, | ||||
|   LG_LOCK(this->copyLock); | ||||
|  | ||||
|   uint8_t * dst = this->buf[this->bufIndex].map + | ||||
|     texture->format.stride * update->y + | ||||
|     texture->format.pitch * update->y + | ||||
|     update->x * texture->format.bpp; | ||||
|  | ||||
|   if (update->topDown) | ||||
| @@ -195,19 +195,19 @@ static bool egl_texBufferStreamUpdate(EGL_Texture * texture, | ||||
|     const uint8_t * src = update->buffer; | ||||
|     for(int y = 0; y < update->height; ++y) | ||||
|     { | ||||
|       memcpy(dst, src, update->stride); | ||||
|       dst += texture->format.stride; | ||||
|       src += update->stride; | ||||
|       memcpy(dst, src, update->pitch); | ||||
|       dst += texture->format.bufferPitch; | ||||
|       src += update->pitch; | ||||
|     } | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     const uint8_t * src = update->buffer + update->stride * update->height; | ||||
|     const uint8_t * src = update->buffer + update->pitch * update->height; | ||||
|     for(int y = 0; y < update->height; ++y) | ||||
|     { | ||||
|       src -= update->stride; | ||||
|       memcpy(dst, src, update->stride); | ||||
|       dst += texture->format.stride; | ||||
|       dst += texture->format.bufferPitch; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -241,7 +241,8 @@ EGL_TexStatus egl_texBufferStreamProcess(EGL_Texture * texture) | ||||
|  | ||||
|     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->pbo); | ||||
|     glBindTexture(GL_TEXTURE_2D, tex); | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->format.pitch); | ||||
|  | ||||
|     glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->format.width); | ||||
|     glTexSubImage2D(GL_TEXTURE_2D, | ||||
|         0, 0, 0, | ||||
|         texture->format.width, | ||||
|   | ||||
| @@ -162,12 +162,12 @@ static bool egl_texDMABUFUpdate(EGL_Texture * texture, | ||||
|     const uint64_t modifier = DRM_FORMAT_MOD_LINEAR; | ||||
|     EGLAttrib attribs[] = | ||||
|     { | ||||
|       EGL_WIDTH                         , texture->format.width, | ||||
|       EGL_WIDTH                         , texture->format.width , | ||||
|       EGL_HEIGHT                        , texture->format.height, | ||||
|       EGL_LINUX_DRM_FOURCC_EXT          , texture->format.fourcc, | ||||
|       EGL_DMA_BUF_PLANE0_FD_EXT         , update->dmaFD, | ||||
|       EGL_DMA_BUF_PLANE0_OFFSET_EXT     , 0, | ||||
|       EGL_DMA_BUF_PLANE0_PITCH_EXT      , texture->format.stride, | ||||
|       EGL_DMA_BUF_PLANE0_PITCH_EXT      , texture->format.bufferPitch, | ||||
|       EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, (modifier & 0xffffffff), | ||||
|       EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, (modifier >> 32), | ||||
|       EGL_NONE                          , EGL_NONE | ||||
|   | ||||
| @@ -92,17 +92,20 @@ static bool egl_texFBUpdate(EGL_Texture * texture, const EGL_TexUpdate * update) | ||||
|     damage->count + update->rectCount > KVMFR_MAX_DAMAGE_RECTS; | ||||
|  | ||||
|   if (damageAll) | ||||
|     framebuffer_read( | ||||
|   { | ||||
|      framebuffer_read( | ||||
|       update->frame, | ||||
|       parent->buf[parent->bufIndex].map, | ||||
|       texture->format.stride, | ||||
|       texture->format.pitch, | ||||
|       texture->format.height, | ||||
|       texture->format.width, | ||||
|       texture->format.bpp, | ||||
|       texture->format.stride | ||||
|       texture->format.pitch | ||||
|     ); | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     //FIXME! This is broken for BGR24 | ||||
|     memcpy(damage->rects + damage->count, update->rects, | ||||
|       update->rectCount * sizeof(FrameDamageRect)); | ||||
|     damage->count += update->rectCount; | ||||
| @@ -111,10 +114,10 @@ static bool egl_texFBUpdate(EGL_Texture * texture, const EGL_TexUpdate * update) | ||||
|       damage->count, | ||||
|       texture->format.bpp, | ||||
|       parent->buf[parent->bufIndex].map, | ||||
|       texture->format.stride, | ||||
|       texture->format.pitch, | ||||
|       texture->format.height, | ||||
|       update->frame, | ||||
|       texture->format.stride | ||||
|       texture->format.pitch | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -32,6 +32,11 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt) | ||||
| { | ||||
|   switch(setup->pixFmt) | ||||
|   { | ||||
|     //EGL has no support for 24-bit formats, so we stuff it into a 32-bit | ||||
|     //texture to unpack with a shader later | ||||
|     case EGL_PF_BGR: | ||||
|       // fallthrough | ||||
|  | ||||
|     case EGL_PF_BGRA: | ||||
|       fmt->bpp        = 4; | ||||
|       fmt->format     = GL_BGRA_EXT; | ||||
| @@ -53,7 +58,7 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt) | ||||
|       fmt->format     = GL_RGBA; | ||||
|       fmt->intFormat  = GL_RGB10_A2; | ||||
|       fmt->dataType   = GL_UNSIGNED_INT_2_10_10_10_REV; | ||||
|       fmt->fourcc     = DRM_FORMAT_BGRA2101010; | ||||
|       fmt->fourcc     = DRM_FORMAT_ABGR2101010; | ||||
|       break; | ||||
|  | ||||
|     case EGL_PF_RGBA16F: | ||||
| @@ -69,22 +74,28 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt) | ||||
|       return false; | ||||
|   } | ||||
|  | ||||
|   fmt->pixFmt = setup->pixFmt; | ||||
|   fmt->width  = setup->width; | ||||
|   fmt->height = setup->height; | ||||
|   fmt->pixFmt      = setup->pixFmt; | ||||
|   fmt->width       = setup->width; | ||||
|   fmt->height      = setup->height; | ||||
|   fmt->stride      = setup->stride; | ||||
|   fmt->pitch       = setup->pitch; | ||||
|  | ||||
|   if (setup->stride == 0) | ||||
|   { | ||||
|     fmt->stride = fmt->width * fmt->bpp; | ||||
|     fmt->pitch  = fmt->width; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     fmt->stride = setup->stride; | ||||
|     fmt->pitch  = setup->stride / fmt->bpp; | ||||
|   } | ||||
|   if (!fmt->stride) | ||||
|     fmt->stride = setup->width; | ||||
|  | ||||
|   if (!fmt->pitch) | ||||
|     fmt->pitch = fmt->stride * fmt->bpp; | ||||
|  | ||||
|   fmt->bufferPitch = setup->pitch; | ||||
|  | ||||
|   // adjust the stride for 24-bit in 32-bit buffers | ||||
|   // this is needed to keep values sane for DMABUF support | ||||
| //  if (setup->pixFmt == EGL_PF_BGR) | ||||
| //    fmt->bufferPitch = setup->width * 4; | ||||
|  | ||||
|   fmt->dataSize   = fmt->height * fmt->pitch; | ||||
|   fmt->bufferSize = fmt->height * fmt->bufferPitch; | ||||
|  | ||||
|   fmt->bufferSize = fmt->height * fmt->stride; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -33,10 +33,14 @@ typedef struct EGL_TexFormat | ||||
|   GLenum       intFormat; | ||||
|   GLenum       dataType; | ||||
|   unsigned int fourcc; | ||||
|   size_t       bufferSize; | ||||
|  | ||||
|   size_t       dataSize; | ||||
|   size_t       width , height; | ||||
|   size_t       stride, pitch; | ||||
|  | ||||
|   // for 24-bit BGR these are the physical adjusted values to get mapping working | ||||
|   size_t       bufferSize; | ||||
|   size_t       bufferPitch; | ||||
| } | ||||
| EGL_TexFormat; | ||||
|  | ||||
| @@ -62,9 +66,10 @@ void egl_texUtilUnmapBuffer(EGL_TexBuffer * buffer); | ||||
|  */ | ||||
| #define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ | ||||
|          ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) | ||||
|  | ||||
| #define DRM_FORMAT_ARGB8888      fourcc_code('A', 'R', '2', '4') | ||||
| #define DRM_FORMAT_ABGR8888      fourcc_code('A', 'B', '2', '4') | ||||
| #define DRM_FORMAT_BGRA2101010   fourcc_code('A', 'B', '3', '0') | ||||
| #define DRM_FORMAT_ABGR2101010   fourcc_code('A', 'B', '3', '0') | ||||
| #define DRM_FORMAT_ABGR16161616F fourcc_code('A', 'B', '4', 'H') | ||||
|  | ||||
| #define DRM_FORMAT_MOD_VENDOR_NONE 0 | ||||
|   | ||||
| @@ -777,13 +777,29 @@ static enum ConfigStatus configure(struct Inst * this) | ||||
|       this->dataFormat = GL_HALF_FLOAT; | ||||
|       break; | ||||
|  | ||||
|     case FRAME_TYPE_BGR: | ||||
|       this->intFormat  = GL_RGB8; | ||||
|       this->vboFormat  = GL_BGR; | ||||
|       this->dataFormat = GL_UNSIGNED_BYTE; | ||||
|  | ||||
|       /* The data that the host returns for 24-bit BGR is tightly packed into an | ||||
|        * array that the host GPU will support, we need to adjust the parameters | ||||
|        * here to the correct dimensions as OpenGL can use it directly | ||||
|        */ | ||||
|       this->format.dataWidth  = this->format.frameWidth; | ||||
|       this->format.dataHeight = this->format.frameHeight; | ||||
|       this->format.stride     = this->format.frameWidth; | ||||
|       this->format.pitch      = this->format.frameWidth * 3; | ||||
|       this->format.bpp        = 24; | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       DEBUG_ERROR("Unknown/unsupported compression type"); | ||||
|       return CONFIG_STATUS_ERROR; | ||||
|   } | ||||
|  | ||||
|   // calculate the texture size in bytes | ||||
|   this->texSize = this->format.frameHeight * this->format.pitch; | ||||
|   this->texSize = this->format.dataHeight * this->format.pitch; | ||||
|   this->texPos  = 0; | ||||
|  | ||||
|   g_gl_dynProcs.glGenBuffers(BUFFER_COUNT, this->vboID); | ||||
| @@ -797,10 +813,10 @@ static enum ConfigStatus configure(struct Inst * this) | ||||
|   if (this->amdPinnedMemSupport) | ||||
|   { | ||||
|     const int pagesize = getpagesize(); | ||||
|  | ||||
|     for(int i = 0; i < BUFFER_COUNT; ++i) | ||||
|     { | ||||
|       this->texPixels[i] = aligned_alloc(pagesize, this->texSize); | ||||
|       this->texPixels[i] = aligned_alloc(pagesize, | ||||
|           ALIGN_TO(this->texSize, pagesize)); | ||||
|       if (!this->texPixels[i]) | ||||
|       { | ||||
|         DEBUG_ERROR("Failed to allocate memory for texture"); | ||||
| @@ -809,7 +825,8 @@ static enum ConfigStatus configure(struct Inst * this) | ||||
|  | ||||
|       memset(this->texPixels[i], 0, this->texSize); | ||||
|  | ||||
|       g_gl_dynProcs.glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]); | ||||
|       g_gl_dynProcs.glBindBuffer( | ||||
|           GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, this->vboID[i]); | ||||
|       if (check_gl_error("glBindBuffer")) | ||||
|       { | ||||
|         LG_UNLOCK(this->formatLock); | ||||
| @@ -1162,16 +1179,15 @@ static bool drawFrame(struct Inst * this) | ||||
|   glBindTexture(GL_TEXTURE_2D, this->frames[this->texWIndex]); | ||||
|   g_gl_dynProcs.glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texWIndex]); | ||||
|  | ||||
|   const int bpp = this->format.bpp / 8; | ||||
|   glPixelStorei(GL_UNPACK_ALIGNMENT , bpp); | ||||
|   glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.frameWidth); | ||||
|   int bpp = this->format.bpp / 8; | ||||
|   glPixelStorei(GL_UNPACK_ALIGNMENT , bpp < 4 ? 1 : 0); | ||||
|   glPixelStorei(GL_UNPACK_ROW_LENGTH, this->format.stride); | ||||
|  | ||||
|   this->texPos = 0; | ||||
|  | ||||
|   framebuffer_read_fn( | ||||
|     this->frame, | ||||
|     this->format.frameHeight, | ||||
|     this->format.frameWidth, | ||||
|     this->format.dataHeight, | ||||
|     this->format.dataWidth, | ||||
|     bpp, | ||||
|     this->format.pitch, | ||||
|     opengl_bufferFn, | ||||
| @@ -1194,9 +1210,17 @@ static bool drawFrame(struct Inst * this) | ||||
|   ); | ||||
|   if (check_gl_error("glTexSubImage2D")) | ||||
|   { | ||||
|     DEBUG_ERROR("texWIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu", | ||||
|       this->texWIndex, this->format.frameWidth, this->format.frameHeight, | ||||
|       this->vboFormat, this->texSize | ||||
|     DEBUG_ERROR( | ||||
|       "texWIndex: %u, " | ||||
|       "width: %u, " | ||||
|       "height: %u, " | ||||
|       "vboFormat: %x, " | ||||
|       "texSize: %lu", | ||||
|       this->texWIndex, | ||||
|       this->format.frameWidth, | ||||
|       this->format.frameHeight, | ||||
|       this->vboFormat, | ||||
|       this->texSize | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -625,6 +625,8 @@ int main_frameThread(void * unused) | ||||
|       lgrFormat.type         = frame->type; | ||||
|       lgrFormat.screenWidth  = frame->screenWidth; | ||||
|       lgrFormat.screenHeight = frame->screenHeight; | ||||
|       lgrFormat.dataWidth    = frame->dataWidth; | ||||
|       lgrFormat.dataHeight   = frame->dataHeight; | ||||
|       lgrFormat.frameWidth   = frame->frameWidth; | ||||
|       lgrFormat.frameHeight  = frame->frameHeight; | ||||
|       lgrFormat.stride       = frame->stride; | ||||
| @@ -664,21 +666,25 @@ int main_frameThread(void * unused) | ||||
|       } | ||||
|       g_state.rotate = lgrFormat.rotate; | ||||
|  | ||||
|       dataSize = lgrFormat.dataHeight * lgrFormat.pitch; | ||||
|  | ||||
|       bool error = false; | ||||
|       switch(frame->type) | ||||
|       { | ||||
|         case FRAME_TYPE_RGBA: | ||||
|         case FRAME_TYPE_BGRA: | ||||
|         case FRAME_TYPE_RGBA10: | ||||
|           dataSize       = lgrFormat.frameHeight * lgrFormat.pitch; | ||||
|           lgrFormat.bpp  = 32; | ||||
|           break; | ||||
|  | ||||
|         case FRAME_TYPE_RGBA16F: | ||||
|           dataSize       = lgrFormat.frameHeight * lgrFormat.pitch; | ||||
|           lgrFormat.bpp  = 64; | ||||
|           break; | ||||
|  | ||||
|         case FRAME_TYPE_BGR: | ||||
|           lgrFormat.bpp  = 24; | ||||
|           break; | ||||
|  | ||||
|         default: | ||||
|           DEBUG_ERROR("Unsupported frameType"); | ||||
|           error = true; | ||||
| @@ -695,9 +701,10 @@ int main_frameThread(void * unused) | ||||
|       g_state.formatValid = true; | ||||
|       formatVer = frame->formatVer; | ||||
|  | ||||
|       DEBUG_INFO("Format: %s %ux%u stride:%u pitch:%u rotation:%d hdr:%d pq:%d", | ||||
|       DEBUG_INFO("Format: %s %ux%u (%ux%u) stride:%u pitch:%u rotation:%d hdr:%d pq:%d", | ||||
|           FrameTypeStr[frame->type], | ||||
|           frame->frameWidth, frame->frameHeight, | ||||
|           frame->dataWidth , frame->dataHeight , | ||||
|           frame->stride, frame->pitch, | ||||
|           frame->rotation, | ||||
|           frame->flags & FRAME_FLAG_HDR    ? 1 : 0, | ||||
|   | ||||
| @@ -149,10 +149,10 @@ typedef struct KVMFRFrame | ||||
|   FrameType       type;               // the frame data type | ||||
|   uint32_t        screenWidth;        // the client's screen width | ||||
|   uint32_t        screenHeight;       // the client's screen height | ||||
|   uint32_t        dataWidth;          // the packed width of the frame data | ||||
|   uint32_t        dataHeight;         // the packed height of the frame data | ||||
|   uint32_t        frameWidth;         // the frame width | ||||
|   uint32_t        frameHeight;        // the frame height | ||||
|   uint32_t        dataWidth;          // the packed frame width | ||||
|   uint32_t        dataHeight;         // the packed frame height | ||||
|   uint32_t        frameWidth;         // the unpacked frame width | ||||
|   uint32_t        frameHeight;        // the unpacked frame height | ||||
|   FrameRotation   rotation;           // the frame rotation | ||||
|   uint32_t        stride;             // the row stride (zero if compressed data) | ||||
|   uint32_t        pitch;              // the row pitch  (stride in bytes or the compressed frame size) | ||||
|   | ||||
| @@ -44,6 +44,12 @@ typedef bool (*FrameBufferReadFn)(void * opaque, const void * src, size_t size); | ||||
|  */ | ||||
| bool framebuffer_wait(const FrameBuffer * frame, size_t size); | ||||
|  | ||||
| /** | ||||
|  * Read `size` bytes from the KVMFRFrame into the dst buffer | ||||
|  */ | ||||
| bool framebuffer_read_linear(const FrameBuffer * frame, void * restrict dst, | ||||
|     size_t size); | ||||
|  | ||||
| /** | ||||
|  * Read data from the KVMFRFrame into the dst buffer | ||||
|  */ | ||||
|   | ||||
| @@ -28,23 +28,23 @@ | ||||
| #include "common/types.h" | ||||
|  | ||||
| inline static void rectCopyUnaligned(uint8_t * dest, const uint8_t * src, | ||||
|     int ystart, int yend, int dx, int dstStride, int srcStride, int width) | ||||
|     int ystart, int yend, int dx, int dstPitch, int srcPitch, int width) | ||||
| { | ||||
|   for (int i = ystart; i < yend; ++i) | ||||
|   { | ||||
|     unsigned int srcOffset = i * srcStride + dx; | ||||
|     unsigned int dstOffset = i * dstStride + dx; | ||||
|     unsigned int srcOffset = i * srcPitch + dx; | ||||
|     unsigned int dstOffset = i * dstPitch + dx; | ||||
|     memcpy(dest + dstOffset, src + srcOffset, width); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void rectsBufferToFramebuffer(FrameDamageRect * rects, int count, int bpp, | ||||
|   FrameBuffer * frame, int dstStride, int height, | ||||
|   const uint8_t * src, int srcStride); | ||||
|   FrameBuffer * frame, int dstPitch, int height, | ||||
|   const uint8_t * src, int srcPitch); | ||||
|  | ||||
| void rectsFramebufferToBuffer(FrameDamageRect * rects, int count, int bpp, | ||||
|   uint8_t * dst, int dstStride, int height, | ||||
|   const FrameBuffer * frame, int srcStride); | ||||
|   uint8_t * dst, int dstPitch, int height, | ||||
|   const FrameBuffer * frame, int srcPitch); | ||||
|  | ||||
| int rectsMergeOverlapping(FrameDamageRect * rects, int count); | ||||
| int rectsRejectContained(FrameDamageRect * rects, int count); | ||||
|   | ||||
| @@ -55,7 +55,7 @@ typedef enum FrameType | ||||
|   FRAME_TYPE_RGBA      , // RGBA interleaved: R,G,B,A 32bpp | ||||
|   FRAME_TYPE_RGBA10    , // RGBA interleaved: R,G,B,A 10,10,10,2 bpp | ||||
|   FRAME_TYPE_RGBA16F   , // RGBA interleaved: R,G,B,A 16,16,16,16 bpp float | ||||
|   FRAME_TYPE_BGR       , // BGR  interleaved: B,G,R 24bpp | ||||
|   FRAME_TYPE_BGR       , // BGR (DO NOT COMMIT THIS) | ||||
|   FRAME_TYPE_MAX       , // sentinel value | ||||
| } | ||||
| FrameType; | ||||
|   | ||||
| @@ -38,4 +38,6 @@ | ||||
| #define UPCAST(type, x) \ | ||||
|   (type *)((uintptr_t)(x) - offsetof(type, base)) | ||||
|  | ||||
| #define ALIGN_TO(value, align) (((value) + (align) - 1) & -(align)) | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -47,8 +47,8 @@ bool framebuffer_wait(const FrameBuffer * frame, size_t size) | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool framebuffer_read(const FrameBuffer * frame, void * restrict dst, | ||||
|     size_t dstpitch, size_t height, size_t width, size_t bpp, size_t pitch) | ||||
| bool framebuffer_read_linear(const FrameBuffer * frame, void * restrict dst, | ||||
|     size_t size) | ||||
| { | ||||
| #ifdef FB_PROFILE | ||||
|   static RunningAvg ra = NULL; | ||||
| @@ -62,34 +62,54 @@ bool framebuffer_read(const FrameBuffer * frame, void * restrict dst, | ||||
|   uint_least32_t rp        = 0; | ||||
|  | ||||
|   // copy in large 1MB chunks if the pitches match | ||||
|   if (dstpitch == pitch) | ||||
|   while(size) | ||||
|   { | ||||
|     size_t remaining = height * pitch; | ||||
|     while(remaining) | ||||
|     { | ||||
|       const size_t copy = remaining < FB_CHUNK_SIZE ? remaining : FB_CHUNK_SIZE; | ||||
|       if (!framebuffer_wait(frame, rp + copy)) | ||||
|         return false; | ||||
|     const size_t copy = size < FB_CHUNK_SIZE ? size : FB_CHUNK_SIZE; | ||||
|     if (!framebuffer_wait(frame, rp + copy)) | ||||
|       return false; | ||||
|  | ||||
|       memcpy(d, frame->data + rp, copy); | ||||
|       remaining -= copy; | ||||
|       rp        += copy; | ||||
|       d         += copy; | ||||
|     } | ||||
|     memcpy(d, frame->data + rp, copy); | ||||
|     size -= copy; | ||||
|     rp   += copy; | ||||
|     d    += copy; | ||||
|   } | ||||
|   else | ||||
|   { | ||||
|     // copy per line to match the pitch of the destination buffer | ||||
|     const size_t linewidth = width * bpp; | ||||
|     for(size_t y = 0; y < height; ++y) | ||||
|     { | ||||
|       if (!framebuffer_wait(frame, rp + linewidth)) | ||||
|         return false; | ||||
|  | ||||
|       memcpy(d, frame->data + rp, dstpitch); | ||||
|       rp += pitch; | ||||
|       d  += dstpitch; | ||||
|     } | ||||
| #ifdef FB_PROFILE | ||||
|   runningavg_push(ra, microtime() - ts); | ||||
|   if (++raCount % 100 == 0) | ||||
|     DEBUG_INFO("Average Copy Time: %.2fμs", runningavg_calc(ra)); | ||||
| #endif | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool framebuffer_read(const FrameBuffer * frame, void * restrict dst, | ||||
|     size_t dstpitch, size_t height, size_t width, size_t bpp, size_t pitch) | ||||
| { | ||||
|   if (dstpitch == pitch) | ||||
|     return framebuffer_read_linear(frame, dst, height * pitch); | ||||
|  | ||||
| #ifdef FB_PROFILE | ||||
|   static RunningAvg ra = NULL; | ||||
|   static int raCount = 0; | ||||
|   const uint64_t ts = microtime(); | ||||
|   if (!ra) | ||||
|     ra = runningavg_new(100); | ||||
| #endif | ||||
|  | ||||
|   uint8_t * restrict d     = (uint8_t*)dst; | ||||
|   uint_least32_t rp        = 0; | ||||
|  | ||||
|   // copy per line to match the pitch of the destination buffer | ||||
|   const size_t linewidth = width * bpp; | ||||
|   for(size_t y = 0; y < height; ++y) | ||||
|   { | ||||
|     if (!framebuffer_wait(frame, rp + linewidth)) | ||||
|       return false; | ||||
|  | ||||
|     memcpy(d, frame->data + rp, dstpitch); | ||||
|     rp += pitch; | ||||
|     d  += dstpitch; | ||||
|   } | ||||
|  | ||||
| #ifdef FB_PROFILE | ||||
|   | ||||
| @@ -194,44 +194,44 @@ inline static void rectsBufferCopy(FrameDamageRect * rects, int count, int bpp, | ||||
| struct ToFramebufferData | ||||
| { | ||||
|   FrameBuffer * frame; | ||||
|   int stride; | ||||
|   int pitch; | ||||
| }; | ||||
|  | ||||
| static void fbRowFinish(int y, void * opaque) | ||||
| { | ||||
|   struct ToFramebufferData * data = opaque; | ||||
|   framebuffer_set_write_ptr(data->frame, y * data->stride); | ||||
|   framebuffer_set_write_ptr(data->frame, y * data->pitch); | ||||
| } | ||||
|  | ||||
| void rectsBufferToFramebuffer(FrameDamageRect * rects, int count, int bpp, | ||||
|   FrameBuffer * frame, int dstStride, int height, | ||||
|   const uint8_t * src, int srcStride) | ||||
|   FrameBuffer * frame, int dstPitch, int height, | ||||
|   const uint8_t * src, int srcPitch) | ||||
| { | ||||
|   struct ToFramebufferData data = { .frame = frame, .stride = dstStride }; | ||||
|   rectsBufferCopy(rects, count, bpp, framebuffer_get_data(frame), dstStride, | ||||
|     height, src, srcStride, &data, NULL, fbRowFinish); | ||||
|   framebuffer_set_write_ptr(frame, height * dstStride); | ||||
|   struct ToFramebufferData data = { .frame = frame, .pitch = dstPitch }; | ||||
|   rectsBufferCopy(rects, count, bpp, framebuffer_get_data(frame), dstPitch, | ||||
|     height, src, srcPitch, &data, NULL, fbRowFinish); | ||||
|   framebuffer_set_write_ptr(frame, height * dstPitch); | ||||
| } | ||||
|  | ||||
| struct FromFramebufferData | ||||
| { | ||||
|   const FrameBuffer * frame; | ||||
|   int stride; | ||||
|   int pitch; | ||||
| }; | ||||
|  | ||||
| static void fbRowStart(int y, void * opaque) | ||||
| { | ||||
|   struct FromFramebufferData * data = opaque; | ||||
|   framebuffer_wait(data->frame, y * data->stride); | ||||
|   framebuffer_wait(data->frame, y * data->pitch); | ||||
| } | ||||
|  | ||||
| void rectsFramebufferToBuffer(FrameDamageRect * rects, int count, int bpp, | ||||
|   uint8_t * dst, int dstStride, int height, | ||||
|   const FrameBuffer * frame, int srcStride) | ||||
|   uint8_t * dst, int dstPitch, int height, | ||||
|   const FrameBuffer * frame, int srcPitch) | ||||
| { | ||||
|   struct FromFramebufferData data = { .frame = frame, .stride = srcStride }; | ||||
|   rectsBufferCopy(rects, count, bpp, dst, dstStride, height, | ||||
|     framebuffer_get_buffer(frame), srcStride, &data, fbRowStart, NULL); | ||||
|   struct FromFramebufferData data = { .frame = frame, .pitch = srcPitch }; | ||||
|   rectsBufferCopy(rects, count, bpp, dst, dstPitch, height, | ||||
|     framebuffer_get_buffer(frame), srcPitch, &data, fbRowStart, NULL); | ||||
| } | ||||
|  | ||||
| int rectsMergeOverlapping(FrameDamageRect * rects, int count) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Geoffrey McRae
					Geoffrey McRae