Skip to content

Use precomputed primaries conversion #9814

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 6 additions & 16 deletions src/protocols/ColorManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@

using namespace NColorManagement;

static uint64_t lastImageID = 0; // FIXME use for deduplication

CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resource) {
if UNLIKELY (!good())
return;
Expand Down Expand Up @@ -191,14 +189,13 @@ CColorManager::CColorManager(SP<CWpColorManagerV1> resource) : m_resource(resour
}

RESOURCE->self = RESOURCE;
RESOURCE->settings.id = ++lastImageID;
RESOURCE->settings.windowsScRGB = true;
RESOURCE->settings.primariesNamed = NColorManagement::CM_PRIMARIES_SRGB;
RESOURCE->settings.primariesNameSet = true;
RESOURCE->settings.primaries = NColorPrimaries::BT709;
RESOURCE->settings.transferFunction = NColorManagement::CM_TRANSFER_FUNCTION_EXT_LINEAR;
RESOURCE->settings.luminances.reference = 203;
RESOURCE->resource()->sendReady(RESOURCE->settings.id);
RESOURCE->resource()->sendReady(RESOURCE->settings.updateId());
});

m_resource->setOnDestroy([this](CWpColorManagerV1* r) { PROTO::colorManagement->destroyResource(this); });
Expand Down Expand Up @@ -239,9 +236,7 @@ CColorManagementOutput::CColorManagementOutput(SP<CWpColorManagementOutputV1> re
RESOURCE->m_resource->sendFailed(WP_IMAGE_DESCRIPTION_V1_CAUSE_NO_OUTPUT, "No output");
else {
RESOURCE->settings = m_monitor->imageDescription;
if (RESOURCE->settings.id)
RESOURCE->settings.id = ++lastImageID;
RESOURCE->m_resource->sendReady(RESOURCE->settings.id); // FIXME: create correct id
RESOURCE->m_resource->sendReady(RESOURCE->settings.updateId());
}
});
}
Expand Down Expand Up @@ -383,10 +378,7 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
m_currentPreferred = RESOURCE;

m_currentPreferred->settings = g_pCompositor->getPreferredImageDescription();
if (!m_currentPreferred->settings.id)
m_currentPreferred->settings.id = ++lastImageID;

RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id
RESOURCE->resource()->sendReady(m_currentPreferred->settings.updateId());
});

m_resource->setGetPreferredParametric([this](CWpColorManagementSurfaceFeedbackV1* r, uint32_t id) {
Expand Down Expand Up @@ -419,7 +411,7 @@ CColorManagementFeedbackSurface::CColorManagementFeedbackSurface(SP<CWpColorMana
return;
}

RESOURCE->resource()->sendReady(++lastImageID); // FIXME: create correct id
RESOURCE->resource()->sendReady(m_currentPreferred->settings.updateId());
});
}

Expand Down Expand Up @@ -467,8 +459,7 @@ CColorManagementIccCreator::CColorManagementIccCreator(SP<CWpImageDescriptionCre

RESOURCE->self = RESOURCE;
RESOURCE->settings = settings;
settings.id = ++lastImageID;
RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id
RESOURCE->resource()->sendReady(settings.updateId());

PROTO::colorManagement->destroyResource(this);
});
Expand Down Expand Up @@ -522,8 +513,7 @@ CColorManagementParametricCreator::CColorManagementParametricCreator(SP<CWpImage

RESOURCE->self = RESOURCE;
RESOURCE->settings = settings;
settings.id = ++lastImageID;
RESOURCE->resource()->sendReady(settings.id); // FIXME: create correct id
RESOURCE->resource()->sendReady(settings.updateId());

PROTO::colorManagement->destroyResource(this);
});
Expand Down
28 changes: 27 additions & 1 deletion src/protocols/types/ColorManagement.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include "ColorManagement.hpp"
#include <map>

namespace NColorManagement {
const SPCPRimaries& getPrimaries(ePrimaries name) {
static uint32_t lastImageID = 0;
static std::map<uint32_t, SImageDescription> knownDescriptionIds; // expected to be small

const SPCPRimaries& getPrimaries(ePrimaries name) {
switch (name) {
case CM_PRIMARIES_SRGB: return NColorPrimaries::BT709;
case CM_PRIMARIES_BT2020: return NColorPrimaries::BT2020;
Expand All @@ -17,4 +21,26 @@ namespace NColorManagement {
}
}

// TODO make image descriptions immutable and always set an id

uint32_t SImageDescription::findId() const {
for (auto it = knownDescriptionIds.begin(); it != knownDescriptionIds.end(); ++it) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: use for-each syntax, and maybe structured binding?

if (it->second == *this)
return it->first;
}

const auto newId = ++lastImageID;
knownDescriptionIds.insert(std::make_pair(newId, *this));
return newId;
}

uint32_t SImageDescription::getId() const {
return id > 0 ? id : findId();
}

uint32_t SImageDescription::updateId() {
id = 0;
id = findId();
return id;
}
}
62 changes: 49 additions & 13 deletions src/protocols/types/ColorManagement.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
#pragma once

#include "color-management-v1.hpp"
#include <hyprgraphics/color/Color.hpp>

#define SDR_MIN_LUMINANCE 0.2
#define SDR_MAX_LUMINANCE 80.0
#define HDR_MIN_LUMINANCE 0.005
#define HDR_MAX_LUMINANCE 10000.0
#define HLG_MAX_LUMINANCE 1000.0

namespace NColorManagement {
enum ePrimaries : uint8_t {
Expand Down Expand Up @@ -47,19 +54,7 @@ namespace NColorManagement {
return (eTransferFunction)tf;
}

struct SPCPRimaries {
struct xy { //NOLINT(readability-identifier-naming)
float x = 0;
float y = 0;

bool operator==(const xy& p2) const {
return x == p2.x && y == p2.y;
}
} red, green, blue, white;
bool operator==(const SPCPRimaries& p2) const {
return red == p2.red && green == p2.green && blue == p2.blue && white == p2.white;
}
};
typedef Hyprgraphics::SPCPRimaries SPCPRimaries;

namespace NColorPrimaries {
static const auto DEFAULT_PRIMARIES = SPCPRimaries{};
Expand Down Expand Up @@ -185,5 +180,46 @@ namespace NColorManagement {
return NColorManagement::getPrimaries(primariesNamed);
return primaries;
}

float getTFMinLuminance() const {
switch (transferFunction) {
case CM_TRANSFER_FUNCTION_EXT_LINEAR: return 0;
case CM_TRANSFER_FUNCTION_ST2084_PQ:
case CM_TRANSFER_FUNCTION_HLG: return HDR_MIN_LUMINANCE;
case CM_TRANSFER_FUNCTION_GAMMA22:
case CM_TRANSFER_FUNCTION_GAMMA28:
case CM_TRANSFER_FUNCTION_BT1886:
case CM_TRANSFER_FUNCTION_ST240:
case CM_TRANSFER_FUNCTION_LOG_100:
case CM_TRANSFER_FUNCTION_LOG_316:
case CM_TRANSFER_FUNCTION_XVYCC:
case CM_TRANSFER_FUNCTION_EXT_SRGB:
case CM_TRANSFER_FUNCTION_ST428:
case CM_TRANSFER_FUNCTION_SRGB:
default: return SDR_MIN_LUMINANCE;
}
};

float getTFMaxLuminance() const {
switch (transferFunction) {
case CM_TRANSFER_FUNCTION_ST2084_PQ: return HDR_MAX_LUMINANCE;
case CM_TRANSFER_FUNCTION_HLG: return HLG_MAX_LUMINANCE;
case CM_TRANSFER_FUNCTION_GAMMA22:
case CM_TRANSFER_FUNCTION_GAMMA28:
case CM_TRANSFER_FUNCTION_BT1886:
case CM_TRANSFER_FUNCTION_ST240:
case CM_TRANSFER_FUNCTION_LOG_100:
case CM_TRANSFER_FUNCTION_LOG_316:
case CM_TRANSFER_FUNCTION_XVYCC:
case CM_TRANSFER_FUNCTION_EXT_SRGB:
case CM_TRANSFER_FUNCTION_ST428:
case CM_TRANSFER_FUNCTION_SRGB:
default: return SDR_MAX_LUMINANCE;
}
};

uint32_t findId() const;
uint32_t getId() const;
uint32_t updateId();
};
}
31 changes: 23 additions & 8 deletions src/render/OpenGL.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <GLES3/gl32.h>
#include <hyprgraphics/color/Color.hpp>
#include <hyprutils/string/String.hpp>
#include <hyprutils/path/Path.hpp>
#include <random>
Expand Down Expand Up @@ -908,13 +910,15 @@ static void getCMShaderUniforms(CShader& shader) {
shader.skipCM = glGetUniformLocation(shader.program, "skipCM");
shader.sourceTF = glGetUniformLocation(shader.program, "sourceTF");
shader.targetTF = glGetUniformLocation(shader.program, "targetTF");
shader.sourcePrimaries = glGetUniformLocation(shader.program, "sourcePrimaries");
shader.srcTFRange = glGetUniformLocation(shader.program, "srcTFRange");
shader.dstTFRange = glGetUniformLocation(shader.program, "dstTFRange");
shader.targetPrimaries = glGetUniformLocation(shader.program, "targetPrimaries");
shader.maxLuminance = glGetUniformLocation(shader.program, "maxLuminance");
shader.dstMaxLuminance = glGetUniformLocation(shader.program, "dstMaxLuminance");
shader.dstRefLuminance = glGetUniformLocation(shader.program, "dstRefLuminance");
shader.sdrSaturation = glGetUniformLocation(shader.program, "sdrSaturation");
shader.sdrBrightness = glGetUniformLocation(shader.program, "sdrBrightnessMultiplier");
shader.convertMatrix = glGetUniformLocation(shader.program, "convertMatrix");
}

// shader has #include "rounding.glsl"
Expand Down Expand Up @@ -1437,27 +1441,26 @@ void CHyprOpenGLImpl::renderTextureWithDamage(SP<CTexture> tex, const CBox& box,
scissor(nullptr);
}

static std::map<std::pair<uint32_t, uint32_t>, std::array<GLfloat, 9>> primariesConversionCache;

void CHyprOpenGLImpl::passCMUniforms(const CShader& shader, const NColorManagement::SImageDescription& imageDescription,
const NColorManagement::SImageDescription& targetImageDescription, bool modifySDR) {
glUniform1i(shader.sourceTF, imageDescription.transferFunction);
glUniform1i(shader.targetTF, targetImageDescription.transferFunction);
const auto sourcePrimaries =
imageDescription.primariesNameSet || imageDescription.primaries == SPCPRimaries{} ? getPrimaries(imageDescription.primariesNamed) : imageDescription.primaries;

const auto targetPrimaries = targetImageDescription.primariesNameSet || targetImageDescription.primaries == SPCPRimaries{} ?
getPrimaries(targetImageDescription.primariesNamed) :
targetImageDescription.primaries;

const GLfloat glSourcePrimaries[8] = {
sourcePrimaries.red.x, sourcePrimaries.red.y, sourcePrimaries.green.x, sourcePrimaries.green.y,
sourcePrimaries.blue.x, sourcePrimaries.blue.y, sourcePrimaries.white.x, sourcePrimaries.white.y,
};
const GLfloat glTargetPrimaries[8] = {
targetPrimaries.red.x, targetPrimaries.red.y, targetPrimaries.green.x, targetPrimaries.green.y,
targetPrimaries.blue.x, targetPrimaries.blue.y, targetPrimaries.white.x, targetPrimaries.white.y,
};
glUniformMatrix4x2fv(shader.sourcePrimaries, 1, false, glSourcePrimaries);
glUniformMatrix4x2fv(shader.targetPrimaries, 1, false, glTargetPrimaries);

glUniform2f(shader.srcTFRange, imageDescription.getTFMinLuminance(), imageDescription.getTFMaxLuminance());
glUniform2f(shader.dstTFRange, targetImageDescription.getTFMinLuminance(), targetImageDescription.getTFMaxLuminance());

const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
glUniform1f(shader.maxLuminance, maxLuminance * targetImageDescription.luminances.reference / imageDescription.luminances.reference);
glUniform1f(shader.dstMaxLuminance, targetImageDescription.luminances.max > 0 ? targetImageDescription.luminances.max : 10000);
Expand All @@ -1469,7 +1472,19 @@ void CHyprOpenGLImpl::passCMUniforms(const CShader& shader, const NColorManageme
glUniform1f(shader.sdrBrightness,
modifySDR && m_RenderData.pMonitor->sdrBrightness > 0 && targetImageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
m_RenderData.pMonitor->sdrBrightness :

1.0f);
const auto cacheKey = std::make_pair(imageDescription.getId(), targetImageDescription.getId());
if (!primariesConversionCache.contains(cacheKey)) {
const auto mat = imageDescription.getPrimaries().convertMatrix(targetImageDescription.getPrimaries()).mat();
const std::array<GLfloat, 9> glConvertMatrix = {
mat[0][0], mat[1][0], mat[2][0], //
mat[0][1], mat[1][1], mat[2][1], //
mat[0][2], mat[1][2], mat[2][2], //
};
primariesConversionCache.insert(std::make_pair(cacheKey, glConvertMatrix));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: is there a reason you're doing insert and std::make_pair instead of just primariesConversionCache[cacheKey] = glConvertMatrix;?

}
glUniformMatrix3fv(shader.convertMatrix, 1, false, &primariesConversionCache[cacheKey][0]);
}

void CHyprOpenGLImpl::passCMUniforms(const CShader& shader, const SImageDescription& imageDescription) {
Expand Down
4 changes: 3 additions & 1 deletion src/render/Shader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ class CShader {
GLint skipCM = -1;
GLint sourceTF = -1;
GLint targetTF = -1;
GLint sourcePrimaries = -1;
GLint srcTFRange = -1;
GLint dstTFRange = -1;
GLint targetPrimaries = -1;
GLint maxLuminance = -1;
GLint dstMaxLuminance = -1;
GLint dstRefLuminance = -1;
GLint sdrSaturation = -1; // sdr -> hdr saturation
GLint sdrBrightness = -1; // sdr -> hdr brightness multiplier
GLint convertMatrix = -1;
GLint tex = -1;
GLint alpha = -1;
GLint posAttrib = -1;
Expand Down
3 changes: 1 addition & 2 deletions src/render/shaders/glsl/CM.frag
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ uniform int texType; // eTextureType: 0 - rgba, 1 - rgbx, 2 - ext
// uniform int skipCM;
uniform int sourceTF; // eTransferFunction
uniform int targetTF; // eTransferFunction
uniform mat4x2 sourcePrimaries;
uniform mat4x2 targetPrimaries;

uniform float alpha;
Expand Down Expand Up @@ -43,7 +42,7 @@ void main() {
discard;

// this shader shouldn't be used when skipCM == 1
pixColor = doColorManagement(pixColor, sourceTF, sourcePrimaries, targetTF, targetPrimaries);
pixColor = doColorManagement(pixColor, sourceTF, targetTF, targetPrimaries);

if (applyTint == 1)
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]);
Expand Down
Loading
Loading