[client] egl: cleanup texture filtering/post-processing

This commit is contained in:
Geoffrey McRae 2021-08-11 18:53:36 +10:00
parent f78154d282
commit f0ea882165
23 changed files with 1289 additions and 612 deletions

View File

@ -86,7 +86,11 @@ add_library(renderer_EGL STATIC
draw.c
splash.c
damage.c
framebuffer.c
postprocess.c
ffx.c
filter_ffx_cas.c
filter_ffx_fsr1.c
${EGL_SHADER_OBJS}
"${CMAKE_CURRENT_BINARY_DIR}/shader/desktop_rgb.def.h"
${PROJECT_TOP}/repos/cimgui/imgui/backends/imgui_impl_opengl3.cpp

View File

@ -80,11 +80,11 @@ struct EGL_Cursor
struct EGL_Model * model;
};
static bool cursorTexInit(EGL * egl, struct CursorTex * t,
static bool cursorTexInit(struct CursorTex * t,
const char * vertex_code , size_t vertex_size,
const char * fragment_code, size_t fragment_size)
{
if (!egl_textureInit(egl, &t->texture, NULL, EGL_TEXTYPE_BUFFER, false))
if (!egl_textureInit(&t->texture, NULL, EGL_TEXTYPE_BUFFER, false))
{
DEBUG_ERROR("Failed to initialize the cursor texture");
return false;
@ -133,7 +133,7 @@ static void cursorTexFree(struct CursorTex * t)
egl_shaderFree (&t->shader );
};
bool egl_cursorInit(EGL * egl, EGL_Cursor ** cursor)
bool egl_cursorInit(EGL_Cursor ** cursor)
{
*cursor = (EGL_Cursor *)malloc(sizeof(EGL_Cursor));
if (!*cursor)
@ -145,12 +145,12 @@ bool egl_cursorInit(EGL * egl, EGL_Cursor ** cursor)
memset(*cursor, 0, sizeof(EGL_Cursor));
LG_LOCK_INIT((*cursor)->lock);
if (!cursorTexInit(egl, &(*cursor)->norm,
if (!cursorTexInit(&(*cursor)->norm,
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_rgb_frag, b_shader_cursor_rgb_frag_size))
return false;
if (!cursorTexInit(egl, &(*cursor)->mono,
if (!cursorTexInit(&(*cursor)->mono,
b_shader_cursor_vert , b_shader_cursor_vert_size,
b_shader_cursor_mono_frag, b_shader_cursor_mono_frag_size))
return false;

View File

@ -32,7 +32,7 @@ struct CursorState {
struct Rect rect;
};
bool egl_cursorInit(EGL * egl, EGL_Cursor ** cursor);
bool egl_cursorInit(EGL_Cursor ** cursor);
void egl_cursorFree(EGL_Cursor ** cursor);
bool egl_cursorSetShape(

View File

@ -38,10 +38,8 @@
#include "desktop_rgb.frag.h"
#include "desktop_rgb.def.h"
#include "basic.vert.h"
#include "ffx_cas.frag.h"
#include "ffx_fsr1_easu.frag.h"
#include "ffx_fsr1_rcas.frag.h"
#include "postprocess.h"
#include "filters.h"
struct DesktopShader
{
@ -60,6 +58,7 @@ struct EGL_Desktop
EGLDisplay * display;
EGL_Texture * texture;
GLuint sampler;
struct DesktopShader shader;
EGL_DesktopRects * mesh;
CountedBuffer * matrix;
@ -67,7 +66,6 @@ struct EGL_Desktop
// internals
int width, height;
LG_RendererRotate rotate;
bool upscale;
// scale algorithm
int scaleAlgo;
@ -82,15 +80,8 @@ struct EGL_Desktop
bool useDMA;
LG_RendererFormat format;
EGL_Shader * ffxFSR1[2];
bool ffxFSR1Enable;
PostProcessHandle ffxFSR1Handle[2];
EGL_Uniform ffxFSR1Uniform;
EGL_Shader * ffxCAS;
bool ffxCASEnable;
PostProcessHandle ffxCASHandle;
EGL_Uniform ffxCASUniform;
EGL_PostProcess * pp;
_Atomic(bool) processFrame;
};
// forwards
@ -122,20 +113,6 @@ static bool egl_initDesktopShader(
return true;
}
static void setupFilters(EGL_Desktop * desktop)
{
desktop->ffxFSR1Handle[0] =
egl_textureAddFilter(desktop->texture, desktop->ffxFSR1[0],
desktop->ffxFSR1Enable);
desktop->ffxFSR1Handle[1] =
egl_textureAddFilter(desktop->texture, desktop->ffxFSR1[1],
desktop->ffxFSR1Enable);
desktop->ffxCASHandle =
egl_textureAddFilter(desktop->texture, desktop->ffxCAS,
desktop->ffxCASEnable);
}
bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
bool useDMA, int maxRects)
{
@ -150,7 +127,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->egl = egl;
desktop->display = display;
if (!egl_textureInit(egl, &desktop->texture, display,
if (!egl_textureInit(&desktop->texture, display,
useDMA ? EGL_TEXTYPE_DMABUF : EGL_TEXTYPE_FRAMEBUFFER, true))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
@ -188,41 +165,14 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
desktop->scaleAlgo = option_get_int("egl", "scale" );
desktop->useDMA = useDMA;
// AMD FidelidyFX FSR
egl_shaderInit(&desktop->ffxFSR1[0]);
egl_shaderCompile(desktop->ffxFSR1[0],
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size);
egl_shaderInit(&desktop->ffxFSR1[1]);
egl_shaderCompile(desktop->ffxFSR1[1],
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size);
desktop->ffxFSR1Enable = option_get_bool("eglFilter", "ffxFSR");
desktop->ffxFSR1Uniform.type = EGL_UNIFORM_TYPE_1F;
desktop->ffxFSR1Uniform.location =
egl_shaderGetUniform(desktop->ffxFSR1[1], "uSharpness");
desktop->ffxFSR1Uniform.f[0] =
option_get_float("eglFilter", "ffxFSRSharpness");
egl_shaderSetUniforms(desktop->ffxFSR1[1], &desktop->ffxFSR1Uniform, 1);
// AMD FidelidyFX CAS
egl_shaderInit(&desktop->ffxCAS);
egl_shaderCompile(desktop->ffxCAS,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size);
desktop->ffxCASEnable = option_get_bool("eglFilter", "ffxCAS");
desktop->ffxCASUniform.type = EGL_UNIFORM_TYPE_1F;
desktop->ffxCASUniform.location =
egl_shaderGetUniform(desktop->ffxCAS, "uSharpness");
desktop->ffxCASUniform.f[0] =
option_get_float("eglFilter", "ffxCASSharpness");
egl_shaderSetUniforms(desktop->ffxCAS, &desktop->ffxCASUniform, 1);
setupFilters(desktop);
if (!egl_postProcessInit(&desktop->pp))
{
DEBUG_ERROR("Failed to initialize the post process manager");
return false;
}
egl_postProcessAdd(desktop->pp, &egl_filterFFXFSR1Ops);
egl_postProcessAdd(desktop->pp, &egl_filterFFXCASOps );
return true;
}
@ -258,9 +208,7 @@ void egl_desktopFree(EGL_Desktop ** desktop)
egl_desktopRectsFree(&(*desktop)->mesh );
countedBufferRelease(&(*desktop)->matrix );
egl_shaderFree(&(*desktop)->ffxFSR1[0]);
egl_shaderFree(&(*desktop)->ffxFSR1[1]);
egl_shaderFree(&(*desktop)->ffxCAS);
egl_postProcessFree(&(*desktop)->pp);
free(*desktop);
*desktop = NULL;
@ -304,81 +252,10 @@ void egl_desktopConfigUI(EGL_Desktop * desktop)
igSliderInt("##nvgain", &desktop->nvGain, 0, desktop->nvMax, format, 0);
igPopItemWidth();
bool invalidateTex = false;
// AMD FidelityFX FSR
bool fsr1 = desktop->ffxFSR1Enable;
igCheckbox("AMD FidelityFX FSR", &fsr1);
if (fsr1 != desktop->ffxFSR1Enable)
if (egl_postProcessImgui(desktop->pp))
{
desktop->ffxFSR1Enable = fsr1;
egl_textureEnableFilter(desktop->ffxFSR1Handle[0],
fsr1 && desktop->upscale);
egl_textureEnableFilter(desktop->ffxFSR1Handle[1],
fsr1 && desktop->upscale);
invalidateTex = true;
}
float fsr1Sharpness = desktop->ffxFSR1Uniform.f[0];
igText("Sharpness:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
igGetStyle()->WindowPadding.x);
igSliderFloat("##fsr1Sharpness", &fsr1Sharpness, 0.0f, 1.0f, NULL, 0);
igPopItemWidth();
if (fsr1Sharpness != desktop->ffxFSR1Uniform.f[0])
{
// enable FSR1 if the sharpness was changed
if (!fsr1)
{
fsr1 = true;
desktop->ffxFSR1Enable = fsr1;
egl_textureEnableFilter(desktop->ffxFSR1Handle[0],
fsr1 && desktop->upscale);
egl_textureEnableFilter(desktop->ffxFSR1Handle[1],
fsr1 && desktop->upscale);
}
desktop->ffxFSR1Uniform.f[0] = 2.0f - fsr1Sharpness * 2.0f;
egl_shaderSetUniforms(desktop->ffxFSR1[1], &desktop->ffxFSR1Uniform, 1);
invalidateTex = true;
}
// AMD FiedlityFX CAS
bool cas = desktop->ffxCASEnable;
igCheckbox("AMD FidelityFX CAS", &cas);
if (cas != desktop->ffxCASEnable)
{
desktop->ffxCASEnable = cas;
egl_textureEnableFilter(desktop->ffxCASHandle, cas);
invalidateTex = true;
}
float casSharpness = desktop->ffxCASUniform.f[0];
igText("Sharpness:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
igGetStyle()->WindowPadding.x);
igSliderFloat("##casSharpness", &casSharpness, 0.0f, 1.0f, NULL, 0);
igPopItemWidth();
if (casSharpness != desktop->ffxCASUniform.f[0])
{
// enable CAS if the sharpness was changed
if (!cas)
{
cas = true;
desktop->ffxCASEnable = cas;
egl_textureEnableFilter(desktop->ffxCASHandle, cas);
}
desktop->ffxCASUniform.f[0] = casSharpness;
egl_shaderSetUniforms(desktop->ffxCAS, &desktop->ffxCASUniform, 1);
invalidateTex = true;
}
if (invalidateTex)
{
egl_textureInvalidate(desktop->texture);
app_invalidateWindow(true);
atomic_store(&desktop->processFrame, true);
app_invalidateWindow(false);
}
}
@ -425,6 +302,12 @@ bool egl_desktopSetup(EGL_Desktop * desktop, const LG_RendererFormat format)
return false;
}
glGenSamplers(1, &desktop->sampler);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(desktop->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
return true;
}
@ -434,52 +317,43 @@ bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dma
if (desktop->useDMA && dmaFd >= 0)
{
if (egl_textureUpdateFromDMA(desktop->texture, frame, dmaFd))
{
atomic_store(&desktop->processFrame, true);
return true;
}
DEBUG_WARN("DMA update failed, disabling DMABUF imports");
desktop->useDMA = false;
egl_textureFree(&desktop->texture);
if (!egl_textureInit(desktop->egl, &desktop->texture, desktop->display,
if (!egl_textureInit(&desktop->texture, desktop->display,
EGL_TEXTYPE_FRAMEBUFFER, true))
{
DEBUG_ERROR("Failed to initialize the desktop texture");
return false;
}
setupFilters(desktop);
if (!egl_desktopSetup(desktop, desktop->format))
return false;
}
return egl_textureUpdateFromFrame(desktop->texture, frame, damageRects, damageRectsCount);
if (egl_textureUpdateFromFrame(desktop->texture, frame,
damageRects, damageRectsCount))
{
atomic_store(&desktop->processFrame, true);
return true;
}
return false;
}
void egl_desktopResize(EGL_Desktop * desktop, int width, int height)
{
if (width > desktop->width && height > desktop->height)
{
desktop->upscale = true;
if (desktop->ffxFSR1Enable)
{
egl_textureEnableFilter(desktop->ffxFSR1Handle[0], true);
egl_textureEnableFilter(desktop->ffxFSR1Handle[1], true);
}
egl_textureSetFilterRes(desktop->ffxFSR1Handle[0], width, height);
egl_textureSetFilterRes(desktop->ffxFSR1Handle[1], width, height);
egl_textureSetFilterRes(desktop->ffxCASHandle , width, height);
}
else
{
desktop->upscale = false;
egl_textureEnableFilter(desktop->ffxFSR1Handle[0], false);
egl_textureEnableFilter(desktop->ffxFSR1Handle[1], false);
egl_textureSetFilterRes(desktop->ffxCASHandle, 0, 0);
}
atomic_store(&desktop->processFrame, true);
}
bool egl_desktopRender(EGL_Desktop * desktop, const float x, const float y,
bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
unsigned int outputHeight, const float x, const float y,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects)
{
@ -492,10 +366,21 @@ bool egl_desktopRender(EGL_Desktop * desktop, const float x, const float y,
int scaleAlgo = EGL_SCALE_NEAREST;
struct Rect finalSize;
egl_textureBind(desktop->texture);
egl_textureGetFinalSize(desktop->texture, &finalSize);
if (finalSize.x > desktop->width || finalSize.y > desktop->height)
if (atomic_exchange(&desktop->processFrame, false))
egl_postProcessRun(desktop->pp, desktop->texture, outputWidth, outputHeight);
unsigned int finalSizeX, finalSizeY;
GLuint texture = egl_postProcessGetOutput(desktop->pp,
&finalSizeX, &finalSizeY);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
egl_resetViewport(desktop->egl);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, desktop->sampler);
if (finalSizeX > desktop->width || finalSizeY > desktop->height)
scaleType = EGL_DESKTOP_DOWNSCALE;
switch (desktop->scaleAlgo)
@ -538,7 +423,7 @@ bool egl_desktopRender(EGL_Desktop * desktop, const float x, const float y,
{
.type = EGL_UNIFORM_TYPE_2I,
.location = shader->uTextureSize,
.i = { finalSize.x, finalSize.y },
.i = { finalSizeX, finalSizeY },
},
{
.type = EGL_UNIFORM_TYPE_M3x2FV,

View File

@ -46,6 +46,7 @@ bool egl_desktopSetup (EGL_Desktop * desktop, const LG_RendererFormat format);
bool egl_desktopUpdate(EGL_Desktop * desktop, const FrameBuffer * frame, int dmaFd,
const FrameDamageRect * damageRects, int damageRectsCount);
void egl_desktopResize(EGL_Desktop * desktop, int width, int height);
bool egl_desktopRender(EGL_Desktop * desktop, const float x, const float y,
bool egl_desktopRender(EGL_Desktop * desktop, unsigned int outputWidth,
unsigned int outputHeight, const float x, const float y,
const float scaleX, const float scaleY, enum EGL_DesktopScaleType scaleType,
LG_RendererRotate rotate, const struct DamageRects * rects);

View File

@ -47,6 +47,7 @@
#include "desktop.h"
#include "cursor.h"
#include "splash.h"
#include "postprocess.h"
#include "util.h"
#define SPLASH_FADE_TIME 1000000
@ -182,35 +183,6 @@ static struct Option egl_options[] =
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxFSR",
.description = "AMD FidelityFX FSR",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxFSRSharpness",
.description = "AMD FidelityFX FSR Sharpness",
.type = OPTION_TYPE_FLOAT,
.value.x_float = 1.0f
},
{
.module = "eglFilter",
.name = "ffxCAS",
.description = "AMD FidelityFX CAS",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxCASSharpness",
.description = "AMD FidelityFX CAS Sharpness",
.type = OPTION_TYPE_FLOAT,
.value.x_float = 0.0f
},
{0}
};
@ -222,6 +194,7 @@ static const char * egl_getName(void)
static void egl_setup(void)
{
option_register(egl_options);
egl_postProcessEarlyInit();
}
static bool egl_create(LG_Renderer ** renderer, const LG_RendererParams params,
@ -824,7 +797,7 @@ static bool egl_renderStartup(LG_Renderer * renderer, bool useDMA)
return false;
}
if (!egl_cursorInit(this, &this->cursor))
if (!egl_cursorInit(&this->cursor))
{
DEBUG_ERROR("Failed to initialize the cursor");
return false;
@ -989,6 +962,7 @@ static bool egl_render(LG_Renderer * renderer, LG_RendererRotate rotate,
if (this->start)
{
if (egl_desktopRender(this->desktop,
this->destRect.w, this->destRect.h,
this->translateX, this->translateY,
this->scaleX , this->scaleY ,
this->scaleType , rotate, renderAll ? NULL : accumulated))

View File

@ -0,0 +1,72 @@
/**
* Looking Glass
* Copyright © 2017-2021 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
*/
#pragma once
#include <stddef.h>
typedef enum EGL_TexType
{
EGL_TEXTYPE_BUFFER,
EGL_TEXTYPE_FRAMEBUFFER,
EGL_TEXTYPE_DMABUF
}
EGL_TexType;
typedef enum EGL_PixelFormat
{
EGL_PF_RGBA,
EGL_PF_BGRA,
EGL_PF_RGBA10,
EGL_PF_RGBA16F
}
EGL_PixelFormat;
typedef enum EGL_TexStatus
{
EGL_TEX_STATUS_NOTREADY,
EGL_TEX_STATUS_OK,
EGL_TEX_STATUS_ERROR
}
EGL_TexStatus;
typedef struct EGL_TexSetup
{
/* the pixel format of the texture */
EGL_PixelFormat pixFmt;
/* the width of the texture in pixels */
size_t width;
/* the height of the texture in pixels */
size_t height;
/* the stide of the texture in bytes */
size_t stride;
}
EGL_TexSetup;
typedef enum EGL_FilterType
{
EGL_FILTER_TYPE_EFFECT,
EGL_FILTER_TYPE_UPSCALE,
EGL_FILTER_TYPE_DOWNSCALE
}
EGL_FilterType;

View File

@ -0,0 +1,140 @@
/**
* Looking Glass
* Copyright © 2017-2021 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
*/
#pragma once
#include "util.h"
#include "shader.h"
#include "egltypes.h"
#include "model.h"
#include <string.h>
typedef struct EGL_Filter EGL_Filter;
typedef struct EGL_FilterOps
{
/* the friendly name of this filter */
const char * name;
/* the type of this filter */
EGL_FilterType type;
/* early initialization for registration of options */
void (*earlyInit)(void);
/* initialize the filter */
bool (*init)(EGL_Filter ** filter);
/* free the filter */
void (*free)(EGL_Filter * filter);
/* render any imgui config
* Returns true if a redraw is required */
bool (*imguiConfig)(EGL_Filter * filter);
/* set the input format of the filter */
bool (*setup)(EGL_Filter * filter, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height);
/* set the output resolution hint for the filter
* this is optional and only a hint */
void (*setOutputResHint)(EGL_Filter * filter,
unsigned int x, unsigned int y);
/* returns the output resolution of the filter */
void (*getOutputRes)(EGL_Filter * filter,
unsigned int *x, unsigned int *y);
/* prepare the shader for use
* A filter can return false to bypass it */
bool (*prepare)(EGL_Filter * filter);
/* runs the filter on the provided texture
* returns the processed texture as the output */
GLuint (*run)(EGL_Filter * filter, EGL_Model * model, GLuint texture);
/* called when the filter output is no loger needed so it can release memory
* this is optional */
void (*release)(EGL_Filter * filter);
}
EGL_FilterOps;
typedef struct EGL_Filter
{
EGL_FilterOps ops;
}
EGL_Filter;
static inline bool egl_filterInit(const EGL_FilterOps * ops, EGL_Filter ** filter)
{
if (!ops->init(filter))
return false;
memcpy(&(*filter)->ops, ops, sizeof(*ops));
return true;
}
static inline void egl_filterFree(EGL_Filter ** filter)
{
(*filter)->ops.free(*filter);
*filter = NULL;
}
static inline bool egl_filterImguiConfig(EGL_Filter * filter)
{
return filter->ops.imguiConfig(filter);
}
static inline bool egl_filterSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
return filter->ops.setup(filter, pixFmt, width, height);
}
static inline void egl_filterSetOutputResHint(EGL_Filter * filter,
unsigned int x, unsigned int y)
{
if (filter->ops.setOutputResHint)
filter->ops.setOutputResHint(filter, x, y);
}
static inline void egl_filterGetOutputRes(EGL_Filter * filter,
unsigned int *x, unsigned int *y)
{
return filter->ops.getOutputRes(filter, x, y);
}
static inline bool egl_filterPrepare(EGL_Filter * filter)
{
return filter->ops.prepare(filter);
}
static inline GLuint egl_filterRun(EGL_Filter * filter, EGL_Model * model,
GLuint texture)
{
return filter->ops.run(filter, model, texture);
}
static inline void egl_filterRelease(EGL_Filter * filter)
{
if (filter->ops.release)
filter->ops.release(filter);
}

View File

@ -0,0 +1,250 @@
/**
* Looking Glass
* Copyright © 2017-2021 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 "common/debug.h"
#include "common/option.h"
#include "cimgui.h"
#include "basic.vert.h"
#include "ffx_cas.frag.h"
typedef struct EGL_FilterFFXCAS
{
EGL_Filter base;
EGL_Shader * shader;
bool enable;
EGL_Uniform uniform;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
bool prepared;
EGL_Framebuffer * fb;
GLuint sampler;
}
EGL_FilterFFXCAS;
static void egl_filterFFXCASEarlyInit(void)
{
static struct Option options[] =
{
{
.module = "eglFilter",
.name = "ffxCAS",
.description = "AMD FidelityFX CAS",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxCASSharpness",
.description = "AMD FidelityFX CAS Sharpness",
.type = OPTION_TYPE_FLOAT,
.value.x_float = 0.0f
},
{ 0 }
};
option_register(options);
}
static bool egl_filterFFXCASInit(EGL_Filter ** filter)
{
EGL_FilterFFXCAS * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
if (!egl_shaderInit(&this->shader))
{
DEBUG_ERROR("Failed to initialize the shader");
goto error_this;
}
if (!egl_shaderCompile(this->shader,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_cas_frag, b_shader_ffx_cas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the shader");
goto error_shader;
}
this->enable = option_get_bool("eglFilter", "ffxCAS");
this->uniform.type = EGL_UNIFORM_TYPE_1F;
this->uniform.location =
egl_shaderGetUniform(this->shader, "uSharpness");
this->uniform.f[0] =
option_get_float("eglFilter", "ffxCASSharpness");
if (!egl_framebufferInit(&this->fb))
{
DEBUG_ERROR("Failed to initialize the framebuffer");
goto error_shader;
}
glGenSamplers(1, &this->sampler);
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler, 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_filterFFXCASFree(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_shaderFree(&this->shader);
egl_framebufferFree(&this->fb);
glDeleteSamplers(1, &this->sampler);
free(this);
}
static bool egl_filterFFXCASImguiConfig(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
bool redraw = false;
bool cas = this->enable;
float casSharpness = this->uniform.f[0];
igCheckbox("AMD FidelityFX CAS", &cas);
if (cas != this->enable)
{
this->enable = cas;
redraw = true;
}
igText("Sharpness:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
igGetStyle()->WindowPadding.x);
igSliderFloat("##casSharpness", &casSharpness, 0.0f, 1.0f, NULL, 0);
igPopItemWidth();
if (casSharpness != this->uniform.f[0])
{
// enable CAS if the sharpness was changed
if (!cas)
{
cas = true;
this->enable = true;
}
this->uniform.f[0] = casSharpness;
redraw = true;
}
if (redraw)
this->prepared = false;
return redraw;
}
static bool egl_filterFFXCASSetup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (pixFmt == this->pixFmt && this->width == width && this->height == height)
return true;
if (!egl_framebufferSetup(this->fb, pixFmt, width, height))
return false;
this->pixFmt = pixFmt;
this->width = width;
this->height = height;
this->prepared = false;
return true;
}
static void egl_filterFFXCASGetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
*width = this->width;
*height = this->height;
}
static bool egl_filterFFXCASPrepare(EGL_Filter * filter)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
if (!this->enable)
return false;
if (this->prepared)
return true;
egl_shaderSetUniforms(this->shader, &this->uniform, 1);
this->prepared = true;
return true;
}
static GLuint egl_filterFFXCASRun(EGL_Filter * filter, EGL_Model * model,
GLuint texture)
{
EGL_FilterFFXCAS * this = UPCAST(EGL_FilterFFXCAS, filter);
egl_framebufferBind(this->fb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->shader);
egl_modelRender(model);
return egl_framebufferGetTexture(this->fb);
}
EGL_FilterOps egl_filterFFXCASOps =
{
.name = "AMD FidelityFX CAS",
.type = EGL_FILTER_TYPE_EFFECT,
.earlyInit = egl_filterFFXCASEarlyInit,
.init = egl_filterFFXCASInit,
.free = egl_filterFFXCASFree,
.imguiConfig = egl_filterFFXCASImguiConfig,
.setup = egl_filterFFXCASSetup,
.getOutputRes = egl_filterFFXCASGetOutputRes,
.prepare = egl_filterFFXCASPrepare,
.run = egl_filterFFXCASRun
};

View File

@ -0,0 +1,317 @@
/**
* Looking Glass
* Copyright © 2017-2021 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 "common/debug.h"
#include "common/option.h"
#include "cimgui.h"
#include "basic.vert.h"
#include "ffx_fsr1_easu.frag.h"
#include "ffx_fsr1_rcas.frag.h"
typedef struct EGL_FilterFFXFSR1
{
EGL_Filter base;
EGL_Shader * easu, * rcas;
bool enable, active;
float sharpness;
EGL_Uniform easuUniform, rcasUniform;
enum EGL_PixelFormat pixFmt;
unsigned int width, height;
bool sizeChanged;
bool prepared;
EGL_Framebuffer * easuFb, * rcasFb;
GLuint sampler;
}
EGL_FilterFFXFSR1;
static void egl_filterFFXFSR1EarlyInit(void)
{
static struct Option options[] =
{
{
.module = "eglFilter",
.name = "ffxFSR",
.description = "AMD FidelityFX FSR",
.type = OPTION_TYPE_BOOL,
.value.x_bool = false
},
{
.module = "eglFilter",
.name = "ffxFSRSharpness",
.description = "AMD FidelityFX FSR Sharpness",
.type = OPTION_TYPE_FLOAT,
.value.x_float = 1.0f
},
{ 0 }
};
option_register(options);
}
static bool egl_filterFFXFSR1Init(EGL_Filter ** filter)
{
EGL_FilterFFXFSR1 * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
if (!egl_shaderInit(&this->easu))
{
DEBUG_ERROR("Failed to initialize the Easu shader");
goto error_this;
}
if (!egl_shaderInit(&this->rcas))
{
DEBUG_ERROR("Failed to initialize the Rcas shader");
goto error_esau;
}
if (!egl_shaderCompile(this->easu,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_easu_frag, b_shader_ffx_fsr1_easu_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Easu shader");
goto error_rcas;
}
if (!egl_shaderCompile(this->rcas,
b_shader_basic_vert , b_shader_basic_vert_size,
b_shader_ffx_fsr1_rcas_frag, b_shader_ffx_fsr1_rcas_frag_size)
)
{
DEBUG_ERROR("Failed to compile the Rcas shader");
goto error_rcas;
}
this->enable = option_get_bool("eglFilter", "ffxFSR");
this->easuUniform.type = EGL_UNIFORM_TYPE_2UI;
this->easuUniform.location =
egl_shaderGetUniform(this->easu, "uOutRes");
this->rcasUniform.type = EGL_UNIFORM_TYPE_1F;
this->rcasUniform.location =
egl_shaderGetUniform(this->rcas, "uSharpness");
this->sharpness = option_get_float("eglFilter", "ffxFSRSharpness");
this->rcasUniform.f[0] = 2.0f - this->sharpness * 2.0f;
if (!egl_framebufferInit(&this->easuFb))
{
DEBUG_ERROR("Failed to initialize the Easu framebuffer");
goto error_rcas;
}
if (!egl_framebufferInit(&this->rcasFb))
{
DEBUG_ERROR("Failed to initialize the Rcas framebuffer");
goto error_easuFb;
}
glGenSamplers(1, &this->sampler);
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
glSamplerParameteri(this->sampler, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
*filter = &this->base;
return true;
error_easuFb:
egl_framebufferFree(&this->rcasFb);
error_rcas:
egl_shaderFree(&this->rcas);
error_esau:
egl_shaderFree(&this->easu);
error_this:
free(this);
return false;
}
static void egl_filterFFXFSR1Free(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
egl_shaderFree(&this->easu);
egl_shaderFree(&this->rcas);
egl_framebufferFree(&this->easuFb);
egl_framebufferFree(&this->rcasFb);
glDeleteSamplers(1, &this->sampler);
free(this);
}
static bool egl_filterFFXFSR1ImguiConfig(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
bool redraw = false;
bool enable = this->enable;
float sharpness = this->sharpness;
igCheckbox("AMD FidelityFX FSR", &enable);
if (enable != this->enable)
{
this->enable = enable;
redraw = true;
}
igText("Sharpness:");
igSameLine(0.0f, -1.0f);
igPushItemWidth(igGetWindowWidth() - igGetCursorPosX() -
igGetStyle()->WindowPadding.x);
igSliderFloat("##fsr1Sharpness", &sharpness, 0.0f, 1.0f, NULL, 0);
igPopItemWidth();
if (sharpness != this->sharpness)
{
// enable FSR1 if the sharpness was changed
if (!enable)
{
enable = true;
this->enable = true;
}
this->sharpness = sharpness;
this->rcasUniform.f[0] = 2.0f - sharpness * 2.0f;
redraw = true;
}
if (redraw)
this->prepared = false;
return redraw;
}
static void egl_filterFFXFSR1SetOutputResHint(EGL_Filter * filter,
unsigned int width, unsigned int height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (this->width == width && this->height == height)
return;
this->width = width;
this->height = height;
this->sizeChanged = true;
this->prepared = false;
}
static bool egl_filterFFXFSR1Setup(EGL_Filter * filter,
enum EGL_PixelFormat pixFmt, unsigned int width, unsigned int height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (pixFmt == this->pixFmt && !this->sizeChanged)
return true;
if (!egl_framebufferSetup(this->easuFb, pixFmt, this->width, this->height))
return false;
if (!egl_framebufferSetup(this->rcasFb, pixFmt, this->width, this->height))
return false;
this->active = this->width > width && this->height > height;
this->sizeChanged = false;
this->pixFmt = pixFmt;
this->prepared = false;
return true;
}
static void egl_filterFFXFSR1GetOutputRes(EGL_Filter * filter,
unsigned int *width, unsigned int *height)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
*width = this->width;
*height = this->height;
}
static bool egl_filterFFXFSR1Prepare(EGL_Filter * filter)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
if (!this->enable || !this->active)
return false;
if (this->prepared)
return true;
this->easuUniform.ui[0] = this->width;
this->easuUniform.ui[1] = this->height;
egl_shaderSetUniforms(this->easu, &this->easuUniform, 1);
egl_shaderSetUniforms(this->rcas, &this->rcasUniform, 1);
this->prepared = true;
return true;
}
static GLuint egl_filterFFXFSR1Run(EGL_Filter * filter, EGL_Model * model,
GLuint texture)
{
EGL_FilterFFXFSR1 * this = UPCAST(EGL_FilterFFXFSR1, filter);
// pass 1, Easu
egl_framebufferBind(this->easuFb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->easu);
egl_modelRender(model);
texture = egl_framebufferGetTexture(this->easuFb);
// pass 2, Rcas
egl_framebufferBind(this->rcasFb);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glBindSampler(0, this->sampler);
egl_shaderUse(this->rcas);
egl_modelRender(model);
texture = egl_framebufferGetTexture(this->rcasFb);
return texture;
}
EGL_FilterOps egl_filterFFXFSR1Ops =
{
.name = "AMD FidelityFX FSR",
.type = EGL_FILTER_TYPE_UPSCALE,
.earlyInit = egl_filterFFXFSR1EarlyInit,
.init = egl_filterFFXFSR1Init,
.free = egl_filterFFXFSR1Free,
.imguiConfig = egl_filterFFXFSR1ImguiConfig,
.setup = egl_filterFFXFSR1Setup,
.setOutputResHint = egl_filterFFXFSR1SetOutputResHint,
.getOutputRes = egl_filterFFXFSR1GetOutputRes,
.prepare = egl_filterFFXFSR1Prepare,
.run = egl_filterFFXFSR1Run
};

View File

@ -0,0 +1,24 @@
/**
* Looking Glass
* Copyright © 2017-2021 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
*/
#pragma once
extern EGL_FilterOps egl_filterFFXCASOps;
extern EGL_FilterOps egl_filterFFXFSR1Ops;

View File

@ -0,0 +1,106 @@
/**
* Looking Glass
* Copyright © 2017-2021 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 "framebuffer.h"
#include "texture.h"
#include <stdlib.h>
#include "common/debug.h"
struct EGL_Framebuffer
{
GLuint fbo;
EGL_Texture * tex;
};
bool egl_framebufferInit(EGL_Framebuffer ** fb)
{
EGL_Framebuffer * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate ram");
return false;
}
if (!egl_textureInit(&this->tex, NULL, EGL_TEXTYPE_BUFFER, false))
{
DEBUG_ERROR("Failed to initialize the texture");
return false;
}
glGenFramebuffers(1, &this->fbo);
*fb = this;
return true;
}
void egl_framebufferFree(EGL_Framebuffer ** fb)
{
EGL_Framebuffer * this = *fb;
free(this);
*fb = NULL;
}
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))
{
DEBUG_ERROR("Failed to setup the texture");
return false;
}
GLuint tex;
egl_textureGet(this->tex, &tex, NULL, NULL);
glBindTexture(GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex, 0);
glDrawBuffers(1, &(GLenum){GL_COLOR_ATTACHMENT0});
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
DEBUG_ERROR("Failed to setup the framebuffer");
return false;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void egl_framebufferBind(EGL_Framebuffer * this)
{
glBindFramebuffer(GL_FRAMEBUFFER, this->fbo);
glViewport(0, 0, this->tex->format.width, this->tex->format.height);
}
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this)
{
GLuint output;
egl_textureGet(this->tex, &output, NULL, NULL);
return output;
}

View File

@ -0,0 +1,35 @@
/**
* Looking Glass
* Copyright © 2017-2021 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
*/
#pragma once
#include "texture.h"
typedef struct EGL_Framebuffer EGL_Framebuffer;
bool egl_framebufferInit(EGL_Framebuffer ** fb);
void egl_framebufferFree(EGL_Framebuffer ** fb);
bool egl_framebufferSetup(EGL_Framebuffer * this, enum EGL_PixelFormat pixFmt,
unsigned int width, unsigned int height);
void egl_framebufferBind(EGL_Framebuffer * this);
GLuint egl_framebufferGetTexture(EGL_Framebuffer * this);

View File

@ -0,0 +1,163 @@
/**
* Looking Glass
* Copyright © 2017-2021 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 "postprocess.h"
#include "filters.h"
#include "common/debug.h"
#include "common/array.h"
#include "ll.h"
static const EGL_FilterOps * EGL_Filters[] =
{
&egl_filterFFXFSR1Ops,
&egl_filterFFXCASOps
};
struct EGL_PostProcess
{
struct ll * filters;
GLuint output;
unsigned int outputX, outputY;
EGL_Model * model;
};
void egl_postProcessEarlyInit(void)
{
for(int i = 0; i < ARRAY_LENGTH(EGL_Filters); ++i)
EGL_Filters[i]->earlyInit();
}
bool egl_postProcessInit(EGL_PostProcess ** pp)
{
EGL_PostProcess * this = calloc(1, sizeof(*this));
if (!this)
{
DEBUG_ERROR("Failed to allocate memory");
return false;
}
this->filters = ll_new(sizeof(EGL_Filter *));
if (!this->filters)
{
DEBUG_ERROR("Failed to allocate the filter list");
goto error_this;
}
if (!egl_modelInit(&this->model))
{
DEBUG_ERROR("Failed to initialize the model");
goto error_filters;
}
egl_modelSetDefault(this->model, false);
*pp = this;
return true;
error_filters:
ll_free(this->filters);
error_this:
free(this);
return false;
}
void egl_postProcessFree(EGL_PostProcess ** pp)
{
if (!*pp)
return;
EGL_PostProcess * this = *pp;
if (this->filters)
{
EGL_Filter * filter;
while(ll_shift(this->filters, (void **)&filter))
egl_filterFree(&filter);
ll_free(this->filters);
}
egl_modelFree(&this->model);
free(this);
*pp = NULL;
}
bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops)
{
EGL_Filter * filter;
if (!egl_filterInit(ops, &filter))
return false;
ll_push(this->filters, filter);
return true;
}
bool egl_postProcessImgui(EGL_PostProcess * this)
{
bool redraw = false;
EGL_Filter * filter;
for(ll_reset(this->filters); ll_walk(this->filters, (void **)&filter); )
redraw |= egl_filterImguiConfig(filter);
return redraw;
}
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
unsigned int targetX, unsigned int targetY)
{
EGL_Filter * lastFilter = NULL, * filter;
unsigned int sizeX, sizeY;
GLuint texture;
if (egl_textureGet(tex, &texture, &sizeX, &sizeY) != EGL_TEX_STATUS_OK)
return false;
for(ll_reset(this->filters); ll_walk(this->filters, (void **)&filter); )
{
egl_filterSetOutputResHint(filter, targetX, targetY);
egl_filterSetup(filter, tex->format.pixFmt, sizeX, sizeY);
if (!egl_filterPrepare(filter))
continue;
texture = egl_filterRun(filter, this->model, texture);
egl_filterGetOutputRes(filter, &sizeX, &sizeY);
if (lastFilter)
egl_filterRelease(lastFilter);
lastFilter = filter;
}
this->output = texture;
this->outputX = sizeX;
this->outputY = sizeY;
return true;
}
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY)
{
*outputX = this->outputX;
*outputY = this->outputY;
return this->output;
}

View File

@ -0,0 +1,46 @@
/**
* Looking Glass
* Copyright © 2017-2021 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
*/
#pragma once
#include "filter.h"
#include "texture.h"
typedef struct EGL_PostProcess EGL_PostProcess;
void egl_postProcessEarlyInit(void);
bool egl_postProcessInit(EGL_PostProcess ** pp);
void egl_postProcessFree(EGL_PostProcess ** pp);
/* create and add a filter to this processor */
bool egl_postProcessAdd(EGL_PostProcess * this, const EGL_FilterOps * ops);
/* render the imgui options
* returns true if the filter needs to be re-run */
bool egl_postProcessImgui(EGL_PostProcess * this);
/* apply the filters to the supplied texture
* targetX/Y is the final target output dimension hint if scalers are present */
bool egl_postProcessRun(EGL_PostProcess * this, EGL_Texture * tex,
unsigned int targetX, unsigned int targetY);
GLuint egl_postProcessGetOutput(EGL_PostProcess * this,
unsigned int * outputX, unsigned int * outputY);

View File

@ -5,10 +5,10 @@ precision mediump float;
layout(location = 0) in vec2 uVertex;
layout(location = 1) in vec2 uUV;
out vec2 iFragCoord;
out vec2 fragCoord;
void main()
{
gl_Position = vec4(uVertex, 0.0, 1.0);
iFragCoord = uUV;
fragCoord = uUV;
}

View File

@ -3,12 +3,10 @@ precision mediump float;
#include "compat.h"
in vec2 iFragCoord;
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D iChannel0;
uniform uvec2 uInRes[8];
uniform uvec2 uOutRes;
uniform sampler2D texture;
uniform float uSharpness;
#define A_GPU 1
@ -18,7 +16,7 @@ uniform float uSharpness;
vec3 imageLoad(ivec2 point)
{
return texelFetch(iChannel0, point, 0).rgb;
return texelFetch(texture, point, 0).rgb;
}
AF3 CasLoad(ASU2 p)
@ -32,18 +30,15 @@ void CasInput(inout AF1 r,inout AF1 g,inout AF1 b) {}
void main()
{
uvec2 point = uvec2(iFragCoord * vec2(uInRes[0].xy));
vec2 res = vec2(textureSize(texture, 0));
uvec2 point = uvec2(fragCoord * res);
vec4 color;
vec2 inputResolution = vec2(uInRes[0]);
vec2 outputResolution = vec2(uOutRes);
uvec4 const0;
uvec4 const1;
CasSetup(const0, const1, uSharpness,
inputResolution.x, inputResolution.y,
outputResolution.x, outputResolution.y);
res.x, res.y, res.x, res.y);
CasFilter(
fragColor.r, fragColor.g, fragColor.b,

View File

@ -3,11 +3,10 @@ precision mediump float;
#include "compat.h"
in vec2 iFragCoord;
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D iChannel0;
uniform uvec2 uInRes[8];
uniform sampler2D texture;
uniform uvec2 uOutRes;
#define A_GPU 1
@ -20,7 +19,8 @@ uniform uvec2 uOutRes;
vec4 _textureGather(sampler2D tex, vec2 uv, int comp)
{
ivec2 p = ivec2((uv * vec2(uInRes[0])) - 0.5f);
vec2 res = vec2(textureSize(tex, 0));
ivec2 p = ivec2((uv * res) - 0.5f);
vec4 c0 = texelFetchOffset(tex, p, 0, ivec2(0,1));
vec4 c1 = texelFetchOffset(tex, p, 0, ivec2(1,1));
vec4 c2 = texelFetchOffset(tex, p, 0, ivec2(1,0));
@ -28,18 +28,18 @@ vec4 _textureGather(sampler2D tex, vec2 uv, int comp)
return vec4(c0[comp], c1[comp], c2[comp],c3[comp]);
}
AF4 FsrEasuRF(AF2 p){return AF4(_textureGather(iChannel0, p, 0));}
AF4 FsrEasuGF(AF2 p){return AF4(_textureGather(iChannel0, p, 1));}
AF4 FsrEasuBF(AF2 p){return AF4(_textureGather(iChannel0, p, 2));}
AF4 FsrEasuRF(AF2 p){return AF4(_textureGather(texture, p, 0));}
AF4 FsrEasuGF(AF2 p){return AF4(_textureGather(texture, p, 1));}
AF4 FsrEasuBF(AF2 p){return AF4(_textureGather(texture, p, 2));}
#include "ffx_fsr1.h"
void main()
{
AU4 con0, con1, con2, con3;
vec2 inRes = vec2(uInRes[0]);
vec2 inRes = vec2(textureSize(texture, 0));
vec2 outRes = vec2(uOutRes);
AU4 con0, con1, con2, con3;
FsrEasuCon(
con0,
con1,
@ -51,7 +51,7 @@ void main()
);
vec3 color;
uvec2 point = uvec2(iFragCoord * outRes);
uvec2 point = uvec2(fragCoord * outRes);
FsrEasuF(color, point, con0, con1, con2, con3);
fragColor = vec4(color.xyz, 1);
}

View File

@ -3,11 +3,10 @@ precision mediump float;
#include "compat.h"
in vec2 iFragCoord;
in vec2 fragCoord;
out vec4 fragColor;
uniform sampler2D iChannel0;
uniform uvec2 uInRes[8];
uniform sampler2D texture;
uniform float uSharpness;
#define A_GPU 1
@ -16,7 +15,7 @@ uniform float uSharpness;
#include "ffx_a.h"
AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(iChannel0, ASU2(p), 0); }
AF4 FsrRcasLoadF(ASU2 p) { return texelFetch(texture, ASU2(p), 0); }
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
#define FSR_RCAS_F 1
@ -25,8 +24,8 @@ void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
void main()
{
vec2 inRes = vec2(uInRes[0]);
uvec2 point = uvec2(iFragCoord * (inRes + 0.5f));
vec2 inRes = vec2(textureSize(texture, 0));
uvec2 point = uvec2(fragCoord * (inRes + 0.5f));
uvec4 const0;
FsrRcasCon(const0, uSharpness);

View File

@ -54,8 +54,8 @@ typedef struct RenderStep
}
RenderStep;
bool egl_textureInit(EGL * egl, EGL_Texture ** texture_,
EGLDisplay * display, EGL_TexType type, bool streaming)
bool egl_textureInit(EGL_Texture ** texture_, EGLDisplay * display,
EGL_TexType type, bool streaming)
{
const EGL_TextureOps * ops;
@ -85,7 +85,6 @@ bool egl_textureInit(EGL * egl, EGL_Texture ** texture_,
EGL_Texture * this = *texture_;
memcpy(&this->ops, ops, sizeof(*ops));
this->egl = egl;
glGenSamplers(1, &this->sampler);
glSamplerParameteri(this->sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -99,52 +98,12 @@ void egl_textureFree(EGL_Texture ** tex)
{
EGL_Texture * this = *tex;
if (this->render)
{
RenderStep * step;
while(ll_shift(this->render, (void **)&step))
{
if (step->fb)
glDeleteFramebuffers(1, &step->fb);
glDeleteTextures(1, &step->tex);
free(step);
}
ll_free(this->render);
egl_modelFree(&this->model);
ringbuffer_free(&this->textures);
free(this->bindData);
}
glDeleteSamplers(1, &this->sampler);
this->ops.free(this);
*tex = NULL;
}
bool setupRenderStep(EGL_Texture * this, RenderStep * step)
{
if (step->ready && (step->width > 0 || step->height > 0))
return true;
glBindTexture(GL_TEXTURE_2D, step->tex);
glTexImage2D(GL_TEXTURE_2D,
0,
this->format.intFormat,
step->width > 0 ? step->width : this->format.width,
step->height > 0 ? step->height : this->format.height,
0,
this->format.format,
this->format.dataType,
NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
step->ready = true;
return true;
}
bool egl_textureSetup(EGL_Texture * this, enum EGL_PixelFormat pixFmt,
size_t width, size_t height, size_t stride)
{
@ -155,21 +114,10 @@ bool egl_textureSetup(EGL_Texture * this, enum EGL_PixelFormat pixFmt,
.height = height,
.stride = stride
};
this->size = height * stride;
if (!egl_texUtilGetFormat(&setup, &this->format))
return false;
this->formatValid = true;
/* reconfigure any intermediate render steps */
if (this->render)
{
RenderStep * step;
for(ll_reset(this->render); ll_walk(this->render, (void **)&step); )
step->ready = false;
}
return this->ops.setup(this, &setup);
}
@ -181,13 +129,7 @@ bool egl_textureUpdate(EGL_Texture * this, const uint8_t * buffer)
.buffer = buffer
};
if (this->ops.update(this, &update))
{
atomic_store(&this->updated, true);
return true;
}
return false;
return this->ops.update(this, &update);
}
bool egl_textureUpdateFromFrame(EGL_Texture * this,
@ -202,13 +144,7 @@ bool egl_textureUpdateFromFrame(EGL_Texture * this,
.rectCount = damageRectsCount,
};
if (this->ops.update(this, &update))
{
atomic_store(&this->updated, true);
return true;
}
return false;
return this->ops.update(this, &update);
}
bool egl_textureUpdateFromDMA(EGL_Texture * this,
@ -221,54 +157,14 @@ bool egl_textureUpdateFromDMA(EGL_Texture * this,
};
/* wait for completion */
framebuffer_wait(frame, this->size);
framebuffer_wait(frame, this->format.bufferSize);
if (this->ops.update(this, &update))
{
atomic_store(&this->updated, true);
return true;
}
return false;
return this->ops.update(this, &update);
}
enum EGL_TexStatus egl_textureProcess(EGL_Texture * this)
{
EGL_TexStatus status;
if ((status = this->ops.process(this)) == EGL_TEX_STATUS_OK)
{
if (atomic_exchange(&this->updated, false))
this->postProcessed = false;
}
return status;
}
typedef struct BindInfo
{
GLuint tex;
unsigned int width;
unsigned int height;
}
BindInfo;
typedef struct BindData
{
GLuint sampler;
GLuint dimensions[];
}
BindData;
static bool rbBindTexture(int index, void * value, void * udata)
{
BindInfo * bi = (BindInfo *)value;
BindData * bd = (BindData *)udata;
glActiveTexture(GL_TEXTURE0 + index);
glBindTexture(GL_TEXTURE_2D, bi->tex);
glBindSampler(index, bd->sampler);
bd->dimensions[index * 2 + 0] = bi->width;
bd->dimensions[index * 2 + 1] = bi->height;
return true;
return this->ops.process(this);
}
enum EGL_TexStatus egl_textureBind(EGL_Texture * this)
@ -276,8 +172,6 @@ enum EGL_TexStatus egl_textureBind(EGL_Texture * this)
GLuint tex;
EGL_TexStatus status;
if (!this->render)
{
if ((status = this->ops.get(this, &tex)) != EGL_TEX_STATUS_OK)
return status;
@ -286,196 +180,3 @@ enum EGL_TexStatus egl_textureBind(EGL_Texture * this)
glBindSampler(0, this->sampler);
return EGL_TEX_STATUS_OK;
}
if (this->bindDataSize < ll_count(this->render))
{
free(this->bindData);
BindData * bd = (BindData *)calloc(1, sizeof(struct BindData) +
sizeof(bd->dimensions[0]) * (ll_count(this->render)+1) * 2);
bd->sampler = this->sampler;
this->bindData = bd;
this->bindDataSize = ll_count(this->render);
}
BindData * bd = (BindData *)this->bindData;
RenderStep * step;
/* if the postProcessing has not yet been done */
if (!this->postProcessed)
{
ringbuffer_reset(this->textures);
/* configure all the filters */
for(ll_reset(this->render); ll_walk(this->render, (void **)&step); )
{
if (!step->enabled)
continue;
if (!step->ready)
setupRenderStep(this, step);
}
if ((status = this->ops.get(this, &tex)) != EGL_TEX_STATUS_OK)
return status;
struct Rect finalSz =
{
.x = this->format.width,
.y = this->format.height
};
ringbuffer_push(this->textures, &(BindInfo) {
.tex = tex,
.width = this->format.width,
.height = this->format.height
});
ringbuffer_forEach(this->textures, rbBindTexture, bd, true);
bool cleanup = false;
for(ll_reset(this->render); ll_walk(this->render, (void **)&step); )
{
if (!step->enabled)
continue;
cleanup = true;
/* create the framebuffer here as it must be in the same gl context as
* it's usage */
if (!step->fb)
{
glGenFramebuffers(1, &step->fb);
glBindFramebuffer(GL_FRAMEBUFFER, step->fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, step->tex, 0);
glDrawBuffers(1, &(GLenum){GL_COLOR_ATTACHMENT0});
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
DEBUG_ERROR("Failed to setup the shader framebuffer");
return EGL_TEX_STATUS_ERROR;
}
}
else
glBindFramebuffer(GL_FRAMEBUFFER, step->fb);
const struct Rect sz =
{
.x = step->width > 0 ? step->width : this->format.width,
.y = step->height > 0 ? step->height : this->format.height
};
glViewport(0, 0, sz.x, sz.y);
/* use the shader (also configures it's set uniforms) */
egl_shaderUse(step->shader);
/* set the size uniforms */
glUniform2uiv(step->uInRes,
ringbuffer_getCount(this->textures), bd->dimensions);
glUniform2ui(step->uOutRes, sz.x, sz.y);
/* render the scene */
egl_modelRender(this->model);
finalSz.x = sz.x;
finalSz.y = sz.y;
/* push the details into the ringbuffer for the next pass */
ringbuffer_push(this->textures, &(BindInfo) {
.tex = step->tex,
.width = sz.x,
.height = sz.y
});
/* bind the textures for the next pass */
ringbuffer_forEach(this->textures, rbBindTexture, bd, true);
}
/* restore the state and the viewport */
if (cleanup)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glUseProgram(0);
egl_resetViewport(this->egl);
}
this->finalWidth = finalSz.x;
this->finalHeight = finalSz.y;
this->postProcessed = true;
}
else
{
/* bind the last texture */
BindInfo * bi = (BindInfo *)ringBuffer_getLastValue(this->textures);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, bi->tex);
glBindSampler(0, this->sampler);
}
return EGL_TEX_STATUS_OK;
}
PostProcessHandle egl_textureAddFilter(EGL_Texture * this, EGL_Shader * shader,
bool enabled)
{
if (!this->render)
{
this->render = ll_new();
egl_modelInit(&this->model);
egl_modelSetDefault(this->model, false);
this->textures = ringbuffer_new(8, sizeof(BindInfo));
}
RenderStep * step = calloc(1, sizeof(*step));
glGenTextures(1, &step->tex);
step->owner = this;
step->shader = shader;
step->uInRes = egl_shaderGetUniform(shader, "uInRes" );
step->uOutRes = egl_shaderGetUniform(shader, "uOutRes");
step->enabled = enabled;
ll_push(this->render, step);
return (PostProcessHandle)step;
}
void egl_textureEnableFilter(PostProcessHandle * handle, bool enable)
{
RenderStep * step = (RenderStep *)handle;
if (step->enabled == enable)
return;
step->enabled = enable;
egl_textureInvalidate(step->owner);
}
void egl_textureSetFilterRes(PostProcessHandle * handle,
unsigned int x, unsigned int y)
{
RenderStep * step = (RenderStep *)handle;
if (step->width == x && step->height == y)
return;
step->width = x;
step->height = y;
step->ready = false;
egl_textureInvalidate(step->owner);
}
void egl_textureInvalidate(EGL_Texture * texture)
{
texture->postProcessed = false;
}
void egl_textureGetFinalSize(EGL_Texture * this, struct Rect * rect)
{
if (!this->render)
{
rect->x = this->format.width;
rect->y = this->format.height;
return;
}
rect->x = this->finalWidth;
rect->y = this->finalHeight;
}

View File

@ -22,10 +22,10 @@
#include <stdbool.h>
#include "egl.h"
#include "egltypes.h"
#include "shader.h"
#include "model.h"
#include "common/framebuffer.h"
#include "common/ringbuffer.h"
#include "common/types.h"
#include "util.h"
@ -38,47 +38,6 @@
typedef struct EGL_Model EGL_Model;
typedef enum EGL_TexType
{
EGL_TEXTYPE_BUFFER,
EGL_TEXTYPE_FRAMEBUFFER,
EGL_TEXTYPE_DMABUF
}
EGL_TexType;
typedef enum EGL_PixelFormat
{
EGL_PF_RGBA,
EGL_PF_BGRA,
EGL_PF_RGBA10,
EGL_PF_RGBA16F
}
EGL_PixelFormat;
typedef enum EGL_TexStatus
{
EGL_TEX_STATUS_NOTREADY,
EGL_TEX_STATUS_OK,
EGL_TEX_STATUS_ERROR
}
EGL_TexStatus;
typedef struct EGL_TexSetup
{
/* the pixel format of the texture */
EGL_PixelFormat pixFmt;
/* the width of the texture in pixels */
size_t width;
/* the height of the texture in pixels */
size_t height;
/* the stide of the texture in bytes */
size_t stride;
}
EGL_TexSetup;
typedef struct EGL_TexUpdate
{
/* the type of this update */
@ -130,28 +89,12 @@ EGL_TextureOps;
struct EGL_Texture
{
struct EGL_TextureOps ops;
EGL * egl;
GLuint sampler;
RingBuffer textures;
EGL_TexFormat format;
bool formatValid;
// needed for dmabuf
size_t size;
// for applying shaders
struct ll * render;
_Atomic(bool) updated;
bool postProcessed;
EGL_Model * model;
unsigned int finalWidth, finalHeight;
void * bindData;
int bindDataSize;
};
bool egl_textureInit(EGL * egl, EGL_Texture ** texture, EGLDisplay * display,
bool egl_textureInit(EGL_Texture ** texture, EGLDisplay * display,
EGL_TexType type, bool streaming);
void egl_textureFree(EGL_Texture ** tex);
@ -169,6 +112,16 @@ bool egl_textureUpdateFromDMA(EGL_Texture * texture,
enum EGL_TexStatus egl_textureProcess(EGL_Texture * texture);
static inline EGL_TexStatus egl_textureGet(EGL_Texture * texture, GLuint * tex,
unsigned int * sizeX, unsigned int * sizeY)
{
if (sizeX)
*sizeX = texture->format.width;
if (sizeY)
*sizeY = texture->format.height;
return texture->ops.get(texture, tex);
}
enum EGL_TexStatus egl_textureBind(EGL_Texture * texture);
typedef void * PostProcessHandle;

View File

@ -79,12 +79,22 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt)
return false;
}
fmt->pixFmt = setup->pixFmt;
fmt->width = setup->width;
fmt->height = setup->height;
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;
fmt->bufferSize = setup->height * setup->stride;
}
fmt->bufferSize = fmt->height * fmt->stride;
return true;
}

View File

@ -20,12 +20,14 @@
#pragma once
#include "texture.h"
#include "egltypes.h"
typedef struct EGL_TexSetup EGL_TexSetup;
//typedef struct EGL_TexSetup EGL_TexSetup;
typedef struct EGL_TexFormat
{
EGL_PixelFormat pixFmt;
size_t bpp;
GLenum format;
GLenum intFormat;