renderer: shader variants refactor (#13434)

Part 0 of renderer reworks.
This commit is contained in:
UjinT34 2026-03-07 00:05:10 +03:00 committed by GitHub
parent 8685fd7b0c
commit a5858018d8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 1952 additions and 1472 deletions

View file

@ -125,6 +125,7 @@ find_package(Threads REQUIRED)
set(GLES_VERSION "GLES3") set(GLES_VERSION "GLES3")
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION}) find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
find_package(glslang CONFIG REQUIRED)
set(AQUAMARINE_MINIMUM_VERSION 0.9.3) set(AQUAMARINE_MINIMUM_VERSION 0.9.3)
set(HYPRLANG_MINIMUM_VERSION 0.6.7) set(HYPRLANG_MINIMUM_VERSION 0.6.7)
@ -479,9 +480,9 @@ function(protocolWayland)
endfunction() endfunction()
if(TARGET OpenGL::GL) 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() 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() endif()
pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4) pkg_check_modules(hyprland_protocols_dep hyprland-protocols>=0.6.4)

View file

@ -12,6 +12,7 @@
epoll-shim, epoll-shim,
git, git,
glaze-hyprland, glaze-hyprland,
glslang,
gtest, gtest,
hyprcursor, hyprcursor,
hyprgraphics, hyprgraphics,
@ -173,6 +174,7 @@ customStdenv.mkDerivation (finalAttrs: {
cairo cairo
git git
glaze-hyprland glaze-hyprland
glslang
gtest gtest
hyprcursor hyprcursor
hyprgraphics hyprgraphics

View file

@ -15,7 +15,7 @@ echo 'static const std::map<std::string, std::string> SHADERS = {' >> ./src/rend
for filename in `ls ${SHADERS_SRC}`; do for filename in `ls ${SHADERS_SRC}`; do
echo "-- ${filename}" 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 "{\"${filename}\"," >> ./src/render/shaders/Shaders.hpp
echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp echo "#include \"./${filename}.inc\"" >> ./src/render/shaders/Shaders.hpp
echo "}," >> ./src/render/shaders/Shaders.hpp echo "}," >> ./src/render/shaders/Shaders.hpp

View file

@ -1585,6 +1585,12 @@ inline static const std::vector<SConfigOptionDescription> CONFIG_OPTIONS = {
.type = CONFIG_OPTION_BOOL, .type = CONFIG_OPTION_BOOL,
.data = SConfigOptionDescription::SBoolData{true}, .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: * cursor:

View file

@ -800,6 +800,7 @@ CConfigManager::CConfigManager() {
registerConfigVar("render:cm_sdr_eotf", {"default"}); registerConfigVar("render:cm_sdr_eotf", {"default"});
registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1}); registerConfigVar("render:commit_timing_enabled", Hyprlang::INT{1});
registerConfigVar("render:icc_vcgt_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_update_news", Hyprlang::INT{0});
registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0}); registerConfigVar("ecosystem:no_donation_nag", Hyprlang::INT{0});

View file

@ -2049,7 +2049,12 @@ static std::string submapRequest(eHyprCtlOutputFormat format, std::string reques
} }
static std::string reloadShaders(eHyprCtlOutputFormat format, std::string request) { 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"; return format == FORMAT_JSON ? "{\"ok\": true}" : "ok";
else else
return format == FORMAT_JSON ? "{\"ok\": false}" : "error"; return format == FORMAT_JSON ? "{\"ok\": false}" : "error";
@ -2076,8 +2081,8 @@ CHyprCtl::CHyprCtl() {
registerCommand(SHyprCtlCommand{"locked", true, getIsLocked}); registerCommand(SHyprCtlCommand{"locked", true, getIsLocked});
registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions}); registerCommand(SHyprCtlCommand{"descriptions", true, getDescriptions});
registerCommand(SHyprCtlCommand{"submap", true, submapRequest}); 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{"monitors", false, monitorsRequest});
registerCommand(SHyprCtlCommand{"reload", false, reloadRequest}); registerCommand(SHyprCtlCommand{"reload", false, reloadRequest});
registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin}); registerCommand(SHyprCtlCommand{"plugin", false, dispatchPlugin});

View file

@ -297,22 +297,6 @@ int NFormatUtils::minStride(const SPixelFormat* const fmt, int32_t width) {
return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt)); 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) { std::string NFormatUtils::drmFormatName(DRMFormat drm) {
auto n = drmGetFormatName(drm); auto n = drmGetFormatName(drm);

View file

@ -53,8 +53,6 @@ namespace NFormatUtils {
bool isFormatOpaque(DRMFormat drm); bool isFormatOpaque(DRMFormat drm);
int pixelsPerBlock(const SPixelFormat* const fmt); int pixelsPerBlock(const SPixelFormat* const fmt);
int minStride(const SPixelFormat* const fmt, int32_t width); 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 drmFormatName(DRMFormat drm);
std::string drmModifierName(uint64_t mod); std::string drmModifierName(uint64_t mod);
DRMFormat alphaFormat(DRMFormat prevFormat); DRMFormat alphaFormat(DRMFormat prevFormat);

View file

@ -1579,10 +1579,20 @@ Vector2D CMonitor::middle() {
return m_position + m_size / 2.f; return m_position + m_size / 2.f;
} }
const Mat3x3& CMonitor::getTransformMatrix() {
return m_projMatrix;
}
const Mat3x3& CMonitor::getScaleMatrix() {
return m_projOutputMatrix;
}
void CMonitor::updateMatrix() { void CMonitor::updateMatrix() {
m_projMatrix = Mat3x3::identity(); m_projMatrix = Mat3x3::identity();
if (m_transform != WL_OUTPUT_TRANSFORM_NORMAL) 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_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() { WORKSPACEID CMonitor::activeWorkspaceID() {

View file

@ -127,7 +127,7 @@ class CMonitor {
bool m_scheduledRecalc = false; bool m_scheduledRecalc = false;
wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL; wl_output_transform m_transform = WL_OUTPUT_TRANSFORM_NORMAL;
float m_xwaylandScale = 1.f; float m_xwaylandScale = 1.f;
Mat3x3 m_projMatrix;
std::optional<Vector2D> m_forceSize; std::optional<Vector2D> m_forceSize;
SP<Aquamarine::SOutputMode> m_currentMode; SP<Aquamarine::SOutputMode> m_currentMode;
SP<Aquamarine::CSwapchain> m_cursorSwapchain; SP<Aquamarine::CSwapchain> m_cursorSwapchain;
@ -303,7 +303,6 @@ class CMonitor {
void setSpecialWorkspace(const WORKSPACEID& id); void setSpecialWorkspace(const WORKSPACEID& id);
void moveTo(const Vector2D& pos); void moveTo(const Vector2D& pos);
Vector2D middle(); Vector2D middle();
void updateMatrix();
WORKSPACEID activeWorkspaceID(); WORKSPACEID activeWorkspaceID();
WORKSPACEID activeSpecialWorkspaceID(); WORKSPACEID activeSpecialWorkspaceID();
CBox logicalBox(); CBox logicalBox();
@ -335,6 +334,10 @@ class CMonitor {
bool inHDR(); bool inHDR();
bool gammaRampsInUse(); bool gammaRampsInUse();
//
const Mat3x3& getTransformMatrix();
const Mat3x3& getScaleMatrix();
/// Has an active workspace with a real fullscreen window (includes special workspace) /// Has an active workspace with a real fullscreen window (includes special workspace)
bool inFullscreenMode(); bool inFullscreenMode();
/// Get fullscreen window from active or special workspace /// 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; return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name;
} }
Mat3x3 m_projMatrix;
private: private:
void updateMatrix();
Mat3x3 m_projOutputMatrix;
void setupDefaultWS(const SMonitorRule&); void setupDefaultWS(const SMonitorRule&);
WORKSPACEID findAvailableDefaultWS(); WORKSPACEID findAvailableDefaultWS();
void commitDPMSState(bool state); void commitDPMSState(bool state);

View file

@ -26,7 +26,10 @@ std::string NTransferFunction::toString(eTF tf) {
return ""; return "";
} }
eTF NTransferFunction::fromConfig() { eTF NTransferFunction::fromConfig(bool useICC) {
if (useICC)
return TF_SRGB;
static auto PSDREOTF = CConfigValue<Hyprlang::STRING>("render:cm_sdr_eotf"); static auto PSDREOTF = CConfigValue<Hyprlang::STRING>("render:cm_sdr_eotf");
static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF); static auto sdrEOTF = NTransferFunction::fromString(*PSDREOTF);
static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); }); static auto P = Event::bus()->m_events.config.reloaded.listen([]() { sdrEOTF = NTransferFunction::fromString(*PSDREOTF); });

View file

@ -15,5 +15,5 @@ namespace NTransferFunction {
eTF fromString(const std::string tfName); eTF fromString(const std::string tfName);
std::string toString(eTF tf); std::string toString(eTF tf);
eTF fromConfig(); eTF fromConfig(bool useICC = false);
} }

View file

@ -322,18 +322,22 @@ namespace NColorManagement {
using PImageDescription = WP<const CImageDescription>; using PImageDescription = WP<const CImageDescription>;
static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22, static const auto DEFAULT_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22,
.primariesNameSet = true, .primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB, .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB), .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_SRGB),
.luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80}}); .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, static const auto DEFAULT_HDR_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
.primariesNameSet = true, .primariesNameSet = true,
.primariesNamed = NColorManagement::CM_PRIMARIES_BT2020, .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
.primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020), .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
.luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203}}); .luminances = {.min = HDR_MIN_LUMINANCE, .max = 10000, .reference = 203},
; });
static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{ static const auto SCRGB_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR, .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR,
.windowsScRGB = true, .windowsScRGB = true,
@ -342,6 +346,6 @@ namespace NColorManagement {
.primaries = NColorPrimaries::BT709, .primaries = NColorPrimaries::BT709,
.luminances = {.reference = 203}, .luminances = {.reference = 203},
}); });
;
static const auto LINEAR_IMAGE_DESCRIPTION = SCRGB_IMAGE_DESCRIPTION; // TODO any reason to use something different?
} }

View file

@ -164,6 +164,7 @@ void CScreenshareFrame::renderMonitor() {
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.transformDamage = false;
g_pHyprOpenGL->m_renderData.noSimplify = true; 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. .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->pushMonitorTransformEnabled(true);
g_pHyprOpenGL->setRenderModifEnabled(false); 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->setRenderModifEnabled(true);
g_pHyprOpenGL->popMonitorTransformEnabled(); g_pHyprOpenGL->popMonitorTransformEnabled();

View file

@ -1,5 +1,6 @@
#include <GLES3/gl32.h> #include <GLES3/gl32.h>
#include <hyprgraphics/color/Color.hpp> #include <hyprgraphics/color/Color.hpp>
#include <hyprutils/math/Misc.hpp>
#include <hyprutils/string/String.hpp> #include <hyprutils/string/String.hpp>
#include <hyprutils/path/Path.hpp> #include <hyprutils/path/Path.hpp>
#include <numbers> #include <numbers>
@ -31,6 +32,7 @@
#include "../managers/screenshare/ScreenshareManager.hpp" #include "../managers/screenshare/ScreenshareManager.hpp"
#include "debug/HyprNotificationOverlay.hpp" #include "debug/HyprNotificationOverlay.hpp"
#include "hyprerror/HyprError.hpp" #include "hyprerror/HyprError.hpp"
#include "macros.hpp"
#include "pass/TexPassElement.hpp" #include "pass/TexPassElement.hpp"
#include "pass/RectPassElement.hpp" #include "pass/RectPassElement.hpp"
#include "pass/PreBlurElement.hpp" #include "pass/PreBlurElement.hpp"
@ -44,20 +46,14 @@
#include <xf86drm.h> #include <xf86drm.h>
#include <fcntl.h> #include <fcntl.h>
#include <gbm.h> #include <gbm.h>
#include <filesystem>
#include <cstring> #include <cstring>
#include "./shaders/Shaders.hpp" #include "ShaderLoader.hpp"
#include "Texture.hpp"
#include <filesystem>
using namespace Hyprutils::OS; using namespace Hyprutils::OS;
using namespace NColorManagement; using namespace NColorManagement;
using namespace Render;
const std::vector<const char*> ASSET_PATHS = {
#ifdef DATAROOTDIR
DATAROOTDIR,
#endif
"/usr/share",
"/usr/local/share",
};
static inline void loadGLProc(void* pProc, const char* name) { static inline void loadGLProc(void* pProc, const char* name) {
void* proc = rc<void*>(eglGetProcAddress(name)); void* proc = rc<void*>(eglGetProcAddress(name));
@ -880,124 +876,30 @@ void CHyprOpenGLImpl::setDamage(const CRegion& damage_, std::optional<CRegion> f
m_renderData.finalDamage.set(finalDamage.value_or(damage_)); m_renderData.finalDamage.set(finalDamage.value_or(damage_));
} }
// TODO notify user if bundled shader is newer than ~/.config override static const std::vector<std::string> SHADER_INCLUDES = {
static std::string loadShader(const std::string& filename) { "defines.h", "constants.h", "cm_helpers.glsl", "rounding.glsl", "CM.glsl", "tonemap.glsl", "gain.glsl",
const auto home = Hyprutils::Path::getHome(); "border.glsl", "shadow.glsl", "blurprepare.glsl", "blur1.glsl", "blur2.glsl", "blurFinish.glsl",
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 void loadShaderInclude(const std::string& filename, std::map<std::string, std::string>& includes) { // order matters, see ePreparedFragmentShader
includes.insert({filename, loadShader(filename)}); const std::array<std::string, SH_FRAG_LAST> 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<std::string, std::string>& includes) { bool CHyprOpenGLImpl::initShaders(const std::string& path) {
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<std::string, std::string>& 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<SPreparedShaders>(); auto shaders = makeShared<SPreparedShaders>();
std::map<std::string, std::string> includes;
const bool isDynamic = m_shadersInitialized;
static const auto PCM = CConfigValue<Hyprlang::INT>("render:cm_enabled"); static const auto PCM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
try { try {
loadShaderInclude("get_rgb_pixel.glsl", includes); auto shaderLoader = makeUnique<CShaderLoader>(SHADER_INCLUDES, FRAG_SHADERS, path);
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);
shaders->TEXVERTSRC = processShader("tex300.vert", includes); shaders->TEXVERTSRC = shaderLoader->process("tex300.vert");
shaders->TEXVERTSRC320 = processShader("tex320.vert", includes); shaders->TEXVERTSRC320 = shaderLoader->process("tex320.vert");
if (!*PCM) m_cmSupported = *PCM;
m_cmSupported = false;
else {
std::vector<SFragShaderDesc> CM_SHADERS = {{
{SH_FRAG_CM_BLURPREPARE, "CMblurprepare.frag"},
{SH_FRAG_CM_BORDER1, "CMborder.frag"},
}};
bool success = false; g_pShaderLoader = std::move(shaderLoader);
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<SFragShaderDesc> 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;
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
if (!m_shadersInitialized) if (!m_shadersInitialized)
@ -1008,7 +910,6 @@ bool CHyprOpenGLImpl::initShaders() {
} }
m_shaders = shaders; m_shaders = shaders;
m_includes = includes;
m_shadersInitialized = true; m_shadersInitialized = true;
Log::logger->log(Log::DEBUG, "Shaders initialized successfully."); 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); 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); 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()); shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
// premultiply the color as well as we don't work with straight alpha // 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<CShader> shader, const NColorManagement::PImageDescription imageDescription, void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement::PImageDescription imageDescription,
const NColorManagement::PImageDescription targetImageDescription, bool modifySDR, float sdrMinLuminance, int sdrMaxLuminance) { 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()) { shader->setUniformInt(SHADER_SOURCE_TF, settings.sourceTF);
if (m_renderData.surface->m_colorManagement.valid()) { shader->setUniformInt(SHADER_TARGET_TF, settings.targetTF);
if (sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22 && imageDescription->value().transferFunction == NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB) shader->setUniformFloat2(SHADER_SRC_TF_RANGE, settings.srcTFRange.min, settings.srcTFRange.max);
shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); shader->setUniformFloat2(SHADER_DST_TF_RANGE, settings.dstTFRange.min, settings.dstTFRange.max);
else shader->setUniformFloat(SHADER_SRC_REF_LUMINANCE, settings.srcRefLuminance);
shader->setUniformInt(SHADER_SOURCE_TF, imageDescription->value().transferFunction); shader->setUniformFloat(SHADER_DST_REF_LUMINANCE, settings.dstRefLuminance);
} else if (sdrEOTF == NTransferFunction::TF_SRGB) shader->setUniformFloat(SHADER_MAX_LUMINANCE, settings.maxLuminance);
shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_SRGB); shader->setUniformFloat(SHADER_DST_MAX_LUMINANCE, settings.dstMaxLuminance);
else if (sdrEOTF == NTransferFunction::TF_GAMMA22 || sdrEOTF == NTransferFunction::TF_FORCED_GAMMA22) shader->setUniformFloat(SHADER_SDR_SATURATION, settings.sdrSaturation);
shader->setUniformInt(SHADER_SOURCE_TF, NColorManagement::eTransferFunction::CM_TRANSFER_FUNCTION_GAMMA22); shader->setUniformFloat(SHADER_SDR_BRIGHTNESS, settings.sdrBrightnessMultiplier);
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<GLfloat, 9> 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);
if (!targetImageDescription->value().icc.present) { if (!targetImageDescription->value().icc.present) {
const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id()); const auto cacheKey = std::make_pair(imageDescription->id(), targetImageDescription->id());
if (!primariesConversionCache.contains(cacheKey)) { if (!primariesConversionCache.contains(cacheKey)) {
auto conversion = imageDescription->getPrimaries()->convertMatrix(targetImageDescription->getPrimaries()); const auto& mat = settings.convertMatrix;
const auto mat = conversion.mat();
const std::array<GLfloat, 9> glConvertMatrix = { const std::array<GLfloat, 9> glConvertMatrix = {
mat[0][0], mat[1][0], mat[2][0], // mat[0][0], mat[1][0], mat[2][0], //
mat[0][1], mat[1][1], mat[2][1], // mat[0][1], mat[1][1], mat[2][1], //
@ -1335,12 +1200,14 @@ void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement:
} }
shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]); shader->setUniformMatrix3fv(SHADER_CONVERT_MATRIX, 1, false, primariesConversionCache[cacheKey]);
shader->setUniformInt(SHADER_USE_ICC, 0); const auto mat = settings.dstPrimaries2XYZ;
shader->setUniformInt(SHADER_LUT_3D, 8); // req'd for ogl const std::array<GLfloat, 9> 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 { } else {
// ICC path, use a 3D LUT
shader->setUniformInt(SHADER_USE_ICC, 1);
// TODO: this sucks // TODO: this sucks
GLCALL(glActiveTexture(GL_TEXTURE8)); GLCALL(glActiveTexture(GL_TEXTURE8));
targetImageDescription->value().icc.lutTexture->bind(); targetImageDescription->value().icc.lutTexture->bind();
@ -1353,193 +1220,24 @@ void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const NColorManagement:
} }
void CHyprOpenGLImpl::passCMUniforms(WP<CShader> shader, const PImageDescription imageDescription) { void CHyprOpenGLImpl::passCMUniforms(WP<CShader> 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<CTexture> tex, const CBox& box, const STextureRenderData& data) { WP<CShader> CHyprOpenGLImpl::renderToOutputInternal() {
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);
static const auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking"); static const auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
static const auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
static const auto PCURSORTIMEOUT = CConfigValue<Hyprlang::FLOAT>("cursor:inactive_timeout"); static const auto PCURSORTIMEOUT = CConfigValue<Hyprlang::FLOAT>("cursor:inactive_timeout");
// get the needed transform for this texture WP<CShader> shader =
const auto MONITOR_INVERTED = Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)); g_pHyprRenderer->m_crashingInProgress ? getShaderVariant(SH_FRAG_GLITCH) : (m_finalScreenShader->program() ? m_finalScreenShader : getShaderVariant(SH_FRAG_PASSTHRURGBA));
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<CShader> 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);
shader = useShader(shader); shader = useShader(shader);
if (!skipCM) { if (*PDT == 0 || g_pHyprRenderer->m_crashingInProgress)
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)
shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime()); shader->setUniformFloat(SHADER_TIME, m_globalTimer.getSeconds() - shader->getInitialTime());
else if (CUSTOM_FINAL_SHADER) else
shader->setUniformFloat(SHADER_TIME, 0.F); shader->setUniformFloat(SHADER_TIME, 0.f);
if (CUSTOM_FINAL_SHADER) {
shader->setUniformInt(SHADER_WL_OUTPUT, m_renderData.pMonitor->m_id); 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->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y);
shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT); shader->setUniformFloat(SHADER_POINTER_INACTIVE_TIMEOUT, *PCURSORTIMEOUT);
@ -1548,9 +1246,8 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape); shader->setUniformInt(SHADER_POINTER_SHAPE, g_pHyprRenderer->m_lastCursorData.shape);
shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious); shader->setUniformInt(SHADER_POINTER_SHAPE_PREVIOUS, g_pHyprRenderer->m_lastCursorData.shapePrevious);
shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize()); shader->setUniformFloat(SHADER_POINTER_SIZE, g_pCursorManager->getScaledSize());
}
if (CUSTOM_FINAL_SHADER && *PDT == 0) { if (*PDT == 0) {
PHLMONITORREF pMonitor = m_renderData.pMonitor; PHLMONITORREF pMonitor = m_renderData.pMonitor;
Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale); Vector2D p = ((g_pInputManager->getMouseCoordsInternal() - pMonitor->m_position) * pMonitor->m_scale);
p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize); p = p.transform(Math::wlTransformToHyprutils(pMonitor->m_transform), pMonitor->m_pixelSize);
@ -1576,7 +1273,7 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds()); shader->setUniformFloat(SHADER_POINTER_LAST_ACTIVE, g_pInputManager->m_lastCursorMovement.getSeconds());
shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, g_pHyprRenderer->m_lastCursorData.switchedTimer.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); shader->setUniformFloat2(SHADER_POINTER, 0.f, 0.f);
static const std::vector<float> pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f); static const std::vector<float> pressedPosDefault(POINTER_PRESSED_HISTORY_LENGTH * 2uz, 0.f);
@ -1590,13 +1287,141 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f); shader->setUniformFloat(SHADER_POINTER_SWITCH_TIME, 0.f);
} }
if (CRASHING) { if (g_pHyprRenderer->m_crashingInProgress) {
shader->setUniformFloat(SHADER_DISTORT, g_pHyprRenderer->m_crashingDistort); 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); shader->setUniformFloat2(SHADER_FULL_SIZE, m_renderData.pMonitor->m_pixelSize.x, m_renderData.pMonitor->m_pixelSize.y);
} }
return shader;
}
WP<CShader> CHyprOpenGLImpl::renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox) {
static const auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
static const auto PENABLECM = CConfigValue<Hyprlang::INT>("render:cm_enabled");
static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend");
float alpha = std::clamp(data.a, 0.f, 1.f);
WP<CShader> 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); 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) { if (data.discardActive) {
shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE)); shader->setUniformInt(SHADER_DISCARD_OPAQUE, !!(m_renderData.discardMode & DISCARD_OPAQUE));
shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA)); shader->setUniformInt(SHADER_DISCARD_ALPHA, !!(m_renderData.discardMode & DISCARD_ALPHA));
@ -1612,7 +1437,6 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y); const auto TOPLEFT = Vector2D(transformedBox.x, transformedBox.y);
const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height); const auto FULLSIZE = Vector2D(transformedBox.width, transformedBox.height);
// Rounded corners // Rounded corners
shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y); shader->setUniformFloat2(SHADER_TOP_LEFT, TOPLEFT.x, TOPLEFT.y);
shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y); shader->setUniformFloat2(SHADER_FULL_SIZE, FULLSIZE.x, FULLSIZE.y);
@ -1633,8 +1457,53 @@ void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> tex, const CBox& box, c
} else } else
shader->setUniformInt(SHADER_APPLY_TINT, 0); shader->setUniformInt(SHADER_APPLY_TINT, 0);
glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); return shader;
glBindBuffer(GL_ARRAY_BUFFER, shader->getUniformLocation(SHADER_SHADER_VBO)); }
void CHyprOpenGLImpl::renderTextureInternal(SP<CTexture> 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. // 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 // to avoid stalls if renderTextureInternal is called multiple times on same renderpass
@ -1719,7 +1588,7 @@ void CHyprOpenGLImpl::renderTexturePrimitive(SP<CTexture> tex, const CBox& box)
tex->setTexParameter(GL_TEXTURE_MIN_FILTER, tex->minFilter); 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_TEX, 0);
glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO)); glBindVertexArray(shader->getUniformLocation(SHADER_SHADER_VAO));
@ -1751,7 +1620,7 @@ void CHyprOpenGLImpl::renderTextureMatte(SP<CTexture> tex, const CBox& box, CFra
Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot); Mat3x3 matrix = m_renderData.monitorProjection.projectBox(newBox, TRANSFORM, newBox.rot);
Mat3x3 glMatrix = m_renderData.projection.copy().multiply(matrix); 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniformInt(SHADER_TEX, 0); shader->setUniformInt(SHADER_TEX, 0);
shader->setUniformInt(SHADER_ALPHA_MATTE, 1); shader->setUniformInt(SHADER_ALPHA_MATTE, 1);
@ -1826,6 +1695,7 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi
{ {
static auto PBLURCONTRAST = CConfigValue<Hyprlang::FLOAT>("decoration:blur:contrast"); static auto PBLURCONTRAST = CConfigValue<Hyprlang::FLOAT>("decoration:blur:contrast");
static auto PBLURBRIGHTNESS = CConfigValue<Hyprlang::FLOAT>("decoration:blur:brightness"); static auto PBLURBRIGHTNESS = CConfigValue<Hyprlang::FLOAT>("decoration:blur:brightness");
static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend");
PMIRRORSWAPFB->bind(); PMIRRORSWAPFB->bind();
@ -1838,7 +1708,26 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi
WP<CShader> shader; WP<CShader> 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniformFloat(SHADER_CONTRAST, *PBLURCONTRAST); shader->setUniformFloat(SHADER_CONTRAST, *PBLURCONTRAST);
shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS);
@ -1910,13 +1799,13 @@ CFramebuffer* CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* origi
CRegion tempDamage{damage}; CRegion tempDamage{damage};
// and draw // 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) { for (auto i = 1; i <= BLUR_PASSES; ++i) {
tempDamage = damage.copy().scale(1.f / (1 << i)); tempDamage = damage.copy().scale(1.f / (1 << i));
drawPass(shader, SH_FRAG_BLUR1, &tempDamage); // down 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) { 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 tempDamage = damage.copy().scale(1.f / (1 << i)); // when upsampling we make the region twice as big
drawPass(shader, SH_FRAG_BLUR2, &tempDamage); // up 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); 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE); shader->setUniformFloat(SHADER_NOISE, *PBLURNOISE);
shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS); shader->setUniformFloat(SHADER_BRIGHTNESS, *PBLURBRIGHTNESS);
@ -2132,6 +2021,8 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox
TRACY_GPU_ZONE("RenderTextureWithBlur"); TRACY_GPU_ZONE("RenderTextureWithBlur");
static auto PBLEND = CConfigValue<Hyprlang::INT>("render:use_shader_blur_blend");
// make a damage region for this window // make a damage region for this window
CRegion texDamage{m_renderData.damage}; CRegion texDamage{m_renderData.damage};
texDamage.intersect(box.x, box.y, box.width, box.height); texDamage.intersect(box.x, box.y, box.width, box.height);
@ -2178,7 +2069,10 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox
m_renderData.currentFB->bind(); m_renderData.currentFB->bind();
const auto NEEDS_STENCIL = m_renderData.discardMode != 0; auto blurredBG = POUTFB->getTexture();
const auto NEEDS_STENCIL = m_renderData.discardMode != 0 && (!data.blockBlurOptimization || (m_renderData.discardMode & DISCARD_ALPHA));
if (!*PBLEND) {
if (NEEDS_STENCIL) { if (NEEDS_STENCIL) {
scissor(nullptr); // allow the entire window and stencil to render scissor(nullptr); // allow the entire window and stencil to render
@ -2191,9 +2085,6 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); 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
renderTexture(tex, box, renderTexture(tex, box,
STextureRenderData{.a = data.a, STextureRenderData{.a = data.a,
.round = data.round, .round = data.round,
@ -2201,7 +2092,8 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox
.discardActive = true, .discardActive = true,
.allowCustomUV = true, .allowCustomUV = true,
.wrapX = data.wrapX, .wrapX = data.wrapX,
.wrapY = data.wrapY}); // discard opaque .wrapY = data.wrapY}); // discard opaque and alpha < discardOpacity
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glStencilFunc(GL_EQUAL, 1, 0xFF); glStencilFunc(GL_EQUAL, 1, 0xFF);
@ -2226,9 +2118,10 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox
static auto PBLURIGNOREOPACITY = CConfigValue<Hyprlang::INT>("decoration:blur:ignore_opacity"); static auto PBLURIGNOREOPACITY = CConfigValue<Hyprlang::INT>("decoration:blur:ignore_opacity");
pushMonitorTransformEnabled(true); pushMonitorTransformEnabled(true);
bool renderModif = m_renderData.renderModif.enabled;
if (!USENEWOPTIMIZE) if (!USENEWOPTIMIZE)
setRenderModifEnabled(false); setRenderModifEnabled(false);
renderTextureInternal(POUTFB->getTexture(), box, renderTextureInternal(blurredBG, box,
STextureRenderData{ STextureRenderData{
.damage = &texDamage, .damage = &texDamage,
.a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA, .a = (*PBLURIGNOREOPACITY ? data.blurA : data.a * data.blurA) * data.overallA,
@ -2241,26 +2134,31 @@ void CHyprOpenGLImpl::renderTextureWithBlurInternal(SP<CTexture> tex, const CBox
.wrapY = data.wrapY, .wrapY = data.wrapY,
}); });
if (!USENEWOPTIMIZE) if (!USENEWOPTIMIZE)
setRenderModifEnabled(true); setRenderModifEnabled(renderModif);
popMonitorTransformEnabled(); popMonitorTransformEnabled();
if (NEEDS_STENCIL)
setCapStatus(GL_STENCIL_TEST, false);
m_renderData.primarySurfaceUVTopLeft = LASTTL; m_renderData.primarySurfaceUVTopLeft = LASTTL;
m_renderData.primarySurfaceUVBottomRight = LASTBR; m_renderData.primarySurfaceUVBottomRight = LASTBR;
}
// draw window // draw window
setCapStatus(GL_STENCIL_TEST, false);
renderTextureInternal(tex, box, renderTextureInternal(tex, box,
STextureRenderData{ STextureRenderData{
.damage = &texDamage, .damage = &texDamage,
.a = data.a * data.overallA, .a = data.a * data.overallA,
.blur = *PBLEND,
.round = data.round, .round = data.round,
.roundingPower = data.roundingPower, .roundingPower = data.roundingPower,
.discardActive = false, .discardActive = *PBLEND && NEEDS_STENCIL,
.allowCustomUV = true, .allowCustomUV = true,
.allowDim = true, .allowDim = true,
.noAA = false, .noAA = false,
.wrapX = data.wrapX, .wrapX = data.wrapX,
.wrapY = data.wrapY, .wrapY = data.wrapY,
.blurredBG = blurredBG,
}); });
m_renderData.currentFB->invalidate({GL_STENCIL_ATTACHMENT}); 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; const auto BLEND = m_blend;
blend(true); blend(true);
WP<CShader> shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); WP<CShader> 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniform4fv(SHADER_GRADIENT, grad.m_colorsOkLabA.size() / 4, grad.m_colorsOkLabA); 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; const auto BLEND = m_blend;
blend(true); blend(true);
WP<CShader> shader = useShader(m_shaders->frag[SH_FRAG_BORDER1]); WP<CShader> 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniform4fv(SHADER_GRADIENT, grad1.m_colorsOkLabA.size() / 4, grad1.m_colorsOkLabA); 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); 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->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a); 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; return m_exts.EGL_ANDROID_native_fence_sync_ext;
} }
WP<CShader> CHyprOpenGLImpl::getSurfaceShader(uint8_t features) { WP<CShader> CHyprOpenGLImpl::getShaderVariant(ePreparedFragmentShader frag, ShaderFeatureFlags features) {
if (!m_shaders->fragVariants.contains(features)) { if (!m_shaders->fragVariants[frag].contains(features)) {
auto shader = makeShared<CShader>(); auto shader = makeShared<CShader>();
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, "getSurfaceShader: compiling feature set {}", features); Log::logger->log(Log::INFO, "compiling feature set {} for {}", features, FRAG_SHADERS[frag]);
const auto fragSrc = processShader("surface.frag", includes, MAX_INCLUDE_DEPTH);
if (shader->createProgram(m_shaders->TEXVERTSRC, fragSrc, true, true)) { const auto fragSrc = g_pShaderLoader->getVariantSource(frag, features);
m_shaders->fragVariants[features] = shader;
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; return shader;
} else {
Log::logger->log(Log::ERR, "getSurfaceShader failed for {}. Falling back to old branching", features);
m_shaders->fragVariants[features] = nullptr;
}
} }
ASSERT(m_shaders->fragVariants[features]); ASSERT(m_shaders->fragVariants[frag][features]);
return m_shaders->fragVariants[features]; return m_shaders->fragVariants[frag][features];
} }
std::vector<SDRMFormat> CHyprOpenGLImpl::getDRMFormats() { std::vector<SDRMFormat> CHyprOpenGLImpl::getDRMFormats() {

View file

@ -31,6 +31,7 @@
#include "../debug/TracyDefines.hpp" #include "../debug/TracyDefines.hpp"
#include "../protocols/core/Compositor.hpp" #include "../protocols/core/Compositor.hpp"
#include "render/ShaderLoader.hpp"
struct gbm_device; struct gbm_device;
class CHyprRenderer; class CHyprRenderer;
@ -87,54 +88,23 @@ enum eMonitorExtraRenderFBs : uint8_t {
FB_MONITOR_RENDER_EXTRA_BLUR, 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 { struct SFragShaderDesc {
ePreparedFragmentShader id; Render::ePreparedFragmentShader id;
const char* file; const char* file;
}; };
struct SPreparedShaders { struct SPreparedShaders {
SPreparedShaders() { // SPreparedShaders() {
for (auto& f : frag) { // for (auto& f : frag) {
f = makeShared<CShader>(); // f = makeShared<CShader>();
} // }
} // }
std::string TEXVERTSRC; std::string TEXVERTSRC;
std::string TEXVERTSRC320; std::string TEXVERTSRC320;
std::array<SP<CShader>, SH_FRAG_LAST> frag; // std::array<SP<CShader>, SH_FRAG_LAST> frag;
std::map<uint8_t, SP<CShader>> fragVariants; // std::map<uint8_t, SP<CShader>> fragVariants;
std::array<std::map<Render::ShaderFeatureFlags, SP<CShader>>, Render::SH_FRAG_LAST> fragVariants;
}; };
struct SMonitorRenderData { struct SMonitorRenderData {
@ -242,6 +212,8 @@ class CHyprOpenGLImpl {
bool cmBackToSRGB = false; bool cmBackToSRGB = false;
bool noCM = false; bool noCM = false;
bool finalMonitorCM = false; bool finalMonitorCM = false;
SP<CMonitor> cmBackToSRGBSource;
SP<CTexture> blurredBG;
}; };
struct SBorderRenderData { struct SBorderRenderData {
@ -316,18 +288,15 @@ class CHyprOpenGLImpl {
std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format); std::vector<uint64_t> getDRMFormatModifiers(DRMFormat format);
EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs); EGLImageKHR createEGLImage(const Aquamarine::SDMABUFAttrs& attrs);
bool initShaders(); bool initShaders(const std::string& path = "");
WP<CShader> useShader(WP<CShader> prog); WP<CShader> useShader(WP<CShader> prog);
void ensureLockTexturesRendered(bool load);
bool explicitSyncSupported(); bool explicitSyncSupported();
WP<CShader> getSurfaceShader(uint8_t features); WP<CShader> getShaderVariant(Render::ePreparedFragmentShader frag, Render::ShaderFeatureFlags features = 0);
bool m_shadersInitialized = false; bool m_shadersInitialized = false;
SP<SPreparedShaders> m_shaders; SP<SPreparedShaders> m_shaders;
std::map<std::string, std::string> m_includes;
SCurrentRenderData m_renderData; SCurrentRenderData m_renderData;
@ -431,6 +400,7 @@ class CHyprOpenGLImpl {
void initEGL(bool gbm); void initEGL(bool gbm);
EGLDeviceEXT eglDeviceFromDRMFD(int drmFD); EGLDeviceEXT eglDeviceFromDRMFD(int drmFD);
void initAssets(); void initAssets();
void ensureLockTexturesRendered(bool load);
void initMissingAssetTexture(); void initMissingAssetTexture();
void requestBackgroundResource(); void requestBackgroundResource();
@ -454,6 +424,8 @@ class CHyprOpenGLImpl {
void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data); void renderRectInternal(const CBox&, const CHyprColor&, const SRectRenderData& data);
void renderRectWithBlurInternal(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); void renderRectWithDamageInternal(const CBox&, const CHyprColor&, const SRectRenderData& data);
WP<CShader> renderToOutputInternal();
WP<CShader> renderToFBInternal(const STextureRenderData& data, eTextureType texType, const CBox& newBox);
void renderTextureInternal(SP<CTexture>, const CBox&, const STextureRenderData& data); void renderTextureInternal(SP<CTexture>, const CBox&, const STextureRenderData& data);
void renderTextureWithBlurInternal(SP<CTexture>, const CBox&, const STextureRenderData& data); void renderTextureWithBlurInternal(SP<CTexture>, const CBox&, const STextureRenderData& data);

View file

@ -41,6 +41,7 @@ CRenderbuffer::CRenderbuffer(SP<Aquamarine::IBuffer> buffer, uint32_t format) :
glGenFramebuffers(1, &m_framebuffer.m_fb); glGenFramebuffers(1, &m_framebuffer.m_fb);
m_framebuffer.m_fbAllocated = true; m_framebuffer.m_fbAllocated = true;
m_framebuffer.m_size = buffer->size; m_framebuffer.m_size = buffer->size;
m_framebuffer.m_drmFormat = dma.format;
m_framebuffer.bind(); m_framebuffer.bind();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);

View file

@ -32,6 +32,7 @@
#include "../event/EventBus.hpp" #include "../event/EventBus.hpp"
#include "helpers/CursorShapes.hpp" #include "helpers/CursorShapes.hpp"
#include "helpers/Monitor.hpp" #include "helpers/Monitor.hpp"
#include "helpers/cm/ColorManagement.hpp"
#include "pass/TexPassElement.hpp" #include "pass/TexPassElement.hpp"
#include "pass/ClearPassElement.hpp" #include "pass/ClearPassElement.hpp"
#include "pass/RectPassElement.hpp" #include "pass/RectPassElement.hpp"
@ -41,7 +42,6 @@
#include "../protocols/ColorManagement.hpp" #include "../protocols/ColorManagement.hpp"
#include "../protocols/types/ContentType.hpp" #include "../protocols/types/ContentType.hpp"
#include "../helpers/MiscFunctions.hpp" #include "../helpers/MiscFunctions.hpp"
#include "../event/EventBus.hpp"
#include "render/OpenGL.hpp" #include "render/OpenGL.hpp"
#include <hyprutils/utils/ScopeGuard.hpp> #include <hyprutils/utils/ScopeGuard.hpp>
@ -1260,6 +1260,79 @@ void CHyprRenderer::calculateUVForSurface(PHLWINDOW pWindow, SP<CWLSurfaceResour
} }
} }
static bool isSDR2HDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) {
// might be too strict
return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB ||
imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22) &&
(targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ||
targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG);
}
static bool isHDR2SDR(const NColorManagement::SImageDescription& imageDescription, const NColorManagement::SImageDescription& targetImageDescription) {
// might be too strict
return (imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ||
imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_HLG) &&
(targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_SRGB ||
targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_GAMMA22);
}
SCMSettings CHyprRenderer::getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription,
SP<CWLSurfaceResource> 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) { 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 renderStart = std::chrono::high_resolution_clock::now();
static std::chrono::high_resolution_clock::time_point renderStartOverlay = 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<float, float, float> CHyprRenderer::getRenderTimes(PHLMONITOR pMonito
float maxRenderTime = 0; float maxRenderTime = 0;
float minRenderTime = 9999; float minRenderTime = 9999;
for (auto const& rt : POVERLAY->m_lastRenderTimes) { for (auto const& rt : POVERLAY->m_lastRenderTimes) {
if (rt > maxRenderTime) maxRenderTime = std::max(rt, maxRenderTime);
maxRenderTime = rt; minRenderTime = std::min(rt, minRenderTime);
if (rt < minRenderTime)
minRenderTime = rt;
avgRenderTime += rt; avgRenderTime += rt;
} }
avgRenderTime /= POVERLAY->m_lastRenderTimes.empty() ? 1 : POVERLAY->m_lastRenderTimes.size(); avgRenderTime /= POVERLAY->m_lastRenderTimes.empty() ? 1 : POVERLAY->m_lastRenderTimes.size();
@ -2706,6 +2777,16 @@ void CHyprRenderer::renderSnapshot(WP<Desktop::View::CPopup> popup) {
m_renderPass.add(makeUnique<CTexPassElement>(std::move(data))); m_renderPass.add(makeUnique<CTexPassElement>(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) { bool CHyprRenderer::shouldBlur(PHLLS ls) {
if (m_bRenderingSnapshot) if (m_bRenderingSnapshot)
return false; return false;
@ -2729,3 +2810,7 @@ bool CHyprRenderer::shouldBlur(WP<Desktop::View::CPopup> p) {
return *PBLURPOPUPS && *PBLUR; return *PBLURPOPUPS && *PBLUR;
} }
bool CHyprRenderer::reloadShaders(const std::string& path) {
return g_pHyprOpenGL->initShaders(path);
}

View file

@ -1,7 +1,10 @@
#pragma once #pragma once
#include "../defines.hpp" #include "../defines.hpp"
#include <hyprgraphics/color/Color.hpp>
#include <hyprutils/math/Box.hpp>
#include <list> #include <list>
#include <optional>
#include "../helpers/Monitor.hpp" #include "../helpers/Monitor.hpp"
#include "../desktop/view/LayerSurface.hpp" #include "../desktop/view/LayerSurface.hpp"
#include "OpenGL.hpp" #include "OpenGL.hpp"
@ -10,12 +13,21 @@
#include "../helpers/math/Math.hpp" #include "../helpers/math/Math.hpp"
#include "../helpers/time/Time.hpp" #include "../helpers/time/Time.hpp"
#include "../../protocols/cursor-shape-v1.hpp" #include "../../protocols/cursor-shape-v1.hpp"
#include "helpers/cm/ColorManagement.hpp"
struct SMonitorRule; struct SMonitorRule;
class CWorkspace; class CWorkspace;
class CInputPopup; class CInputPopup;
class IHLBuffer; class IHLBuffer;
class CEventLoopTimer; class CEventLoopTimer;
const std::vector<const char*> ASSET_PATHS = {
#ifdef DATAROOTDIR
DATAROOTDIR,
#endif
"/usr/share",
"/usr/local/share",
};
class CToplevelExportProtocolManager; class CToplevelExportProtocolManager;
class CInputManager; class CInputManager;
struct SSessionLockSurface; struct SSessionLockSurface;
@ -48,6 +60,29 @@ struct SRenderWorkspaceUntilData {
PHLWINDOW w; 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<std::array<double, 3>, 3> convertMatrix;
bool needsTonemap = false;
float maxLuminance = 80;
float dstMaxLuminance = 80;
std::array<std::array<double, 3>, 3> dstPrimaries2XYZ;
bool needsSDRmod = false;
float sdrSaturation = 1.0;
float sdrBrightnessMultiplier = 1.0;
};
class CHyprRenderer { class CHyprRenderer {
public: public:
CHyprRenderer(); CHyprRenderer();
@ -89,6 +124,9 @@ class CHyprRenderer {
void renderSnapshot(PHLLS); void renderSnapshot(PHLLS);
void renderSnapshot(WP<Desktop::View::CPopup>); void renderSnapshot(WP<Desktop::View::CPopup>);
//
NColorManagement::PImageDescription workBufferImageDescription();
// if RENDER_MODE_NORMAL, provided damage will be written to. // if RENDER_MODE_NORMAL, provided damage will be written to.
// otherwise, it will be the one used. // otherwise, it will be the one used.
bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP<IHLBuffer> buffer = {}, CFramebuffer* fb = nullptr, bool simple = false); bool beginRender(PHLMONITOR pMonitor, CRegion& damage, eRenderMode mode = RENDER_MODE_NORMAL, SP<IHLBuffer> buffer = {}, CFramebuffer* fb = nullptr, bool simple = false);
@ -121,6 +159,10 @@ class CHyprRenderer {
CRenderPass m_renderPass = {}; CRenderPass m_renderPass = {};
SCMSettings getCMSettings(const NColorManagement::PImageDescription imageDescription, const NColorManagement::PImageDescription targetImageDescription,
SP<CWLSurfaceResource> surface = nullptr, bool modifySDR = false, float sdrMinLuminance = -1.0f, int sdrMaxLuminance = -1);
bool reloadShaders(const std::string& path = "");
private: private:
void arrangeLayerArray(PHLMONITOR, const std::vector<PHLLSREF>&, bool, CBox*); void arrangeLayerArray(PHLMONITOR, const std::vector<PHLLSREF>&, bool, CBox*);
void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry); void renderWorkspace(PHLMONITOR pMonitor, PHLWORKSPACE pWorkspace, const Time::steady_tp& now, const CBox& geometry);

View file

@ -139,11 +139,13 @@ void CShader::getUniformLocations() {
m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation"); m_uniformLocations[SHADER_SDR_SATURATION] = getUniform("sdrSaturation");
m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier"); m_uniformLocations[SHADER_SDR_BRIGHTNESS] = getUniform("sdrBrightnessMultiplier");
m_uniformLocations[SHADER_CONVERT_MATRIX] = getUniform("convertMatrix"); 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_3D] = getUniform("iccLut3D");
m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize"); m_uniformLocations[SHADER_LUT_SIZE] = getUniform("iccLutSize");
// //
m_uniformLocations[SHADER_TEX] = getUniform("tex"); 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_ALPHA] = getUniform("alpha");
m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos"); m_uniformLocations[SHADER_POS_ATTRIB] = getAttrib("pos");
m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord"); m_uniformLocations[SHADER_TEX_ATTRIB] = getAttrib("texcoord");

View file

@ -74,9 +74,11 @@ enum eShaderUniform : uint8_t {
SHADER_POINTER_INACTIVE_TIMEOUT, SHADER_POINTER_INACTIVE_TIMEOUT,
SHADER_POINTER_LAST_ACTIVE, SHADER_POINTER_LAST_ACTIVE,
SHADER_POINTER_SIZE, SHADER_POINTER_SIZE,
SHADER_USE_ICC,
SHADER_LUT_3D, SHADER_LUT_3D,
SHADER_LUT_SIZE, SHADER_LUT_SIZE,
SHADER_BLURRED_BG,
SHADER_UV_SIZE,
SHADER_UV_OFFSET,
SHADER_LAST, SHADER_LAST,
}; };

176
src/render/ShaderLoader.cpp Normal file
View file

@ -0,0 +1,176 @@
#include "ShaderLoader.hpp"
#include <format>
#include <hyprutils/memory/Casts.hpp>
#include <hyprutils/memory/UniquePtr.hpp>
#include <hyprutils/string/String.hpp>
#include <hyprutils/path/Path.hpp>
#include "../debug/log/Logger.hpp"
#include "shaders/Shaders.hpp"
#include "../helpers/fs/FsUtils.hpp"
#include "Renderer.hpp"
#include <glslang/Public/resource_limits_c.h>
#include <string>
#include <filesystem>
using namespace Render;
CShaderLoader::CShaderLoader(const std::vector<std::string> includes, const std::array<std::string, SH_FRAG_LAST>& 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<CShaderLoader*>(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<CShaderLoader*>(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<std::string, std::string> 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<std::string, std::string>& 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<Hyprlang::INT>("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<std::string, std::string>& 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));
}

View file

@ -0,0 +1,77 @@
#pragma once
#include <array>
#include <glslang/Include/glslang_c_interface.h>
#include <string>
#include <vector>
#include <map>
#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<std::string> includes, const std::array<std::string, SH_FRAG_LAST>& 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<std::string, std::string>& defines);
std::string getVariantSource(ePreparedFragmentShader frag, ShaderFeatureFlags features);
const std::map<std::string, std::string>& includes();
std::vector<glsl_include_result_t*> 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<std::string, SH_FRAG_LAST> m_fragFiles;
std::array<std::map<ShaderFeatureFlags, std::string>, SH_FRAG_LAST> m_fragVariants;
std::map<std::string, std::string> m_includes;
std::string m_overrideDefines;
glsl_include_callbacks_t m_callbacks;
};
inline UP<CShaderLoader> g_pShaderLoader;
}

View file

@ -1,306 +1,27 @@
#ifndef ALLOW_INCLUDES
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
#endif
#include "cm_helpers.glsl"
uniform vec2 srcTFRange; uniform vec2 srcTFRange;
uniform vec2 dstTFRange; uniform vec2 dstTFRange;
uniform float srcRefLuminance; uniform float srcRefLuminance;
uniform mat3 convertMatrix; uniform mat3 convertMatrix;
#include "sdr_mod.glsl" #if USE_ICC
uniform int useIcc;
uniform highp sampler3D iccLut3D; uniform highp sampler3D iccLut3D;
uniform float iccLutSize; uniform float iccLutSize;
#endif
//enum eTransferFunction #if USE_SDR_MOD
#define CM_TRANSFER_FUNCTION_BT1886 1 uniform float sdrSaturation;
#define CM_TRANSFER_FUNCTION_GAMMA22 2 uniform float sdrBrightnessMultiplier;
#define CM_TRANSFER_FUNCTION_GAMMA28 3 #endif
#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 #if USE_TONEMAP
#define SRGB_POW 2.4 uniform float maxLuminance;
#define SRGB_CUT 0.0031308 uniform float dstMaxLuminance;
#define SRGB_SCALE 12.92 uniform float dstRefLuminance;
#define SRGB_ALPHA 1.055 #endif
#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;
}

View file

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

View file

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

View file

@ -1,4 +1,7 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float; precision highp float;
uniform sampler2D tex; uniform sampler2D tex;
@ -9,135 +12,10 @@ uniform float vibrancy;
uniform float vibrancy_darkness; uniform float vibrancy_darkness;
in vec2 v_texcoord; 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;
}
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
#include "blur1.glsl"
void main() { void main() {
vec2 uv = v_texcoord * 2.0; fragColor = blur1(v_texcoord, tex, radius, halfpixel, passes, vibrancy, vibrancy_darkness);
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]);
}
} }

View file

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

View file

@ -1,4 +1,7 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable
precision highp float; precision highp float;
uniform sampler2D tex; uniform sampler2D tex;
@ -8,18 +11,8 @@ uniform vec2 halfpixel;
in vec2 v_texcoord; in vec2 v_texcoord;
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
#include "blur2.glsl"
void main() { void main() {
vec2 uv = v_texcoord / 2.0; fragColor = blur2(v_texcoord, tex, radius, halfpixel);
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;
} }

View file

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

View file

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

View file

@ -1,4 +1,5 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
precision highp float; precision highp float;
@ -8,23 +9,11 @@ uniform sampler2D tex;
uniform float noise; uniform float noise;
uniform float brightness; uniform float brightness;
float hash(vec2 p) { #include "blurFinish.glsl"
vec3 p3 = fract(vec3(p.xyx) * 1689.1984);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
void main() { void main() {
vec4 pixColor = texture(tex, v_texcoord); vec4 pixColor = texture(tex, v_texcoord);
// noise fragColor = blurFinish(pixColor, v_texcoord, noise, brightness);
float noiseHash = hash(v_texcoord);
float noiseAmount = noiseHash - 0.5;
pixColor.rgb += noiseAmount * noise;
// brightness
pixColor.rgb *= min(1.0, brightness);
fragColor = pixColor;
} }

View file

@ -1,6 +1,9 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
#include "defines.h"
precision highp float; precision highp float;
in vec2 v_texcoord; // is in 0-1 in vec2 v_texcoord; // is in 0-1
uniform sampler2D tex; uniform sampler2D tex;
@ -8,19 +11,28 @@ uniform sampler2D tex;
uniform float contrast; uniform float contrast;
uniform float brightness; uniform float brightness;
#include "CM.glsl" uniform int sourceTF; // eTransferFunction
#include "gain.glsl" 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; layout(location = 0) out vec4 fragColor;
void main() { void main() {
vec4 pixColor = texture(tex, v_texcoord); fragColor = fragColor = blurPrepare(texture(tex, v_texcoord), contrast, brightness
#if USE_CM
// contrast ,
if (contrast != 1.0) sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange, srcRefLuminance, sdrBrightnessMultiplier
pixColor.rgb = gain(pixColor.rgb, contrast); #endif
);
// brightness
pixColor.rgb *= max(1.0, brightness);
fragColor = pixColor;
} }

View file

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

View file

@ -1,9 +1,14 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
precision highp float; precision highp float;
in vec2 v_texcoord; in vec2 v_texcoord;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat3 targetPrimariesXYZ;
uniform vec2 fullSizeUntransformed; uniform vec2 fullSizeUntransformed;
uniform float radiusOuter; uniform float radiusOuter;
uniform float thick; uniform float thick;
@ -18,75 +23,38 @@ uniform float angle2;
uniform float gradientLerp; uniform float gradientLerp;
uniform float alpha; uniform float alpha;
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl" #include "rounding.glsl"
#include "CM.glsl" #include "CM.glsl"
#include "border.glsl" #include "border.glsl"
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
void main() { void main() {
highp vec2 pixCoord = vec2(gl_FragCoord); fragColor = getBorder(v_texcoord, alpha, fullSizeUntransformed, radiusOuter, thick, radius, roundingPower, topLeft, fullSize, gradientLength, gradient, angle, gradient2Length,
highp vec2 pixCoordOuter = pixCoord; gradient2, angle2, gradientLerp
highp vec2 originalPixCoord = v_texcoord; #if USE_CM
originalPixCoord *= fullSizeUntransformed; ,
float additionalAlpha = 1.0; sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
#if USE_ICC
vec4 pixColor = vec4(1.0, 1.0, 1.0, 1.0); ,
iccLut3D, iccLutSize
bool done = false; #else
#if USE_TONEMAP || USE_SDR_MOD
pixCoord -= topLeft + fullSize * 0.5; ,
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; targetPrimariesXYZ
pixCoordOuter = pixCoord; #endif
pixCoord -= fullSize * 0.5 - radius; #if USE_TONEMAP
pixCoordOuter -= fullSize * 0.5 - radiusOuter; ,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
// center the pixes don't make it top-left #endif
pixCoord += vec2(1.0, 1.0) / fullSize; #if USE_SDR_MOD
pixCoordOuter += vec2(1.0, 1.0) / fullSize; ,
sdrSaturation, sdrBrightnessMultiplier
if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { #endif
float dist = pow(pow(pixCoord.x,roundingPower)+pow(pixCoord.y,roundingPower),1.0/roundingPower); #endif
float distOuter = pow(pow(pixCoordOuter.x,roundingPower)+pow(pixCoordOuter.y,roundingPower),1.0/roundingPower); #endif
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;
} }

View file

@ -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) { vec4 okLabAToSrgb(vec4 lab) {
float l = pow(lab[0] + lab[1] * 0.3963377774 + lab[2] * 0.2158037573, 3.0); 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 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); float s = pow(lab[0] + lab[1] * (-0.0894841775) + lab[2] * (-1.2914855480), 3.0);
return vec4(fromLinearRGB( return vec4(fromLinearRGB(vec3(l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965),
vec3( l * (-0.0041960863) + m * (-0.7034186147) + s * 1.7076147010),
l * 4.0767416621 + m * -3.3077115913 + s * 0.2309699292, CM_TRANSFER_FUNCTION_GAMMA22),
l * (-1.2684380046) + m * 2.6097574011 + s * (-0.3413193965), lab[3]);
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) if (gradientLength < 2)
return gradient[0]; return gradient[0];
@ -41,7 +47,7 @@ vec4 getOkColorForCoordArray1(vec2 normalizedCoord) {
return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); 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) if (gradient2Length < 2)
return gradient2[0]; return gradient2[0];
@ -70,13 +76,128 @@ vec4 getOkColorForCoordArray2(vec2 normalizedCoord) {
return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress); return gradient2[top] * (progress - float(bottom)) + gradient2[bottom] * (float(top) - progress);
} }
vec4 getColorForCoord(vec2 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); vec4 result1 = getOkColorForCoordArray1(normalizedCoord, gradientLength, gradient, angle);
if (gradient2Length <= 0) if (gradient2Length <= 0)
return okLabAToSrgb(result1); return okLabAToSrgb(result1);
vec4 result2 = getOkColorForCoordArray2(normalizedCoord); vec4 result2 = getOkColorForCoordArray2(normalizedCoord, angle, gradient2Length, gradient2, angle2);
return okLabAToSrgb(mix(result1, result2, gradientLerp)); 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;
}

View file

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

View file

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

View file

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

View file

@ -1,3 +0,0 @@
uniform bool discardOpaque;
uniform bool discardAlpha;
uniform float discardAlphaValue;

View file

@ -1 +0,0 @@
pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimariesXYZ);

View file

@ -1,5 +0,0 @@
if (discardOpaque && pixColor.a * alpha == 1.0)
discard;
if (discardAlpha && pixColor.a <= discardAlphaValue)
discard;

View file

@ -1 +0,0 @@
pixColor = rounding(pixColor);

View file

@ -1,2 +0,0 @@
pixColor = saturate(pixColor, dstxyz, sdrSaturation);
pixColor.rgb *= sdrBrightnessMultiplier;

View file

@ -1 +0,0 @@
pixColor.rgb = pixColor.rgb * tint;

View file

@ -1 +0,0 @@
pixColor = tonemap(pixColor, dstxyz);

View file

@ -1,5 +1,6 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
#extension GL_OES_EGL_image_external_essl3 : require #extension GL_OES_EGL_image_external_essl3 : require
@ -8,6 +9,10 @@ in vec2 v_texcoord;
uniform samplerExternalOES tex; uniform samplerExternalOES tex;
uniform float alpha; uniform float alpha;
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl" #include "rounding.glsl"
uniform int discardOpaque; uniform int discardOpaque;
@ -32,7 +37,7 @@ void main() {
} }
if (radius > 0.0) if (radius > 0.0)
pixColor = rounding(pixColor); pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize);
fragColor = pixColor * alpha; fragColor = pixColor * alpha;
} }

View file

@ -1 +0,0 @@
#include "get_rgbx_pixel.glsl"

View file

@ -1 +0,0 @@
vec4 pixColor = texture(tex, v_texcoord);

View file

@ -1 +0,0 @@
vec4 pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);

View file

@ -1 +0,0 @@
#include "primaries_xyz_uniform.glsl"

View file

@ -1 +0,0 @@
const mat3 targetPrimariesXYZ = mat3(0.0);

View file

@ -1 +0,0 @@
uniform mat3 targetPrimariesXYZ;

View file

@ -1,17 +1,27 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
#include "defines.h"
precision highp float; precision highp float;
in vec4 v_color; in vec4 v_color;
#if USE_ROUNDING
uniform float radius;
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
#include "rounding.glsl" #include "rounding.glsl"
#endif
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
void main() { void main() {
vec4 pixColor = v_color; vec4 pixColor = v_color;
if (radius > 0.0) #if USE_ROUNDING
pixColor = rounding(pixColor); pixColor = rounding(pixColor, radius, roundingPower, topLeft, fullSize);
#endif
fragColor = pixColor; fragColor = pixColor;
} }

View file

@ -1,13 +1,10 @@
#ifndef ROUNDING_GLSL
#define ROUNDING_GLSL
// smoothing constant for the edge: more = blurrier, but smoother // 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) #define SMOOTHING_CONSTANT (M_PI / 5.34665792551)
uniform float radius; vec4 rounding(vec4 color, float radius, float roundingPower, vec2 topLeft, vec2 fullSize) {
uniform float roundingPower;
uniform vec2 topLeft;
uniform vec2 fullSize;
vec4 rounding(vec4 color) {
vec2 pixCoord = vec2(gl_FragCoord); vec2 pixCoord = vec2(gl_FragCoord);
pixCoord -= topLeft + fullSize * 0.5; pixCoord -= topLeft + fullSize * 0.5;
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; 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 pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix don't make it top-left
if (pixCoord.x + pixCoord.y > radius) { 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) if (dist > radius + SMOOTHING_CONSTANT)
discard; discard;
@ -27,3 +24,4 @@ vec4 rounding(vec4 color) {
return color; return color;
} }
#endif

View file

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

View file

@ -1,6 +1,9 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
#include "defines.h"
precision highp float; precision highp float;
in vec4 v_color; in vec4 v_color;
in vec2 v_texcoord; in vec2 v_texcoord;
@ -17,77 +20,38 @@ uniform float roundingPower;
uniform float range; uniform float range;
uniform float shadowPower; uniform float shadowPower;
float pixAlphaRoundedDistance(float distanceToCorner) { #if USE_CM
if (distanceToCorner > radius) { #include "cm_helpers.glsl"
return 0.0; #include "CM.glsl"
} #endif
if (distanceToCorner > radius - range) { #include "shadow.glsl"
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);
}
layout(location = 0) out vec4 fragColor; layout(location = 0) out vec4 fragColor;
void main() { void main() {
vec4 pixColor = v_color; vec4 pixColor = v_color;
float originalAlpha = pixColor[3];
bool done = false; fragColor = getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight
#if USE_CM
vec2 pixCoord = fullSize * v_texcoord; ,
sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
// ok, now we check the distance to a border. #if USE_ICC
,
if (pixCoord[0] < topLeft[0]) { iccLut3D, iccLutSize
if (pixCoord[1] < topLeft[1]) { #else
// top left #if USE_TONEMAP || USE_SDR_MOD
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - topLeft)); ,
done = true; targetPrimariesXYZ
} else if (pixCoord[1] > bottomRight[1]) { #endif
// bottom left #if USE_TONEMAP
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(topLeft[0], bottomRight[1]))); ,
done = true; maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
} #endif
} else if (pixCoord[0] > bottomRight[0]) { #if USE_SDR_MOD
if (pixCoord[1] < topLeft[1]) { ,
// top right sdrSaturation, sdrBrightnessMultiplier
pixColor[3] = pixColor[3] * pixAlphaRoundedDistance(modifiedLength(pixCoord - vec2(bottomRight[0], topLeft[1]))); #endif
done = true; #endif
} else if (pixCoord[1] > bottomRight[1]) { #endif
// 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;
} }

View file

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

View file

@ -1,25 +1,104 @@
#version 300 es #version 300 es
#define ALLOW_INCLUDES
#extension GL_ARB_shading_language_include : enable #extension GL_ARB_shading_language_include : enable
#include "defines.h"
precision highp float; precision highp float;
in vec2 v_texcoord; in vec2 v_texcoord;
uniform sampler2D tex; uniform sampler2D tex;
#if USE_BLUR
uniform vec2 uvSize;
uniform vec2 uvOffset;
uniform sampler2D blurredBG;
#endif
uniform float alpha; uniform float alpha;
#include "discard.glsl" #if USE_DISCARD
#include "tint.glsl" 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 "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; layout(location = 0) out vec4 fragColor;
void main() { 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" #if USE_DISCARD && !USE_BLUR
#include "do_CM.glsl" if (discardOpaque && pixColor.a * alpha == 1.0)
#include "do_tint.glsl" discard;
#include "do_rounding.glsl"
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; fragColor = pixColor * alpha;
} }

View file

@ -1,4 +0,0 @@
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
#include "primaries_xyz.glsl"
#include "CM.glsl"

View file

@ -1 +0,0 @@
uniform vec3 tint;

View file

@ -1,17 +1,15 @@
uniform float maxLuminance; #ifndef ALLOW_INCLUDES
uniform float dstMaxLuminance; #define ALLOW_INCLUDES
uniform float dstRefLuminance; #extension GL_ARB_shading_language_include : enable
#endif
#include "constants.h"
const mat3 BT2020toLMS = mat3( const mat3 BT2020toLMS = mat3(0.3592, 0.6976, -0.0358, -0.1922, 1.1004, 0.0755, 0.0070, 0.0749, 0.8434);
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 = inverse(BT2020toLMS);
const mat3 LMStoBT2020 = mat3( const mat3 LMStoBT2020 = mat3( //
2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, 2.0701800566956135096, -1.3264568761030210255, 0.20661600684785517081, //
0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, 0.36498825003265747974, 0.68046736285223514102, -0.045421753075853231409, //
-0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 -0.049595542238932107896, -0.049421161186757487412, 1.1879959417328034394 //
); );
// const mat3 ICtCpPQ = transpose(mat3( // const mat3 ICtCpPQ = transpose(mat3(
@ -19,16 +17,16 @@ const mat3 LMStoBT2020 = mat3(
// 6610.0, -13613.0, 7003.0, // 6610.0, -13613.0, 7003.0,
// 17933.0, -17390.0, -543.0 // 17933.0, -17390.0, -543.0
// ) / 4096.0); // ) / 4096.0);
const mat3 ICtCpPQ = mat3( const mat3 ICtCpPQ = mat3( //
0.5, 1.61376953125, 4.378173828125, 0.5, 1.61376953125, 4.378173828125, //
0.5, -3.323486328125, -4.24560546875, 0.5, -3.323486328125, -4.24560546875, //
0.0, 1.709716796875, -0.132568359375 0.0, 1.709716796875, -0.132568359375 //
); );
//const mat3 ICtCpPQInv = inverse(ICtCpPQ); //const mat3 ICtCpPQInv = inverse(ICtCpPQ);
const mat3 ICtCpPQInv = mat3( const mat3 ICtCpPQInv = mat3( //
1.0, 1.0, 1.0, 1.0, 1.0, 1.0, //
0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, 0.0086090370379327566, -0.0086090370379327566, 0.560031335710679118, //
0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 0.11102962500302595656, -0.11102962500302595656, -0.32062717498731885185 //
); );
// unused for now // unused for now
@ -39,7 +37,7 @@ const mat3 ICtCpPQInv = mat3(
// ) / 4096.0); // ) / 4096.0);
// const mat3 ICtCpHLGInv = inverse(ICtCpHLG); // 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) if (maxLuminance < dstMaxLuminance * 1.01)
return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]); return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]);
@ -50,10 +48,7 @@ vec4 tonemap(vec4 color, mat3 dstXYZ) {
vec3 ICtCp = ICtCpPQ * lms; vec3 ICtCp = ICtCpPQ * lms;
float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2); float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2);
float luminance = pow( float luminance = pow((max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1) * HDR_MAX_LUMINANCE;
(max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E),
PQ_INV_M1
) * HDR_MAX_LUMINANCE;
float linearPart = min(luminance, dstRefLuminance); float linearPart = min(luminance, dstRefLuminance);
float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0); float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0);