diff --git a/CMakeLists.txt b/CMakeLists.txt index c5e2de46..87574b82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ find_package(Threads REQUIRED) set(GLES_VERSION "GLES3") find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) +find_package(glslang CONFIG REQUIRED) set(AQUAMARINE_MINIMUM_VERSION 0.9.3) set(HYPRLANG_MINIMUM_VERSION 0.6.7) @@ -479,9 +480,9 @@ function(protocolWayland) endfunction() if(TARGET OpenGL::GL) - target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL Threads::Threads) + target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GL glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads) else() - target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 Threads::Threads) + target_link_libraries(hyprland_lib PUBLIC OpenGL::EGL OpenGL::GLES3 glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV Threads::Threads) endif() pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4) diff --git a/nix/default.nix b/nix/default.nix index 6a6b4abc..2c58403f 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -12,6 +12,7 @@ epoll-shim, git, glaze-hyprland, + glslang, gtest, hyprcursor, hyprgraphics, @@ -173,6 +174,7 @@ customStdenv.mkDerivation (finalAttrs: { cairo git glaze-hyprland + glslang gtest hyprcursor hyprgraphics diff --git a/scripts/generateShaderIncludes.sh b/scripts/generateShaderIncludes.sh index 20c78e9d..c9419031 100755 --- a/scripts/generateShaderIncludes.sh +++ b/scripts/generateShaderIncludes.sh @@ -15,7 +15,7 @@ echo 'static const std::map SHADERS = {' >> ./src/rend for filename in `ls ${SHADERS_SRC}`; do echo "-- ${filename}" - { echo 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc + { echo -n 'R"#('; cat ${SHADERS_SRC}/${filename}; echo ')#"'; } > ./src/render/shaders/${filename}.inc echo "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp echo "}," >> ./src/render/shaders/Shaders.hpp diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 811c6ee2..f6f777d1 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -1585,6 +1585,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{true}, }, + { + .value = "render:use_shader_blur_blend", + .description = "Use experimental blurred bg blending", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, /* * cursor: diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 46073c09..29967e58 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -800,6 +800,7 @@ CConfigManager::CConfigManager() { registerConfigVar("render:cm_sdr_eotf", {"default"}); registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1}); registerConfigVar("render:icc_vcgt_enabled", Hyprlang::INT{1}); + registerConfigVar("render:use_shader_blur_blend", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_update_news", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 417767e1..3f51e8df 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -2049,7 +2049,12 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques } static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) { - if (g_pHyprOpenGL->initShaders()) + CVarList vars(request, 0, ' '); + + if (vars.size() > 2) + return "too many args"; + + if (g_pHyprOpenGL && g_pHyprRenderer->reloadShaders(vars.size() == 2 ? vars[1] : "")) return format == FORMAT_JSON ? "{\"ok\": true}" : "ok"; else return format == FORMAT_JSON ? "{\"ok\": false}" : "error"; @@ -2076,8 +2081,8 @@ CHyprCtl::CHyprCtl() { registerCommand(SHyprCtlCommand{"locked", true, getIsLocked}); registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions}); registerCommand(SHyprCtlCommand{"submap", true, submapRequest}); - registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = true, .fn = reloadShaders}); + registerCommand(SHyprCtlCommand{.name = "reloadshaders", .exact = false, .fn = reloadShaders}); registerCommand(SHyprCtlCommand{"monitors", false, monitorsRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); diff --git a/src/helpers/Format.cpp b/src/helpers/Format.cpp index 5c35b8ea..7660934e 100644 --- a/src/helpers/Format.cpp +++ b/src/helpers/Format.cpp @@ -297,22 +297,6 @@ int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) { return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt)); } -uint32_t NFormatUtils::drmFormatToGL(DRMFormat drm) { - switch (drm) { - case DRM_FORMAT_XRGB8888: - case DRM_FORMAT_XBGR8888: return GL_RGBA; // doesn't matter, opengl is gucci in this case. - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: return GL_RGB10_A2; - default: return GL_RGBA; - } - UNREACHABLE(); - return GL_RGBA; -} - -uint32_t NFormatUtils::glFormatToType(uint32_t gl) { - return gl != GL_RGBA ? GL_UNSIGNED_INT_2_10_10_10_REV : GL_UNSIGNED_BYTE; -} - std::string NFormatUtils::drmFormatName(DRMFormat drm) { auto n = drmGetFormatName(drm); diff --git a/src/helpers/Format.hpp b/src/helpers/Format.hpp index ce5d8b40..02925e22 100644 --- a/src/helpers/Format.hpp +++ b/src/helpers/Format.hpp @@ -53,8 +53,6 @@ namespace NFormatUtils { bool isFormatOpaque(DRMFormat drm); int pixelsPerBlock(const SPixelFormat* const fmt); int minStride(const SPixelFormat* const fmt, int32_t width); - uint32_t drmFormatToGL(DRMFormat drm); - uint32_t glFormatToType(uint32_t gl); std::string drmFormatName(DRMFormat drm); std::string drmModifierName(uint64_t mod); DRMFormat alphaFormat(DRMFormat prevFormat); diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 626c3bce..557a6315 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -1579,10 +1579,20 @@ Vector2D CMonitor::middle() { return m_position + m_size / 2.f; } +const Mat3x3& CMonitor::getTransformMatrix() { + return m_projMatrix; +} + +const Mat3x3& CMonitor::getScaleMatrix() { + return m_projOutputMatrix; +} + void CMonitor::updateMatrix() { m_projMatrix = Mat3x3::identity(); if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL) m_projMatrix.translate(m_pixelSize / 2.0).transform(Math::wlTransformToHyprutils(m_transform)).translate(-m_transformedSize / 2.0); + + m_projOutputMatrix = Mat3x3::outputProjection(m_pixelSize, HYPRUTILS_TRANSFORM_NORMAL); } WORKSPACEID CMonitor::activeWorkspaceID() { diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index dd14a19b..7467467a 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -127,7 +127,7 @@ class CMonitor { bool m_scheduledRecalc = false; wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL; float m_xwaylandScale = 1.f; - Mat3x3 m_projMatrix; + std::optional m_forceSize; SP m_currentMode; SP m_cursorSwapchain; @@ -303,7 +303,6 @@ class CMonitor { void setSpecialWorkspace(const WORKSPACEID& id); void moveTo(const Vector2D& pos); Vector2D middle(); - void updateMatrix(); WORKSPACEID activeWorkspaceID(); WORKSPACEID activeSpecialWorkspaceID(); CBox logicalBox(); @@ -335,6 +334,10 @@ class CMonitor { bool inHDR(); bool gammaRampsInUse(); + // + const Mat3x3& getTransformMatrix(); + const Mat3x3& getScaleMatrix(); + /// Has an active workspace with a real fullscreen window (includes special workspace) bool inFullscreenMode(); /// Get fullscreen window from active or special workspace @@ -364,7 +367,12 @@ class CMonitor { return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name; } + Mat3x3 m_projMatrix; + private: + void updateMatrix(); + Mat3x3 m_projOutputMatrix; + void setupDefaultWS(const SMonitorRule&); WORKSPACEID findAvailableDefaultWS(); void commitDPMSState(bool state); diff --git a/src/helpers/TransferFunction.cpp b/src/helpers/TransferFunction.cpp index 935f77fe..074f4b19 100644 --- a/src/helpers/TransferFunction.cpp +++ b/src/helpers/TransferFunction.cpp @@ -26,7 +26,10 @@ std::string NTransferFunction::toString(eTF tf) { return ""; } -eTF NTransferFunction::fromConfig() { +eTF NTransferFunction::fromConfig(bool useICC) { + if (useICC) + return TF_SRGB; + static auto PSDREOTF = CConfigValue("render:cm_sdr_eotf"); static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF); static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); }); diff --git a/src/helpers/TransferFunction.hpp b/src/helpers/TransferFunction.hpp index cbf35f37..ae575158 100644 --- a/src/helpers/TransferFunction.hpp +++ b/src/helpers/TransferFunction.hpp @@ -15,5 +15,5 @@ namespace NTransferFunction { eTF fromString(const std::string tfName); std::string toString(eTF tf); - eTF fromConfig(); + eTF fromConfig(bool useICC = false); } diff --git a/src/helpers/cm/ColorManagement.hpp b/src/helpers/cm/ColorManagement.hpp index e8d47fae..9938ffbf 100644 --- a/src/helpers/cm/ColorManagement.hpp +++ b/src/helpers/cm/ColorManagement.hpp @@ -322,18 +322,22 @@ namespace NColorManagement { using PImageDescription = WP; - static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), - .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}}); + static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ + .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), + .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}, + }); + + static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ + .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, + .primariesNameSet = true, + .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, + .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), + .luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}, + }); - static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ, - .primariesNameSet = true, - .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, - .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), - .luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}}); - ; static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR, .windowsScRGB = true, @@ -342,6 +346,6 @@ namespace NColorManagement { .primaries = NColorPrimaries::BT709, .luminances = {.reference = 203}, }); - ; -} \ No newline at end of file + static const auto LINEAR_IMAGE_DESCRIPTION = SCRGB_IMAGE_DESCRIPTION; // TODO any reason to use something different? +} diff --git a/src/managers/screenshare/ScreenshareFrame.cpp b/src/managers/screenshare/ScreenshareFrame.cpp index bd2b5b83..18d5aac9 100644 --- a/src/managers/screenshare/ScreenshareFrame.cpp +++ b/src/managers/screenshare/ScreenshareFrame.cpp @@ -162,8 +162,9 @@ void CScreenshareFrame::renderMonitor() { if (!g_pHyprOpenGL->m_monitorRenderResources.contains(PMONITOR)) return; // wtf? - auto TEXTURE = g_pHyprOpenGL->m_monitorRenderResources[PMONITOR].monitorMirrorFB.getTexture(); + auto TEXTURE = g_pHyprOpenGL->m_monitorRenderResources[PMONITOR].monitorMirrorFB.getTexture(); + const bool IS_CM_AWARE = PROTO::colorManagement && PROTO::colorManagement->isClientCMAware(m_session->m_client); g_pHyprOpenGL->m_renderData.transformDamage = false; g_pHyprOpenGL->m_renderData.noSimplify = true; @@ -173,7 +174,11 @@ void CScreenshareFrame::renderMonitor() { .translate(-m_session->m_captureBox.pos()); // vvvv kinda ass-backwards but that's how I designed the renderer... sigh. g_pHyprOpenGL->pushMonitorTransformEnabled(true); g_pHyprOpenGL->setRenderModifEnabled(false); - g_pHyprOpenGL->renderTexturePrimitive(TEXTURE, monbox); + g_pHyprOpenGL->renderTexture(TEXTURE, monbox, + { + .cmBackToSRGB = !IS_CM_AWARE, + .cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr, + }); g_pHyprOpenGL->setRenderModifEnabled(true); g_pHyprOpenGL->popMonitorTransformEnabled(); diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index d43e1c69..7fe001d4 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "../managers/screenshare/ScreenshareManager.hpp" #include "debug/HyprNotificationOverlay.hpp" #include "hyprerror/HyprError.hpp" +#include "macros.hpp" #include "pass/TexPassElement.hpp" #include "pass/RectPassElement.hpp" #include "pass/PreBlurElement.hpp" @@ -44,20 +46,14 @@ #include #include #include -#include #include -#include "./shaders/Shaders.hpp" +#include "ShaderLoader.hpp" +#include "Texture.hpp" +#include using namespace Hyprutils::OS; using namespace NColorManagement; - -const std::vector ASSET_PATHS = { -#ifdef DATAROOTDIR - DATAROOTDIR, -#endif - "/usr/share", - "/usr/local/share", -}; +using namespace Render; static inline void loadGLProc(void* pProc, const char* name) { void* proc = rc(eglGetProcAddress(name)); @@ -880,124 +876,30 @@ void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional f m_renderData.finalDamage.set(finalDamage.value_or(damage_)); } -// TODO notify user if bundled shader is newer than ~/.config override -static std::string loadShader(const std::string& filename) { - const auto home = Hyprutils::Path::getHome(); - if (home.has_value()) { - const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename); - if (src.has_value()) - return src.value(); - } - for (auto& e : ASSET_PATHS) { - const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename); - if (src.has_value()) - return src.value(); - } - if (SHADERS.contains(filename)) - return SHADERS.at(filename); - throw std::runtime_error(std::format("Couldn't load shader {}", filename)); -} +static const std::vector SHADER_INCLUDES = { + "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl", + "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl", +}; -static void loadShaderInclude(const std::string& filename, std::map& includes) { - includes.insert({filename, loadShader(filename)}); -} +// order matters, see ePreparedFragmentShader +const std::array FRAG_SHADERS = { + "quad.frag", "passthru.frag", "rgbamatte.frag", "ext.frag", "blur1.frag", "blur2.frag", + "blurprepare.frag", "blurfinish.frag", "shadow.frag", "surface.frag", "border.frag", "glitch.frag", +}; -static void processShaderIncludes(std::string& source, const std::map& includes) { - for (auto it = includes.begin(); it != includes.end(); ++it) { - Hyprutils::String::replaceInString(source, "#include \"" + it->first + "\"", it->second); - } -} - -static const uint8_t MAX_INCLUDE_DEPTH = 3; - -static std::string processShader(const std::string& filename, const std::map& includes, const uint8_t includeDepth = 1) { - auto source = loadShader(filename); - for (auto i = 0; i < includeDepth; i++) { - processShaderIncludes(source, includes); - } - return source; -} - -bool CHyprOpenGLImpl::initShaders() { - auto shaders = makeShared(); - std::map includes; - const bool isDynamic = m_shadersInitialized; - static const auto PCM = CConfigValue("render:cm_enabled"); +bool CHyprOpenGLImpl::initShaders(const std::string& path) { + auto shaders = makeShared(); + static const auto PCM = CConfigValue("render:cm_enabled"); try { - loadShaderInclude("get_rgb_pixel.glsl", includes); - loadShaderInclude("get_rgba_pixel.glsl", includes); - loadShaderInclude("get_rgbx_pixel.glsl", includes); - loadShaderInclude("discard.glsl", includes); - loadShaderInclude("do_discard.glsl", includes); - loadShaderInclude("tint.glsl", includes); - loadShaderInclude("do_tint.glsl", includes); - loadShaderInclude("rounding.glsl", includes); - loadShaderInclude("do_rounding.glsl", includes); - loadShaderInclude("surface_CM.glsl", includes); - loadShaderInclude("CM.glsl", includes); - loadShaderInclude("do_CM.glsl", includes); - loadShaderInclude("tonemap.glsl", includes); - loadShaderInclude("do_tonemap.glsl", includes); - loadShaderInclude("sdr_mod.glsl", includes); - loadShaderInclude("do_sdr_mod.glsl", includes); - loadShaderInclude("primaries_xyz.glsl", includes); - loadShaderInclude("primaries_xyz_uniform.glsl", includes); - loadShaderInclude("primaries_xyz_const.glsl", includes); - loadShaderInclude("gain.glsl", includes); - loadShaderInclude("border.glsl", includes); + auto shaderLoader = makeUnique(SHADER_INCLUDES, FRAG_SHADERS, path); - shaders->TEXVERTSRC = processShader("tex300.vert", includes); - shaders->TEXVERTSRC320 = processShader("tex320.vert", includes); + shaders->TEXVERTSRC = shaderLoader->process("tex300.vert"); + shaders->TEXVERTSRC320 = shaderLoader->process("tex320.vert"); - if (!*PCM) - m_cmSupported = false; - else { - std::vector CM_SHADERS = {{ - {SH_FRAG_CM_BLURPREPARE, "CMblurprepare.frag"}, - {SH_FRAG_CM_BORDER1, "CMborder.frag"}, - }}; + m_cmSupported = *PCM; - bool success = false; - for (const auto& desc : CM_SHADERS) { - const auto fragSrc = processShader(desc.file, includes, MAX_INCLUDE_DEPTH); - - if (!(success = shaders->frag[desc.id]->createProgram(shaders->TEXVERTSRC, fragSrc, true, true))) - break; - } - - if (m_shadersInitialized && m_cmSupported && !success) - g_pHyprNotificationOverlay->addNotification(I18n::i18nEngine()->localize(I18n::TXT_KEY_NOTIF_CM_RELOAD_FAILED), CHyprColor{}, 15000, ICON_WARNING); - - m_cmSupported = success; - - if (!m_cmSupported) - Log::logger->log( - Log::ERR, - "WARNING: CM Shader failed compiling, color management will not work. It's likely because your GPU is an old piece of garbage, don't file bug reports " - "about this!"); - } - - std::vector FRAG_SHADERS = {{ - {SH_FRAG_QUAD, "quad.frag"}, - {SH_FRAG_PASSTHRURGBA, "passthru.frag"}, - {SH_FRAG_MATTE, "rgbamatte.frag"}, - {SH_FRAG_GLITCH, "glitch.frag"}, - {SH_FRAG_EXT, "ext.frag"}, - {SH_FRAG_BLUR1, "blur1.frag"}, - {SH_FRAG_BLUR2, "blur2.frag"}, - {SH_FRAG_BLURPREPARE, "blurprepare.frag"}, - {SH_FRAG_BLURFINISH, "blurfinish.frag"}, - {SH_FRAG_SHADOW, "shadow.frag"}, - {SH_FRAG_BORDER1, "border.frag"}, - }}; - - for (const auto& desc : FRAG_SHADERS) { - const auto fragSrc = processShader(desc.file, includes, MAX_INCLUDE_DEPTH); - - if (!shaders->frag[desc.id]->createProgram(shaders->TEXVERTSRC, fragSrc, isDynamic)) - return false; - } + g_pShaderLoader = std::move(shaderLoader); } catch (const std::exception& e) { if (!m_shadersInitialized) @@ -1008,7 +910,6 @@ bool CHyprOpenGLImpl::initShaders() { } m_shaders = shaders; - m_includes = includes; m_shadersInitialized = true; Log::logger->log(Log::DEBUG, "Shaders initialized successfully."); @@ -1192,7 +1093,7 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC newBox, Math::wlTransformToHyprutils(Math::invertTransform(!m_monitorTransformEnabled ? WL_OUTPUT_TRANSFORM_NORMAL : m_renderData.pMonitor->m_transform)), newBox.rot); Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - auto shader = useShader(m_shaders->frag[SH_FRAG_QUAD]); + auto shader = useShader(getShaderVariant(SH_FRAG_QUAD, data.round > 0 ? SH_FEAT_ROUNDING : 0)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); // premultiply the color as well as we don't work with straight alpha @@ -1272,60 +1173,24 @@ static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescriptio void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { - const auto sdrEOTF = NTransferFunction::fromConfig(); + const auto settings = g_pHyprRenderer->getCMSettings(imageDescription, targetImageDescription, m_renderData.surface.valid() ? m_renderData.surface.lock() : nullptr, modifySDR, + sdrMinLuminance, sdrMaxLuminance); - if (m_renderData.surface.valid()) { - if (m_renderData.surface->m_colorManagement.valid()) { - if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) - shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); - else - shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - } else if (sdrEOTF == NTransferFunction::TF_SRGB) - shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB); - else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) - shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); - else - shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - } else - shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); - - shader->setUniformInt(SHADER_TARGET_TF, targetImageDescription->value().transferFunction); - - const auto targetPrimaries = targetImageDescription->getPrimaries(); - const auto mat = targetPrimaries->value().toXYZ().mat(); - const std::array glTargetPrimariesXYZ = { - mat[0][0], mat[1][0], mat[2][0], // - mat[0][1], mat[1][1], mat[2][1], // - mat[0][2], mat[1][2], mat[2][2], // - }; - shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); - - const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); - - shader->setUniformFloat2(SHADER_SRC_TF_RANGE, imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); - shader->setUniformFloat2(SHADER_DST_TF_RANGE, targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), - targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)); - - shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, imageDescription->value().luminances.reference); - shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, targetImageDescription->value().luminances.reference); - - const float maxLuminance = needsHDRmod ? - imageDescription->value().getTFMaxLuminance(-1) : - (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); - - shader->setUniformFloat(SHADER_MAX_LUMINANCE, maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference); - shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000); - shader->setUniformFloat(SHADER_SDR_SATURATION, needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f); - shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f); + shader->setUniformInt(SHADER_SOURCE_TF, settings.sourceTF); + shader->setUniformInt(SHADER_TARGET_TF, settings.targetTF); + shader->setUniformFloat2(SHADER_SRC_TF_RANGE, settings.srcTFRange.min, settings.srcTFRange.max); + shader->setUniformFloat2(SHADER_DST_TF_RANGE, settings.dstTFRange.min, settings.dstTFRange.max); + shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, settings.srcRefLuminance); + shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, settings.dstRefLuminance); + shader->setUniformFloat(SHADER_MAX_LUMINANCE, settings.maxLuminance); + shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, settings.dstMaxLuminance); + shader->setUniformFloat(SHADER_SDR_SATURATION, settings.sdrSaturation); + shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, settings.sdrBrightnessMultiplier); if (!targetImageDescription->value().icc.present) { const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id()); - if (!primariesConversionCache.contains(cacheKey)) { - auto conversion = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); - const auto mat = conversion.mat(); + const auto& mat = settings.convertMatrix; const std::array glConvertMatrix = { mat[0][0], mat[1][0], mat[2][0], // mat[0][1], mat[1][1], mat[2][1], // @@ -1335,12 +1200,14 @@ void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement: } shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]); - shader->setUniformInt(SHADER_USE_ICC, 0); - shader->setUniformInt(SHADER_LUT_3D, 8); // req'd for ogl + const auto mat = settings.dstPrimaries2XYZ; + const std::array glTargetPrimariesXYZ = { + mat[0][0], mat[1][0], mat[2][0], // + mat[0][1], mat[1][1], mat[2][1], // + mat[0][2], mat[1][2], mat[2][2], // + }; + shader->setUniformMatrix3fv(SHADER_TARGET_PRIMARIES_XYZ, 1, false, glTargetPrimariesXYZ); } else { - // ICC path, use a 3D LUT - shader->setUniformInt(SHADER_USE_ICC, 1); - // TODO: this sucks GLCALL(glActiveTexture(GL_TEXTURE8)); targetImageDescription->value().icc.lutTexture->bind(); @@ -1353,204 +1220,34 @@ void CHyprOpenGLImpl::passCMUniforms(WP shader, const NColorManagement: } void CHyprOpenGLImpl::passCMUniforms(WP shader, const PImageDescription imageDescription) { - passCMUniforms(shader, imageDescription, m_renderData.pMonitor->m_imageDescription, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); + passCMUniforms(shader, imageDescription, g_pHyprRenderer->workBufferImageDescription(), true, m_renderData.pMonitor->m_sdrMinLuminance, + m_renderData.pMonitor->m_sdrMaxLuminance); } -void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, const STextureRenderData& data) { - RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); - RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); - - TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); - - float alpha = std::clamp(data.a, 0.f, 1.f); - - if (data.damage->empty()) - return; - - CBox newBox = box; - m_renderData.renderModif.applyToBox(newBox); - +WP CHyprOpenGLImpl::renderToOutputInternal() { static const auto PDT = CConfigValue("debug:damage_tracking"); - static const auto PPASS = CConfigValue("render:cm_fs_passthrough"); - static const auto PENABLECM = CConfigValue("render:cm_enabled"); static const auto PCURSORTIMEOUT = CConfigValue("cursor:inactive_timeout"); - // get the needed transform for this texture - const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); - Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; - - if (m_monitorTransformEnabled) - TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); - - Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); - Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - - WP shader; - - const bool CRASHING = m_applyFinalShader && g_pHyprRenderer->m_crashingInProgress; - const bool CUSTOM_FINAL_SHADER = !CRASHING && m_applyFinalShader && m_finalScreenShader->program(); - - uint8_t shaderFeatures = 0; - - if (CRASHING) - shader = m_shaders->frag[SH_FRAG_GLITCH]; - else if (CUSTOM_FINAL_SHADER) - shader = m_finalScreenShader; - else { - if (m_applyFinalShader) - shaderFeatures &= ~SH_FEAT_RGBA; - else { - switch (tex->m_type) { - case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; - case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; - - case TEXTURE_EXTERNAL: shader = m_shaders->frag[SH_FRAG_EXT]; break; // might be unused - default: RASSERT(false, "tex->m_iTarget unsupported!"); - } - } - } - - if (data.finalMonitorCM || (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault())) - shaderFeatures &= ~SH_FEAT_RGBA; - - glActiveTexture(GL_TEXTURE0); - tex->bind(); - - tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); - tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); - - if (m_renderData.useNearestNeighbor) { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); - } else { - tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); - tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); - } - - const bool isHDRSurface = m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid() ? m_renderData.surface->m_colorManagement->isHDR() : false; - const bool canPassHDRSurface = isHDRSurface && !m_renderData.surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader - - // Color pipeline: - // Client ---> sRGB chosen EOTF (render buf) ---> Monitor (target) - // - - const auto CONFIG_SDR_EOTF = NTransferFunction::fromConfig(); - const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; - const auto CHOSEN_SDR_EOTF = [&] { - // if the monitor is ICC'd, use SRGB for best ΔE. - if (IS_MONITOR_ICC) - return CM_TRANSFER_FUNCTION_SRGB; - - // otherwise use configured - if (CONFIG_SDR_EOTF != NTransferFunction::TF_SRGB) - return CM_TRANSFER_FUNCTION_GAMMA22; - return CM_TRANSFER_FUNCTION_SRGB; - }(); - const auto WORK_BUFFER_IMAGE_DESCRIPTION = CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); - - // chosenSdrEotf contains the valid eotf for this display - - const auto SOURCE_IMAGE_DESCRIPTION = [&] { - // if valid CM surface, use that as a source - if (m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid()) - return CImageDescription::from(m_renderData.surface->m_colorManagement->imageDescription()); - - // otherwise, if we are CM'ing back into source, use chosen, because that's what our work buffer is in - // the same applies to the final monitor CM - if (data.cmBackToSRGB || data.finalMonitorCM) // NOLINTNEXTLINE - return WORK_BUFFER_IMAGE_DESCRIPTION; - - // otherwise, default - return DEFAULT_IMAGE_DESCRIPTION; - }(); - - const auto TARGET_IMAGE_DESCRIPTION = [&] { - // if we are CM'ing back, use default sRGB - if (data.cmBackToSRGB) - return DEFAULT_IMAGE_DESCRIPTION; - - // for final CM, use the target description - if (data.finalMonitorCM) - return m_renderData.pMonitor->m_imageDescription; - - // otherwise, use chosen, we're drawing into the work buffer - // NOLINTNEXTLINE - return WORK_BUFFER_IMAGE_DESCRIPTION; - }(); - - const bool CANT_CHECK_CM_EQUALITY = data.cmBackToSRGB || data.finalMonitorCM || (!m_renderData.surface || !m_renderData.surface->m_colorManagement); - - const bool skipCM = data.noCM /* manual CM disable */ - || !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ - || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ - || (SOURCE_IMAGE_DESCRIPTION->id() == TARGET_IMAGE_DESCRIPTION->id() && !CANT_CHECK_CM_EQUALITY) /* Source and target have the same image description */ - || (((*PPASS && canPassHDRSurface) || - (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && - m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; - - if (data.discardActive) - shaderFeatures |= SH_FEAT_DISCARD; - - if (data.allowDim && m_renderData.currentWindow && (m_renderData.currentWindow->m_notRespondingTint->value() > 0 || m_renderData.currentWindow->m_dimPercent->value() > 0)) - shaderFeatures |= SH_FEAT_TINT; - - if (data.round > 0) - shaderFeatures |= SH_FEAT_ROUNDING; - - if (!skipCM) { - shaderFeatures |= SH_FEAT_CM; - - const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); - const float maxLuminance = needsHDRmod ? - SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : - (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); - const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; - - if (maxLuminance >= dstMaxLuminance * 1.01) - shaderFeatures |= SH_FEAT_TONEMAP; - - if (data.finalMonitorCM && - (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || - SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && - TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && - ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || - (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) - shaderFeatures |= SH_FEAT_SDR_MOD; - } - - if (!shader) - shader = getSurfaceShader(shaderFeatures); + WP shader = + g_pHyprRenderer->m_crashingInProgress ? getShaderVariant(SH_FRAG_GLITCH) : (m_finalScreenShader->program() ? m_finalScreenShader : getShaderVariant(SH_FRAG_PASSTHRURGBA)); shader = useShader(shader); - if (!skipCM) { - if (data.finalMonitorCM) - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); - else - passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, -1, -1); - } - - shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); - shader->setUniformInt(SHADER_TEX, 0); - - if ((CUSTOM_FINAL_SHADER && *PDT == 0) || CRASHING) + if (*PDT == 0 || g_pHyprRenderer->m_crashingInProgress) shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime()); - else if (CUSTOM_FINAL_SHADER) - shader->setUniformFloat(SHADER_TIME, 0.F); + else + shader->setUniformFloat(SHADER_TIME, 0.f); - if (CUSTOM_FINAL_SHADER) { - shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); - shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); - shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); - shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); - shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); - shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); - shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); - shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); - } + shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); + shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); + shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); + shader->setUniformInt(SHADER_POINTER_HIDDEN, g_pHyprRenderer->m_cursorHiddenByCondition); + shader->setUniformInt(SHADER_POINTER_KILLING, g_pInputManager->getClickMode() == CLICKMODE_KILL); + shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); + shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); + shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); - if (CUSTOM_FINAL_SHADER && *PDT == 0) { + if (*PDT == 0) { PHLMONITORREF pMonitor = m_renderData.pMonitor; Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); @@ -1576,7 +1273,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds()); shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, g_pHyprRenderer->m_lastCursorData.switchedTimer.getSeconds()); - } else if (CUSTOM_FINAL_SHADER) { + } else { shader->setUniformFloat2(SHADER_POINTER, 0.f, 0.f); static const std::vector pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f); @@ -1590,13 +1287,141 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f); } - if (CRASHING) { + if (g_pHyprRenderer->m_crashingInProgress) { shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y); } + return shader; +} + +WP CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox) { + static const auto PPASS = CConfigValue("render:cm_fs_passthrough"); + static const auto PENABLECM = CConfigValue("render:cm_enabled"); + static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); + + float alpha = std::clamp(data.a, 0.f, 1.f); + + WP shader; + ShaderFeatureFlags shaderFeatures = 0; + + switch (texType) { + case TEXTURE_RGBA: shaderFeatures |= SH_FEAT_RGBA; break; + case TEXTURE_RGBX: shaderFeatures &= ~SH_FEAT_RGBA; break; + + // TODO set correct features + case TEXTURE_EXTERNAL: shader = getShaderVariant(SH_FRAG_EXT, SH_FEAT_ROUNDING | SH_FEAT_DISCARD | SH_FEAT_TINT); break; // might be unused + default: RASSERT(false, "tex->m_iTarget unsupported!"); + } + + if (data.finalMonitorCM || (m_renderData.currentWindow && m_renderData.currentWindow->m_ruleApplicator->RGBX().valueOrDefault())) + shaderFeatures &= ~SH_FEAT_RGBA; + + const auto surface = m_renderData.surface; + const bool isHDRSurface = surface.valid() && surface->m_colorManagement.valid() ? surface->m_colorManagement->isHDR() : false; + const bool canPassHDRSurface = isHDRSurface && !surface->m_colorManagement->isWindowsScRGB(); // windows scRGB requires CM shader + + const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->workBufferImageDescription(); + + // chosenSdrEotf contains the valid eotf for this display + + const auto SOURCE_IMAGE_DESCRIPTION = [&] { + // if valid CM surface, use that as a source + if (m_renderData.surface.valid() && m_renderData.surface->m_colorManagement.valid()) + return CImageDescription::from(m_renderData.surface->m_colorManagement->imageDescription()); + + // otherwise, if we are CM'ing back into source, use chosen, because that's what our work buffer is in + // the same applies to the final monitor CM + if (data.cmBackToSRGB || data.finalMonitorCM) // NOLINTNEXTLINE + return WORK_BUFFER_IMAGE_DESCRIPTION; + + // otherwise, default + return DEFAULT_IMAGE_DESCRIPTION; + }(); + + const auto TARGET_IMAGE_DESCRIPTION = [&] { + // if we are CM'ing back, use default sRGB + if (data.cmBackToSRGB) + return DEFAULT_IMAGE_DESCRIPTION; + + // for final CM, use the target description + if (data.finalMonitorCM) + return m_renderData.pMonitor->m_imageDescription; + // otherwise, use chosen, we're drawing into the work buffer + // NOLINTNEXTLINE + return WORK_BUFFER_IMAGE_DESCRIPTION; + }(); + + if (data.blur && *PBLEND && data.blurredBG) + shaderFeatures |= SH_FEAT_BLUR; + + if (data.discardActive) + shaderFeatures |= SH_FEAT_DISCARD; + + const bool CANT_CHECK_CM_EQUALITY = data.cmBackToSRGB || data.finalMonitorCM || (!m_renderData.surface || !m_renderData.surface->m_colorManagement); + + const bool skipCM = !*PENABLECM || !m_cmSupported /* CM unsupported or disabled */ + || m_renderData.pMonitor->doesNoShaderCM() /* no shader needed */ + || (SOURCE_IMAGE_DESCRIPTION->id() == TARGET_IMAGE_DESCRIPTION->id() && !CANT_CHECK_CM_EQUALITY) /* Source and target have the same image description */ + || (((*PPASS && canPassHDRSurface) || + (*PPASS == 1 && !isHDRSurface && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR && m_renderData.pMonitor->m_cmType != NCMType::CM_HDR_EDID)) && + m_renderData.pMonitor->inFullscreenMode()) /* Fullscreen window with pass cm enabled */; + + if (data.allowDim && m_renderData.currentWindow && (m_renderData.currentWindow->m_notRespondingTint->value() > 0 || m_renderData.currentWindow->m_dimPercent->value() > 0)) + shaderFeatures |= SH_FEAT_TINT; + + if (data.round > 0) + shaderFeatures |= SH_FEAT_ROUNDING; + + if (!skipCM) { + shaderFeatures |= SH_FEAT_CM; + + if (TARGET_IMAGE_DESCRIPTION->value().icc.present) + shaderFeatures |= SH_FEAT_ICC; + else { + const bool needsSDRmod = isSDR2HDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); + const bool needsHDRmod = !needsSDRmod && isHDR2SDR(SOURCE_IMAGE_DESCRIPTION->value(), TARGET_IMAGE_DESCRIPTION->value()); + const float maxLuminance = needsHDRmod ? + SOURCE_IMAGE_DESCRIPTION->value().getTFMaxLuminance(-1) : + (SOURCE_IMAGE_DESCRIPTION->value().luminances.max > 0 ? SOURCE_IMAGE_DESCRIPTION->value().luminances.max : SOURCE_IMAGE_DESCRIPTION->value().luminances.reference); + const auto dstMaxLuminance = TARGET_IMAGE_DESCRIPTION->value().luminances.max > 0 ? TARGET_IMAGE_DESCRIPTION->value().luminances.max : 10000; + + if (maxLuminance >= dstMaxLuminance * 1.01) + shaderFeatures |= SH_FEAT_TONEMAP; + + if (data.finalMonitorCM && + (SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || + SOURCE_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && + TARGET_IMAGE_DESCRIPTION->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && + ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || + (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f))) + shaderFeatures |= SH_FEAT_SDR_MOD; + } + } + + if (!shader) + shader = getShaderVariant(SH_FRAG_SURFACE, shaderFeatures); + shader = useShader(shader); + + if (!skipCM) { + if (data.finalMonitorCM || data.cmBackToSRGB) + passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION, TARGET_IMAGE_DESCRIPTION, true, m_renderData.pMonitor->m_sdrMinLuminance, m_renderData.pMonitor->m_sdrMaxLuminance); + else + passCMUniforms(shader, SOURCE_IMAGE_DESCRIPTION); + } + shader->setUniformFloat(SHADER_ALPHA, alpha); + if (shaderFeatures & SH_FEAT_BLUR) { + shader->setUniformInt(SHADER_BLURRED_BG, 1); + // shader->setUniformFloat2(SHADER_UV_OFFSET, 0, 0); + shader->setUniformFloat2(SHADER_UV_OFFSET, newBox.x / m_renderData.pMonitor->m_transformedSize.x, newBox.y / m_renderData.pMonitor->m_transformedSize.y); + shader->setUniformFloat2(SHADER_UV_SIZE, newBox.width / m_renderData.pMonitor->m_transformedSize.x, newBox.height / m_renderData.pMonitor->m_transformedSize.y); + + glActiveTexture(GL_TEXTURE0 + 1); + data.blurredBG->bind(); + } + if (data.discardActive) { shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE)); shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA)); @@ -1612,7 +1437,6 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); - // Rounded corners shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y); shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y); @@ -1633,8 +1457,53 @@ void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, c } else shader->setUniformInt(SHADER_APPLY_TINT, 0); - glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); - glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO)); + return shader; +} + +void CHyprOpenGLImpl::renderTextureInternal(SP tex, const CBox& box, const STextureRenderData& data) { + RASSERT(m_renderData.pMonitor, "Tried to render texture without begin()!"); + RASSERT((tex->m_texID > 0), "Attempted to draw nullptr texture!"); + + TRACY_GPU_ZONE("RenderTextureInternalWithDamage"); + + if (data.damage->empty()) + return; + + CBox newBox = box; + m_renderData.renderModif.applyToBox(newBox); + + // get the needed transform for this texture + const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); + Hyprutils::Math::eTransform TRANSFORM = tex->m_transform; + + if (m_monitorTransformEnabled) + TRANSFORM = Math::composeTransform(MONITOR_INVERTED, TRANSFORM); + + Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); + Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); + + const bool renderToOutput = m_applyFinalShader && g_pHyprRenderer->workBufferImageDescription()->id() == m_renderData.pMonitor->m_imageDescription->id(); + + glActiveTexture(GL_TEXTURE0); + tex->bind(); + + tex->setTexParameter(GL_TEXTURE_WRAP_S, data.wrapX); + tex->setTexParameter(GL_TEXTURE_WRAP_T, data.wrapY); + + if (m_renderData.useNearestNeighbor) { + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, GL_NEAREST); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } else { + tex->setTexParameter(GL_TEXTURE_MAG_FILTER, tex->magFilter); + tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); + } + + auto shader = renderToOutput ? renderToOutputInternal() : renderToFBInternal(data, tex->m_type, newBox); + + shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); + shader->setUniformInt(SHADER_TEX, 0); + GLCALL(glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO))); + GLCALL(glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO))); // this tells GPU can keep reading the old block for previous draws while the CPU writes to a new one. // to avoid stalls if renderTextureInternal is called multiple times on same renderpass @@ -1719,7 +1588,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(SP tex, const CBox& box) tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); } - auto shader = useShader(m_shaders->frag[SH_FRAG_PASSTHRURGBA]); + auto shader = useShader(getShaderVariant(SH_FRAG_PASSTHRURGBA)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); @@ -1751,7 +1620,7 @@ void CHyprOpenGLImpl::renderTextureMatte(SP tex, const CBox& box, CFra Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); - auto shader = useShader(m_shaders->frag[SH_FRAG_MATTE]); + auto shader = useShader(getShaderVariant(SH_FRAG_MATTE)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_ALPHA_MATTE, 1); @@ -1826,6 +1695,7 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi { static auto PBLURCONTRAST = CConfigValue("decoration:blur:contrast"); static auto PBLURBRIGHTNESS = CConfigValue("decoration:blur:brightness"); + static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); PMIRRORSWAPFB->bind(); @@ -1838,7 +1708,26 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi WP shader; - shader = useShader(m_shaders->frag[SH_FRAG_BLURPREPARE]); + // From FB to sRGB + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE, SH_FEAT_CM)); + passCMUniforms(shader, m_renderData.pMonitor->m_imageDescription, DEFAULT_IMAGE_DESCRIPTION); + shader->setUniformFloat(SHADER_SDR_SATURATION, + m_renderData.pMonitor->m_sdrSaturation > 0 && + m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrSaturation : + 1.0f); + shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, + m_renderData.pMonitor->m_sdrBrightness > 0 && + m_renderData.pMonitor->m_imageDescription->value().transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ? + m_renderData.pMonitor->m_sdrBrightness : + 1.0f); + } else + shader = useShader(getShaderVariant(SH_FRAG_BLURPREPARE)); + + Mat3x3 matrix = m_renderData.monitorProjection.projectBox(MONITORBOX, *PBLEND ? HYPRUTILS_TRANSFORM_NORMAL : TRANSFORM); + Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_CONTRAST, *PBLURCONTRAST); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -1910,13 +1799,13 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi CRegion tempDamage{damage}; // and draw - auto shader = useShader(m_shaders->frag[SH_FRAG_BLUR1]); + auto shader = useShader(getShaderVariant(SH_FRAG_BLUR1)); for (auto i = 1; i <= BLUR_PASSES; ++i) { tempDamage = damage.copy().scale(1.f / (1 << i)); drawPass(shader, SH_FRAG_BLUR1, &tempDamage); // down } - shader = useShader(m_shaders->frag[SH_FRAG_BLUR2]); + shader = useShader(getShaderVariant(SH_FRAG_BLUR2)); for (auto i = BLUR_PASSES - 1; i >= 0; --i) { tempDamage = damage.copy().scale(1.f / (1 << i)); // when upsampling we make the region twice as big drawPass(shader, SH_FRAG_BLUR2, &tempDamage); // up @@ -1940,7 +1829,7 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi currentTex->setTexParameter(GL_TEXTURE_MIN_FILTER, GL_LINEAR); - auto shader = useShader(m_shaders->frag[SH_FRAG_BLURFINISH]); + auto shader = useShader(getShaderVariant(SH_FRAG_BLURFINISH)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); @@ -2132,6 +2021,8 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox TRACY_GPU_ZONE("RenderTextureWithBlur"); + static auto PBLEND = CConfigValue("render:use_shader_blur_blend"); + // make a damage region for this window CRegion texDamage{m_renderData.damage}; texDamage.intersect(box.x, box.y, box.width, box.height); @@ -2178,22 +2069,22 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox m_renderData.currentFB->bind(); - const auto NEEDS_STENCIL = m_renderData.discardMode != 0; + auto blurredBG = POUTFB->getTexture(); - if (NEEDS_STENCIL) { - scissor(nullptr); // allow the entire window and stencil to render - glClearStencil(0); - glClear(GL_STENCIL_BUFFER_BIT); + const auto NEEDS_STENCIL = m_renderData.discardMode != 0 && (!data.blockBlurOptimization || (m_renderData.discardMode & DISCARD_ALPHA)); + if (!*PBLEND) { - setCapStatus(GL_STENCIL_TEST, true); + if (NEEDS_STENCIL) { + scissor(nullptr); // allow the entire window and stencil to render + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + setCapStatus(GL_STENCIL_TEST, true); - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - if (USENEWOPTIMIZE && !(m_renderData.discardMode & DISCARD_ALPHA)) - renderRect(box, CHyprColor(0, 0, 0, 0), SRectRenderData{.round = data.round, .roundingPower = data.roundingPower}); - else + glStencilFunc(GL_ALWAYS, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); renderTexture(tex, box, STextureRenderData{.a = data.a, .round = data.round, @@ -2201,66 +2092,73 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP tex, const CBox .discardActive = true, .allowCustomUV = true, .wrapX = data.wrapX, - .wrapY = data.wrapY}); // discard opaque - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + .wrapY = data.wrapY}); // discard opaque and alpha < discardOpacity - glStencilFunc(GL_EQUAL, 1, 0xFF); - glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glStencilFunc(GL_EQUAL, 1, 0xFF); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } + + // stencil done. Render everything. + const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; + const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; + + CBox transformedBox = box; + transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, + m_renderData.pMonitor->m_transformedSize.y); + + CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, + transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, + transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; + + m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; + m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; + + static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); + pushMonitorTransformEnabled(true); + bool renderModif = m_renderData.renderModif.enabled; + if (!USENEWOPTIMIZE) + setRenderModifEnabled(false); + renderTextureInternal(blurredBG, box, + STextureRenderData{ + .damage = &texDamage, + .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, + .round = data.round, + .roundingPower = data.roundingPower, + .discardActive = false, + .allowCustomUV = true, + .noAA = false, + .wrapX = data.wrapX, + .wrapY = data.wrapY, + }); + if (!USENEWOPTIMIZE) + setRenderModifEnabled(renderModif); + popMonitorTransformEnabled(); + + if (NEEDS_STENCIL) + setCapStatus(GL_STENCIL_TEST, false); + + m_renderData.primarySurfaceUVTopLeft = LASTTL; + m_renderData.primarySurfaceUVBottomRight = LASTBR; } - // stencil done. Render everything. - const auto LASTTL = m_renderData.primarySurfaceUVTopLeft; - const auto LASTBR = m_renderData.primarySurfaceUVBottomRight; - - CBox transformedBox = box; - transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x, - m_renderData.pMonitor->m_transformedSize.y); - - CBox monitorSpaceBox = {transformedBox.pos().x / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.pos().y / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y, - transformedBox.width / m_renderData.pMonitor->m_pixelSize.x * m_renderData.pMonitor->m_transformedSize.x, - transformedBox.height / m_renderData.pMonitor->m_pixelSize.y * m_renderData.pMonitor->m_transformedSize.y}; - - m_renderData.primarySurfaceUVTopLeft = monitorSpaceBox.pos() / m_renderData.pMonitor->m_transformedSize; - m_renderData.primarySurfaceUVBottomRight = (monitorSpaceBox.pos() + monitorSpaceBox.size()) / m_renderData.pMonitor->m_transformedSize; - - static auto PBLURIGNOREOPACITY = CConfigValue("decoration:blur:ignore_opacity"); - pushMonitorTransformEnabled(true); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(false); - renderTextureInternal(POUTFB->getTexture(), box, - STextureRenderData{ - .damage = &texDamage, - .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, - .round = data.round, - .roundingPower = data.roundingPower, - .discardActive = false, - .allowCustomUV = true, - .noAA = false, - .wrapX = data.wrapX, - .wrapY = data.wrapY, - }); - if (!USENEWOPTIMIZE) - setRenderModifEnabled(true); - popMonitorTransformEnabled(); - - m_renderData.primarySurfaceUVTopLeft = LASTTL; - m_renderData.primarySurfaceUVBottomRight = LASTBR; - // draw window - setCapStatus(GL_STENCIL_TEST, false); renderTextureInternal(tex, box, STextureRenderData{ .damage = &texDamage, .a = data.a * data.overallA, + .blur = *PBLEND, .round = data.round, .roundingPower = data.roundingPower, - .discardActive = false, + .discardActive = *PBLEND && NEEDS_STENCIL, .allowCustomUV = true, .allowDim = true, .noAA = false, .wrapX = data.wrapX, .wrapY = data.wrapY, + .blurredBG = blurredBG, }); m_renderData.currentFB->invalidate({GL_STENCIL_ATTACHMENT}); @@ -2300,7 +2198,15 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr const auto BLEND = m_blend; blend(true); - WP shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); + WP shader; + + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); + } else + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA); @@ -2379,7 +2285,14 @@ void CHyprOpenGLImpl::renderBorder(const CBox& box, const CGradientValueData& gr const auto BLEND = m_blend; blend(true); - WP shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); + WP shader; + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + if (!skipCM) { + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING | SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); + } else + shader = useShader(getShaderVariant(SH_FRAG_BORDER1, SH_FEAT_ROUNDING)); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA); @@ -2452,7 +2365,11 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun blend(true); - auto shader = useShader(m_shaders->frag[SH_FRAG_SHADOW]); + const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present; + const bool skipCM = !m_cmSupported || g_pHyprRenderer->workBufferImageDescription()->id() == DEFAULT_IMAGE_DESCRIPTION->id(); + auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD))); + if (!skipCM) + passCMUniforms(shader, DEFAULT_IMAGE_DESCRIPTION); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix()); shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a); @@ -3141,53 +3058,23 @@ bool CHyprOpenGLImpl::explicitSyncSupported() { return m_exts.EGL_ANDROID_native_fence_sync_ext; } -WP CHyprOpenGLImpl::getSurfaceShader(uint8_t features) { - if (!m_shaders->fragVariants.contains(features)) { +WP CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) { + if (!m_shaders->fragVariants[frag].contains(features)) { + auto shader = makeShared(); - auto shader = makeShared(); - auto includes = m_includes; - includes["get_rgb_pixel.glsl"] = includes[features & SH_FEAT_RGBA ? "get_rgba_pixel.glsl" : "get_rgbx_pixel.glsl"]; - if (!(features & SH_FEAT_DISCARD)) { - includes["discard.glsl"] = ""; - includes["do_discard.glsl"] = ""; - } - if (!(features & SH_FEAT_TINT)) { - includes["tint.glsl"] = ""; - includes["do_tint.glsl"] = ""; - } - if (!(features & SH_FEAT_ROUNDING)) { - includes["rounding.glsl"] = ""; - includes["do_rounding.glsl"] = ""; - } - if (!(features & SH_FEAT_CM)) { - includes["surface_CM.glsl"] = ""; - includes["CM.glsl"] = ""; - includes["do_CM.glsl"] = ""; - } - if (!(features & SH_FEAT_TONEMAP)) { - includes["tonemap.glsl"] = ""; - includes["do_tonemap.glsl"] = ""; - } - if (!(features & SH_FEAT_SDR_MOD)) { - includes["sdr_mod.glsl"] = ""; - includes["do_sdr_mod.glsl"] = ""; - } - if (!(features & SH_FEAT_TONEMAP || features & SH_FEAT_SDR_MOD)) - includes["primaries_xyz.glsl"] = includes["primaries_xyz_const.glsl"]; + Log::logger->log(Log::INFO, "compiling feature set {} for {}", features, FRAG_SHADERS[frag]); - Log::logger->log(Log::INFO, "getSurfaceShader: compiling feature set {}", features); - const auto fragSrc = processShader("surface.frag", includes, MAX_INCLUDE_DEPTH); - if (shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) { - m_shaders->fragVariants[features] = shader; - return shader; - } else { - Log::logger->log(Log::ERR, "getSurfaceShader failed for {}. Falling back to old branching", features); - m_shaders->fragVariants[features] = nullptr; - } + const auto fragSrc = g_pShaderLoader->getVariantSource(frag, features); + + if (!shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) + Log::logger->log(Log::ERR, "shader features {} failed for {}", features, FRAG_SHADERS[frag]); + + m_shaders->fragVariants[frag][features] = shader; + return shader; } - ASSERT(m_shaders->fragVariants[features]); - return m_shaders->fragVariants[features]; + ASSERT(m_shaders->fragVariants[frag][features]); + return m_shaders->fragVariants[frag][features]; } std::vector CHyprOpenGLImpl::getDRMFormats() { diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index d801d40b..82b34119 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -31,6 +31,7 @@ #include "../debug/TracyDefines.hpp" #include "../protocols/core/Compositor.hpp" +#include "render/ShaderLoader.hpp" struct gbm_device; class CHyprRenderer; @@ -87,54 +88,23 @@ enum eMonitorExtraRenderFBs : uint8_t { FB_MONITOR_RENDER_EXTRA_BLUR, }; -enum ePreparedFragmentShader : uint8_t { - SH_FRAG_QUAD = 0, - SH_FRAG_PASSTHRURGBA, - SH_FRAG_MATTE, - SH_FRAG_EXT, - SH_FRAG_BLUR1, - SH_FRAG_BLUR2, - SH_FRAG_CM_BLURPREPARE, - SH_FRAG_BLURPREPARE, - SH_FRAG_BLURFINISH, - SH_FRAG_SHADOW, - SH_FRAG_CM_BORDER1, - SH_FRAG_BORDER1, - SH_FRAG_GLITCH, - - SH_FRAG_LAST, -}; - -enum ePreparedFragmentShaderFeature : uint8_t { - SH_FEAT_UNKNOWN = 0, // all features just in case - - SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling - SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling - SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint - SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 - SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM - SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 - SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) - - // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD -}; - struct SFragShaderDesc { - ePreparedFragmentShader id; - const char* file; + Render::ePreparedFragmentShader id; + const char* file; }; struct SPreparedShaders { - SPreparedShaders() { - for (auto& f : frag) { - f = makeShared(); - } - } + // SPreparedShaders() { + // for (auto& f : frag) { + // f = makeShared(); + // } + // } - std::string TEXVERTSRC; - std::string TEXVERTSRC320; - std::array, SH_FRAG_LAST> frag; - std::map> fragVariants; + std::string TEXVERTSRC; + std::string TEXVERTSRC320; + // std::array, SH_FRAG_LAST> frag; + // std::map> fragVariants; + std::array>, Render::SH_FRAG_LAST> fragVariants; }; struct SMonitorRenderData { @@ -242,6 +212,8 @@ class CHyprOpenGLImpl { bool cmBackToSRGB = false; bool noCM = false; bool finalMonitorCM = false; + SP cmBackToSRGBSource; + SP blurredBG; }; struct SBorderRenderData { @@ -316,18 +288,15 @@ class CHyprOpenGLImpl { std::vector getDRMFormatModifiers(DRMFormat format); EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); - bool initShaders(); + bool initShaders(const std::string& path = ""); WP useShader(WP prog); - void ensureLockTexturesRendered(bool load); - bool explicitSyncSupported(); - WP getSurfaceShader(uint8_t features); + WP getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0); bool m_shadersInitialized = false; SP m_shaders; - std::map m_includes; SCurrentRenderData m_renderData; @@ -431,6 +400,7 @@ class CHyprOpenGLImpl { void initEGL(bool gbm); EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); void initAssets(); + void ensureLockTexturesRendered(bool load); void initMissingAssetTexture(); void requestBackgroundResource(); @@ -454,6 +424,8 @@ class CHyprOpenGLImpl { void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); void renderRectWithBlurInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); + WP renderToOutputInternal(); + WP renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox); void renderTextureInternal(SP, const CBox&, const STextureRenderData& data); void renderTextureWithBlurInternal(SP, const CBox&, const STextureRenderData& data); diff --git a/src/render/Renderbuffer.cpp b/src/render/Renderbuffer.cpp index bb638e20..ebc4958f 100644 --- a/src/render/Renderbuffer.cpp +++ b/src/render/Renderbuffer.cpp @@ -41,6 +41,7 @@ CRenderbuffer::CRenderbuffer(SP buffer, uint32_t format) : glGenFramebuffers(1, &m_framebuffer.m_fb); m_framebuffer.m_fbAllocated = true; m_framebuffer.m_size = buffer->size; + m_framebuffer.m_drmFormat = dma.format; m_framebuffer.bind(); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index a2b7c60a..8a1cf4b3 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -32,6 +32,7 @@ #include "../event/EventBus.hpp" #include "helpers/CursorShapes.hpp" #include "helpers/Monitor.hpp" +#include "helpers/cm/ColorManagement.hpp" #include "pass/TexPassElement.hpp" #include "pass/ClearPassElement.hpp" #include "pass/RectPassElement.hpp" @@ -41,7 +42,6 @@ #include "../protocols/ColorManagement.hpp" #include "../protocols/types/ContentType.hpp" #include "../helpers/MiscFunctions.hpp" -#include "../event/EventBus.hpp" #include "render/OpenGL.hpp" #include @@ -1260,6 +1260,79 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP surface, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { + const auto sdrEOTF = NTransferFunction::fromConfig(); + NColorManagement::eTransferFunction srcTF; + + auto& m_renderData = g_pHyprOpenGL->m_renderData; + if (m_renderData.surface.valid()) { + if (m_renderData.surface->m_colorManagement.valid()) { + if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) + srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22; + else + srcTF = imageDescription->value().transferFunction; + } else if (sdrEOTF == NTransferFunction::TF_SRGB) + srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB; + else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) + srcTF = NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22; + else + srcTF = imageDescription->value().transferFunction; + } else + srcTF = imageDescription->value().transferFunction; + + const bool needsSDRmod = modifySDR && isSDR2HDR(imageDescription->value(), targetImageDescription->value()); + const bool needsHDRmod = !needsSDRmod && isHDR2SDR(imageDescription->value(), targetImageDescription->value()); + const float maxLuminance = needsHDRmod ? + imageDescription->value().getTFMaxLuminance(-1) : + (imageDescription->value().luminances.max > 0 ? imageDescription->value().luminances.max : imageDescription->value().luminances.reference); + const auto dstMaxLuminance = targetImageDescription->value().luminances.max > 0 ? targetImageDescription->value().luminances.max : 10000; + + auto matrix = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); + auto toXYZ = targetImageDescription->getPrimaries()->value().toXYZ(); + + const bool needsMod = (imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_SRGB || imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_GAMMA22) && + targetImageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ && + ((m_renderData.pMonitor->m_sdrSaturation > 0 && m_renderData.pMonitor->m_sdrSaturation != 1.0f) || + (m_renderData.pMonitor->m_sdrBrightness > 0 && m_renderData.pMonitor->m_sdrBrightness != 1.0f)); + + return { + .sourceTF = srcTF, + .targetTF = targetImageDescription->value().transferFunction, + .srcTFRange = {.min = imageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), + .max = imageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)}, + .dstTFRange = {.min = targetImageDescription->value().getTFMinLuminance(needsSDRmod ? sdrMinLuminance : -1), + .max = targetImageDescription->value().getTFMaxLuminance(needsSDRmod ? sdrMaxLuminance : -1)}, + .srcRefLuminance = imageDescription->value().luminances.reference, + .dstRefLuminance = targetImageDescription->value().luminances.reference, + .convertMatrix = matrix.mat(), + + .needsTonemap = maxLuminance >= dstMaxLuminance * 1.01, + .maxLuminance = maxLuminance * targetImageDescription->value().luminances.reference / imageDescription->value().luminances.reference, + .dstMaxLuminance = dstMaxLuminance, + .dstPrimaries2XYZ = toXYZ.mat(), + .needsSDRmod = needsMod, + .sdrSaturation = needsSDRmod && m_renderData.pMonitor->m_sdrSaturation > 0 ? m_renderData.pMonitor->m_sdrSaturation : 1.0f, + .sdrBrightnessMultiplier = needsSDRmod && m_renderData.pMonitor->m_sdrBrightness > 0 ? m_renderData.pMonitor->m_sdrBrightness : 1.0f, + }; +} + void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) { static std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now(); static std::chrono::high_resolution_clock::time_point renderStartOverlay = std::chrono::high_resolution_clock::now(); @@ -2200,10 +2273,8 @@ std::tuple CHyprRenderer::getRenderTimes(PHLMONITOR pMonito float maxRenderTime = 0; float minRenderTime = 9999; for (auto const& rt : POVERLAY->m_lastRenderTimes) { - if (rt > maxRenderTime) - maxRenderTime = rt; - if (rt < minRenderTime) - minRenderTime = rt; + maxRenderTime = std::max(rt, maxRenderTime); + minRenderTime = std::min(rt, minRenderTime); avgRenderTime += rt; } avgRenderTime /= POVERLAY->m_lastRenderTimes.empty() ? 1 : POVERLAY->m_lastRenderTimes.size(); @@ -2706,6 +2777,16 @@ void CHyprRenderer::renderSnapshot(WP popup) { m_renderPass.add(makeUnique(std::move(data))); } +NColorManagement::PImageDescription CHyprRenderer::workBufferImageDescription() { + const auto& m_renderData = g_pHyprOpenGL->m_renderData; + // TODO + // const bool IS_MONITOR_ICC = m_renderData.pMonitor->m_imageDescription.valid() && m_renderData.pMonitor->m_imageDescription->value().icc.present; + // const auto sdrEOTF = NTransferFunction::fromConfig(IS_MONITOR_ICC); + // const auto CHOSEN_SDR_EOTF = sdrEOTF != NTransferFunction::TF_SRGB ? NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22 : NColorManagement::CM_TRANSFER_FUNCTION_SRGB; + + return m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF}); +} + bool CHyprRenderer::shouldBlur(PHLLS ls) { if (m_bRenderingSnapshot) return false; @@ -2729,3 +2810,7 @@ bool CHyprRenderer::shouldBlur(WP p) { return *PBLURPOPUPS && *PBLUR; } + +bool CHyprRenderer::reloadShaders(const std::string& path) { + return g_pHyprOpenGL->initShaders(path); +} \ No newline at end of file diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index 24e0fb66..74b70283 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -1,7 +1,10 @@ #pragma once #include "../defines.hpp" +#include +#include #include +#include #include "../helpers/Monitor.hpp" #include "../desktop/view/LayerSurface.hpp" #include "OpenGL.hpp" @@ -10,12 +13,21 @@ #include "../helpers/math/Math.hpp" #include "../helpers/time/Time.hpp" #include "../../protocols/cursor-shape-v1.hpp" +#include "helpers/cm/ColorManagement.hpp" struct SMonitorRule; class CWorkspace; class CInputPopup; class IHLBuffer; class CEventLoopTimer; + +const std::vector ASSET_PATHS = { +#ifdef DATAROOTDIR + DATAROOTDIR, +#endif + "/usr/share", + "/usr/local/share", +}; class CToplevelExportProtocolManager; class CInputManager; struct SSessionLockSurface; @@ -48,6 +60,29 @@ struct SRenderWorkspaceUntilData { PHLWINDOW w; }; +struct STFRange { + float min = 0; + float max = 80; +}; + +struct SCMSettings { + NColorManagement::eTransferFunction sourceTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; + NColorManagement::eTransferFunction targetTF = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22; + STFRange srcTFRange; + STFRange dstTFRange; + float srcRefLuminance = 80; + float dstRefLuminance = 80; + std::array, 3> convertMatrix; + + bool needsTonemap = false; + float maxLuminance = 80; + float dstMaxLuminance = 80; + std::array, 3> dstPrimaries2XYZ; + bool needsSDRmod = false; + float sdrSaturation = 1.0; + float sdrBrightnessMultiplier = 1.0; +}; + class CHyprRenderer { public: CHyprRenderer(); @@ -89,6 +124,9 @@ class CHyprRenderer { void renderSnapshot(PHLLS); void renderSnapshot(WP); + // + NColorManagement::PImageDescription workBufferImageDescription(); + // if RENDER_MODE_NORMAL, provided damage will be written to. // otherwise, it will be the one used. bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP buffer = {}, CFramebuffer* fb = nullptr, bool simple = false); @@ -121,6 +159,10 @@ class CHyprRenderer { CRenderPass m_renderPass = {}; + SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription, + SP surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1); + bool reloadShaders(const std::string& path = ""); + private: void arrangeLayerArray(PHLMONITOR, const std::vector&, bool, CBox*); void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry); diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp index 5f18b906..ead841a5 100644 --- a/src/render/Shader.cpp +++ b/src/render/Shader.cpp @@ -139,11 +139,13 @@ void CShader::getUniformLocations() { m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation"); m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier"); m_uniformLocations[SHADER_CONVERT_MATRIX] = getUniform("convertMatrix"); - m_uniformLocations[SHADER_USE_ICC] = getUniform("useIcc"); m_uniformLocations[SHADER_LUT_3D] = getUniform("iccLut3D"); m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize"); // m_uniformLocations[SHADER_TEX] = getUniform("tex"); + m_uniformLocations[SHADER_BLURRED_BG] = getUniform("blurredBG"); + m_uniformLocations[SHADER_UV_SIZE] = getUniform("uvSize"); + m_uniformLocations[SHADER_UV_OFFSET] = getUniform("uvOffset"); m_uniformLocations[SHADER_ALPHA] = getUniform("alpha"); m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos"); m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord"); diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index 184f6771..9b097c44 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -74,9 +74,11 @@ enum eShaderUniform : uint8_t { SHADER_POINTER_INACTIVE_TIMEOUT, SHADER_POINTER_LAST_ACTIVE, SHADER_POINTER_SIZE, - SHADER_USE_ICC, SHADER_LUT_3D, SHADER_LUT_SIZE, + SHADER_BLURRED_BG, + SHADER_UV_SIZE, + SHADER_UV_OFFSET, SHADER_LAST, }; diff --git a/src/render/ShaderLoader.cpp b/src/render/ShaderLoader.cpp new file mode 100644 index 00000000..0d2d0ee4 --- /dev/null +++ b/src/render/ShaderLoader.cpp @@ -0,0 +1,176 @@ +#include "ShaderLoader.hpp" +#include +#include +#include +#include +#include +#include "../debug/log/Logger.hpp" +#include "shaders/Shaders.hpp" +#include "../helpers/fs/FsUtils.hpp" +#include "Renderer.hpp" +#include +#include +#include + +using namespace Render; + +CShaderLoader::CShaderLoader(const std::vector includes, const std::array& frags, const std::string shaderPath) : m_shaderPath(shaderPath) { + m_callbacks = glsl_include_callbacks_t{ + .include_local = + [](void* ctx, const char* header_name, const char* includer_name, size_t include_depth) { + auto shaderLoader = sc(ctx); + auto res = new glsl_include_result_t; + if (shaderLoader->m_overrideDefines.length() && std::string{header_name} == "defines.h") { + res->header_name = header_name; + res->header_data = shaderLoader->m_overrideDefines.c_str(); + res->header_length = shaderLoader->m_overrideDefines.length(); + } else if (shaderLoader->includes().contains(header_name)) { + res->header_name = header_name; + res->header_data = shaderLoader->includes().at(header_name).c_str(); + res->header_length = shaderLoader->includes().at(header_name).length(); + } else { + res->header_name = nullptr; + res->header_data = nullptr; + res->header_length = 0; + } + + shaderLoader->m_includeResults.push_back(res); + return res; + }, + .free_include_result = + [](void* ctx, glsl_include_result_t* result) { + auto shaderLoader = sc(ctx); + std::erase(shaderLoader->m_includeResults, result); + delete result; + return 0; + }, + }; + + for (const auto& inc : includes) { + include(inc); + } + + std::ranges::transform(frags, m_fragFiles.begin(), [&](const auto& filename) { return loadShader(filename); }); +} + +CShaderLoader::~CShaderLoader() { + // glslFreeIncludeResult should leave it empty by this point + for (const auto& res : m_includeResults) { + delete res; + } +} + +void CShaderLoader::include(const std::string& filename) { + m_includes.insert({filename, loadShader(filename)}); +} + +std::string CShaderLoader::getDefines(ShaderFeatureFlags features) { + std::string res = ""; + std::map defines = { + {"USE_RGBA", features & SH_FEAT_RGBA ? "1" : "0"}, {"USE_DISCARD", features & SH_FEAT_DISCARD ? "1" : "0"}, {"USE_TINT", features & SH_FEAT_TINT ? "1" : "0"}, + {"USE_ROUNDING", features & SH_FEAT_ROUNDING ? "1" : "0"}, {"USE_CM", features & SH_FEAT_CM ? "1" : "0"}, {"USE_TONEMAP", features & SH_FEAT_TONEMAP ? "1" : "0"}, + {"USE_SDR_MOD", features & SH_FEAT_SDR_MOD ? "1" : "0"}, {"USE_BLUR", features & SH_FEAT_BLUR ? "1" : "0"}, {"USE_ICC", features & SH_FEAT_ICC ? "1" : "0"}, + }; + for (const auto& [name, value] : defines) { + res += std::format("#define {} {}\n", name, value); + } + return res; +} + +std::string CShaderLoader::processSource(const std::string& source, glslang_stage_t stage) { + const glslang_input_t input = { + .language = GLSLANG_SOURCE_GLSL, + .stage = stage, + .client = GLSLANG_CLIENT_NONE, + .target_language = GLSLANG_TARGET_NONE, + .code = source.c_str(), + .default_version = 100, + .default_profile = GLSLANG_NO_PROFILE, + .force_default_version_and_profile = false, + .forward_compatible = false, + .messages = GLSLANG_MSG_DEFAULT_BIT, + .resource = glslang_default_resource(), + .callbacks = m_callbacks, + .callbacks_ctx = this, + }; + + glslang_shader_t* shader = glslang_shader_create(&input); + + if (!glslang_shader_preprocess(shader, &input)) { + Log::logger->log(Log::ERR, "GLSL preprocessing failed"); + Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_log(shader)); + Log::logger->log(Log::ERR, "{}", glslang_shader_get_info_debug_log(shader)); + Log::logger->log(Log::ERR, "{}", input.code); + glslang_shader_delete(shader); + return source; + } + + std::stringstream stream(glslang_shader_get_preprocessed_code(shader)); + std::string code = ""; + std::string line; + + while (std::getline(stream, line)) { + if (!line.starts_with("#line ")) + code += line + "\n"; + } + + return code; +} + +std::string CShaderLoader::process(const std::string& filename) { + auto source = loadShader(filename); + return processSource(source, filename.ends_with(".vert") ? GLSLANG_STAGE_VERTEX : GLSLANG_STAGE_FRAGMENT); +} + +std::string CShaderLoader::process(const std::string& filename, const std::map& defines) { + m_overrideDefines = ""; + for (const auto& [name, value] : defines) { + m_overrideDefines += std::format("#define {} {}\n", name, value); + } + const auto& res = process(filename); + m_overrideDefines = ""; + return res; +} + +std::string CShaderLoader::getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features) { + static const auto PCM = CConfigValue("render:cm_enabled"); + if (!*PCM) + features &= ~(SH_FEAT_CM | SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD); + + if (!m_fragVariants[frag].contains(features)) { + ASSERT(m_fragFiles[frag].length()); + m_overrideDefines = getDefines(features); + m_fragVariants[frag][features] = processSource(m_fragFiles[frag]); + m_overrideDefines = ""; + } + + return m_fragVariants[frag][features]; +} + +const std::map& CShaderLoader::includes() { + return m_includes; +} + +// TODO notify user if bundled shader is newer than ~/.config override +std::string CShaderLoader::loadShader(const std::string& filename) { + if (m_shaderPath.length()) { + std::filesystem::path path = m_shaderPath; + const auto src = NFsUtils::readFileAsString(path / filename); + if (src.has_value()) + return src.value(); + } + const auto home = Hyprutils::Path::getHome(); + if (home.has_value()) { + const auto src = NFsUtils::readFileAsString(home.value() + "/hypr/shaders/" + filename); + if (src.has_value()) + return src.value(); + } + for (auto& e : ASSET_PATHS) { + const auto src = NFsUtils::readFileAsString(std::string{e} + "/hypr/shaders/" + filename); + if (src.has_value()) + return src.value(); + } + if (SHADERS.contains(filename)) + return SHADERS.at(filename); + throw std::runtime_error(std::format("Couldn't load shader {}", filename)); +} diff --git a/src/render/ShaderLoader.hpp b/src/render/ShaderLoader.hpp new file mode 100644 index 00000000..e522e9fa --- /dev/null +++ b/src/render/ShaderLoader.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "../helpers/memory/Memory.hpp" + +namespace Render { + enum ePreparedFragmentShaderFeature : uint16_t { + SH_FEAT_UNKNOWN = 0, // all features just in case + + SH_FEAT_RGBA = (1 << 0), // RGBA/RGBX texture sampling + SH_FEAT_DISCARD = (1 << 1), // RGBA/RGBX texture sampling + SH_FEAT_TINT = (1 << 2), // uniforms: tint; condition: applyTint + SH_FEAT_ROUNDING = (1 << 3), // uniforms: radius, roundingPower, topLeft, fullSize; condition: radius > 0 + SH_FEAT_CM = (1 << 4), // uniforms: srcTFRange, dstTFRange, srcRefLuminance, convertMatrix; condition: !skipCM + SH_FEAT_TONEMAP = (1 << 5), // uniforms: maxLuminance, dstMaxLuminance, dstRefLuminance; condition: maxLuminance < dstMaxLuminance * 1.01 + SH_FEAT_SDR_MOD = (1 << 6), // uniforms: sdrSaturation, sdrBrightnessMultiplier; condition: SDR <-> HDR && (sdrSaturation != 1 || sdrBrightnessMultiplier != 1) + SH_FEAT_BLUR = (1 << 7), // condition: render:use_shader_blur_blend + SH_FEAT_ICC = (1 << 8), // + + // uniforms: targetPrimariesXYZ; condition: SH_FEAT_TONEMAP || SH_FEAT_SDR_MOD + }; + + using ShaderFeatureFlags = uint16_t; + + enum ePreparedFragmentShader : uint8_t { + SH_FRAG_QUAD = 0, + SH_FRAG_PASSTHRURGBA, + SH_FRAG_MATTE, + SH_FRAG_EXT, + SH_FRAG_BLUR1, + SH_FRAG_BLUR2, + SH_FRAG_BLURPREPARE, + SH_FRAG_BLURFINISH, + SH_FRAG_SHADOW, + SH_FRAG_SURFACE, + SH_FRAG_BORDER1, + SH_FRAG_GLITCH, + + SH_FRAG_LAST, + }; + + class CShaderLoader { + public: + CShaderLoader(const std::vector includes, const std::array& frags, const std::string shaderPath = ""); + ~CShaderLoader(); + + void include(const std::string& filename); + std::string process(const std::string& filename); + std::string process(const std::string& filename, const std::map& defines); + + std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features); + + const std::map& includes(); + + std::vector m_includeResults; + + private: + std::string loadShader(const std::string& filename); + std::string getDefines(ShaderFeatureFlags features); + std::string processSource(const std::string& source, glslang_stage_t stage = GLSLANG_STAGE_FRAGMENT); + + // + std::string m_shaderPath; + std::array m_fragFiles; + std::array, SH_FRAG_LAST> m_fragVariants; + std::map m_includes; + + std::string m_overrideDefines; + glsl_include_callbacks_t m_callbacks; + }; + + inline UP g_pShaderLoader; +} diff --git a/src/render/shaders/glsl/CM.glsl b/src/render/shaders/glsl/CM.glsl index 66d84885..323a3008 100644 --- a/src/render/shaders/glsl/CM.glsl +++ b/src/render/shaders/glsl/CM.glsl @@ -1,306 +1,27 @@ -uniform vec2 srcTFRange; -uniform vec2 dstTFRange; +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif +#include "cm_helpers.glsl" + +uniform vec2 srcTFRange; +uniform vec2 dstTFRange; uniform float srcRefLuminance; -uniform mat3 convertMatrix; +uniform mat3 convertMatrix; -#include "sdr_mod.glsl" - -uniform int useIcc; +#if USE_ICC uniform highp sampler3D iccLut3D; -uniform float iccLutSize; +uniform float iccLutSize; +#endif -//enum eTransferFunction -#define CM_TRANSFER_FUNCTION_BT1886 1 -#define CM_TRANSFER_FUNCTION_GAMMA22 2 -#define CM_TRANSFER_FUNCTION_GAMMA28 3 -#define CM_TRANSFER_FUNCTION_ST240 4 -#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5 -#define CM_TRANSFER_FUNCTION_LOG_100 6 -#define CM_TRANSFER_FUNCTION_LOG_316 7 -#define CM_TRANSFER_FUNCTION_XVYCC 8 -#define CM_TRANSFER_FUNCTION_SRGB 9 -#define CM_TRANSFER_FUNCTION_EXT_SRGB 10 -#define CM_TRANSFER_FUNCTION_ST2084_PQ 11 -#define CM_TRANSFER_FUNCTION_ST428 12 -#define CM_TRANSFER_FUNCTION_HLG 13 +#if USE_SDR_MOD +uniform float sdrSaturation; +uniform float sdrBrightnessMultiplier; +#endif -// sRGB constants -#define SRGB_POW 2.4 -#define SRGB_CUT 0.0031308 -#define SRGB_SCALE 12.92 -#define SRGB_ALPHA 1.055 - -#define BT1886_POW (1.0 / 0.45) -#define BT1886_CUT 0.018053968510807 -#define BT1886_SCALE 4.5 -#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT) - -// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf -#define ST240_POW (1.0 / 0.45) -#define ST240_CUT 0.0228 -#define ST240_SCALE 4.0 -#define ST240_ALPHA 1.1115 - -#define ST428_POW 2.6 -#define ST428_SCALE (52.37 / 48.0) - -// PQ constants -#define PQ_M1 0.1593017578125 -#define PQ_M2 78.84375 -#define PQ_INV_M1 (1.0 / PQ_M1) -#define PQ_INV_M2 (1.0 / PQ_M2) -#define PQ_C1 0.8359375 -#define PQ_C2 18.8515625 -#define PQ_C3 18.6875 - -// HLG constants -#define HLG_D_CUT (1.0 / 12.0) -#define HLG_E_CUT 0.5 -#define HLG_A 0.17883277 -#define HLG_B 0.28466892 -#define HLG_C 0.55991073 - -#define SDR_MIN_LUMINANCE 0.2 -#define SDR_MAX_LUMINANCE 80.0 -#define HDR_MIN_LUMINANCE 0.005 -#define HDR_MAX_LUMINANCE 10000.0 -#define HLG_MAX_LUMINANCE 1000.0 - -#define M_E 2.718281828459045 - - -vec3 applyIcc3DLut(vec3 linearRgb01) { - vec3 x = clamp(linearRgb01, 0.0, 1.0); - - // Map [0..1] to texel centers to avoid edge issues - float N = iccLutSize; - vec3 coord = (x * (N - 1.0) + 0.5) / N; - - return texture(iccLut3D, coord).rgb; -} - -vec3 xy2xyz(vec2 xy) { - if (xy.y == 0.0) - return vec3(0.0, 0.0, 0.0); - - return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y); -} - -// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf -vec3 tfInvPQ(vec3 color) { - vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2)); - return pow( - (max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), - vec3(PQ_INV_M1) - ); -} - -vec3 tfInvHLG(vec3 color) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT)); - vec3 lo = color.rgb * color.rgb / 3.0; - vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0; - return mix(hi, lo, isLow); -} - -// Many transfer functions (including sRGB) follow the same pattern: a linear -// segment for small values and a power function for larger values. The -// following function implements this pattern from which sRGB, BT.1886, and -// others can be derived by plugging in the right constants. -vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale)); - vec3 lo = color.rgb / scale; - vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma)); - return mix(hi, lo, isLow); -} - -vec3 tfInvSRGB(vec3 color) { - return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); -} - -vec3 tfInvExtSRGB(vec3 color) { - // EXT sRGB is the sRGB transfer function mirrored around 0. - return sign(color) * tfInvSRGB(abs(color)); -} - -vec3 tfInvBT1886(vec3 color) { - return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); -} - -vec3 tfInvXVYCC(vec3 color) { - // The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0, - // same as what EXT sRGB is to sRGB. - return sign(color) * tfInvBT1886(abs(color)); -} - -vec3 tfInvST240(vec3 color) { - return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); -} - -// Forward transfer functions corresponding to the inverse functions above. -vec3 tfPQ(vec3 color) { - vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1)); - return pow( - (vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), - vec3(PQ_M2) - ); -} - -vec3 tfHLG(vec3 color) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT)); - vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0); - vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C; - return mix(hi, lo, isLow); -} - -vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { - bvec3 isLow = lessThanEqual(color.rgb, vec3(thres)); - vec3 lo = color.rgb * scale; - vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0); - return mix(hi, lo, isLow); -} - -vec3 tfSRGB(vec3 color) { - return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); -} - -vec3 tfExtSRGB(vec3 color) { - // EXT sRGB is the sRGB transfer function mirrored around 0. - return sign(color) * tfSRGB(abs(color)); -} - -vec3 tfBT1886(vec3 color) { - return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); -} - -vec3 tfXVYCC(vec3 color) { - // The transfer function for XVYCC is the BT1886 transfer function mirrored around 0, - // same as what EXT sRGB is to sRGB. - return sign(color) * tfBT1886(abs(color)); -} - -vec3 tfST240(vec3 color) { - return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); -} - -vec3 toLinearRGB(vec3 color, int tf) { - switch (tf) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: - return color; - case CM_TRANSFER_FUNCTION_ST2084_PQ: - return tfInvPQ(color); - case CM_TRANSFER_FUNCTION_GAMMA22: - return pow(max(color, vec3(0.0)), vec3(2.2)); - case CM_TRANSFER_FUNCTION_GAMMA28: - return pow(max(color, vec3(0.0)), vec3(2.8)); - case CM_TRANSFER_FUNCTION_HLG: - return tfInvHLG(color); - case CM_TRANSFER_FUNCTION_EXT_SRGB: - return tfInvExtSRGB(color); - case CM_TRANSFER_FUNCTION_BT1886: - return tfInvBT1886(color); - case CM_TRANSFER_FUNCTION_ST240: - return tfInvST240(color); - case CM_TRANSFER_FUNCTION_LOG_100: - return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); - case CM_TRANSFER_FUNCTION_LOG_316: - return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); - case CM_TRANSFER_FUNCTION_XVYCC: - return tfInvXVYCC(color); - case CM_TRANSFER_FUNCTION_ST428: - return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE; - case CM_TRANSFER_FUNCTION_SRGB: - default: - return tfInvSRGB(color); - } -} - -vec4 toLinear(vec4 color, int tf) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - return color; - - color.rgb /= max(color.a, 0.001); - color.rgb = toLinearRGB(color.rgb, tf); - color.rgb *= color.a; - return color; -} - -vec4 toNit(vec4 color, vec2 range) { - color.rgb = color.rgb * (range[1] - range[0]) + range[0]; - return color; -} - -vec3 fromLinearRGB(vec3 color, int tf) { - switch (tf) { - case CM_TRANSFER_FUNCTION_EXT_LINEAR: - return color; - case CM_TRANSFER_FUNCTION_ST2084_PQ: - return tfPQ(color); - case CM_TRANSFER_FUNCTION_GAMMA22: - return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2)); - case CM_TRANSFER_FUNCTION_GAMMA28: - return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8)); - case CM_TRANSFER_FUNCTION_HLG: - return tfHLG(color); - case CM_TRANSFER_FUNCTION_EXT_SRGB: - return tfExtSRGB(color); - case CM_TRANSFER_FUNCTION_BT1886: - return tfBT1886(color); - case CM_TRANSFER_FUNCTION_ST240: - return tfST240(color); - case CM_TRANSFER_FUNCTION_LOG_100: - return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01))); - case CM_TRANSFER_FUNCTION_LOG_316: - return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0))); - case CM_TRANSFER_FUNCTION_XVYCC: - return tfXVYCC(color); - case CM_TRANSFER_FUNCTION_ST428: - return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW)); - case CM_TRANSFER_FUNCTION_SRGB: - default: - return tfSRGB(color); - } -} - -vec4 fromLinear(vec4 color, int tf) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - return color; - - color.rgb /= max(color.a, 0.001); - color.rgb = fromLinearRGB(color.rgb, tf); - color.rgb *= color.a; - return color; -} - -vec4 fromLinearNit(vec4 color, int tf, vec2 range) { - if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) - color.rgb = color.rgb / SDR_MAX_LUMINANCE; - else { - color.rgb /= max(color.a, 0.001); - color.rgb = (color.rgb - range[0]) / (range[1] - range[0]); - color.rgb = fromLinearRGB(color.rgb, tf); - color.rgb *= color.a; - } - return color; -} - -#include "tonemap.glsl" - -vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 dstxyz) { - pixColor.rgb /= max(pixColor.a, 0.001); - pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); - - if (useIcc == 1) { - pixColor.rgb = applyIcc3DLut(pixColor.rgb); - pixColor.rgb *= pixColor.a; - } else { - pixColor.rgb = convertMatrix * pixColor.rgb; - pixColor = toNit(pixColor, srcTFRange); - pixColor.rgb *= pixColor.a; - #include "do_tonemap.glsl" - pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); - #include "do_sdr_mod.glsl" - } - - return pixColor; -} +#if USE_TONEMAP +uniform float maxLuminance; +uniform float dstMaxLuminance; +uniform float dstRefLuminance; +#endif diff --git a/src/render/shaders/glsl/CMblurprepare.frag b/src/render/shaders/glsl/CMblurprepare.frag deleted file mode 100644 index 8ba2d3f8..00000000 --- a/src/render/shaders/glsl/CMblurprepare.frag +++ /dev/null @@ -1,36 +0,0 @@ -#version 300 es -#extension GL_ARB_shading_language_include : enable - -precision highp float; -in vec2 v_texcoord; // is in 0-1 -uniform sampler2D tex; - -uniform float contrast; -uniform float brightness; - -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction - -#include "CM.glsl" -#include "gain.glsl" - -layout(location = 0) out vec4 fragColor; -void main() { - vec4 pixColor = texture(tex, v_texcoord); - - if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { - pixColor.rgb /= sdrBrightnessMultiplier; - } - pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF); - pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance)); - pixColor = fromLinearNit(pixColor, targetTF, dstTFRange); - - // contrast - if (contrast != 1.0) - pixColor.rgb = gain(pixColor.rgb, contrast); - - // brightness - pixColor.rgb *= max(1.0, brightness); - - fragColor = pixColor; -} diff --git a/src/render/shaders/glsl/CMborder.frag b/src/render/shaders/glsl/CMborder.frag deleted file mode 100644 index 079f940d..00000000 --- a/src/render/shaders/glsl/CMborder.frag +++ /dev/null @@ -1,98 +0,0 @@ -#version 300 es -#extension GL_ARB_shading_language_include : enable - -precision highp float; -in vec2 v_texcoord; - -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -uniform mat3 targetPrimariesXYZ; - -uniform vec2 fullSizeUntransformed; -uniform float radiusOuter; -uniform float thick; - -// Gradients are in OkLabA!!!! {l, a, b, alpha} -uniform vec4 gradient[10]; -uniform vec4 gradient2[10]; -uniform int gradientLength; -uniform int gradient2Length; -uniform float angle; -uniform float angle2; -uniform float gradientLerp; -uniform float alpha; - -#include "rounding.glsl" -#include "CM.glsl" -#include "border.glsl" - -layout(location = 0) out vec4 fragColor; -void main() { - highp vec2 pixCoord = vec2(gl_FragCoord); - highp vec2 pixCoordOuter = pixCoord; - highp vec2 originalPixCoord = v_texcoord; - originalPixCoord *= fullSizeUntransformed; - float additionalAlpha = 1.0; - - vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); - - bool done = false; - - pixCoord -= topLeft + fullSize * 0.5; - pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; - pixCoordOuter = pixCoord; - pixCoord -= fullSize * 0.5 - radius; - pixCoordOuter -= fullSize * 0.5 - radiusOuter; - - // center the pixes don't make it top-left - pixCoord += vec2(1.0, 1.0) / fullSize; - pixCoordOuter += vec2(1.0, 1.0) / fullSize; - - if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { - float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower); - float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower); - float h = (thick / 2.0); - - if (dist < radius - h) { - // lower - float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { - // higher - float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (distOuter < radiusOuter - h) { - additionalAlpha = 1.0; - done = true; - } - } - - // now check for other shit - if (!done) { - // distance to all straight bb borders - float distanceT = originalPixCoord[1]; - float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; - float distanceL = originalPixCoord[0]; - float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest > thick) - discard; - } - - if (additionalAlpha == 0.0) - discard; - - pixColor = getColorForCoord(v_texcoord); - pixColor.rgb *= pixColor[3]; - - pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ); - - pixColor *= alpha * additionalAlpha; - - fragColor = pixColor; -} diff --git a/src/render/shaders/glsl/blur1.frag b/src/render/shaders/glsl/blur1.frag index 796fb42d..044df3cc 100644 --- a/src/render/shaders/glsl/blur1.frag +++ b/src/render/shaders/glsl/blur1.frag @@ -1,143 +1,21 @@ #version 300 es -precision highp float; -uniform sampler2D tex; +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable -uniform float radius; -uniform vec2 halfpixel; -uniform int passes; -uniform float vibrancy; -uniform float vibrancy_darkness; +precision highp float; +uniform sampler2D tex; -in vec2 v_texcoord; - -// see http://alienryderflex.com/hsp.html -const float Pr = 0.299; -const float Pg = 0.587; -const float Pb = 0.114; - -// Y is "v" ( brightness ). X is "s" ( saturation ) -// see https://www.desmos.com/3d/a88652b9a4 -// Determines if high brightness or high saturation is more important -const float a = 0.93; -const float b = 0.11; -const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors -// - -// http://www.flong.com/archive/texts/code/shapers_circ/ -float doubleCircleSigmoid(float x, float a) { - a = clamp(a, 0.0, 1.0); - - float y = .0; - if (x <= a) { - y = a - sqrt(a * a - x * x); - } else { - y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); - } - return y; -} - -vec3 rgb2hsl(vec3 col) { - float red = col.r; - float green = col.g; - float blue = col.b; - - float minc = min(col.r, min(col.g, col.b)); - float maxc = max(col.r, max(col.g, col.b)); - float delta = maxc - minc; - - float lum = (minc + maxc) * 0.5; - float sat = 0.0; - float hue = 0.0; - - if (lum > 0.0 && lum < 1.0) { - float mul = (lum < 0.5) ? (lum) : (1.0 - lum); - sat = delta / (mul * 2.0); - } - - if (delta > 0.0) { - vec3 maxcVec = vec3(maxc); - vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red))); - vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta; - - hue += dot(adds, masks); - hue /= 6.0; - - if (hue < 0.0) - hue += 1.0; - } - - return vec3(hue, sat, lum); -} - -vec3 hsl2rgb(vec3 col) { - const float onethird = 1.0 / 3.0; - const float twothird = 2.0 / 3.0; - const float rcpsixth = 6.0; - - float hue = col.x; - float sat = col.y; - float lum = col.z; - - vec3 xt = vec3(0.0); - - if (hue < onethird) { - xt.r = rcpsixth * (onethird - hue); - xt.g = rcpsixth * hue; - xt.b = 0.0; - } else if (hue < twothird) { - xt.r = 0.0; - xt.g = rcpsixth * (twothird - hue); - xt.b = rcpsixth * (hue - onethird); - } else - xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); - - xt = min(xt, 1.0); - - float sat2 = 2.0 * sat; - float satinv = 1.0 - sat; - float luminv = 1.0 - lum; - float lum2m1 = (2.0 * lum) - 1.0; - vec3 ct = (sat2 * xt) + satinv; - - vec3 rgb; - if (lum >= 0.5) - rgb = (luminv * ct) + lum2m1; - else - rgb = lum * ct; - - return rgb; -} +uniform float radius; +uniform vec2 halfpixel; +uniform int passes; +uniform float vibrancy; +uniform float vibrancy_darkness; +in vec2 v_texcoord; layout(location = 0) out vec4 fragColor; + +#include "blur1.glsl" + void main() { - vec2 uv = v_texcoord * 2.0; - - vec4 sum = texture(tex, uv) * 4.0; - sum += texture(tex, uv - halfpixel.xy * radius); - sum += texture(tex, uv + halfpixel.xy * radius); - sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); - sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); - - vec4 color = sum / 8.0; - - if (vibrancy == 0.0) { - fragColor = color; - } else { - // Invert it so that it correctly maps to the config setting - float vibrancy_darkness1 = 1.0 - vibrancy_darkness; - - // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. - vec3 hsl = rgb2hsl(color.rgb); - // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow - float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); - - float b1 = b * vibrancy_darkness1; - float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; - - float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); - - vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); - - fragColor = vec4(newColor, color[3]); - } + fragColor = blur1(v_texcoord, tex, radius, halfpixel, passes, vibrancy, vibrancy_darkness); } diff --git a/src/render/shaders/glsl/blur1.glsl b/src/render/shaders/glsl/blur1.glsl new file mode 100644 index 00000000..36e7d660 --- /dev/null +++ b/src/render/shaders/glsl/blur1.glsl @@ -0,0 +1,130 @@ +// see http://alienryderflex.com/hsp.html +const float Pr = 0.299; +const float Pg = 0.587; +const float Pb = 0.114; + +// Y is "v" ( brightness ). X is "s" ( saturation ) +// see https://www.desmos.com/3d/a88652b9a4 +// Determines if high brightness or high saturation is more important +const float a = 0.93; +const float b = 0.11; +const float c = 0.66; // Determines the smoothness of the transition of unboosted to boosted colors +// + +// http://www.flong.com/archive/texts/code/shapers_circ/ +float doubleCircleSigmoid(float x, float a) { + a = clamp(a, 0.0, 1.0); + + float y = .0; + if (x <= a) { + y = a - sqrt(a * a - x * x); + } else { + y = a + sqrt(pow(1. - a, 2.) - pow(x - 1., 2.)); + } + return y; +} + +vec3 rgb2hsl(vec3 col) { + float red = col.r; + float green = col.g; + float blue = col.b; + + float minc = min(col.r, min(col.g, col.b)); + float maxc = max(col.r, max(col.g, col.b)); + float delta = maxc - minc; + + float lum = (minc + maxc) * 0.5; + float sat = 0.0; + float hue = 0.0; + + if (lum > 0.0 && lum < 1.0) { + float mul = (lum < 0.5) ? (lum) : (1.0 - lum); + sat = delta / (mul * 2.0); + } + + if (delta > 0.0) { + vec3 maxcVec = vec3(maxc); + vec3 masks = vec3(equal(maxcVec, col)) * vec3(notEqual(maxcVec, vec3(green, blue, red))); + vec3 adds = vec3(0.0, 2.0, 4.0) + vec3(green - blue, blue - red, red - green) / delta; + + hue += dot(adds, masks); + hue /= 6.0; + + if (hue < 0.0) + hue += 1.0; + } + + return vec3(hue, sat, lum); +} + +vec3 hsl2rgb(vec3 col) { + const float onethird = 1.0 / 3.0; + const float twothird = 2.0 / 3.0; + const float rcpsixth = 6.0; + + float hue = col.x; + float sat = col.y; + float lum = col.z; + + vec3 xt = vec3(0.0); + + if (hue < onethird) { + xt.r = rcpsixth * (onethird - hue); + xt.g = rcpsixth * hue; + xt.b = 0.0; + } else if (hue < twothird) { + xt.r = 0.0; + xt.g = rcpsixth * (twothird - hue); + xt.b = rcpsixth * (hue - onethird); + } else + xt = vec3(rcpsixth * (hue - twothird), 0.0, rcpsixth * (1.0 - hue)); + + xt = min(xt, 1.0); + + float sat2 = 2.0 * sat; + float satinv = 1.0 - sat; + float luminv = 1.0 - lum; + float lum2m1 = (2.0 * lum) - 1.0; + vec3 ct = (sat2 * xt) + satinv; + + vec3 rgb; + if (lum >= 0.5) + rgb = (luminv * ct) + lum2m1; + else + rgb = lum * ct; + + return rgb; +} + +vec4 blur1(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel, int passes, float vibrancy, float vibrancy_darkness) { + vec2 uv = v_texcoord * 2.0; + + vec4 sum = texture(tex, uv) * 4.0; + sum += texture(tex, uv - halfpixel.xy * radius); + sum += texture(tex, uv + halfpixel.xy * radius); + sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius); + sum += texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius); + + vec4 color = sum / 8.0; + + if (vibrancy == 0.0) { + return color; + } else { + // Invert it so that it correctly maps to the config setting + float vibrancy_darkness1 = 1.0 - vibrancy_darkness; + + // Decrease the RGB components based on their perceived brightness, to prevent visually dark colors from overblowing the rest. + vec3 hsl = rgb2hsl(color.rgb); + // Calculate perceived brightness, as not boost visually dark colors like deep blue as much as equally saturated yellow + float perceivedBrightness = doubleCircleSigmoid(sqrt(color.r * color.r * Pr + color.g * color.g * Pg + color.b * color.b * Pb), 0.8 * vibrancy_darkness1); + + float b1 = b * vibrancy_darkness1; + float boostBase = hsl[1] > 0.0 ? smoothstep(b1 - c * 0.5, b1 + c * 0.5, 1.0 - (pow(1.0 - hsl[1] * cos(a), 2.0) + pow(1.0 - perceivedBrightness * sin(a), 2.0))) : 0.0; + + float saturation = clamp(hsl[1] + (boostBase * vibrancy) / float(passes), 0.0, 1.0); + + vec3 newColor = hsl2rgb(vec3(hsl[0], saturation, hsl[2])); + + return vec4(newColor, color[3]); + } +} diff --git a/src/render/shaders/glsl/blur2.frag b/src/render/shaders/glsl/blur2.frag index bfe448d5..62caae56 100644 --- a/src/render/shaders/glsl/blur2.frag +++ b/src/render/shaders/glsl/blur2.frag @@ -1,25 +1,18 @@ #version 300 es -precision highp float; +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable + +precision highp float; uniform sampler2D tex; -uniform float radius; -uniform vec2 halfpixel; +uniform float radius; +uniform vec2 halfpixel; -in vec2 v_texcoord; +in vec2 v_texcoord; layout(location = 0) out vec4 fragColor; +#include "blur2.glsl" + void main() { - vec2 uv = v_texcoord / 2.0; - - vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); - - sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); - sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); - sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; - sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); - sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; - - fragColor = sum / 12.0; + fragColor = blur2(v_texcoord, tex, radius, halfpixel); } diff --git a/src/render/shaders/glsl/blur2.glsl b/src/render/shaders/glsl/blur2.glsl new file mode 100644 index 00000000..e73e90e3 --- /dev/null +++ b/src/render/shaders/glsl/blur2.glsl @@ -0,0 +1,15 @@ +vec4 blur2(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel) { + vec2 uv = v_texcoord / 2.0; + + vec4 sum = texture(tex, uv + vec2(-halfpixel.x * 2.0, 0.0) * radius); + + sum += texture(tex, uv + vec2(-halfpixel.x, halfpixel.y) * radius) * 2.0; + sum += texture(tex, uv + vec2(0.0, halfpixel.y * 2.0) * radius); + sum += texture(tex, uv + vec2(halfpixel.x, halfpixel.y) * radius) * 2.0; + sum += texture(tex, uv + vec2(halfpixel.x * 2.0, 0.0) * radius); + sum += texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius) * 2.0; + sum += texture(tex, uv + vec2(0.0, -halfpixel.y * 2.0) * radius); + sum += texture(tex, uv + vec2(-halfpixel.x, -halfpixel.y) * radius) * 2.0; + + return sum / 12.0; +} diff --git a/src/render/shaders/glsl/blurFinish.glsl b/src/render/shaders/glsl/blurFinish.glsl new file mode 100644 index 00000000..f3d225c3 --- /dev/null +++ b/src/render/shaders/glsl/blurFinish.glsl @@ -0,0 +1,17 @@ +float hash(vec2 p) { + vec3 p3 = fract(vec3(p.xyx) * 1689.1984); + p3 += dot(p3, p3.yzx + 33.33); + return fract((p3.x + p3.y) * p3.z); +} + +vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness) { + // noise + float noiseHash = hash(v_texcoord); + float noiseAmount = noiseHash - 0.5; + pixColor.rgb += noiseAmount * noise; + + // brightness + pixColor.rgb *= min(1.0, brightness); + + return pixColor; +} diff --git a/src/render/shaders/glsl/blurfinish.frag b/src/render/shaders/glsl/blurfinish.frag index e3c560e8..0342646b 100644 --- a/src/render/shaders/glsl/blurfinish.frag +++ b/src/render/shaders/glsl/blurfinish.frag @@ -1,30 +1,19 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; // is in 0-1 +precision highp float; +in vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; -uniform float noise; -uniform float brightness; +uniform float noise; +uniform float brightness; -float hash(vec2 p) { - vec3 p3 = fract(vec3(p.xyx) * 1689.1984); - p3 += dot(p3, p3.yzx + 33.33); - return fract((p3.x + p3.y) * p3.z); -} +#include "blurFinish.glsl" layout(location = 0) out vec4 fragColor; void main() { vec4 pixColor = texture(tex, v_texcoord); - // noise - float noiseHash = hash(v_texcoord); - float noiseAmount = noiseHash - 0.5; - pixColor.rgb += noiseAmount * noise; - - // brightness - pixColor.rgb *= min(1.0, brightness); - - fragColor = pixColor; + fragColor = blurFinish(pixColor, v_texcoord, noise, brightness); } diff --git a/src/render/shaders/glsl/blurprepare.frag b/src/render/shaders/glsl/blurprepare.frag index 67cd9966..e96c54bb 100644 --- a/src/render/shaders/glsl/blurprepare.frag +++ b/src/render/shaders/glsl/blurprepare.frag @@ -1,26 +1,38 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; // is in 0-1 +#include "defines.h" + +precision highp float; +in vec2 v_texcoord; // is in 0-1 uniform sampler2D tex; -uniform float contrast; -uniform float brightness; +uniform float contrast; +uniform float brightness; -#include "CM.glsl" -#include "gain.glsl" +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction + +#if USE_CM +uniform vec2 srcTFRange; +uniform vec2 dstTFRange; + +uniform float srcRefLuminance; +uniform mat3 convertMatrix; + +uniform float sdrBrightnessMultiplier; +#include "cm_helpers.glsl" +#endif + +#include "blurprepare.glsl" layout(location = 0) out vec4 fragColor; void main() { - vec4 pixColor = texture(tex, v_texcoord); - - // contrast - if (contrast != 1.0) - pixColor.rgb = gain(pixColor.rgb, contrast); - - // brightness - pixColor.rgb *= max(1.0, brightness); - - fragColor = pixColor; + fragColor = fragColor = blurPrepare(texture(tex, v_texcoord), contrast, brightness +#if USE_CM + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange, srcRefLuminance, sdrBrightnessMultiplier +#endif + ); } diff --git a/src/render/shaders/glsl/blurprepare.glsl b/src/render/shaders/glsl/blurprepare.glsl new file mode 100644 index 00000000..e4a0daad --- /dev/null +++ b/src/render/shaders/glsl/blurprepare.glsl @@ -0,0 +1,37 @@ +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif + +#include "defines.h" + +#if USE_CM +#include "cm_helpers.glsl" +#endif + +#include "gain.glsl" + +vec4 blurPrepare(vec4 pixColor, float contrast, float brightness +#if USE_CM + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange, float srcRefLuminance, float sdrBrightnessMultiplier +#endif +) { +#if USE_CM + if (sourceTF == CM_TRANSFER_FUNCTION_ST2084_PQ) { + pixColor.rgb /= sdrBrightnessMultiplier; + } + pixColor.rgb = convertMatrix * toLinearRGB(pixColor.rgb, sourceTF); + pixColor = toNit(pixColor, vec2(srcTFRange[0], srcRefLuminance)); + pixColor = fromLinearNit(pixColor, targetTF, dstTFRange); +#endif + + // contrast + if (contrast != 1.0) + pixColor.rgb = gain(pixColor.rgb, contrast); + + // brightness + pixColor.rgb *= max(1.0, brightness); + + return pixColor; +} diff --git a/src/render/shaders/glsl/border.frag b/src/render/shaders/glsl/border.frag index a672452b..151593c1 100644 --- a/src/render/shaders/glsl/border.frag +++ b/src/render/shaders/glsl/border.frag @@ -1,92 +1,60 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; +precision highp float; +in vec2 v_texcoord; -uniform vec2 fullSizeUntransformed; +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +uniform mat3 targetPrimariesXYZ; + +uniform vec2 fullSizeUntransformed; uniform float radiusOuter; uniform float thick; // Gradients are in OkLabA!!!! {l, a, b, alpha} -uniform vec4 gradient[10]; -uniform vec4 gradient2[10]; -uniform int gradientLength; -uniform int gradient2Length; +uniform vec4 gradient[10]; +uniform vec4 gradient2[10]; +uniform int gradientLength; +uniform int gradient2Length; uniform float angle; uniform float angle2; uniform float gradientLerp; uniform float alpha; +uniform float radius; +uniform float roundingPower; +uniform vec2 topLeft; +uniform vec2 fullSize; #include "rounding.glsl" #include "CM.glsl" #include "border.glsl" layout(location = 0) out vec4 fragColor; void main() { - highp vec2 pixCoord = vec2(gl_FragCoord); - highp vec2 pixCoordOuter = pixCoord; - highp vec2 originalPixCoord = v_texcoord; - originalPixCoord *= fullSizeUntransformed; - float additionalAlpha = 1.0; - - vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); - - bool done = false; - - pixCoord -= topLeft + fullSize * 0.5; - pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; - pixCoordOuter = pixCoord; - pixCoord -= fullSize * 0.5 - radius; - pixCoordOuter -= fullSize * 0.5 - radiusOuter; - - // center the pixes don't make it top-left - pixCoord += vec2(1.0, 1.0) / fullSize; - pixCoordOuter += vec2(1.0, 1.0) / fullSize; - - if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { - float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower); - float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower); - float h = (thick / 2.0); - - if (dist < radius - h) { - // lower - float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { - // higher - float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); - additionalAlpha *= normalized; - done = true; - } else if (distOuter < radiusOuter - h) { - additionalAlpha = 1.0; - done = true; - } - } - - // now check for other shit - if (!done) { - // distance to all straight bb borders - float distanceT = originalPixCoord[1]; - float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; - float distanceL = originalPixCoord[0]; - float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest > thick) - discard; - } - - if (additionalAlpha == 0.0) - discard; - - pixColor = getColorForCoord(v_texcoord); - pixColor.rgb *= pixColor[3]; - - pixColor *= alpha * additionalAlpha; - - fragColor = pixColor; + fragColor = getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length, + gradient2, angle2, gradientLerp +#if USE_CM + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif +#endif + ); } diff --git a/src/render/shaders/glsl/border.glsl b/src/render/shaders/glsl/border.glsl index c5ad7f3d..fa2a6980 100644 --- a/src/render/shaders/glsl/border.glsl +++ b/src/render/shaders/glsl/border.glsl @@ -1,18 +1,24 @@ +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif +#include "cm_helpers.glsl" +#if USE_ROUNDING +#include "rounding.glsl" +#endif + vec4 okLabAToSrgb(vec4 lab) { float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0); float m = pow(lab[0] + lab[1] * (-0.1055613458) + lab[2] * (-0.0638541728), 3.0); float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0); - return vec4(fromLinearRGB( - vec3( - l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, - l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965), - l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010 - ), CM_TRANSFER_FUNCTION_GAMMA22 - ), lab[3]); + return vec4(fromLinearRGB(vec3(l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965), + l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010), + CM_TRANSFER_FUNCTION_GAMMA22), + lab[3]); } -vec4 getOkColorForCoordArray1(vec2 normalizedCoord) { +vec4 getOkColorForCoordArray1(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle) { if (gradientLength < 2) return gradient[0]; @@ -20,14 +26,14 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord) { if (angle > 4.71 /* 270 deg */) { normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = 6.28 - angle; + finalAng = 6.28 - angle; } else if (angle > 3.14 /* 180 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = angle - 3.14; + finalAng = angle - 3.14; } else if (angle > 1.57 /* 90 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; - finalAng = 3.14 - angle; + finalAng = 3.14 - angle; } else { finalAng = angle; } @@ -35,13 +41,13 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord) { float sine = sin(finalAng); float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1); - int bottom = int(floor(progress)); - int top = bottom + 1; + int bottom = int(floor(progress)); + int top = bottom + 1; return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); } -vec4 getOkColorForCoordArray2(vec2 normalizedCoord) { +vec4 getOkColorForCoordArray2(vec2 normalizedCoord, float angle, int gradient2Length, vec4 gradient2[10], float angle2) { if (gradient2Length < 2) return gradient2[0]; @@ -49,14 +55,14 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord) { if (angle2 > 4.71 /* 270 deg */) { normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = 6.28 - angle; + finalAng = 6.28 - angle; } else if (angle2 > 3.14 /* 180 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; normalizedCoord[1] = 1.0 - normalizedCoord[1]; - finalAng = angle - 3.14; + finalAng = angle - 3.14; } else if (angle2 > 1.57 /* 90 deg */) { normalizedCoord[0] = 1.0 - normalizedCoord[0]; - finalAng = 3.14 - angle2; + finalAng = 3.14 - angle2; } else { finalAng = angle2; } @@ -64,19 +70,134 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord) { float sine = sin(finalAng); float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradient2Length - 1); - int bottom = int(floor(progress)); - int top = bottom + 1; + int bottom = int(floor(progress)); + int top = bottom + 1; return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress); } -vec4 getColorForCoord(vec2 normalizedCoord) { - vec4 result1 = getOkColorForCoordArray1(normalizedCoord); +vec4 getColorForCoord(vec2 normalizedCoord, int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp) { + vec4 result1 = getOkColorForCoordArray1(normalizedCoord, gradientLength, gradient, angle); if (gradient2Length <= 0) return okLabAToSrgb(result1); - vec4 result2 = getOkColorForCoordArray2(normalizedCoord); + vec4 result2 = getOkColorForCoordArray2(normalizedCoord, angle, gradient2Length, gradient2, angle2); return okLabAToSrgb(mix(result1, result2, gradientLerp)); } + +vec4 getBorder(vec2 v_texcoord, float alpha, vec2 fullSizeUntransformed, float radiusOuter, float thick, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, + int gradientLength, vec4 gradient[10], float angle, int gradient2Length, vec4 gradient2[10], float angle2, float gradientLerp +#if USE_CM + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange +#if USE_ICC + , + highp sampler3D iccLut3D, float iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + mat3 targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance +#endif +#if USE_SDR_MOD + , + float sdrSaturation, float sdrBrightnessMultiplier +#endif +#endif +#endif +) { + vec2 pixCoord = vec2(gl_FragCoord); + vec2 pixCoordOuter = pixCoord; + vec2 originalPixCoord = v_texcoord; + originalPixCoord *= fullSizeUntransformed; + float additionalAlpha = 1.0; + + vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); + + bool done = false; + + pixCoord -= topLeft + fullSize * 0.5; + pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; + pixCoordOuter = pixCoord; + pixCoord -= fullSize * 0.5 - radius; + pixCoordOuter -= fullSize * 0.5 - radiusOuter; + + // center the pixes don't make it top-left + pixCoord += vec2(1.0, 1.0) / fullSize; + pixCoordOuter += vec2(1.0, 1.0) / fullSize; + +#if USE_ROUNDING + if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { + float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower); + float distOuter = pow(pow(pixCoordOuter.x, roundingPower) + pow(pixCoordOuter.y, roundingPower), 1.0 / roundingPower); + float h = (thick / 2.0); + + if (dist < radius - h) { + // lower + float normalized = smoothstep(0.0, 1.0, (dist - radius + thick + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (min(pixCoordOuter.x, pixCoordOuter.y) > 0.0) { + // higher + float normalized = 1.0 - smoothstep(0.0, 1.0, (distOuter - radiusOuter + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0)); + additionalAlpha *= normalized; + done = true; + } else if (distOuter < radiusOuter - h) { + additionalAlpha = 1.0; + done = true; + } + } +#endif + + // now check for other shit + if (!done) { + // distance to all straight bb borders + float distanceT = originalPixCoord[1]; + float distanceB = fullSizeUntransformed[1] - originalPixCoord[1]; + float distanceL = originalPixCoord[0]; + float distanceR = fullSizeUntransformed[0] - originalPixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest > thick) + discard; + } + + if (additionalAlpha == 0.0) + discard; + + pixColor = getColorForCoord(v_texcoord, gradientLength, gradient, angle, gradient2Length, gradient2, angle2, gradientLerp); + pixColor.rgb *= pixColor[3]; + +#if USE_CM + pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif + ); +#endif + + pixColor *= alpha * additionalAlpha; + + return pixColor; +} diff --git a/src/render/shaders/glsl/cm_helpers.glsl b/src/render/shaders/glsl/cm_helpers.glsl new file mode 100644 index 00000000..5e0d14f6 --- /dev/null +++ b/src/render/shaders/glsl/cm_helpers.glsl @@ -0,0 +1,248 @@ +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif +#ifndef CM_HELPERS_GLSL +#define CM_HELPERS_GLSL + +#include "defines.h" +#include "constants.h" + +#if USE_SDR_MOD +vec4 saturate(vec4 color, mat3 primaries, float saturation) { + if (saturation == 1.0) + return color; + vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]); + float Y = dot(color.rgb, brightness); + return vec4(mix(vec3(Y), color.rgb, saturation), color[3]); +} +#endif + +vec3 applyIcc3DLut(vec3 linearRgb01, highp sampler3D iccLut3D, float iccLutSize) { + vec3 x = clamp(linearRgb01, 0.0, 1.0); + + // Map [0..1] to texel centers to avoid edge issues + float N = iccLutSize; + vec3 coord = (x * (N - 1.0) + 0.5) / N; + + return texture(iccLut3D, coord).rgb; +} + +vec3 xy2xyz(vec2 xy) { + if (xy.y == 0.0) + return vec3(0.0, 0.0, 0.0); + + return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y); +} + +// The primary source for these transfer functions is https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.1361-0-199802-W!!PDF-E.pdf +vec3 tfInvPQ(vec3 color) { + vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2)); + return pow((max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E), vec3(PQ_INV_M1)); +} + +vec3 tfInvHLG(vec3 color) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT)); + vec3 lo = color.rgb * color.rgb / 3.0; + vec3 hi = (exp((color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0; + return mix(hi, lo, isLow); +} + +// Many transfer functions (including sRGB) follow the same pattern: a linear +// segment for small values and a power function for larger values. The +// following function implements this pattern from which sRGB, BT.1886, and +// others can be derived by plugging in the right constants. +vec3 tfInvLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(thres * scale)); + vec3 lo = color.rgb / scale; + vec3 hi = pow((color.rgb + alpha - 1.0) / alpha, vec3(gamma)); + return mix(hi, lo, isLow); +} + +vec3 tfInvSRGB(vec3 color) { + return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); +} + +vec3 tfInvExtSRGB(vec3 color) { + // EXT sRGB is the sRGB transfer function mirrored around 0. + return sign(color) * tfInvSRGB(abs(color)); +} + +vec3 tfInvBT1886(vec3 color) { + return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); +} + +vec3 tfInvXVYCC(vec3 color) { + // The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0, + // same as what EXT sRGB is to sRGB. + return sign(color) * tfInvBT1886(abs(color)); +} + +vec3 tfInvST240(vec3 color) { + return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); +} + +// Forward transfer functions corresponding to the inverse functions above. +vec3 tfPQ(vec3 color) { + vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1)); + return pow((vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E), vec3(PQ_M2)); +} + +vec3 tfHLG(vec3 color) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT)); + vec3 lo = sqrt(max(color.rgb, vec3(0.0)) * 3.0); + vec3 hi = HLG_A * log(max(12.0 * color.rgb - HLG_B, vec3(0.0001))) + HLG_C; + return mix(hi, lo, isLow); +} + +vec3 tfLinPow(vec3 color, float gamma, float thres, float scale, float alpha) { + bvec3 isLow = lessThanEqual(color.rgb, vec3(thres)); + vec3 lo = color.rgb * scale; + vec3 hi = pow(color.rgb, vec3(1.0 / gamma)) * alpha - (alpha - 1.0); + return mix(hi, lo, isLow); +} + +vec3 tfSRGB(vec3 color) { + return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA); +} + +vec3 tfExtSRGB(vec3 color) { + // EXT sRGB is the sRGB transfer function mirrored around 0. + return sign(color) * tfSRGB(abs(color)); +} + +vec3 tfBT1886(vec3 color) { + return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA); +} + +vec3 tfXVYCC(vec3 color) { + // The transfer function for XVYCC is the BT1886 transfer function mirrored around 0, + // same as what EXT sRGB is to sRGB. + return sign(color) * tfBT1886(abs(color)); +} + +vec3 tfST240(vec3 color) { + return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA); +} + +vec3 toLinearRGB(vec3 color, int tf) { + switch (tf) { + case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color; + case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfInvPQ(color); + case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(2.2)); + case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(2.8)); + case CM_TRANSFER_FUNCTION_HLG: return tfInvHLG(color); + case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfInvExtSRGB(color); + case CM_TRANSFER_FUNCTION_BT1886: return tfInvBT1886(color); + case CM_TRANSFER_FUNCTION_ST240: return tfInvST240(color); + case CM_TRANSFER_FUNCTION_LOG_100: return mix(exp((color - 1.0) * 2.0 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); + case CM_TRANSFER_FUNCTION_LOG_316: return mix(exp((color - 1.0) * 2.5 * log(10.0)), vec3(0.0), lessThanEqual(color, vec3(0.0))); + case CM_TRANSFER_FUNCTION_XVYCC: return tfInvXVYCC(color); + case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)), vec3(ST428_POW)) * ST428_SCALE; + case CM_TRANSFER_FUNCTION_SRGB: + default: return tfInvSRGB(color); + } +} + +vec4 toLinear(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + color.rgb /= max(color.a, 0.001); + color.rgb = toLinearRGB(color.rgb, tf); + color.rgb *= color.a; + return color; +} + +vec4 toNit(vec4 color, vec2 range) { + color.rgb = color.rgb * (range[1] - range[0]) + range[0]; + return color; +} + +vec3 fromLinearRGB(vec3 color, int tf) { + switch (tf) { + case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color; + case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfPQ(color); + case CM_TRANSFER_FUNCTION_GAMMA22: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.2)); + case CM_TRANSFER_FUNCTION_GAMMA28: return pow(max(color, vec3(0.0)), vec3(1.0 / 2.8)); + case CM_TRANSFER_FUNCTION_HLG: return tfHLG(color); + case CM_TRANSFER_FUNCTION_EXT_SRGB: return tfExtSRGB(color); + case CM_TRANSFER_FUNCTION_BT1886: return tfBT1886(color); + case CM_TRANSFER_FUNCTION_ST240: return tfST240(color); + case CM_TRANSFER_FUNCTION_LOG_100: return mix(1.0 + log(color) / log(10.0) / 2.0, vec3(0.0), lessThanEqual(color, vec3(0.01))); + case CM_TRANSFER_FUNCTION_LOG_316: return mix(1.0 + log(color) / log(10.0) / 2.5, vec3(0.0), lessThanEqual(color, vec3(sqrt(10.0) / 1000.0))); + case CM_TRANSFER_FUNCTION_XVYCC: return tfXVYCC(color); + case CM_TRANSFER_FUNCTION_ST428: return pow(max(color, vec3(0.0)) / ST428_SCALE, vec3(1.0 / ST428_POW)); + case CM_TRANSFER_FUNCTION_SRGB: + default: return tfSRGB(color); + } +} + +vec4 fromLinear(vec4 color, int tf) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + return color; + + color.rgb /= max(color.a, 0.001); + color.rgb = fromLinearRGB(color.rgb, tf); + color.rgb *= color.a; + return color; +} + +vec4 fromLinearNit(vec4 color, int tf, vec2 range) { + if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR) + color.rgb = color.rgb / SDR_MAX_LUMINANCE; + else { + color.rgb /= max(color.a, 0.001); + color.rgb = (color.rgb - range[0]) / (range[1] - range[0]); + color.rgb = fromLinearRGB(color.rgb, tf); + color.rgb *= color.a; + } + return color; +} + +#if USE_TONEMAP +#include "tonemap.glsl" +#endif + +vec4 doColorManagement(vec4 pixColor, int srcTF, int dstTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange +#if USE_ICC + , + highp sampler3D iccLut3D, float iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + mat3 dstxyz +#endif +#if USE_TONEMAP + , + float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance +#endif +#if USE_SDR_MOD + , + float sdrSaturation, float sdrBrightnessMultiplier +#endif +#endif +) { + pixColor.rgb /= max(pixColor.a, 0.001); + pixColor.rgb = toLinearRGB(pixColor.rgb, srcTF); +#if USE_ICC + pixColor.rgb = applyIcc3DLut(pixColor.rgb, iccLut3D, iccLutSize); + pixColor.rgb *= pixColor.a; +#else + pixColor.rgb = convertMatrix * pixColor.rgb; + pixColor = toNit(pixColor, srcTFRange); + pixColor.rgb *= pixColor.a; +#if USE_TONEMAP + pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance); +#endif + pixColor = fromLinearNit(pixColor, dstTF, dstTFRange); +#if USE_SDR_MOD + pixColor = saturate(pixColor, dstxyz, sdrSaturation); + pixColor.rgb *= sdrBrightnessMultiplier; +#endif +#endif + + return pixColor; +} + +#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/constants.h b/src/render/shaders/glsl/constants.h new file mode 100644 index 00000000..bbab5284 --- /dev/null +++ b/src/render/shaders/glsl/constants.h @@ -0,0 +1,62 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H +//enum eTransferFunction +#define CM_TRANSFER_FUNCTION_BT1886 1 +#define CM_TRANSFER_FUNCTION_GAMMA22 2 +#define CM_TRANSFER_FUNCTION_GAMMA28 3 +#define CM_TRANSFER_FUNCTION_ST240 4 +#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5 +#define CM_TRANSFER_FUNCTION_LOG_100 6 +#define CM_TRANSFER_FUNCTION_LOG_316 7 +#define CM_TRANSFER_FUNCTION_XVYCC 8 +#define CM_TRANSFER_FUNCTION_SRGB 9 +#define CM_TRANSFER_FUNCTION_EXT_SRGB 10 +#define CM_TRANSFER_FUNCTION_ST2084_PQ 11 +#define CM_TRANSFER_FUNCTION_ST428 12 +#define CM_TRANSFER_FUNCTION_HLG 13 + +// sRGB constants +#define SRGB_POW 2.4 +#define SRGB_CUT 0.0031308 +#define SRGB_SCALE 12.92 +#define SRGB_ALPHA 1.055 + +#define BT1886_POW (1.0 / 0.45) +#define BT1886_CUT 0.018053968510807 +#define BT1886_SCALE 4.5 +#define BT1886_ALPHA (1.0 + 5.5 * BT1886_CUT) + +// See http://car.france3.mars.free.fr/HD/INA-%2026%20jan%2006/SMPTE%20normes%20et%20confs/s240m.pdf +#define ST240_POW (1.0 / 0.45) +#define ST240_CUT 0.0228 +#define ST240_SCALE 4.0 +#define ST240_ALPHA 1.1115 + +#define ST428_POW 2.6 +#define ST428_SCALE (52.37 / 48.0) + +// PQ constants +#define PQ_M1 0.1593017578125 +#define PQ_M2 78.84375 +#define PQ_INV_M1 (1.0 / PQ_M1) +#define PQ_INV_M2 (1.0 / PQ_M2) +#define PQ_C1 0.8359375 +#define PQ_C2 18.8515625 +#define PQ_C3 18.6875 + +// HLG constants +#define HLG_D_CUT (1.0 / 12.0) +#define HLG_E_CUT 0.5 +#define HLG_A 0.17883277 +#define HLG_B 0.28466892 +#define HLG_C 0.55991073 + +#define SDR_MIN_LUMINANCE 0.2 +#define SDR_MAX_LUMINANCE 80.0 +#define HDR_MIN_LUMINANCE 0.005 +#define HDR_MAX_LUMINANCE 10000.0 +#define HLG_MAX_LUMINANCE 1000.0 + +#define M_E 2.718281828459045 + +#endif diff --git a/src/render/shaders/glsl/defines.h b/src/render/shaders/glsl/defines.h new file mode 100644 index 00000000..31b120a4 --- /dev/null +++ b/src/render/shaders/glsl/defines.h @@ -0,0 +1,10 @@ +// DO NOT EDIT. Will be overwritten in runtime +#define USE_RGBA 1 +#define USE_DISCARD 1 +#define USE_TINT 1 +#define USE_ROUNDING 1 +#define USE_CM 1 +#define USE_TONEMAP 1 +#define USE_SDR_MOD 1 +#define USE_BLUR 1 +#define USE_ICC 1 diff --git a/src/render/shaders/glsl/discard.glsl b/src/render/shaders/glsl/discard.glsl deleted file mode 100644 index 311776de..00000000 --- a/src/render/shaders/glsl/discard.glsl +++ /dev/null @@ -1,3 +0,0 @@ -uniform bool discardOpaque; -uniform bool discardAlpha; -uniform float discardAlphaValue; diff --git a/src/render/shaders/glsl/do_CM.glsl b/src/render/shaders/glsl/do_CM.glsl deleted file mode 100644 index b63d03e5..00000000 --- a/src/render/shaders/glsl/do_CM.glsl +++ /dev/null @@ -1 +0,0 @@ -pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ); \ No newline at end of file diff --git a/src/render/shaders/glsl/do_discard.glsl b/src/render/shaders/glsl/do_discard.glsl deleted file mode 100644 index df6e57e3..00000000 --- a/src/render/shaders/glsl/do_discard.glsl +++ /dev/null @@ -1,5 +0,0 @@ -if (discardOpaque && pixColor.a * alpha == 1.0) - discard; - -if (discardAlpha && pixColor.a <= discardAlphaValue) - discard; \ No newline at end of file diff --git a/src/render/shaders/glsl/do_rounding.glsl b/src/render/shaders/glsl/do_rounding.glsl deleted file mode 100644 index 60368fb1..00000000 --- a/src/render/shaders/glsl/do_rounding.glsl +++ /dev/null @@ -1 +0,0 @@ -pixColor = rounding(pixColor); \ No newline at end of file diff --git a/src/render/shaders/glsl/do_sdr_mod.glsl b/src/render/shaders/glsl/do_sdr_mod.glsl deleted file mode 100644 index 05dbe180..00000000 --- a/src/render/shaders/glsl/do_sdr_mod.glsl +++ /dev/null @@ -1,2 +0,0 @@ -pixColor = saturate(pixColor, dstxyz, sdrSaturation); -pixColor.rgb *= sdrBrightnessMultiplier; diff --git a/src/render/shaders/glsl/do_tint.glsl b/src/render/shaders/glsl/do_tint.glsl deleted file mode 100644 index b761b704..00000000 --- a/src/render/shaders/glsl/do_tint.glsl +++ /dev/null @@ -1 +0,0 @@ -pixColor.rgb = pixColor.rgb * tint; diff --git a/src/render/shaders/glsl/do_tonemap.glsl b/src/render/shaders/glsl/do_tonemap.glsl deleted file mode 100644 index db23b0f8..00000000 --- a/src/render/shaders/glsl/do_tonemap.glsl +++ /dev/null @@ -1 +0,0 @@ -pixColor = tonemap(pixColor, dstxyz); \ No newline at end of file diff --git a/src/render/shaders/glsl/ext.frag b/src/render/shaders/glsl/ext.frag index e855a832..1c614bd3 100644 --- a/src/render/shaders/glsl/ext.frag +++ b/src/render/shaders/glsl/ext.frag @@ -1,20 +1,25 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable #extension GL_OES_EGL_image_external_essl3 : require -precision highp float; -in vec2 v_texcoord; +precision highp float; +in vec2 v_texcoord; uniform samplerExternalOES tex; -uniform float alpha; +uniform float alpha; +uniform float radius; +uniform float roundingPower; +uniform vec2 topLeft; +uniform vec2 fullSize; #include "rounding.glsl" -uniform int discardOpaque; -uniform int discardAlpha; -uniform int discardAlphaValue; +uniform int discardOpaque; +uniform int discardAlpha; +uniform int discardAlphaValue; -uniform int applyTint; +uniform int applyTint; uniform vec3 tint; layout(location = 0) out vec4 fragColor; @@ -23,16 +28,16 @@ void main() { vec4 pixColor = texture(tex, v_texcoord); if (discardOpaque == 1 && pixColor[3] * alpha == 1.0) - discard; + discard; if (applyTint == 1) { - pixColor[0] = pixColor[0] * tint[0]; - pixColor[1] = pixColor[1] * tint[1]; - pixColor[2] = pixColor[2] * tint[2]; + pixColor[0] = pixColor[0] * tint[0]; + pixColor[1] = pixColor[1] * tint[1]; + pixColor[2] = pixColor[2] * tint[2]; } if (radius > 0.0) - pixColor = rounding(pixColor); + pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); fragColor = pixColor * alpha; } diff --git a/src/render/shaders/glsl/get_rgb_pixel.glsl b/src/render/shaders/glsl/get_rgb_pixel.glsl deleted file mode 100644 index 31097c58..00000000 --- a/src/render/shaders/glsl/get_rgb_pixel.glsl +++ /dev/null @@ -1 +0,0 @@ -#include "get_rgbx_pixel.glsl" \ No newline at end of file diff --git a/src/render/shaders/glsl/get_rgba_pixel.glsl b/src/render/shaders/glsl/get_rgba_pixel.glsl deleted file mode 100644 index 23ad0cf2..00000000 --- a/src/render/shaders/glsl/get_rgba_pixel.glsl +++ /dev/null @@ -1 +0,0 @@ -vec4 pixColor = texture(tex, v_texcoord); diff --git a/src/render/shaders/glsl/get_rgbx_pixel.glsl b/src/render/shaders/glsl/get_rgbx_pixel.glsl deleted file mode 100644 index fa4eb74b..00000000 --- a/src/render/shaders/glsl/get_rgbx_pixel.glsl +++ /dev/null @@ -1 +0,0 @@ -vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0); diff --git a/src/render/shaders/glsl/primaries_xyz.glsl b/src/render/shaders/glsl/primaries_xyz.glsl deleted file mode 100644 index ddcb5c70..00000000 --- a/src/render/shaders/glsl/primaries_xyz.glsl +++ /dev/null @@ -1 +0,0 @@ -#include "primaries_xyz_uniform.glsl" \ No newline at end of file diff --git a/src/render/shaders/glsl/primaries_xyz_const.glsl b/src/render/shaders/glsl/primaries_xyz_const.glsl deleted file mode 100644 index 5499d1cd..00000000 --- a/src/render/shaders/glsl/primaries_xyz_const.glsl +++ /dev/null @@ -1 +0,0 @@ -const mat3 targetPrimariesXYZ = mat3(0.0); diff --git a/src/render/shaders/glsl/primaries_xyz_uniform.glsl b/src/render/shaders/glsl/primaries_xyz_uniform.glsl deleted file mode 100644 index 6c0558f0..00000000 --- a/src/render/shaders/glsl/primaries_xyz_uniform.glsl +++ /dev/null @@ -1 +0,0 @@ -uniform mat3 targetPrimariesXYZ; \ No newline at end of file diff --git a/src/render/shaders/glsl/quad.frag b/src/render/shaders/glsl/quad.frag index 5dae493e..61895a60 100644 --- a/src/render/shaders/glsl/quad.frag +++ b/src/render/shaders/glsl/quad.frag @@ -1,17 +1,27 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec4 v_color; +#include "defines.h" +precision highp float; +in vec4 v_color; + +#if USE_ROUNDING +uniform float radius; +uniform float roundingPower; +uniform vec2 topLeft; +uniform vec2 fullSize; #include "rounding.glsl" +#endif layout(location = 0) out vec4 fragColor; void main() { vec4 pixColor = v_color; - if (radius > 0.0) - pixColor = rounding(pixColor); +#if USE_ROUNDING + pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); +#endif fragColor = pixColor; } diff --git a/src/render/shaders/glsl/rounding.glsl b/src/render/shaders/glsl/rounding.glsl index 472415fd..61a0bb9c 100644 --- a/src/render/shaders/glsl/rounding.glsl +++ b/src/render/shaders/glsl/rounding.glsl @@ -1,13 +1,10 @@ +#ifndef ROUNDING_GLSL +#define ROUNDING_GLSL // smoothing constant for the edge: more = blurrier, but smoother -#define M_PI 3.1415926535897932384626433832795 +#define M_PI 3.1415926535897932384626433832795 #define SMOOTHING_CONSTANT (M_PI / 5.34665792551) -uniform float radius; -uniform float roundingPower; -uniform vec2 topLeft; -uniform vec2 fullSize; - -vec4 rounding(vec4 color) { +vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 fullSize) { vec2 pixCoord = vec2(gl_FragCoord); pixCoord -= topLeft + fullSize * 0.5; pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; @@ -15,7 +12,7 @@ vec4 rounding(vec4 color) { pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix don't make it top-left if (pixCoord.x + pixCoord.y > radius) { - float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower); + float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0 / roundingPower); if (dist > radius + SMOOTHING_CONSTANT) discard; @@ -27,3 +24,4 @@ vec4 rounding(vec4 color) { return color; } +#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/sdr_mod.glsl b/src/render/shaders/glsl/sdr_mod.glsl deleted file mode 100644 index 7803d804..00000000 --- a/src/render/shaders/glsl/sdr_mod.glsl +++ /dev/null @@ -1,10 +0,0 @@ -uniform float sdrSaturation; -uniform float sdrBrightnessMultiplier; - -vec4 saturate(vec4 color, mat3 primaries, float saturation) { - if (saturation == 1.0) - return color; - vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]); - float Y = dot(color.rgb, brightness); - return vec4(mix(vec3(Y), color.rgb, saturation), color[3]); -} diff --git a/src/render/shaders/glsl/shadow.frag b/src/render/shaders/glsl/shadow.frag index 06aa605c..c23ebd5d 100644 --- a/src/render/shaders/glsl/shadow.frag +++ b/src/render/shaders/glsl/shadow.frag @@ -1,93 +1,57 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec4 v_color; -in vec2 v_texcoord; +#include "defines.h" -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -uniform mat3 targetPrimariesXYZ; +precision highp float; +in vec4 v_color; +in vec2 v_texcoord; -uniform vec2 topLeft; -uniform vec2 bottomRight; -uniform vec2 fullSize; +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction +uniform mat3 targetPrimariesXYZ; + +uniform vec2 topLeft; +uniform vec2 bottomRight; +uniform vec2 fullSize; uniform float radius; uniform float roundingPower; uniform float range; uniform float shadowPower; -float pixAlphaRoundedDistance(float distanceToCorner) { - if (distanceToCorner > radius) { - return 0.0; - } +#if USE_CM +#include "cm_helpers.glsl" +#include "CM.glsl" +#endif - if (distanceToCorner > radius - range) { - return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? - } - - return 1.0; -} - -float modifiedLength(vec2 a) { - return pow(pow(abs(a.x),roundingPower)+pow(abs(a.y),roundingPower),1.0/roundingPower); -} +#include "shadow.glsl" layout(location = 0) out vec4 fragColor; void main() { + vec4 pixColor = v_color; - vec4 pixColor = v_color; - float originalAlpha = pixColor[3]; - - bool done = false; - - vec2 pixCoord = fullSize * v_texcoord; - - // ok, now we check the distance to a border. - - if (pixCoord[0] < topLeft[0]) { - if (pixCoord[1] < topLeft[1]) { - // top left - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft)); - done = true; - } else if (pixCoord[1] > bottomRight[1]) { - // bottom left - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]))); - done = true; - } - } else if (pixCoord[0] > bottomRight[0]) { - if (pixCoord[1] < topLeft[1]) { - // top right - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]))); - done = true; - } else if (pixCoord[1] > bottomRight[1]) { - // bottom right - pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight)); - done = true; - } - } - - if (!done) { - // distance to all straight bb borders - float distanceT = pixCoord[1]; - float distanceB = fullSize[1] - pixCoord[1]; - float distanceL = pixCoord[0]; - float distanceR = fullSize[0] - pixCoord[0]; - - // get the smallest - float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); - - if (smallest < range) { - pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); - } - } - - if (pixColor[3] == 0.0) { - discard; return; - } - - // premultiply - pixColor.rgb *= pixColor[3]; - - fragColor = pixColor; + fragColor = getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight +#if USE_CM + , + sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif +#endif + ); } \ No newline at end of file diff --git a/src/render/shaders/glsl/shadow.glsl b/src/render/shaders/glsl/shadow.glsl new file mode 100644 index 00000000..48cde562 --- /dev/null +++ b/src/render/shaders/glsl/shadow.glsl @@ -0,0 +1,126 @@ +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif +#ifndef SHADOW_GLSL +#define SHADOW_GLSL + +#include "cm_helpers.glsl" + +float pixAlphaRoundedDistance(float distanceToCorner, float radius, float range, float shadowPower) { + if (distanceToCorner > radius) { + return 0.0; + } + + if (distanceToCorner > radius - range) { + return pow((range - (distanceToCorner - radius + range)) / range, shadowPower); // i think? + } + + return 1.0; +} + +float modifiedLength(vec2 a, float roundingPower) { + return pow(pow(abs(a.x), roundingPower) + pow(abs(a.y), roundingPower), 1.0 / roundingPower); +} + +vec4 getShadow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight +#if USE_CM + , + int sourceTF, int targetTF, mat3 convertMatrix, vec2 srcTFRange, vec2 dstTFRange +#if USE_ICC + , + highp sampler3D iccLut3D, float iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + mat3 targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance +#endif +#if USE_SDR_MOD + , + float sdrSaturation, float sdrBrightnessMultiplier +#endif +#endif +#endif +) { + float originalAlpha = pixColor[3]; + + bool done = false; + + vec2 pixCoord = fullSize * v_texcoord; + + // ok, now we check the distance to a border. + + if (pixCoord[0] < topLeft[0]) { + if (pixCoord[1] < topLeft[1]) { + // top left + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft, roundingPower), radius, range, shadowPower); + done = true; + } else if (pixCoord[1] > bottomRight[1]) { + // bottom left + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]), roundingPower), radius, range, shadowPower); + done = true; + } + } else if (pixCoord[0] > bottomRight[0]) { + if (pixCoord[1] < topLeft[1]) { + // top right + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]), roundingPower), radius, range, shadowPower); + done = true; + } else if (pixCoord[1] > bottomRight[1]) { + // bottom right + pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - bottomRight, roundingPower), radius, range, shadowPower); + done = true; + } + } + + if (!done) { + // distance to all straight bb borders + float distanceT = pixCoord[1]; + float distanceB = fullSize[1] - pixCoord[1]; + float distanceL = pixCoord[0]; + float distanceR = fullSize[0] - pixCoord[0]; + + // get the smallest + float smallest = min(min(distanceT, distanceB), min(distanceL, distanceR)); + + if (smallest < range) { + pixColor[3] = pixColor[3] * pow((smallest / range), shadowPower); + } + } + + if (pixColor[3] == 0.0) { + discard; + return pixColor; + } + + // premultiply + pixColor.rgb *= pixColor[3]; + +#if USE_CM + pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif + ); +#endif + + return pixColor; +} +#endif \ No newline at end of file diff --git a/src/render/shaders/glsl/surface.frag b/src/render/shaders/glsl/surface.frag index 1d3e80b8..30023bc8 100644 --- a/src/render/shaders/glsl/surface.frag +++ b/src/render/shaders/glsl/surface.frag @@ -1,25 +1,104 @@ #version 300 es +#define ALLOW_INCLUDES #extension GL_ARB_shading_language_include : enable -precision highp float; -in vec2 v_texcoord; +#include "defines.h" + +precision highp float; +in vec2 v_texcoord; uniform sampler2D tex; +#if USE_BLUR +uniform vec2 uvSize; +uniform vec2 uvOffset; +uniform sampler2D blurredBG; +#endif uniform float alpha; -#include "discard.glsl" -#include "tint.glsl" +#if USE_DISCARD +uniform bool discardOpaque; +uniform bool discardAlpha; +uniform float discardAlphaValue; +#endif + +#if USE_TINT +uniform vec3 tint; +#endif + +#if USE_ROUNDING +uniform float radius; +uniform float roundingPower; +uniform vec2 topLeft; +uniform vec2 fullSize; #include "rounding.glsl" -#include "surface_CM.glsl" +#endif + +#if USE_CM +uniform int sourceTF; // eTransferFunction +uniform int targetTF; // eTransferFunction + +#if USE_TONEMAP || USE_SDR_MOD +uniform mat3 targetPrimariesXYZ; +#else +const mat3 targetPrimariesXYZ = mat3(0.0); +#endif + +#include "CM.glsl" +#endif layout(location = 0) out vec4 fragColor; void main() { - #include "get_rgb_pixel.glsl" +#if USE_RGBA + vec4 pixColor = texture(tex, v_texcoord); +#else + vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0); +#endif - #include "do_discard.glsl" - #include "do_CM.glsl" - #include "do_tint.glsl" - #include "do_rounding.glsl" +#if USE_DISCARD && !USE_BLUR + if (discardOpaque && pixColor.a * alpha == 1.0) + discard; + + if (discardAlpha && pixColor.a <= discardAlphaValue) + discard; +#endif + +#if USE_CM + pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange +#if USE_ICC + , + iccLut3D, iccLutSize +#else +#if USE_TONEMAP || USE_SDR_MOD + , + targetPrimariesXYZ +#endif +#if USE_TONEMAP + , + maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance +#endif +#if USE_SDR_MOD + , + sdrSaturation, sdrBrightnessMultiplier +#endif +#endif + ); +#endif + +#if USE_TINT + pixColor.rgb = pixColor.rgb * tint; +#endif + +#if USE_ROUNDING + pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize); +#endif +#if USE_BLUR +#if USE_DISCARD + pixColor = mix(pixColor, vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0), + discardAlpha && (pixColor.a <= discardAlphaValue) ? 0.0 : 1.0); +#else + pixColor = vec4(mix(texture(blurredBG, v_texcoord * uvSize + uvOffset).rgb, pixColor.rgb, pixColor.a), 1.0); +#endif +#endif fragColor = pixColor * alpha; } diff --git a/src/render/shaders/glsl/surface_CM.glsl b/src/render/shaders/glsl/surface_CM.glsl deleted file mode 100644 index f90b23c2..00000000 --- a/src/render/shaders/glsl/surface_CM.glsl +++ /dev/null @@ -1,4 +0,0 @@ -uniform int sourceTF; // eTransferFunction -uniform int targetTF; // eTransferFunction -#include "primaries_xyz.glsl" -#include "CM.glsl" diff --git a/src/render/shaders/glsl/tint.glsl b/src/render/shaders/glsl/tint.glsl deleted file mode 100644 index 1523100e..00000000 --- a/src/render/shaders/glsl/tint.glsl +++ /dev/null @@ -1 +0,0 @@ -uniform vec3 tint; diff --git a/src/render/shaders/glsl/tonemap.glsl b/src/render/shaders/glsl/tonemap.glsl index f6ac01f0..a0ba24ef 100644 --- a/src/render/shaders/glsl/tonemap.glsl +++ b/src/render/shaders/glsl/tonemap.glsl @@ -1,17 +1,15 @@ -uniform float maxLuminance; -uniform float dstMaxLuminance; -uniform float dstRefLuminance; +#ifndef ALLOW_INCLUDES +#define ALLOW_INCLUDES +#extension GL_ARB_shading_language_include : enable +#endif +#include "constants.h" -const mat3 BT2020toLMS = mat3( - 0.3592, 0.6976, -0.0358, - -0.1922, 1.1004, 0.0755, - 0.0070, 0.0749, 0.8434 -); +const mat3 BT2020toLMS = mat3(0.3592, 0.6976, -0.0358, -0.1922, 1.1004, 0.0755, 0.0070, 0.0749, 0.8434); //const mat3 LMStoBT2020 = inverse(BT2020toLMS); -const mat3 LMStoBT2020 = mat3( - 2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, - 0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, - -0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 +const mat3 LMStoBT2020 = mat3( // + 2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, // + 0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, // + -0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 // ); // const mat3 ICtCpPQ = transpose(mat3( @@ -19,16 +17,16 @@ const mat3 LMStoBT2020 = mat3( // 6610.0, -13613.0, 7003.0, // 17933.0, -17390.0, -543.0 // ) / 4096.0); -const mat3 ICtCpPQ = mat3( - 0.5, 1.61376953125, 4.378173828125, - 0.5, -3.323486328125, -4.24560546875, - 0.0, 1.709716796875, -0.132568359375 +const mat3 ICtCpPQ = mat3( // + 0.5, 1.61376953125, 4.378173828125, // + 0.5, -3.323486328125, -4.24560546875, // + 0.0, 1.709716796875, -0.132568359375 // ); //const mat3 ICtCpPQInv = inverse(ICtCpPQ); -const mat3 ICtCpPQInv = mat3( - 1.0, 1.0, 1.0, - 0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, - 0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 +const mat3 ICtCpPQInv = mat3( // + 1.0, 1.0, 1.0, // + 0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, // + 0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 // ); // unused for now @@ -39,31 +37,28 @@ const mat3 ICtCpPQInv = mat3( // ) / 4096.0); // const mat3 ICtCpHLGInv = inverse(ICtCpHLG); -vec4 tonemap(vec4 color, mat3 dstXYZ) { +vec4 tonemap(vec4 color, mat3 dstXYZ, float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance) { if (maxLuminance < dstMaxLuminance * 1.01) return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]); - mat3 toLMS = BT2020toLMS * dstXYZ; - mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020; + mat3 toLMS = BT2020toLMS * dstXYZ; + mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020; - vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb; - vec3 ICtCp = ICtCpPQ * lms; + vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb; + vec3 ICtCp = ICtCpPQ * lms; - float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); - float luminance = pow( - (max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), - PQ_INV_M1 - ) * HDR_MAX_LUMINANCE; + float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); + float luminance = pow((max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1) * HDR_MAX_LUMINANCE; - float linearPart = min(luminance, dstRefLuminance); - float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); + float linearPart = min(luminance, dstRefLuminance); + float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0); - float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0)); - float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance); - float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance); + float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0)); + float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance); + float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance); // scale src to dst reference float refScale = dstRefLuminance / srcRefLuminance; - return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]); + return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]); }