This commit is contained in:
UjinT34 2026-05-06 23:54:58 +12:00 committed by GitHub
commit 30367f272c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 569 additions and 262 deletions

View file

@ -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"
@ -1185,6 +1186,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

View file

@ -540,6 +540,8 @@ 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 = 2, .map = OptionMap{{"default", 0}, {"linear", 1}, {"monitor", 2}}}),
/*
* cursor:
@ -604,7 +606,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:

View file

@ -617,6 +617,7 @@ void CMonitor::applyCMType(NCMType::eCMType cmType, NTransferFunction::eTF cmSdr
if (oldImageDescription != m_imageDescription) {
if (PROTO::colorManagement)
PROTO::colorManagement->onMonitorImageDescriptionChanged(m_self);
m_blurFBDirty = true;
}
}
@ -2063,6 +2064,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;
}
@ -2549,9 +2564,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 != 2) {
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);

View file

@ -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 = 2
// avoids lookup for an id when ::from is used
NColorManagement::PImageDescription m_cachedInternalDescription = NColorManagement::CImageDescription::from(NColorManagement::SImageDescription{});
struct {
CHyprSignalListener frame;

View file

@ -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,16 @@ 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},
});
if (!m_monitor)
return DEFAULT_SRGB_IMAGE_DESCRIPTION;
return m_monitor->workBufferImageDescription();
}
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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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;
if (!m_renderData.pMonitor)
return LINEAR_IMAGE_DESCRIPTION;
return m_renderData.pMonitor->useFP16() ?
LINEAR_IMAGE_DESCRIPTION :
m_renderData.pMonitor->m_imageDescription; //CImageDescription::from(NColorManagement::SImageDescription{.transferFunction = CHOSEN_SDR_EOTF});
return m_renderData.pMonitor->workBufferImageDescription();
}
bool IHyprRenderer::shouldBlur(PHLLS ls) {

View file

@ -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");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
fragColor =
#endif
#if USE_TONEMAP
,
maxLuminance, dstMaxLuminance, dstRefLuminance, srcRefLuminance
getInnerGlow(pixColor, colorSRGB, v_texcoord, radius, roundingPower, topLeft, fullSize, range, shadowPower, bottomRight);
#if USE_MIRROR
fragColor = pixColors[0];
mirrorColor = pixColors[1];
#endif
#if USE_SDR_MOD
,
sdrSaturation, sdrBrightnessMultiplier
#endif
#endif
#endif
);
}

View file

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

View file

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

View file

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

View file

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

View file

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