summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meta/hl.meta.lua2
-rw-r--r--src/config/values/ConfigValues.cpp3
-rw-r--r--src/helpers/Monitor.cpp88
-rw-r--r--src/helpers/Monitor.hpp13
-rw-r--r--src/helpers/MonitorResources.cpp20
-rw-r--r--src/helpers/cm/ColorManagement.cpp318
-rw-r--r--src/helpers/cm/ColorManagement.hpp52
-rw-r--r--src/managers/screenshare/ScreenshareFrame.cpp9
-rw-r--r--src/protocols/ColorManagement.cpp2
-rw-r--r--src/render/OpenGL.cpp50
-rw-r--r--src/render/OpenGL.hpp1
-rw-r--r--src/render/Renderer.cpp29
-rw-r--r--src/render/Shader.cpp1
-rw-r--r--src/render/Shader.hpp1
-rw-r--r--src/render/gl/GLElementRenderer.cpp25
-rw-r--r--src/render/gl/GLFramebuffer.cpp11
-rw-r--r--src/render/gl/GLFramebuffer.hpp6
-rw-r--r--src/render/pass/TexPassElement.hpp1
-rw-r--r--src/render/shaders/glsl/blur1.glsl8
-rw-r--r--src/render/shaders/glsl/blurFinish.glsl6
-rw-r--r--src/render/shaders/glsl/cm_helpers.glsl11
-rw-r--r--src/render/shaders/glsl/constants.h1
-rw-r--r--src/render/shaders/glsl/inner_glow.frag41
-rw-r--r--src/render/shaders/glsl/inner_glow.glsl55
-rw-r--r--src/render/shaders/glsl/quad.frag7
-rw-r--r--src/render/shaders/glsl/shadow.frag34
-rw-r--r--src/render/shaders/glsl/shadow.glsl58
-rw-r--r--src/render/shaders/glsl/tonemap.glsl25
28 files changed, 607 insertions, 271 deletions
diff --git a/meta/hl.meta.lua b/meta/hl.meta.lua
index a85f4a8f..1920c277 100644
--- a/meta/hl.meta.lua
+++ b/meta/hl.meta.lua
@@ -349,6 +349,7 @@
---| "render.ctm_animation"
---| "render.direct_scanout"
---| "render.expand_undersized_textures"
+---| "render.fp16_sdr_tf"
---| "render.icc_vcgt_enabled"
---| "render.keep_unmodified_copy"
---| "render.new_render_scheduling"
@@ -1219,6 +1220,7 @@ hl = {}
---@field ['render.ctm_animation'] integer|boolean
---@field ['render.direct_scanout'] integer|boolean
---@field ['render.expand_undersized_textures'] boolean
+---@field ['render.fp16_sdr_tf'] integer|boolean
---@field ['render.icc_vcgt_enabled'] boolean
---@field ['render.keep_unmodified_copy'] integer|boolean
---@field ['render.new_render_scheduling'] boolean
diff --git a/src/config/values/ConfigValues.cpp b/src/config/values/ConfigValues.cpp
index 49fdda02..73f2e9db 100644
--- a/src/config/values/ConfigValues.cpp
+++ b/src/config/values/ConfigValues.cpp
@@ -540,6 +540,7 @@ std::vector<SP<IValue>> Values::getConfigValues() {
{.min = 0, .max = 2, .map = OptionMap{{"disable", 0}, {"enable", 1}, {"auto", 2}}}),
MS<Int>("render:non_shader_cm_interop", "non_shader_cm interaction with ctm proto (hyprsunset and similar).", 2,
{.min = 0, .max = 2, .map = OptionMap{{"disable", 0}, {"enable", 1}, {"auto", 2}}}),
+ MS<Int>("render:fp16_sdr_tf", "Internal workbuffer transfer function for fp16 in SDR mode", 0, {.min = 0, .max = 1, .map = OptionMap{{"monitor", 0}, {"linear", 1}}}),
/*
* cursor:
@@ -604,7 +605,7 @@ std::vector<SP<IValue>> Values::getConfigValues() {
MS<Bool>("debug:fifo_pending_workaround", "Fifo workaround for empty pending list", false),
MS<Bool>("debug:render_solitary_wo_damage", "Render solitary window with empty damage", false),
MS<Bool>("debug:vfr", "controls the VFR status of Hyprland. Do not turn off unless debugging", true),
- MS<Int>("debug:invalidate_fp16", "allow fp16 buffer invalidation.", 2, {.min = 0, .max = 2, .map = OptionMap{{"disable", 0}, {"enable", 1}, {"auto", 2}}}),
+ MS<Int>("debug:invalidate_fp16", "allow fp16 buffer invalidation.", 1, {.min = 0, .max = 2, .map = OptionMap{{"disable", 0}, {"enable", 1}, {"auto", 2}}}),
/*
* layout:
diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp
index 958c8480..eebc2daa 100644
--- a/src/helpers/Monitor.cpp
+++ b/src/helpers/Monitor.cpp
@@ -591,7 +591,19 @@ void CMonitor::applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdr
.maxCLL = maxCLL,
.maxFALL = maxFALL});
break;
- case NCMType::CM_HDR: m_imageDescription = DEFAULT_HDR_IMAGE_DESCRIPTION; break;
+ case NCMType::CM_HDR:
+ m_imageDescription = CImageDescription::from({.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
+ .primariesNameSet = true,
+ .primariesNamed = NColorManagement::CM_PRIMARIES_BT2020,
+ .primaries = NColorManagement::getPrimaries(NColorManagement::CM_PRIMARIES_BT2020),
+ .masteringPrimaries = masteringPrimaries,
+ .luminances = {.min = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMinLuminance(),
+ .max = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFMaxLuminance(),
+ .reference = DEFAULT_HDR_IMAGE_DESCRIPTION->value().getTFRefLuminance()},
+ .masteringLuminances = masteringLuminances,
+ .maxCLL = maxCLL,
+ .maxFALL = maxFALL});
+ break;
case NCMType::CM_HDR_EDID:
m_imageDescription = CImageDescription::from(
{.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ,
@@ -609,16 +621,17 @@ void CMonitor::applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdr
break;
default: UNREACHABLE();
}
- if ((m_minLuminance >= 0 || m_maxLuminance >= 0 || m_maxAvgLuminance >= 0) && (cmType == NCMType::CM_HDR || cmType == NCMType::CM_HDR_EDID))
+ if ((minLuminance() >= 0 || maxLuminance() > 0) && (cmType == NCMType::CM_HDR || cmType == NCMType::CM_HDR_EDID))
m_imageDescription = m_imageDescription->with({
- .min = m_minLuminance >= 0 ? m_minLuminance : m_imageDescription->value().luminances.min, //
- .max = m_maxLuminance >= 0 ? m_maxLuminance : m_imageDescription->value().luminances.max, //
+ .min = minLuminance() >= 0 ? minLuminance() : m_imageDescription->value().luminances.min, //
+ .max = maxLuminance() > 0 ? maxLuminance() : m_imageDescription->value().luminances.max, //
.reference = m_imageDescription->value().luminances.reference //
});
if (oldImageDescription != m_imageDescription) {
if (PROTO::colorManagement)
PROTO::colorManagement->onMonitorImageDescriptionChanged(m_self);
+ m_blurFBDirty = true;
}
}
@@ -2066,6 +2079,20 @@ bool CMonitor::attemptDirectScanout() {
return true;
}
+void CMonitor::handleDSleave() {
+ Log::logger->log(Log::DEBUG, "Left a direct scanout.");
+ m_lastScanout.reset();
+ m_previousFSWindow.reset(); // recalc fs settings
+ m_directScanoutIsActive = false;
+
+ // reset DRM format, but only if needed since it might modeset
+ if (m_output->state->state().drmFormat != m_prevDrmFormat)
+ m_output->state->setFormat(m_prevDrmFormat);
+
+ m_drmFormat = m_prevDrmFormat;
+ m_blurFBDirty = true;
+}
+
bool CMonitor::canAttemptDirectScanoutFast() const {
return !m_solitaryClient.expired() || !m_lastScanout.expired() || m_directScanoutIsActive;
}
@@ -2559,9 +2586,21 @@ bool CMonitor::needsUnmodifiedCopy() {
}
bool CMonitor::useFP16() {
- static const auto PFP16 = CConfigValue<Hyprlang::INT>("render:use_fp16");
- bool shouldUse = *PFP16 == 1 || (*PFP16 == 2 && m_imageDescription->value().transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ);
- static bool usedBefore = shouldUse;
+ static const auto PFP16 = CConfigValue<Hyprlang::INT>("render:use_fp16");
+
+ auto isSRGB = [this] {
+ if (m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_SRGB && m_imageDescription->value().transferFunction != CM_TRANSFER_FUNCTION_GAMMA22)
+ return false;
+
+ if (m_imageDescription->value().primariesNamed != CM_PRIMARIES_SRGB)
+ return false;
+
+ return true;
+ };
+
+ // Auto: use FP16 if the monitor is not sRGB
+ bool shouldUse = *PFP16 == 1 || (*PFP16 == 2 && !isSRGB());
+ static bool usedBefore = shouldUse;
if (usedBefore != shouldUse) {
usedBefore = shouldUse;
m_blurFBDirty = true;
@@ -2569,9 +2608,42 @@ bool CMonitor::useFP16() {
return shouldUse;
}
+PImageDescription CMonitor::workBufferImageDescription() {
+ static const auto PFP16TF = CConfigValue<Hyprlang::INT>("render:fp16_sdr_tf");
+
+ if (!useFP16() && !m_imageDescription->value().icc.present)
+ return m_imageDescription;
+
+ const auto& value = m_imageDescription->value();
+
+ const bool isHDRLikeTF =
+ value.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ || value.transferFunction == CM_TRANSFER_FUNCTION_HLG || value.transferFunction == CM_TRANSFER_FUNCTION_EXT_LINEAR;
+
+ const auto& cached = m_cachedInternalDescription->value();
+
+ // HDR
+ if (isHDRLikeTF || value.windowsScRGB || *PFP16TF != 0) {
+ if (cached.transferFunction != LINEAR_IMAGE_DESCRIPTION->value().transferFunction || cached.luminances != value.luminances)
+ m_cachedInternalDescription = LINEAR_IMAGE_DESCRIPTION->with(value.luminances);
+ return m_cachedInternalDescription;
+ }
+
+ // SDR
+ if (cached.transferFunction != chooseTF(m_sdrEotf))
+ m_cachedInternalDescription = CImageDescription::from(SImageDescription{
+ .transferFunction = chooseTF(m_sdrEotf),
+ .primariesNameSet = true,
+ // render:keep_unmodified_copy and other conditions that trigger MRT for screen sharing expect a work buffer with sRGB primaries
+ .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
+ .primaries = NColorPrimaries::BT709,
+ });
+
+ return m_cachedInternalDescription;
+}
+
WP<CMonitorResources> CMonitor::resources() {
const auto DRM_FORMAT = useFP16() ? DRM_FORMAT_ABGR16161616F : m_output->state->state().drmFormat;
- const auto DESC = useFP16() ? LINEAR_IMAGE_DESCRIPTION : m_imageDescription;
+ const auto DESC = workBufferImageDescription();
if (!m_resources || m_resources->m_drmFormat != DRM_FORMAT || m_resources->m_size != m_pixelSize)
m_resources = makeUnique<CMonitorResources>(m_self, DRM_FORMAT, m_pixelSize, DESC);
diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp
index 59a64237..7ec47c3f 100644
--- a/src/helpers/Monitor.hpp
+++ b/src/helpers/Monitor.hpp
@@ -326,6 +326,7 @@ class CMonitor {
bool updateTearing();
uint16_t isDSBlocked(bool full = false);
bool attemptDirectScanout();
+ void handleDSleave();
bool canAttemptDirectScanoutFast() const;
bool isMultiGPU();
void setCTM(const Mat3x3& ctm);
@@ -393,10 +394,11 @@ class CMonitor {
return m_position == rhs.m_position && m_size == rhs.m_size && m_name == rhs.m_name;
}
- bool needsACopyFB();
- bool needsUnmodifiedCopy();
- bool useFP16();
- WP<Monitor::CMonitorResources> resources();
+ bool needsACopyFB();
+ bool needsUnmodifiedCopy();
+ bool useFP16();
+ NColorManagement::PImageDescription workBufferImageDescription();
+ WP<Monitor::CMonitorResources> resources();
private:
void updateMatrix();
@@ -414,6 +416,9 @@ class CMonitor {
// Resources
UP<Monitor::CMonitorResources> m_resources;
+ // cached should contain one of predefined descriptions for FP16: sRGB primaries with either linear TF by default and in HDR mode or monitor's TF in SDR with render:fp16_sdr_tf = 0
+ // avoids lookup for an id when ::from is used
+ NColorManagement::PImageDescription m_cachedInternalDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{});
struct {
CHyprSignalListener frame;
diff --git a/src/helpers/MonitorResources.cpp b/src/helpers/MonitorResources.cpp
index 8e7723c2..ef08fc37 100644
--- a/src/helpers/MonitorResources.cpp
+++ b/src/helpers/MonitorResources.cpp
@@ -32,7 +32,7 @@ void CMonitorResources::setImageDescription(NColorManagement::PImageDescription
for (const auto& res : m_workBuffers)
res.buffer->setImageDescription(imageDescription);
if (m_monitorMirrorFB)
- m_monitorMirrorFB->setImageDescription(NColorManagement::getDefaultImageDescription());
+ m_monitorMirrorFB->setImageDescription(getMirrorTexImageDescription());
if (m_mirrorTex)
m_mirrorTex->m_imageDescription = getMirrorTexImageDescription();
}
@@ -78,8 +78,8 @@ SP<Render::IFramebuffer> CMonitorResources::mirrorFB() {
m_monitorMirrorFB = g_pHyprRenderer->createFB(std::format("Monitor {} mirror FB", m_monitor->m_name));
if (!m_monitorMirrorFB->isAllocated()) {
- m_monitorMirrorFB->alloc(m_size.x, m_size.y, DRM_FORMAT_XRGB8888);
- m_monitorMirrorFB->setImageDescription(NColorManagement::getDefaultImageDescription());
+ m_monitorMirrorFB->alloc(m_size.x, m_size.y, m_monitor->m_activeMonitorRule.m_enable10bit ? DRM_FORMAT_XRGB2101010 : DRM_FORMAT_XRGB8888);
+ m_monitorMirrorFB->setImageDescription(getMirrorTexImageDescription());
}
return m_monitorMirrorFB;
@@ -90,20 +90,18 @@ SP<Render::ITexture> CMonitorResources::getMirrorTexture() {
}
NColorManagement::PImageDescription CMonitorResources::getMirrorTexImageDescription() {
- return CImageDescription::from(SImageDescription{
- .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_SRGB,
- .primariesNameSet = m_imageDescription->value().primariesNameSet,
- .primariesNamed = m_imageDescription->value().primariesNamed,
- .primaries = m_imageDescription->value().primaries,
- .luminances = {.min = SDR_MIN_LUMINANCE, .max = 80, .reference = 80},
- });
+ const auto TF = m_imageDescription->value().transferFunction;
+ if (TF == CM_TRANSFER_FUNCTION_GAMMA22 || TF == CM_TRANSFER_FUNCTION_SRGB)
+ return m_imageDescription;
+
+ return DEFAULT_SRGB_IMAGE_DESCRIPTION;
}
void CMonitorResources::enableMirror() {
if (m_mirrorTex)
return;
m_mirrorTex = g_pHyprRenderer->createTexture();
- m_mirrorTex->allocate({m_size.x, m_size.y}, DRM_FORMAT_XRGB8888);
+ m_mirrorTex->allocate({m_size.x, m_size.y}, m_monitor->m_activeMonitorRule.m_enable10bit ? DRM_FORMAT_XRGB2101010 : DRM_FORMAT_XRGB8888);
m_mirrorTex->m_imageDescription = getMirrorTexImageDescription();
m_monitor->m_blurFBDirty = true;
}
diff --git a/src/helpers/cm/ColorManagement.cpp b/src/helpers/cm/ColorManagement.cpp
index 5e4725d4..8428f29e 100644
--- a/src/helpers/cm/ColorManagement.cpp
+++ b/src/helpers/cm/ColorManagement.cpp
@@ -1,9 +1,13 @@
#include "ColorManagement.hpp"
#include "../../macros.hpp"
-#include "helpers/TransferFunction.hpp"
+#include "../Color.hpp"
+#include "../TransferFunction.hpp"
+#include "../../render/Renderer.hpp"
+#include "render/types.hpp"
#include <hyprutils/memory/UniquePtr.hpp>
#include <map>
#include <vector>
+#include <numeric>
using namespace NColorManagement;
using namespace NTransferFunction;
@@ -208,6 +212,318 @@ Mat3x3 NColorManagement::adaptBradford(Hyprgraphics::CColor::xy srcW, Hyprgraphi
return result;
}
+// 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
+
+static inline int sign(double value) {
+ return value < 0 ? -1 : 1;
+}
+
+// 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
+static RGBAColor tfInvPQ(RGBAColor color) {
+ for (uint i = 0; i <= 2; i++) {
+ const double E = pow(std::clamp(color.v[i], 0.0, 1.0), PQ_INV_M2);
+ color.v[i] = pow((std::max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1);
+ }
+ return color;
+}
+
+static RGBAColor tfInvHLG(RGBAColor color) {
+ for (uint i = 0; i <= 2; i++) {
+ const bool isLow = color.v[i] <= HLG_E_CUT;
+ color.v[i] = isLow ? color.v[i] * color.v[i] / 3.0 : (exp((color.v[i] - HLG_C) / HLG_A) + HLG_B) / 12.0;
+ }
+ return color;
+}
+
+// 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.
+static RGBAColor tfInvLinPow(RGBAColor color, float gamma, float thres, float scale, float alpha) {
+ for (uint i = 0; i <= 2; i++) {
+ const bool isLow = color.v[i] <= thres * scale;
+ color.v[i] = isLow ? color.v[i] / scale : pow((color.v[i] + alpha - 1.0) / alpha, gamma);
+ }
+ return color;
+}
+
+static RGBAColor tfInvSRGB(RGBAColor color) {
+ return tfInvLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA);
+}
+
+static RGBAColor tfInvExtSRGB(RGBAColor color) {
+ // EXT sRGB is the sRGB transfer function mirrored around 0.
+ const auto absColor = tfInvSRGB({{.r = abs(color.c.r), .g = abs(color.c.g), .b = abs(color.c.b), .a = color.c.a}});
+ return {{
+ .r = absColor.c.r * sign(color.c.r),
+ .g = absColor.c.g * sign(color.c.g),
+ .b = absColor.c.b * sign(color.c.b),
+ .a = absColor.c.a,
+ }};
+}
+
+static RGBAColor tfInvBT1886(RGBAColor color) {
+ return tfInvLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA);
+}
+
+static RGBAColor tfInvXVYCC(RGBAColor color) {
+ // The inverse transfer function for XVYCC is the BT1886 transfer function mirrored around 0,
+ // same as what EXT sRGB is to sRGB.
+ const auto absColor = tfInvBT1886({{.r = abs(color.c.r), .g = abs(color.c.g), .b = abs(color.c.b), .a = color.c.a}});
+ return {{
+ .r = absColor.c.r * sign(color.c.r),
+ .g = absColor.c.g * sign(color.c.g),
+ .b = absColor.c.b * sign(color.c.b),
+ .a = absColor.c.a,
+ }};
+}
+
+static RGBAColor tfInvST240(RGBAColor color) {
+ return tfInvLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA);
+}
+
+// Forward transfer functions corresponding to the inverse functions above.
+static RGBAColor tfPQ(RGBAColor color) {
+ for (uint i = 0; i <= 2; i++) {
+ const double E = pow(std::clamp(color.v[i], 0.0, 1.0), PQ_M1);
+ color.v[i] = pow((PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E), PQ_M2);
+ }
+ return color;
+}
+
+static RGBAColor tfHLG(RGBAColor color) {
+ for (uint i = 0; i <= 2; i++) {
+ const bool isLow = color.v[i] <= HLG_D_CUT;
+ color.v[i] = isLow ? sqrt(std::max(color.v[i], 0.0) * 3.0) : HLG_A * log(std::max(12.0 * color.v[i] - HLG_B, 0.0001)) + HLG_C;
+ }
+ return color;
+}
+
+static RGBAColor tfLinPow(RGBAColor color, float gamma, float thres, float scale, float alpha) {
+ for (uint i = 0; i <= 2; i++) {
+ const bool isLow = color.v[i] <= thres;
+ color.v[i] = isLow ? color.v[i] * scale : pow(color.v[i], 1.0 / gamma) * alpha - (alpha - 1.0);
+ }
+ return color;
+}
+
+static RGBAColor tfSRGB(RGBAColor color) {
+ return tfLinPow(color, SRGB_POW, SRGB_CUT, SRGB_SCALE, SRGB_ALPHA);
+}
+
+static RGBAColor tfExtSRGB(RGBAColor color) {
+ // EXT sRGB is the sRGB transfer function mirrored around 0.
+ const auto absColor = tfSRGB({{.r = abs(color.c.r), .g = abs(color.c.g), .b = abs(color.c.b), .a = color.c.a}});
+ return {{
+ .r = absColor.c.r * sign(color.c.r),
+ .g = absColor.c.g * sign(color.c.g),
+ .b = absColor.c.b * sign(color.c.b),
+ .a = absColor.c.a,
+ }};
+}
+
+static RGBAColor tfBT1886(RGBAColor color) {
+ return tfLinPow(color, BT1886_POW, BT1886_CUT, BT1886_SCALE, BT1886_ALPHA);
+}
+
+static RGBAColor tfXVYCC(RGBAColor color) {
+ // The transfer function for XVYCC is the BT1886 transfer function mirrored around 0,
+ // same as what EXT sRGB is to sRGB.
+ const auto absColor = tfBT1886({{.r = abs(color.c.r), .g = abs(color.c.g), .b = abs(color.c.b), .a = color.c.a}});
+ return {{
+ .r = absColor.c.r * sign(color.c.r),
+ .g = absColor.c.g * sign(color.c.g),
+ .b = absColor.c.b * sign(color.c.b),
+ .a = absColor.c.a,
+ }};
+}
+
+static RGBAColor tfST240(RGBAColor color) {
+ return tfLinPow(color, ST240_POW, ST240_CUT, ST240_SCALE, ST240_ALPHA);
+}
+
+static RGBAColor tfST428(RGBAColor color) {
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = pow(std::max(color.v[i], 0.0), ST428_POW) * ST428_SCALE;
+ }
+ return color;
+}
+
+static RGBAColor tfInvST428(RGBAColor color) {
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = pow(std::max(color.v[i], 0.0) / ST428_SCALE, 1.0 / ST428_POW);
+ }
+ return color;
+}
+
+static RGBAColor tfGamma(RGBAColor color, float gamma) {
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = pow(std::max(color.v[i], 0.0), gamma);
+ }
+ return color;
+}
+
+static RGBAColor tfLog(RGBAColor color, float mult) {
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = color.v[i] <= 0 ? 0.0 : exp((color.v[i] - 1.0) * mult * std::numbers::ln10);
+ }
+ return color;
+}
+
+static RGBAColor tfInvLog(RGBAColor color, float mult, float min) {
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = color.v[i] <= min ? 0.0 : 1.0 + log(color.v[i]) / std::numbers::ln10 / mult;
+ }
+ return color;
+}
+
+static RGBAColor toLinearRGB(RGBAColor color, eTransferFunction tf) {
+ switch (tf) {
+ case CM_TRANSFER_FUNCTION_LINEAR: return color;
+ case CM_TRANSFER_FUNCTION_EXT_LINEAR: return color;
+ case CM_TRANSFER_FUNCTION_ST2084_PQ: return tfInvPQ(color);
+ case CM_TRANSFER_FUNCTION_GAMMA22: return tfGamma(color, 2.2);
+ case CM_TRANSFER_FUNCTION_GAMMA28: return tfGamma(color, 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 tfLog(color, 2.0);
+ case CM_TRANSFER_FUNCTION_LOG_316: return tfLog(color, 2.5);
+ case CM_TRANSFER_FUNCTION_XVYCC: return tfInvXVYCC(color);
+ case CM_TRANSFER_FUNCTION_ST428: return tfST428(color);
+ case CM_TRANSFER_FUNCTION_SRGB:
+ default: return tfInvSRGB(color);
+ }
+}
+
+static RGBAColor fromLinearRGB(RGBAColor color, eTransferFunction 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 tfGamma(color, 1.0 / 2.2);
+ case CM_TRANSFER_FUNCTION_GAMMA28: return tfGamma(color, 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 tfInvLog(color, 2.0, 0.01);
+ case CM_TRANSFER_FUNCTION_LOG_316: return tfInvLog(color, 2.5, sqrt(10.0) / 1000.0);
+ case CM_TRANSFER_FUNCTION_XVYCC: return tfXVYCC(color);
+ case CM_TRANSFER_FUNCTION_ST428: return tfInvST428(color);
+ case CM_TRANSFER_FUNCTION_SRGB:
+ default: return tfSRGB(color);
+ }
+}
+
+static RGBAColor toNit(RGBAColor color, Render::STFRange range) {
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = color.v[i] * (range.max - range.min) + range.min;
+ }
+ return color;
+}
+
+static RGBAColor fromLinearNit(RGBAColor color, eTransferFunction tf, Render::STFRange range) {
+ if (tf == CM_TRANSFER_FUNCTION_LINEAR)
+ return color;
+
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = (color.v[i] - range.min * color.c.a) / (range.max - range.min);
+ }
+ color /= std::max(color.c.a, 0.001);
+ color = fromLinearRGB(color, tf);
+ color *= color.c.a;
+ return color;
+}
+
+static RGBAColor saturate(RGBAColor color, std::array<std::array<double, 3>, 3> primaries, float saturation) {
+ if (saturation == 1.0)
+ return color;
+
+ std::array<double, 3> colorArr = {color.v[0], color.v[1], color.v[2]};
+ std::array<double, 3> brightness = {primaries[1][0], primaries[1][1], primaries[1][2]};
+ const auto Y = std::inner_product(colorArr.begin(), colorArr.end(), brightness.begin(), 0.0);
+ for (uint i = 0; i <= 2; i++) {
+ color.v[i] = (color.v[i] * saturation) + (Y * (1.0 - saturation));
+ }
+ return color;
+}
+
+static RGBAColor tonemap(RGBAColor color, std::array<std::array<double, 3>, 3> dstXYZ, float maxLuminance, float dstMaxLuminance, float dstRefLuminance, float srcRefLuminance) {
+ // TODO source color is expected to be in sRGB colorspace and tonamepping shouldn't be needed
+ return color;
+}
+
+RGBAColor NColorManagement::convertColor(RGBAColor color, PImageDescription srcDesc, PImageDescription dstDesc) {
+ const auto settings =
+ g_pHyprRenderer->getCMSettings(srcDesc, dstDesc, nullptr, true, g_pHyprRenderer->m_renderData.pMonitor ? g_pHyprRenderer->m_renderData.pMonitor->m_sdrMinLuminance : -1,
+ g_pHyprRenderer->m_renderData.pMonitor ? g_pHyprRenderer->m_renderData.pMonitor->m_sdrMaxLuminance : -1);
+
+ color /= std::max(color.c.a, 0.001);
+ color = toLinearRGB(color, srcDesc->value().transferFunction);
+ if (dstDesc->value().icc.present) {
+ // color.rgb = applyIcc3DLut(color.rgb, iccLut3D, iccLutSize);
+ color *= color.c.a;
+ } else {
+ const auto convertMatrix = srcDesc->getPrimaries()->convertMatrix(dstDesc->getPrimaries());
+ const auto converted = convertMatrix * Hyprgraphics::CColor::XYZ{.x = color.c.r, .y = color.c.g, .z = color.c.b};
+ color.c.r = converted.x;
+ color.c.g = converted.y;
+ color.c.b = converted.z;
+ if (srcDesc->value().transferFunction != CM_TRANSFER_FUNCTION_LINEAR)
+ color = toNit(color, settings.srcTFRange);
+ color *= color.c.a;
+ if (settings.needsTonemap)
+ color = tonemap(color, settings.dstPrimaries2XYZ, settings.maxLuminance, settings.dstMaxLuminance, settings.dstRefLuminance, settings.srcRefLuminance);
+
+ color = fromLinearNit(color, dstDesc->value().transferFunction, settings.dstTFRange);
+ if (settings.needsSDRmod) {
+ color = saturate(color, settings.dstPrimaries2XYZ, settings.sdrSaturation);
+ color *= settings.sdrBrightnessMultiplier;
+ }
+ }
+ return color;
+}
+
+CHyprColor NColorManagement::convertColor(const CHyprColor& color, PImageDescription srcDesc, PImageDescription dstDesc) {
+ const auto& converted = convertColor(RGBAColor{{.r = color.r, .g = color.g, .b = color.b, .a = color.a}}, srcDesc, dstDesc);
+ return CHyprColor(converted.c.r, converted.c.g, converted.c.b, converted.c.a);
+}
+
PImageDescription NColorManagement::getDefaultImageDescription() {
const auto TF = fromConfig();
switch (TF) {
diff --git a/src/helpers/cm/ColorManagement.hpp b/src/helpers/cm/ColorManagement.hpp
index 183a8544..c8d1cfcb 100644
--- a/src/helpers/cm/ColorManagement.hpp
+++ b/src/helpers/cm/ColorManagement.hpp
@@ -3,8 +3,10 @@
#include "color-management-v1.hpp"
#include <format>
#include <hyprgraphics/color/Color.hpp>
-#include "../../helpers/memory/Memory.hpp"
-#include "../../helpers/math/Math.hpp"
+#include "../memory/Memory.hpp"
+#include "../math/Math.hpp"
+#include "../Color.hpp"
+#include "../../debug/log/Logger.hpp"
#include <filesystem>
#include <string>
@@ -45,6 +47,7 @@ namespace NColorManagement {
};
enum eTransferFunction : uint8_t {
+ CM_TRANSFER_FUNCTION_LINEAR = 0,
CM_TRANSFER_FUNCTION_BT1886 = 1,
CM_TRANSFER_FUNCTION_GAMMA22 = 2,
CM_TRANSFER_FUNCTION_GAMMA28 = 3,
@@ -68,9 +71,14 @@ namespace NColorManagement {
inline ePrimaries convertPrimaries(wpColorManagerV1Primaries primaries) {
return sc<ePrimaries>(primaries);
}
- inline wpColorManagerV1TransferFunction convertTransferFunction(eTransferFunction tf) {
+ inline wpColorManagerV1TransferFunction convertTransferFunction(eTransferFunction tf, bool useV1SRGB = true) {
switch (tf) {
- case CM_TRANSFER_FUNCTION_SRGB: return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_COMPOUND_POWER_2_4;
+ case CM_TRANSFER_FUNCTION_LINEAR:
+ Log::logger->log(Log::TRACE,
+ "CM_TRANSFER_FUNCTION_LINEAR is internal and buffers with this TF shouldn't go outside. Returning "
+ "WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR for preferred description instead");
+ return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR;
+ case CM_TRANSFER_FUNCTION_SRGB: return useV1SRGB ? WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB : WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_COMPOUND_POWER_2_4;
default: return sc<wpColorManagerV1TransferFunction>(tf);
}
}
@@ -82,6 +90,7 @@ namespace NColorManagement {
}
inline std::string tfToString(eTransferFunction tf) {
switch (tf) {
+ case CM_TRANSFER_FUNCTION_LINEAR: return "TF:INTERNAL LINEAR NOT NORMALISED";
case CM_TRANSFER_FUNCTION_BT1886: return "TF:BT1886";
case CM_TRANSFER_FUNCTION_GAMMA22: return "TF:GAMMA22";
case CM_TRANSFER_FUNCTION_GAMMA28: return "TF:GAMMA28";
@@ -349,8 +358,32 @@ namespace NColorManagement {
SImageDescription m_imageDescription;
};
+ union RGBAColor {
+ struct {
+ double r = 0, g = 0, b = 0, a = 0;
+ } c;
+ double v[4];
+
+ RGBAColor& operator*=(double value) {
+ c.r *= value;
+ c.g *= value;
+ c.b *= value;
+ return *this;
+ }
+
+ RGBAColor& operator/=(double value) {
+ c.r /= value;
+ c.g /= value;
+ c.b /= value;
+ return *this;
+ }
+ };
+
using PImageDescription = WP<const CImageDescription>;
+ RGBAColor convertColor(RGBAColor color, PImageDescription srcDesc, PImageDescription dstDesc);
+ CHyprColor convertColor(const CHyprColor& color, PImageDescription srcDesc, PImageDescription dstDesc);
+
PImageDescription getDefaultImageDescription();
static const auto DEFAULT_GAMMA22_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
@@ -393,6 +426,17 @@ namespace NColorManagement {
.primaries = NColorPrimaries::BT709,
.luminances = {.min = 0, .max = 10000, .reference = 80},
});
+
+ // For internal use only
+ // not normalised to 0.0 - 1.0
+ // luminance values should be set to default SDR settings in SDR mode and to output settings in HDR mode
+ // keep srgb primaries to avoid conversions for image exports
+ static const auto LINEAR_NN_IMAGE_DESCRIPTION = CImageDescription::from(SImageDescription{
+ .transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_LINEAR,
+ .primariesNameSet = true,
+ .primariesNamed = NColorManagement::CM_PRIMARIES_SRGB,
+ .primaries = NColorPrimaries::BT709,
+ });
}
template <typename CharT>
diff --git a/src/managers/screenshare/ScreenshareFrame.cpp b/src/managers/screenshare/ScreenshareFrame.cpp
index 7a596d0c..4cc51335 100644
--- a/src/managers/screenshare/ScreenshareFrame.cpp
+++ b/src/managers/screenshare/ScreenshareFrame.cpp
@@ -196,11 +196,10 @@ void CScreenshareFrame::renderMonitor() {
g_pHyprRenderer->startRenderPass();
g_pHyprRenderer->draw(
CTexPassElement::SRenderData{
- .tex = TEXTURE,
- .box = monbox,
- .flipEndFrame = true,
- .cmBackToSRGB = !IS_CM_AWARE,
- .cmBackToSRGBSource = !IS_CM_AWARE ? PMONITOR : nullptr,
+ .tex = TEXTURE,
+ .box = monbox,
+ .flipEndFrame = true,
+ .cmBackToSRGB = !IS_CM_AWARE,
},
{0, 0, PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y});
g_pHyprRenderer->m_renderData.renderModif.enabled = OLD;
diff --git a/src/protocols/ColorManagement.cpp b/src/protocols/ColorManagement.cpp
index fce91691..a704d4d7 100644
--- a/src/protocols/ColorManagement.cpp
+++ b/src/protocols/ColorManagement.cpp
@@ -807,7 +807,7 @@ CColorManagementImageDescriptionInfo::CColorManagementImageDescriptionInfo(SP<CW
if (m_settings.primariesNameSet)
m_resource->sendPrimariesNamed(m_settings.primariesNamed);
- m_resource->sendTfNamed(m_settings.transferFunction);
+ m_resource->sendTfNamed(convertTransferFunction(m_settings.transferFunction, m_resource->version() == 1));
if (m_settings.transferFunctionPower != 1.0f)
m_resource->sendTfPower(std::round(m_settings.transferFunctionPower * 10000));
diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp
index 6bb04cfa..0a53442e 100644
--- a/src/render/OpenGL.cpp
+++ b/src/render/OpenGL.cpp
@@ -34,6 +34,7 @@
#include "../managers/screenshare/ScreenshareManager.hpp"
#include "../notification/NotificationOverlay.hpp"
#include "errorOverlay/Overlay.hpp"
+#include "helpers/Color.hpp"
#include "macros.hpp"
#include "pass/TexPassElement.hpp"
#include "pass/RectPassElement.hpp"
@@ -758,6 +759,7 @@ void CHyprOpenGLImpl::begin(PHLMONITOR pMonitor, const CRegion& damage_, SP<IFra
g_pHyprRenderer->bindFB(g_pHyprRenderer->m_renderData.pMonitor->resources()->getUnusedWorkBuffer());
m_offloadedFramebuffer = true;
+ GLFB(g_pHyprRenderer->m_renderData.currentFB)->clearAfterInvalidation();
g_pHyprRenderer->m_renderData.mainFB = g_pHyprRenderer->m_renderData.currentFB;
g_pHyprRenderer->m_renderData.outFB = fb ? fb : dc<CHyprGLRenderer*>(g_pHyprRenderer.get())->m_currentRenderbuffer->getFB();
@@ -1062,6 +1064,19 @@ void CHyprOpenGLImpl::renderRectWithBlurInternal(const CBox& box, const CHyprCol
renderRectWithDamageInternal(box, col, data);
}
+using ColorConversionKey = std::tuple<float, float, float, float, uint64_t>;
+static std::map<ColorConversionKey, CHyprColor> colorConversionCache;
+
+static CHyprColor getConvertedColor(const CHyprColor& color) {
+ const auto targetId = g_pHyprRenderer->workBufferImageDescription()->id();
+ const ColorConversionKey key = {color.r, color.g, color.b, color.a, targetId};
+ if (colorConversionCache.contains(key))
+ return colorConversionCache[key];
+ const auto converted = convertColor(color, DEFAULT_SRGB_IMAGE_DESCRIPTION, g_pHyprRenderer->workBufferImageDescription());
+ colorConversionCache[key] = converted;
+ return converted;
+}
+
void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprColor& col, const SRectRenderData& data) {
auto& m_renderData = g_pHyprRenderer->m_renderData;
RASSERT((box.width > 0 && box.height > 0), "Tried to render rect with width/height < 0!");
@@ -1078,7 +1093,10 @@ void CHyprOpenGLImpl::renderRectWithDamageInternal(const CBox& box, const CHyprC
shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
// premultiply the color as well as we don't work with straight alpha
- shader->setUniformFloat4(SHADER_COLOR, col.r * col.a, col.g * col.a, col.b * col.a, col.a);
+ const auto premultiplied = CHyprColor(col.r * col.a, col.g * col.a, col.b * col.a, col.a);
+ const auto converted = getConvertedColor(premultiplied);
+ shader->setUniformFloat4(SHADER_COLOR, converted.r, converted.g, converted.b, converted.a);
+ shader->setUniformFloat4(SHADER_COLOR_SRGB, premultiplied.r, premultiplied.g, premultiplied.b, premultiplied.a);
CBox transformedBox = box;
transformedBox.transform(Math::wlTransformToHyprutils(Math::invertTransform(m_renderData.pMonitor->m_transform)), m_renderData.pMonitor->m_transformedSize.x,
@@ -1298,7 +1316,7 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(SP<ITexture> tex, const STexture
shaderFeatures &= ~SH_FEAT_RGBA;
const auto surface = g_pHyprRenderer->m_renderData.surface;
- const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->workBufferImageDescription();
+ const auto WORK_BUFFER_IMAGE_DESCRIPTION = g_pHyprRenderer->m_renderData.pMonitor->workBufferImageDescription();
// chosenSdrEotf contains the valid eotf for this display
@@ -1311,7 +1329,7 @@ WP<CShader> CHyprOpenGLImpl::renderToFBInternal(SP<ITexture> tex, const STexture
return CImageDescription::from(surface->m_colorManagement->imageDescription());
if (data.cmBackToSRGB)
- return g_pHyprRenderer->m_renderData.pMonitor->m_imageDescription;
+ return tex->m_imageDescription ? tex->m_imageDescription : WORK_BUFFER_IMAGE_DESCRIPTION;
// 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
@@ -1660,6 +1678,7 @@ SP<IFramebuffer> CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or
static auto PBLEND = CConfigValue<Config::INTEGER>("render:use_shader_blur_blend");
PMIRRORSWAPFB->bind();
+ GLFB(PMIRRORSWAPFB)->clearAfterInvalidation();
glActiveTexture(GL_TEXTURE0);
@@ -1754,6 +1773,7 @@ SP<IFramebuffer> CHyprOpenGLImpl::blurFramebufferWithDamage(float a, CRegion* or
// draw the things.
// first draw is swap -> mirr
PMIRRORFB->bind();
+ GLFB(PMIRRORFB)->clearAfterInvalidation();
PMIRRORSWAPFB->getTexture()->bind();
// damage region will be scaled, make a temp
@@ -2240,14 +2260,12 @@ void CHyprOpenGLImpl::renderRoundedShadow(const CBox& box, int round, float roun
blend(true);
- const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present;
- const bool skipCM = !m_cmSupported || !g_pHyprRenderer->workBufferImageDescription()->needsCM(getDefaultImageDescription());
- auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD) | globalFeatures()));
- if (!skipCM)
- passCMUniforms(shader, getDefaultImageDescription());
+ auto shader = useShader(getShaderVariant(SH_FRAG_SHADOW, globalFeatures()));
shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
- shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a);
+ const auto converted = getConvertedColor(col);
+ shader->setUniformFloat4(SHADER_COLOR, converted.r, converted.g, converted.b, converted.a * a);
+ shader->setUniformFloat4(SHADER_COLOR_SRGB, col.r, col.g, col.b, col.a * a);
const auto TOPLEFT = Vector2D(range + round, range + round);
const auto BOTTOMRIGHT = Vector2D(newBox.width - (range + round), newBox.height - (range + round));
@@ -2334,14 +2352,12 @@ void CHyprOpenGLImpl::renderInnerGlow(const CBox& box, int round, float rounding
blend(true);
- const bool IS_ICC = g_pHyprRenderer->workBufferImageDescription()->value().icc.present;
- const bool skipCM = !m_cmSupported || !g_pHyprRenderer->workBufferImageDescription()->needsCM(getDefaultImageDescription());
- auto shader = useShader(getShaderVariant(SH_FRAG_INNER_GLOW, skipCM ? 0 : SH_FEAT_CM | (IS_ICC ? SH_FEAT_ICC : SH_FEAT_TONEMAP | SH_FEAT_SDR_MOD)));
- if (!skipCM)
- passCMUniforms(shader, getDefaultImageDescription());
+ auto shader = useShader(getShaderVariant(SH_FRAG_INNER_GLOW, globalFeatures()));
shader->setUniformMatrix3fv(SHADER_PROJ, 1, GL_TRUE, glMatrix.getMatrix());
- shader->setUniformFloat4(SHADER_COLOR, col.r, col.g, col.b, col.a * a);
+ const auto converted = getConvertedColor(col);
+ shader->setUniformFloat4(SHADER_COLOR, converted.r, converted.g, converted.b, converted.a * a);
+ shader->setUniformFloat4(SHADER_COLOR_SRGB, col.r, col.g, col.b, col.a * a);
const auto TOPLEFT = Vector2D(round, round);
const auto BOTTOMRIGHT = Vector2D(newBox.width - round, newBox.height - round);
@@ -2385,7 +2401,8 @@ void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) {
Log::logger->log(Log::ERR, "Invalid source texture for mirror");
return;
}
- auto guard = g_pHyprRenderer->bindTempFB(g_pHyprRenderer->m_renderData.pMonitor->resources()->mirrorFB());
+ auto fb = g_pHyprRenderer->m_renderData.pMonitor->resources()->mirrorFB();
+ auto guard = g_pHyprRenderer->bindTempFB(fb);
Log::logger->log(Log::TRACE, "CM: saveBufferForMirror {} -> {}", TEX->m_imageDescription->value(), g_pHyprRenderer->m_renderData.currentFB->imageDescription()->value());
@@ -2398,7 +2415,6 @@ void CHyprOpenGLImpl::saveBufferForMirror(const CBox& box) {
.round = 0,
.discardActive = false,
.allowCustomUV = false,
- .cmBackToSRGB = true,
});
blend(true);
diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp
index 82b8f054..2434c0ed 100644
--- a/src/render/OpenGL.hpp
+++ b/src/render/OpenGL.hpp
@@ -174,7 +174,6 @@ namespace Render::GL {
GLenum wrapX = GL_CLAMP_TO_EDGE, wrapY = GL_CLAMP_TO_EDGE;
bool cmBackToSRGB = false;
bool finalMonitorCM = false;
- SP<CMonitor> cmBackToSRGBSource;
uint32_t discardMode = DISCARD_OPAQUE;
float discardOpacity = 0.f;
diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp
index 635bef55..0cb713f1 100644
--- a/src/render/Renderer.cpp
+++ b/src/render/Renderer.cpp
@@ -2020,18 +2020,8 @@ void IHyprRenderer::renderMonitor(PHLMONITOR pMonitor, bool commit) {
}
handleFullscreenSettings(pMonitor);
return;
- } else if (!pMonitor->m_lastScanout.expired() || pMonitor->m_directScanoutIsActive) {
- Log::logger->log(Log::DEBUG, "Left a direct scanout.");
- pMonitor->m_lastScanout.reset();
- pMonitor->m_previousFSWindow.reset(); // recalc fs settings
- pMonitor->m_directScanoutIsActive = false;
-
- // reset DRM format, but only if needed since it might modeset
- if (pMonitor->m_output->state->state().drmFormat != pMonitor->m_prevDrmFormat)
- pMonitor->m_output->state->setFormat(pMonitor->m_prevDrmFormat);
-
- pMonitor->m_drmFormat = pMonitor->m_prevDrmFormat;
- }
+ } else if (!pMonitor->m_lastScanout.expired() || pMonitor->m_directScanoutIsActive)
+ pMonitor->handleDSleave();
}
Event::bus()->m_events.render.pre.emit(pMonitor);
@@ -3049,6 +3039,7 @@ void IHyprRenderer::makeSnapshot(PHLWINDOW pWindow) {
const auto PFRAMEBUFFER = ref->m_snapshotFB;
PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888);
+ PFRAMEBUFFER->setImageDescription(workBufferImageDescription());
beginFullFakeRender(PMONITOR, fakeDamage, PFRAMEBUFFER);
@@ -3090,6 +3081,7 @@ void IHyprRenderer::makeSnapshot(PHLLS pLayer) {
const auto PFRAMEBUFFER = pLayer->m_snapshotFB;
PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888);
+ PFRAMEBUFFER->setImageDescription(workBufferImageDescription());
beginFullFakeRender(PMONITOR, fakeDamage, PFRAMEBUFFER);
@@ -3132,6 +3124,7 @@ void IHyprRenderer::makeSnapshot(WP<Desktop::View::CPopup> popup) {
const auto PFRAMEBUFFER = popup->m_snapshotFB;
PFRAMEBUFFER->alloc(PMONITOR->m_pixelSize.x, PMONITOR->m_pixelSize.y, DRM_FORMAT_ABGR8888);
+ PFRAMEBUFFER->setImageDescription(workBufferImageDescription());
beginFullFakeRender(PMONITOR, fakeDamage, PFRAMEBUFFER);
@@ -3314,14 +3307,10 @@ void IHyprRenderer::renderSnapshot(WP<Desktop::View::CPopup> popup) {
}
NColorManagement::PImageDescription IHyprRenderer::workBufferImageDescription() {
- // 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->useFP16() ?
- LINEAR_IMAGE_DESCRIPTION :
- m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF});
+ if (!m_renderData.pMonitor)
+ return LINEAR_IMAGE_DESCRIPTION;
+
+ return m_renderData.pMonitor->workBufferImageDescription();
}
bool IHyprRenderer::shouldBlur(PHLLS ls) {
diff --git a/src/render/Shader.cpp b/src/render/Shader.cpp
index 3359de66..8a7b8ba9 100644
--- a/src/render/Shader.cpp
+++ b/src/render/Shader.cpp
@@ -126,6 +126,7 @@ void CShader::getUniformLocations() {
m_uniformLocations[SHADER_PROJ] = getUniform("proj");
m_uniformLocations[SHADER_COLOR] = getUniform("color");
+ m_uniformLocations[SHADER_COLOR_SRGB] = getUniform("colorSRGB");
m_uniformLocations[SHADER_ALPHA_MATTE] = getUniform("texMatte");
m_uniformLocations[SHADER_TEX_TYPE] = getUniform("texType");
diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp
index 84a4014c..cf15c805 100644
--- a/src/render/Shader.hpp
+++ b/src/render/Shader.hpp
@@ -7,6 +7,7 @@
enum eShaderUniform : uint8_t {
SHADER_PROJ = 0,
SHADER_COLOR,
+ SHADER_COLOR_SRGB,
SHADER_ALPHA_MATTE,
SHADER_TEX_TYPE,
SHADER_SOURCE_TF,
diff --git a/src/render/gl/GLElementRenderer.cpp b/src/render/gl/GLElementRenderer.cpp
index dbf62cb8..c20fffc0 100644
--- a/src/render/gl/GLElementRenderer.cpp
+++ b/src/render/gl/GLElementRenderer.cpp
@@ -114,19 +114,18 @@ void CGLElementRenderer::draw(WP<CTexPassElement> element, const CRegion& damage
.blurredBG = m_data.blurredBG,
// common settings
- .damage = m_data.damage.empty() ? &damage : &m_data.damage,
- .surface = m_data.surface,
- .a = m_data.a,
- .round = m_data.round,
- .roundingPower = m_data.roundingPower,
- .discardActive = m_data.discardActive,
- .allowCustomUV = m_data.allowCustomUV,
- .cmBackToSRGB = m_data.cmBackToSRGB,
- .cmBackToSRGBSource = m_data.cmBackToSRGBSource,
- .discardMode = m_data.ignoreAlpha.has_value() ? sc<uint32_t>(DISCARD_ALPHA) : m_data.discardMode,
- .discardOpacity = m_data.ignoreAlpha.has_value() ? *m_data.ignoreAlpha : m_data.discardOpacity,
- .clipRegion = m_data.clipRegion,
- .currentLS = m_data.currentLS,
+ .damage = m_data.damage.empty() ? &damage : &m_data.damage,
+ .surface = m_data.surface,
+ .a = m_data.a,
+ .round = m_data.round,
+ .roundingPower = m_data.roundingPower,
+ .discardActive = m_data.discardActive,
+ .allowCustomUV = m_data.allowCustomUV,
+ .cmBackToSRGB = m_data.cmBackToSRGB,
+ .discardMode = m_data.ignoreAlpha.has_value() ? sc<uint32_t>(DISCARD_ALPHA) : m_data.discardMode,
+ .discardOpacity = m_data.ignoreAlpha.has_value() ? *m_data.ignoreAlpha : m_data.discardOpacity,
+ .clipRegion = m_data.clipRegion,
+ .currentLS = m_data.currentLS,
.primarySurfaceUVTopLeft = g_pHyprRenderer->m_renderData.primarySurfaceUVTopLeft,
.primarySurfaceUVBottomRight = g_pHyprRenderer->m_renderData.primarySurfaceUVBottomRight,
diff --git a/src/render/gl/GLFramebuffer.cpp b/src/render/gl/GLFramebuffer.cpp
index d8b198c3..196f3327 100644
--- a/src/render/gl/GLFramebuffer.cpp
+++ b/src/render/gl/GLFramebuffer.cpp
@@ -171,4 +171,15 @@ void CGLFramebuffer::invalidate(const std::vector<GLenum>& attachments) {
return;
glInvalidateFramebuffer(GL_FRAMEBUFFER, attachments.size(), attachments.data());
+ m_cleared = false;
+}
+
+void CGLFramebuffer::clearAfterInvalidation() {
+ if (m_cleared)
+ return;
+
+ m_cleared = true;
+ glClearColor(0, 0, 0, 0);
+ g_pHyprOpenGL->scissor(nullptr);
+ glClear(GL_COLOR_BUFFER_BIT);
}
diff --git a/src/render/gl/GLFramebuffer.hpp b/src/render/gl/GLFramebuffer.hpp
index 1be5d0e0..34051656 100644
--- a/src/render/gl/GLFramebuffer.hpp
+++ b/src/render/gl/GLFramebuffer.hpp
@@ -21,11 +21,15 @@ namespace Render::GL {
GLuint getFBID();
void invalidate(const std::vector<GLenum>& attachments);
+ // clear at most once per invalidate()
+ void clearAfterInvalidation();
+
protected:
bool internalAlloc(int w, int h, DRMFormat format = DRM_FORMAT_ARGB8888) override;
private:
- GLuint m_fb = -1;
+ GLuint m_fb = -1;
+ bool m_cleared = false;
friend class CGLRenderbuffer;
};
diff --git a/src/render/pass/TexPassElement.hpp b/src/render/pass/TexPassElement.hpp
index 6c0cfab1..413f9875 100644
--- a/src/render/pass/TexPassElement.hpp
+++ b/src/render/pass/TexPassElement.hpp
@@ -31,7 +31,6 @@ class CTexPassElement : public IPassElement {
std::optional<float> ignoreAlpha;
std::optional<bool> blockBlurOptimization;
bool cmBackToSRGB = false;
- SP<CMonitor> cmBackToSRGBSource;
bool discardActive = false;
bool allowCustomUV = false;
diff --git a/src/render/shaders/glsl/blur1.glsl b/src/render/shaders/glsl/blur1.glsl
index 86a37d88..8d9e39ae 100644
--- a/src/render/shaders/glsl/blur1.glsl
+++ b/src/render/shaders/glsl/blur1.glsl
@@ -104,10 +104,10 @@ vec4 blur1(vec2 v_texcoord, sampler2D tex, float radius, vec2 halfpixel, int pas
// That garbage maps to 0.0-1.0 range with UINT8 buffer and doesn't have any significant impact on the end result.
// FP16 garbage maps to -65,504 - 65,504 and defines the end result. Clamp it here to 0.0 - 1.0 to get the same quality outcome as with UINT8.
// Rerendering an undamaged area to get some insignificant color accuracy increase on blur edges isn't worth it.
- sum += clamp(texture(tex, uv - halfpixel.xy * radius), 0.0, 1.0);
- sum += clamp(texture(tex, uv + halfpixel.xy * radius), 0.0, 1.0);
- sum += clamp(texture(tex, uv + vec2(halfpixel.x, -halfpixel.y) * radius), 0.0, 1.0);
- sum += clamp(texture(tex, uv - vec2(halfpixel.x, -halfpixel.y) * radius), 0.0, 1.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;
diff --git a/src/render/shaders/glsl/blurFinish.glsl b/src/render/shaders/glsl/blurFinish.glsl
index e7e1f1c6..bdfe2bc8 100644
--- a/src/render/shaders/glsl/blurFinish.glsl
+++ b/src/render/shaders/glsl/blurFinish.glsl
@@ -30,7 +30,11 @@ vec4 blurFinish(vec4 pixColor, vec2 v_texcoord, float noise, float brightness
pixColor.rgb *= min(1.0, brightness);
#if USE_CM
- pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange);
+ if (targetTF == CM_TRANSFER_FUNCTION_EXT_LINEAR) {
+ pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, vec2(0.0, 203.0)); // FIXME wtf
+ } else {
+ pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange);
+ }
#endif
return pixColor;
diff --git a/src/render/shaders/glsl/cm_helpers.glsl b/src/render/shaders/glsl/cm_helpers.glsl
index 0d8c68e9..ae4b63ae 100644
--- a/src/render/shaders/glsl/cm_helpers.glsl
+++ b/src/render/shaders/glsl/cm_helpers.glsl
@@ -127,6 +127,7 @@ vec3 tfST240(vec3 color) {
vec3 toLinearRGB(vec3 color, int tf) {
switch (tf) {
+ case CM_TRANSFER_FUNCTION_LINEAR: return color;
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));
@@ -179,7 +180,7 @@ vec3 fromLinearRGB(vec3 color, int tf) {
}
vec4 fromLinear(vec4 color, int tf) {
- if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
+ if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR || tf == CM_TRANSFER_FUNCTION_LINEAR)
return color;
color.rgb /= max(color.a, 0.001);
@@ -189,6 +190,9 @@ vec4 fromLinear(vec4 color, int tf) {
}
vec4 fromLinearNit(vec4 color, int tf, vec2 range) {
+ if (tf == CM_TRANSFER_FUNCTION_LINEAR)
+ return color;
+
color.rgb = (color.rgb - range[0] * color.a) / (range[1] - range[0]); // @gulafaran
color.rgb /= max(color.a, 0.001);
color.rgb = fromLinearRGB(color.rgb, tf);
@@ -231,7 +235,8 @@ vec4
pixColor.rgb *= pixColor.a;
#else
pixColor.rgb = convertMatrix * pixColor.rgb;
- pixColor = toNit(pixColor, srcTFRange);
+ if (srcTF != CM_TRANSFER_FUNCTION_LINEAR)
+ pixColor = toNit(pixColor, srcTFRange);
pixColor.rgb *= pixColor.a;
#if USE_TONEMAP
pixColor = tonemap(pixColor, dstxyz, maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance);
@@ -258,4 +263,4 @@ vec4
#endif
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/render/shaders/glsl/constants.h b/src/render/shaders/glsl/constants.h
index bbab5284..db8b2cc1 100644
--- a/src/render/shaders/glsl/constants.h
+++ b/src/render/shaders/glsl/constants.h
@@ -1,6 +1,7 @@
#ifndef CONSTANTS_H
#define CONSTANTS_H
//enum eTransferFunction
+#define CM_TRANSFER_FUNCTION_LINEAR 0 // not normalised
#define CM_TRANSFER_FUNCTION_BT1886 1
#define CM_TRANSFER_FUNCTION_GAMMA22 2
#define CM_TRANSFER_FUNCTION_GAMMA28 3
diff --git a/src/render/shaders/glsl/inner_glow.frag b/src/render/shaders/glsl/inner_glow.frag
index 2fc405c0..63369e0a 100644
--- a/src/render/shaders/glsl/inner_glow.frag
+++ b/src/render/shaders/glsl/inner_glow.frag
@@ -8,10 +8,7 @@ precision highp float;
in vec4 v_color;
in vec2 v_texcoord;
-uniform int sourceTF; // eTransferFunction
-uniform int targetTF; // eTransferFunction
-uniform mat3 targetPrimariesXYZ;
-
+uniform vec4 colorSRGB;
uniform vec2 topLeft;
uniform vec2 bottomRight;
uniform vec2 fullSize;
@@ -20,38 +17,22 @@ uniform float roundingPower;
uniform float range;
uniform float shadowPower;
-#if USE_CM
-#include "cm_helpers.glsl"
-#include "CM.glsl"
-#endif
-
#include "inner_glow.glsl"
layout(location = 0) out vec4 fragColor;
+#if USE_MIRROR
+layout(location = 1) out vec4 mirrorColor;
+#endif
void main() {
vec4 pixColor = v_color;
-
- fragColor = getInnerGlow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight
-#if USE_CM
- ,
- sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
-#if USE_ICC
- ,
- iccLut3D, iccLutSize
+#if USE_MIRROR
+ vec4[2] pixColors =
#else
-#if USE_TONEMAP || USE_SDR_MOD
- ,
- targetPrimariesXYZ
-#endif
-#if USE_TONEMAP
- ,
- maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
-#endif
-#if USE_SDR_MOD
- ,
- sdrSaturation, sdrBrightnessMultiplier
-#endif
+ fragColor =
#endif
+ getInnerGlow(pixColor, colorSRGB, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight);
+#if USE_MIRROR
+ fragColor = pixColors[0];
+ mirrorColor = pixColors[1];
#endif
- );
}
diff --git a/src/render/shaders/glsl/inner_glow.glsl b/src/render/shaders/glsl/inner_glow.glsl
index b0d55b19..307c3648 100644
--- a/src/render/shaders/glsl/inner_glow.glsl
+++ b/src/render/shaders/glsl/inner_glow.glsl
@@ -5,8 +5,6 @@
#ifndef INNER_GLOW_GLSL
#define INNER_GLOW_GLSL
-#include "cm_helpers.glsl"
-
float innerGlowAlpha(float distFromEdge, float range, float glowPower) {
if (distFromEdge >= range)
return 0.0;
@@ -26,29 +24,7 @@ float innerGlowSmin(float a, float b, float k) {
return min(a, b) - h * h * h * k * (1.0 / 6.0);
}
-vec4 getInnerGlow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float glowPower, 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
-) {
+vec4 getInnerGlow(vec4 pixColor, vec4 colorSRGB, vec2 v_texcoord, float radius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float glowPower, vec2 bottomRight) {
vec2 pixCoord = fullSize * v_texcoord;
// clip to the rounded rectangle shape using actual SDF
@@ -79,28 +55,15 @@ vec4 getInnerGlow(vec4 pixColor, vec2 v_texcoord, float radius, float roundingPo
// premultiply
pixColor.rgb *= pixColor[3];
-#if USE_CM
- pixColor = doColorManagement(pixColor, sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
-#if USE_ICC
- ,
- iccLut3D, iccLutSize
+#if USE_MIRROR
+ vec4[2] pixColors;
+ pixColors[0] = pixColor;
+ pixColors[1] = colorSRGB;
+ pixColors[1].a = pixColor.a;
+ pixColors[1].rgb *= pixColors[1].a;
+ return pixColors;
#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
}
#endif
diff --git a/src/render/shaders/glsl/quad.frag b/src/render/shaders/glsl/quad.frag
index dc1945af..46d42a8c 100644
--- a/src/render/shaders/glsl/quad.frag
+++ b/src/render/shaders/glsl/quad.frag
@@ -7,6 +7,7 @@
precision highp float;
in vec4 v_color;
+uniform vec4 colorSRGB;
#if USE_ROUNDING
uniform float radius;
uniform float roundingPower;
@@ -28,6 +29,10 @@ void main() {
fragColor = pixColor;
#if USE_MIRROR
- mirrorColor = fragColor;
+#if USE_ROUNDING
+ mirrorColor = rounding(colorSRGB, radius, roundingPower, topLeft, fullSize);
+#else
+ mirrorColor = colorSRGB;
+#endif
#endif
}
diff --git a/src/render/shaders/glsl/shadow.frag b/src/render/shaders/glsl/shadow.frag
index e91e9a14..eb6f3174 100644
--- a/src/render/shaders/glsl/shadow.frag
+++ b/src/render/shaders/glsl/shadow.frag
@@ -8,10 +8,7 @@ precision highp float;
in vec4 v_color;
in vec2 v_texcoord;
-uniform int sourceTF; // eTransferFunction
-uniform int targetTF; // eTransferFunction
-uniform mat3 targetPrimariesXYZ;
-
+uniform vec4 colorSRGB;
uniform vec2 topLeft;
uniform vec2 bottomRight;
uniform vec2 windowTopLeft;
@@ -23,11 +20,6 @@ uniform float range;
uniform float shadowPower;
uniform float thick;
-#if USE_CM
-#include "cm_helpers.glsl"
-#include "CM.glsl"
-#endif
-
#include "shadow.glsl"
layout(location = 0) out vec4 fragColor;
@@ -41,29 +33,7 @@ void main() {
#else
fragColor =
#endif
- getShadow(pixColor, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight, windowTopLeft, windowBottomRight, thick
-#if USE_CM
- ,
- sourceTF, targetTF, convertMatrix, srcTFRange, dstTFRange
-#if USE_ICC
- ,
- iccLut3D, iccLutSize
-#else
-#if USE_TONEMAP || USE_SDR_MOD
- ,
- targetPrimariesXYZ
-#endif
-#if USE_TONEMAP
- ,
- maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
-#endif
-#if USE_SDR_MOD
- ,
- sdrSaturation, sdrBrightnessMultiplier
-#endif
-#endif
-#endif
- );
+ getShadow(pixColor, colorSRGB, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight, windowTopLeft, windowBottomRight, thick);
#if USE_MIRROR
fragColor = pixColors[0];
mirrorColor = pixColors[1];
diff --git a/src/render/shaders/glsl/shadow.glsl b/src/render/shaders/glsl/shadow.glsl
index fdfa697c..ba68bc49 100644
--- a/src/render/shaders/glsl/shadow.glsl
+++ b/src/render/shaders/glsl/shadow.glsl
@@ -5,7 +5,6 @@
#ifndef SHADOW_GLSL
#define SHADOW_GLSL
-#include "cm_helpers.glsl"
#include "rounding.glsl"
float pixAlphaRoundedDistance(float distanceToCorner, float radius, float range, float shadowPower) {
@@ -54,30 +53,8 @@ vec4[2]
#else
vec4
#endif
- getShadow(vec4 pixColor, vec2 v_texcoord, float borderRadius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight,
- vec2 windowTopLeft, vec2 windowBottomRight, float windowRadius
-#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
- ) {
+ getShadow(vec4 pixColor, vec4 colorSRGB, vec2 v_texcoord, float borderRadius, float roundingPower, vec2 topLeft, vec2 fullSize, float range, float shadowPower, vec2 bottomRight,
+ vec2 windowTopLeft, vec2 windowBottomRight, float windowRadius) {
float radius = range + borderRadius;
float originalAlpha = pixColor[3];
@@ -144,33 +121,12 @@ vec4
// premultiply
pixColor.rgb *= pixColor[3];
-#if USE_CM
-#if USE_MIRROR
- vec4[2] pixColors =
-#else
- pixColor =
-#endif
- 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_MIRROR
+ vec4[2] pixColors;
+ pixColors[0] = pixColor;
+ pixColors[1] = colorSRGB;
+ pixColors[1].a = pixColor.a;
+ pixColors[1].rgb *= pixColors[1].a;
return pixColors;
#else
return pixColor;
diff --git a/src/render/shaders/glsl/tonemap.glsl b/src/render/shaders/glsl/tonemap.glsl
index dcce45a6..a2db7e6e 100644
--- a/src/render/shaders/glsl/tonemap.glsl
+++ b/src/render/shaders/glsl/tonemap.glsl
@@ -50,21 +50,16 @@ vec4 tonemap(vec4 color, mat3 dstXYZ, float maxLuminance, float dstMaxLuminance,
float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2);
float luminance = pow((max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E), PQ_INV_M1) * HDR_MAX_LUMINANCE;
- float linearPart = min(luminance, dstRefLuminance);
- float luminanceAboveRef = max(luminance - dstRefLuminance, 0.0);
- float maxExcessLuminance = max(maxLuminance - dstRefLuminance, 1.0);
- float shoulder = log((luminanceAboveRef / maxExcessLuminance + 1.0) * (M_E - 1.0));
- float mappedHigh = shoulder * (dstMaxLuminance - dstRefLuminance);
- float newLum = clamp(linearPart + mappedHigh, 0.0, dstMaxLuminance);
+ float luminanceRatio = max(luminance / dstRefLuminance, 0.0);
+ float srcScale = maxLuminance / dstRefLuminance;
+ float dstScale = dstMaxLuminance / dstRefLuminance;
+ float v = (dstScale * (1.0 + srcScale) - srcScale) / pow(srcScale, 2.0);
+ float newLuminance = (luminanceRatio * (1.0 + luminanceRatio * v) / (1.0 + luminanceRatio)) * dstRefLuminance;
- // scale src to dst reference
- float refScale = dstRefLuminance / srcRefLuminance;
+ E = pow(clamp(newLuminance / HDR_MAX_LUMINANCE, 0.0, 1.0), PQ_M1);
+ ICtCp[0] = pow((PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E), PQ_M2);
- // kind of works but doesn't use newLum at all
- return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE * refScale, color[3]);
- // breaks with overriden monitor luminances. might be caused by incorrect imput values
- // @gulafaran
- // vec3 outRGB = fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb;
- // outRGB *= (newLum / max(luminance, 0.0001)); // actually apply the tone mapping
- // return vec4(clamp(outRGB * HDR_MAX_LUMINANCE * refScale, 0.0, dstMaxLuminance), color[3]);
+ color = vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]);
+
+ return clamp(color, 0.0, dstMaxLuminance);
}