diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index 06e675bd..bdef79a3 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -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) diff --git a/client/renderers/EGL/CMakeLists.txt b/client/renderers/EGL/CMakeLists.txt index f6fc9081..56df4c46 100644 --- a/client/renderers/EGL/CMakeLists.txt +++ b/client/renderers/EGL/CMakeLists.txt @@ -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 diff --git a/client/renderers/EGL/cursor.c b/client/renderers/EGL/cursor.c index 07036551..90bbda39 100644 --- a/client/renderers/EGL/cursor.c +++ b/client/renderers/EGL/cursor.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; diff --git a/client/renderers/EGL/desktop.c b/client/renderers/EGL/desktop.c index d69ea4d0..147af0b5 100644 --- a/client/renderers/EGL/desktop.c +++ b/client/renderers/EGL/desktop.c @@ -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); } diff --git a/client/renderers/EGL/egltypes.h b/client/renderers/EGL/egltypes.h index 5b9c8c06..da12a645 100644 --- a/client/renderers/EGL/egltypes.h +++ b/client/renderers/EGL/egltypes.h @@ -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 diff --git a/client/renderers/EGL/filter.h b/client/renderers/EGL/filter.h index 2dced90e..f46edb47 100644 --- a/client/renderers/EGL/filter.h +++ b/client/renderers/EGL/filter.h @@ -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, diff --git a/client/renderers/EGL/filter_bgr_bgra.c b/client/renderers/EGL/filter_bgr_bgra.c new file mode 100644 index 00000000..8acaadc8 --- /dev/null +++ b/client/renderers/EGL/filter_bgr_bgra.c @@ -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 + +#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 +}; diff --git a/client/renderers/EGL/filter_downscale.c b/client/renderers/EGL/filter_downscale.c index f7e915f7..10def0ac 100644 --- a/client/renderers/EGL/filter_downscale.c +++ b/client/renderers/EGL/filter_downscale.c @@ -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); diff --git a/client/renderers/EGL/filter_ffx_cas.c b/client/renderers/EGL/filter_ffx_cas.c index 72817890..29d1a511 100644 --- a/client/renderers/EGL/filter_ffx_cas.c +++ b/client/renderers/EGL/filter_ffx_cas.c @@ -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); diff --git a/client/renderers/EGL/filter_ffx_fsr1.c b/client/renderers/EGL/filter_ffx_fsr1.c index 6b02333d..bd5bc953 100644 --- a/client/renderers/EGL/filter_ffx_fsr1.c +++ b/client/renderers/EGL/filter_ffx_fsr1.c @@ -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); diff --git a/client/renderers/EGL/filters.h b/client/renderers/EGL/filters.h index 659eda33..f662aa4c 100644 --- a/client/renderers/EGL/filters.h +++ b/client/renderers/EGL/filters.h @@ -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; diff --git a/client/renderers/EGL/framebuffer.c b/client/renderers/EGL/framebuffer.c index 092f1707..63df950d 100644 --- a/client/renderers/EGL/framebuffer.c +++ b/client/renderers/EGL/framebuffer.c @@ -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; diff --git a/client/renderers/EGL/postprocess.c b/client/renderers/EGL/postprocess.c index 26800db7..5485ab6c 100644 --- a/client/renderers/EGL/postprocess.c +++ b/client/renderers/EGL/postprocess.c @@ -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; diff --git a/client/renderers/EGL/shader/convert_bgr_bgra.frag b/client/renderers/EGL/shader/convert_bgr_bgra.frag new file mode 100644 index 00000000..060545e9 --- /dev/null +++ b/client/renderers/EGL/shader/convert_bgr_bgra.frag @@ -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 + ); +} diff --git a/client/renderers/EGL/texture.c b/client/renderers/EGL/texture.c index 017314cc..b07ac0dd 100644 --- a/client/renderers/EGL/texture.c +++ b/client/renderers/EGL/texture.c @@ -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); } diff --git a/client/renderers/EGL/texture.h b/client/renderers/EGL/texture.h index c32edfc3..37901642 100644 --- a/client/renderers/EGL/texture.h +++ b/client/renderers/EGL/texture.h @@ -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, diff --git a/client/renderers/EGL/texture_buffer.c b/client/renderers/EGL/texture_buffer.c index cf908694..f3340818 100644 --- a/client/renderers/EGL/texture_buffer.c +++ b/client/renderers/EGL/texture_buffer.c @@ -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, diff --git a/client/renderers/EGL/texture_dmabuf.c b/client/renderers/EGL/texture_dmabuf.c index 6b8e6d6f..96927504 100644 --- a/client/renderers/EGL/texture_dmabuf.c +++ b/client/renderers/EGL/texture_dmabuf.c @@ -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 diff --git a/client/renderers/EGL/texture_framebuffer.c b/client/renderers/EGL/texture_framebuffer.c index 8838c93b..983b1fc0 100644 --- a/client/renderers/EGL/texture_framebuffer.c +++ b/client/renderers/EGL/texture_framebuffer.c @@ -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 ); } diff --git a/client/renderers/EGL/texture_util.c b/client/renderers/EGL/texture_util.c index a0d398bb..9e30da59 100644 --- a/client/renderers/EGL/texture_util.c +++ b/client/renderers/EGL/texture_util.c @@ -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; } diff --git a/client/renderers/EGL/texture_util.h b/client/renderers/EGL/texture_util.h index e08bc86f..0b4cb0a5 100644 --- a/client/renderers/EGL/texture_util.h +++ b/client/renderers/EGL/texture_util.h @@ -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 diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index 35d0463f..a715c390 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -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 ); } diff --git a/client/src/main.c b/client/src/main.c index f99af601..fc30e521 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -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, diff --git a/common/include/common/KVMFR.h b/common/include/common/KVMFR.h index abfb32f6..41547729 100644 --- a/common/include/common/KVMFR.h +++ b/common/include/common/KVMFR.h @@ -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) diff --git a/common/include/common/framebuffer.h b/common/include/common/framebuffer.h index 51afa6ba..5a6714a1 100644 --- a/common/include/common/framebuffer.h +++ b/common/include/common/framebuffer.h @@ -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 */ diff --git a/common/include/common/rects.h b/common/include/common/rects.h index 0a325cfe..9d2dbeed 100644 --- a/common/include/common/rects.h +++ b/common/include/common/rects.h @@ -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); diff --git a/common/include/common/types.h b/common/include/common/types.h index afdd61d9..054cb4c8 100644 --- a/common/include/common/types.h +++ b/common/include/common/types.h @@ -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; diff --git a/common/include/common/util.h b/common/include/common/util.h index cd255eb2..193d228c 100644 --- a/common/include/common/util.h +++ b/common/include/common/util.h @@ -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 diff --git a/common/src/framebuffer.c b/common/src/framebuffer.c index 62c3916d..c743d5cf 100644 --- a/common/src/framebuffer.c +++ b/common/src/framebuffer.c @@ -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 diff --git a/common/src/rects.c b/common/src/rects.c index f2ad7836..eaa645ae 100644 --- a/common/src/rects.c +++ b/common/src/rects.c @@ -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)