mirror of
				https://github.com/gnif/LookingGlass.git
				synced 2025-10-31 12:42:02 +00:00 
			
		
		
		
	[client] egl: cleanup texture filtering/post-processing
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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)) | ||||
|   | ||||
							
								
								
									
										72
									
								
								client/renderers/EGL/egltypes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								client/renderers/EGL/egltypes.h
									
									
									
									
									
										Normal 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; | ||||
							
								
								
									
										140
									
								
								client/renderers/EGL/filter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								client/renderers/EGL/filter.h
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
							
								
								
									
										250
									
								
								client/renderers/EGL/filter_ffx_cas.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								client/renderers/EGL/filter_ffx_cas.c
									
									
									
									
									
										Normal 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 | ||||
| }; | ||||
							
								
								
									
										317
									
								
								client/renderers/EGL/filter_ffx_fsr1.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										317
									
								
								client/renderers/EGL/filter_ffx_fsr1.c
									
									
									
									
									
										Normal 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 | ||||
| }; | ||||
							
								
								
									
										24
									
								
								client/renderers/EGL/filters.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								client/renderers/EGL/filters.h
									
									
									
									
									
										Normal 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; | ||||
							
								
								
									
										106
									
								
								client/renderers/EGL/framebuffer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								client/renderers/EGL/framebuffer.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										35
									
								
								client/renderers/EGL/framebuffer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								client/renderers/EGL/framebuffer.h
									
									
									
									
									
										Normal 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); | ||||
							
								
								
									
										163
									
								
								client/renderers/EGL/postprocess.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										163
									
								
								client/renderers/EGL/postprocess.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										46
									
								
								client/renderers/EGL/postprocess.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								client/renderers/EGL/postprocess.h
									
									
									
									
									
										Normal 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); | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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); | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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,206 +172,11 @@ 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; | ||||
|  | ||||
|     glActiveTexture(GL_TEXTURE0); | ||||
|     glBindTexture(GL_TEXTURE_2D, tex); | ||||
|     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); | ||||
|   } | ||||
|   if ((status = this->ops.get(this, &tex)) != EGL_TEX_STATUS_OK) | ||||
|     return status; | ||||
|  | ||||
|   glActiveTexture(GL_TEXTURE0); | ||||
|   glBindTexture(GL_TEXTURE_2D, 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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -79,12 +79,22 @@ bool egl_texUtilGetFormat(const EGL_TexSetup * setup, EGL_TexFormat * fmt) | ||||
|       return false; | ||||
|   } | ||||
|  | ||||
|   fmt->width      = setup->width; | ||||
|   fmt->height     = setup->height; | ||||
|   fmt->stride     = setup->stride; | ||||
|   fmt->pitch      = setup->stride / fmt->bpp; | ||||
|   fmt->bufferSize = setup->height * setup->stride; | ||||
|   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 = fmt->height * fmt->stride; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Geoffrey McRae
					Geoffrey McRae