[client] implement support for RGB24 packed data

This commit is contained in:
Geoffrey McRae 2023-11-03 07:03:32 +11:00
parent 578d98fd22
commit c665044bfa
30 changed files with 548 additions and 151 deletions

View file

@ -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)

View file

@ -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

View file

@ -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;

View file

@ -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);
}

View file

@ -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

View file

@ -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,

View 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
};

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View 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
);
}

View file

@ -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);
}

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -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
);
}

View file

@ -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;
}

View file

@ -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

View file

@ -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
);
}

View file

@ -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,

View file

@ -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)

View file

@ -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
*/

View file

@ -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);

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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)