renderer: add simple color management (#9506)
Adds proper color management and transformations for CM surfaces.
This commit is contained in:
parent
e86d3a14e4
commit
8c97cb7858
14 changed files with 823 additions and 179 deletions
|
|
@ -9,6 +9,7 @@
|
|||
#include "../desktop/LayerSurface.hpp"
|
||||
#include "../protocols/LayerShell.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
#include "../protocols/ColorManagement.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../managers/input/InputManager.hpp"
|
||||
#include "pass/TexPassElement.hpp"
|
||||
|
|
@ -20,6 +21,7 @@
|
|||
#include <gbm.h>
|
||||
#include <filesystem>
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace NColorManagement;
|
||||
|
||||
const std::vector<const char*> ASSET_PATHS = {
|
||||
#ifdef DATAROOTDIR
|
||||
|
|
@ -866,6 +868,39 @@ void CHyprOpenGLImpl::initShaders() {
|
|||
m_RenderData.pCurrentMonData->m_shQUAD.radius = glGetUniformLocation(prog, "radius");
|
||||
m_RenderData.pCurrentMonData->m_shQUAD.roundingPower = glGetUniformLocation(prog, "roundingPower");
|
||||
|
||||
#ifndef GLES2
|
||||
prog = createProgram(TEXVERTSRC320, TEXFRAGSRCCM);
|
||||
m_RenderData.pCurrentMonData->m_shCM.program = prog;
|
||||
m_RenderData.pCurrentMonData->m_shCM.proj = glGetUniformLocation(prog, "proj");
|
||||
m_RenderData.pCurrentMonData->m_shCM.tex = glGetUniformLocation(prog, "tex");
|
||||
m_RenderData.pCurrentMonData->m_shCM.texType = glGetUniformLocation(prog, "texType");
|
||||
m_RenderData.pCurrentMonData->m_shCM.skipCM = glGetUniformLocation(prog, "skipCM");
|
||||
m_RenderData.pCurrentMonData->m_shCM.sourceTF = glGetUniformLocation(prog, "sourceTF");
|
||||
m_RenderData.pCurrentMonData->m_shCM.targetTF = glGetUniformLocation(prog, "targetTF");
|
||||
m_RenderData.pCurrentMonData->m_shCM.sourcePrimaries = glGetUniformLocation(prog, "sourcePrimaries");
|
||||
m_RenderData.pCurrentMonData->m_shCM.targetPrimaries = glGetUniformLocation(prog, "targetPrimaries");
|
||||
m_RenderData.pCurrentMonData->m_shCM.maxLuminance = glGetUniformLocation(prog, "maxLuminance");
|
||||
m_RenderData.pCurrentMonData->m_shCM.dstMaxLuminance = glGetUniformLocation(prog, "dstMaxLuminance");
|
||||
m_RenderData.pCurrentMonData->m_shCM.dstRefLuminance = glGetUniformLocation(prog, "dstRefLuminance");
|
||||
m_RenderData.pCurrentMonData->m_shCM.sdrSaturation = glGetUniformLocation(prog, "sdrSaturation");
|
||||
m_RenderData.pCurrentMonData->m_shCM.sdrBrightness = glGetUniformLocation(prog, "sdrBrightnessMultiplier");
|
||||
m_RenderData.pCurrentMonData->m_shCM.alphaMatte = glGetUniformLocation(prog, "texMatte");
|
||||
m_RenderData.pCurrentMonData->m_shCM.alpha = glGetUniformLocation(prog, "alpha");
|
||||
m_RenderData.pCurrentMonData->m_shCM.texAttrib = glGetAttribLocation(prog, "texcoord");
|
||||
m_RenderData.pCurrentMonData->m_shCM.matteTexAttrib = glGetAttribLocation(prog, "texcoordMatte");
|
||||
m_RenderData.pCurrentMonData->m_shCM.posAttrib = glGetAttribLocation(prog, "pos");
|
||||
m_RenderData.pCurrentMonData->m_shCM.discardOpaque = glGetUniformLocation(prog, "discardOpaque");
|
||||
m_RenderData.pCurrentMonData->m_shCM.discardAlpha = glGetUniformLocation(prog, "discardAlpha");
|
||||
m_RenderData.pCurrentMonData->m_shCM.discardAlphaValue = glGetUniformLocation(prog, "discardAlphaValue");
|
||||
m_RenderData.pCurrentMonData->m_shCM.topLeft = glGetUniformLocation(prog, "topLeft");
|
||||
m_RenderData.pCurrentMonData->m_shCM.fullSize = glGetUniformLocation(prog, "fullSize");
|
||||
m_RenderData.pCurrentMonData->m_shCM.radius = glGetUniformLocation(prog, "radius");
|
||||
m_RenderData.pCurrentMonData->m_shCM.roundingPower = glGetUniformLocation(prog, "roundingPower");
|
||||
m_RenderData.pCurrentMonData->m_shCM.applyTint = glGetUniformLocation(prog, "applyTint");
|
||||
m_RenderData.pCurrentMonData->m_shCM.tint = glGetUniformLocation(prog, "tint");
|
||||
m_RenderData.pCurrentMonData->m_shCM.useAlphaMatte = glGetUniformLocation(prog, "useAlphaMatte");
|
||||
#endif
|
||||
|
||||
prog = createProgram(TEXVERTSRC, TEXFRAGSRCRGBA);
|
||||
m_RenderData.pCurrentMonData->m_shRGBA.program = prog;
|
||||
m_RenderData.pCurrentMonData->m_shRGBA.proj = glGetUniformLocation(prog, "proj");
|
||||
|
|
@ -1280,7 +1315,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
|
|||
CBox newBox = box;
|
||||
m_RenderData.renderModif.applyToBox(newBox);
|
||||
|
||||
static auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
|
||||
static auto PDT = CConfigValue<Hyprlang::INT>("debug:damage_tracking");
|
||||
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
|
||||
|
||||
// get the needed transform for this texture
|
||||
const bool TRANSFORMS_MATCH = wlTransformToHyprutils(m_RenderData.pMonitor->transform) == tex->m_eTransform; // FIXME: combine them properly!!!
|
||||
|
|
@ -1304,6 +1340,8 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
|
|||
|
||||
const bool CRASHING = m_bApplyFinalShader && g_pHyprRenderer->m_bCrashingInProgress;
|
||||
|
||||
auto texType = tex->m_iType;
|
||||
|
||||
if (CRASHING) {
|
||||
shader = &m_RenderData.pCurrentMonData->m_shGLITCH;
|
||||
usingFinalShader = true;
|
||||
|
|
@ -1316,16 +1354,27 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
|
|||
usingFinalShader = true;
|
||||
} else {
|
||||
switch (tex->m_iType) {
|
||||
#ifdef GLES2
|
||||
case TEXTURE_RGBA: shader = &m_RenderData.pCurrentMonData->m_shRGBA; break;
|
||||
case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shRGBX; break;
|
||||
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break;
|
||||
#else
|
||||
case TEXTURE_RGBA:
|
||||
case TEXTURE_RGBX: shader = &m_RenderData.pCurrentMonData->m_shCM; break;
|
||||
#endif
|
||||
case TEXTURE_EXTERNAL: shader = &m_RenderData.pCurrentMonData->m_shEXT; break; // might be unused
|
||||
default: RASSERT(false, "tex->m_iTarget unsupported!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault())
|
||||
if (m_RenderData.currentWindow && m_RenderData.currentWindow->m_sWindowData.RGBX.valueOrDefault()) {
|
||||
#ifdef GLES2
|
||||
shader = &m_RenderData.pCurrentMonData->m_shRGBX;
|
||||
#else
|
||||
shader = &m_RenderData.pCurrentMonData->m_shCM;
|
||||
#endif
|
||||
texType = TEXTURE_RGBX;
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(tex->m_iTarget, tex->m_iTexID);
|
||||
|
|
@ -1350,6 +1399,49 @@ void CHyprOpenGLImpl::renderTextureInternalWithDamage(SP<CTexture> tex, const CB
|
|||
glUniformMatrix3fv(shader->proj, 1, GL_FALSE, glMatrix.getMatrix().data());
|
||||
#endif
|
||||
glUniform1i(shader->tex, 0);
|
||||
#ifndef GLES2
|
||||
if (!usingFinalShader && (texType == TEXTURE_RGBA || texType == TEXTURE_RGBX)) {
|
||||
const bool skipCM = *PPASS && m_RenderData.pMonitor->activeWorkspace && m_RenderData.pMonitor->activeWorkspace->m_bHasFullscreenWindow &&
|
||||
m_RenderData.pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN;
|
||||
glUniform1i(shader->texType, texType);
|
||||
glUniform1i(shader->skipCM, skipCM);
|
||||
if (!skipCM) {
|
||||
const auto imageDescription =
|
||||
m_RenderData.surface.valid() && m_RenderData.surface->colorManagement.valid() ? m_RenderData.surface->colorManagement->imageDescription() : SImageDescription{};
|
||||
glUniform1i(shader->sourceTF, imageDescription.transferFunction);
|
||||
glUniform1i(shader->targetTF, m_RenderData.pMonitor->imageDescription.transferFunction);
|
||||
const auto sourcePrimaries =
|
||||
imageDescription.primariesNameSet || imageDescription.primaries == SPCPRimaries{} ? getPrimaries(imageDescription.primariesNamed) : imageDescription.primaries;
|
||||
const auto targetPrimaries = m_RenderData.pMonitor->imageDescription.primariesNameSet || m_RenderData.pMonitor->imageDescription.primaries == SPCPRimaries{} ?
|
||||
getPrimaries(m_RenderData.pMonitor->imageDescription.primariesNamed) :
|
||||
m_RenderData.pMonitor->imageDescription.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);
|
||||
|
||||
const float maxLuminance = imageDescription.luminances.max > 0 ? imageDescription.luminances.max : imageDescription.luminances.reference;
|
||||
glUniform1f(shader->maxLuminance, maxLuminance * m_RenderData.pMonitor->imageDescription.luminances.reference / imageDescription.luminances.reference);
|
||||
glUniform1f(shader->dstMaxLuminance, m_RenderData.pMonitor->imageDescription.luminances.max > 0 ? m_RenderData.pMonitor->imageDescription.luminances.max : 10000);
|
||||
glUniform1f(shader->dstRefLuminance, m_RenderData.pMonitor->imageDescription.luminances.reference);
|
||||
glUniform1f(shader->sdrSaturation,
|
||||
m_RenderData.pMonitor->sdrSaturation > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
|
||||
m_RenderData.pMonitor->sdrSaturation :
|
||||
1.0f);
|
||||
glUniform1f(shader->sdrBrightness,
|
||||
m_RenderData.pMonitor->sdrBrightness > 0 && m_RenderData.pMonitor->imageDescription.transferFunction == NColorManagement::CM_TRANSFER_FUNCTION_ST2084_PQ ?
|
||||
m_RenderData.pMonitor->sdrBrightness :
|
||||
1.0f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if ((usingFinalShader && *PDT == 0) || CRASHING) {
|
||||
glUniform1f(shader->time, m_tGlobalTimer.getSeconds() - shader->initialTime);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <hyprutils/os/FileDescriptor.hpp>
|
||||
|
||||
#include "../debug/TracyDefines.hpp"
|
||||
#include "../protocols/core/Compositor.hpp"
|
||||
|
||||
struct gbm_device;
|
||||
class CHyprRenderer;
|
||||
|
|
@ -105,41 +106,42 @@ struct SMonitorRenderData {
|
|||
CShader m_shSHADOW;
|
||||
CShader m_shBORDER1;
|
||||
CShader m_shGLITCH;
|
||||
//
|
||||
CShader m_shCM;
|
||||
};
|
||||
|
||||
struct SCurrentRenderData {
|
||||
PHLMONITORREF pMonitor;
|
||||
Mat3x3 projection;
|
||||
Mat3x3 savedProjection;
|
||||
Mat3x3 monitorProjection;
|
||||
PHLMONITORREF pMonitor;
|
||||
Mat3x3 projection;
|
||||
Mat3x3 savedProjection;
|
||||
Mat3x3 monitorProjection;
|
||||
|
||||
SMonitorRenderData* pCurrentMonData = nullptr;
|
||||
CFramebuffer* currentFB = nullptr; // current rendering to
|
||||
CFramebuffer* mainFB = nullptr; // main to render to
|
||||
CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc)
|
||||
SMonitorRenderData* pCurrentMonData = nullptr;
|
||||
CFramebuffer* currentFB = nullptr; // current rendering to
|
||||
CFramebuffer* mainFB = nullptr; // main to render to
|
||||
CFramebuffer* outFB = nullptr; // out to render to (if offloaded, etc)
|
||||
|
||||
CRegion damage;
|
||||
CRegion finalDamage; // damage used for funal off -> main
|
||||
CRegion damage;
|
||||
CRegion finalDamage; // damage used for funal off -> main
|
||||
|
||||
SRenderModifData renderModif;
|
||||
float mouseZoomFactor = 1.f;
|
||||
bool mouseZoomUseMouse = true; // true by default
|
||||
bool useNearestNeighbor = false;
|
||||
bool blockScreenShader = false;
|
||||
bool simplePass = false;
|
||||
SRenderModifData renderModif;
|
||||
float mouseZoomFactor = 1.f;
|
||||
bool mouseZoomUseMouse = true; // true by default
|
||||
bool useNearestNeighbor = false;
|
||||
bool blockScreenShader = false;
|
||||
bool simplePass = false;
|
||||
|
||||
Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
|
||||
Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
|
||||
Vector2D primarySurfaceUVTopLeft = Vector2D(-1, -1);
|
||||
Vector2D primarySurfaceUVBottomRight = Vector2D(-1, -1);
|
||||
|
||||
CBox clipBox = {}; // scaled coordinates
|
||||
CRegion clipRegion;
|
||||
CBox clipBox = {}; // scaled coordinates
|
||||
CRegion clipRegion;
|
||||
|
||||
uint32_t discardMode = DISCARD_OPAQUE;
|
||||
float discardOpacity = 0.f;
|
||||
uint32_t discardMode = DISCARD_OPAQUE;
|
||||
float discardOpacity = 0.f;
|
||||
|
||||
PHLLSREF currentLS;
|
||||
PHLWINDOWREF currentWindow;
|
||||
PHLLSREF currentLS;
|
||||
PHLWINDOWREF currentWindow;
|
||||
WP<CWLSurfaceResource> surface;
|
||||
};
|
||||
|
||||
class CEGLSync {
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@
|
|||
#include "pass/RendererHintsPassElement.hpp"
|
||||
#include "pass/SurfacePassElement.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "protocols/ColorManagement.hpp"
|
||||
#include "../protocols/ColorManagement.hpp"
|
||||
#if AQUAMARINE_VERSION_NUMBER > 702 // >0.7.2
|
||||
#include "protocols/types/ContentType.hpp"
|
||||
#include "../protocols/types/ContentType.hpp"
|
||||
#endif
|
||||
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
|
|
@ -1409,78 +1409,40 @@ void CHyprRenderer::renderMonitor(PHLMONITOR pMonitor) {
|
|||
}
|
||||
}
|
||||
|
||||
static const auto BT709 = Aquamarine::IOutput::SChromaticityCoords{
|
||||
.red = Aquamarine::IOutput::xy{.x = 0.64, .y = 0.33},
|
||||
.green = Aquamarine::IOutput::xy{.x = 0.30, .y = 0.60},
|
||||
.blue = Aquamarine::IOutput::xy{.x = 0.15, .y = 0.06},
|
||||
.white = Aquamarine::IOutput::xy{.x = 0.3127, .y = 0.3290},
|
||||
};
|
||||
static const hdr_output_metadata NO_HDR_METADATA = {.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}};
|
||||
|
||||
static hdr_output_metadata createHDRMetadata(uint8_t eotf, Aquamarine::IOutput::SParsedEDID edid) {
|
||||
if (eotf == 0)
|
||||
return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR
|
||||
|
||||
const auto toNits = [](float value) { return uint16_t(std::round(value)); };
|
||||
const auto to16Bit = [](float value) { return uint16_t(std::round(value * 50000)); };
|
||||
const auto colorimetry = edid.chromaticityCoords.value_or(BT709);
|
||||
|
||||
Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x,
|
||||
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
|
||||
Debug::log(TRACE, "ColorManagement max avg {}, min {}, max {}", edid.hdrMetadata->desiredMaxFrameAverageLuminance, edid.hdrMetadata->desiredContentMinLuminance,
|
||||
edid.hdrMetadata->desiredContentMaxLuminance);
|
||||
return hdr_output_metadata{
|
||||
.metadata_type = 0,
|
||||
.hdmi_metadata_type1 =
|
||||
hdr_metadata_infoframe{
|
||||
.eotf = eotf,
|
||||
.metadata_type = 0,
|
||||
.display_primaries =
|
||||
{
|
||||
{.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)},
|
||||
{.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)},
|
||||
{.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)},
|
||||
},
|
||||
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
|
||||
.max_display_mastering_luminance = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
|
||||
.min_display_mastering_luminance = toNits(edid.hdrMetadata->desiredContentMinLuminance * 10000),
|
||||
.max_cll = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
|
||||
.max_fall = toNits(edid.hdrMetadata->desiredMaxFrameAverageLuminance),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
|
||||
static hdr_output_metadata createHDRMetadata(SImageDescription settings, Aquamarine::IOutput::SParsedEDID edid) {
|
||||
if (settings.transferFunction != CM_TRANSFER_FUNCTION_ST2084_PQ)
|
||||
return hdr_output_metadata{.hdmi_metadata_type1 = hdr_metadata_infoframe{.eotf = 0}}; // empty metadata for SDR
|
||||
return NO_HDR_METADATA; // empty metadata for SDR
|
||||
|
||||
const auto toNits = [](uint32_t value) { return uint16_t(std::round(value)); };
|
||||
const auto to16Bit = [](uint32_t value) { return uint16_t(std::round(value * 50000)); };
|
||||
|
||||
auto colorimetry = settings.primaries;
|
||||
auto colorimetry = settings.primariesNameSet || settings.primaries == SPCPRimaries{} ? getPrimaries(settings.primariesNamed) : settings.primaries;
|
||||
auto luminances = settings.masteringLuminances.max > 0 ?
|
||||
settings.masteringLuminances :
|
||||
SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance};
|
||||
settings.masteringLuminances :
|
||||
SImageDescription::SPCMasteringLuminances{.min = edid.hdrMetadata->desiredContentMinLuminance, .max = edid.hdrMetadata->desiredContentMaxLuminance};
|
||||
|
||||
Debug::log(TRACE, "ColorManagement primaries {},{} {},{} {},{} {},{}", colorimetry.red.x, colorimetry.red.y, colorimetry.green.x, colorimetry.green.y, colorimetry.blue.x,
|
||||
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
|
||||
colorimetry.blue.y, colorimetry.white.x, colorimetry.white.y);
|
||||
Debug::log(TRACE, "ColorManagement min {}, max {}, cll {}, fall {}", luminances.min, luminances.max, settings.maxCLL, settings.maxFALL);
|
||||
return hdr_output_metadata{
|
||||
.metadata_type = 0,
|
||||
.hdmi_metadata_type1 =
|
||||
.metadata_type = 0,
|
||||
.hdmi_metadata_type1 =
|
||||
hdr_metadata_infoframe{
|
||||
.eotf = 2,
|
||||
.metadata_type = 0,
|
||||
.display_primaries =
|
||||
{
|
||||
.eotf = 2,
|
||||
.metadata_type = 0,
|
||||
.display_primaries =
|
||||
{
|
||||
{.x = to16Bit(colorimetry.red.x), .y = to16Bit(colorimetry.red.y)},
|
||||
{.x = to16Bit(colorimetry.green.x), .y = to16Bit(colorimetry.green.y)},
|
||||
{.x = to16Bit(colorimetry.blue.x), .y = to16Bit(colorimetry.blue.y)},
|
||||
},
|
||||
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
|
||||
.max_display_mastering_luminance = toNits(luminances.max),
|
||||
.min_display_mastering_luminance = toNits(luminances.min * 10000),
|
||||
.max_cll = toNits(settings.maxCLL),
|
||||
.max_fall = toNits(settings.maxFALL),
|
||||
.white_point = {.x = to16Bit(colorimetry.white.x), .y = to16Bit(colorimetry.white.y)},
|
||||
.max_display_mastering_luminance = toNits(luminances.max),
|
||||
.min_display_mastering_luminance = toNits(luminances.min * 10000),
|
||||
.max_cll = toNits(settings.maxCLL),
|
||||
.max_fall = toNits(settings.maxFALL),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -1495,12 +1457,14 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
|
|||
if (inFD.isValid())
|
||||
pMonitor->output->state->setExplicitInFence(inFD.get());
|
||||
|
||||
static auto PHDR = CConfigValue<Hyprlang::INT>("experimental:hdr");
|
||||
static auto PPASS = CConfigValue<Hyprlang::INT>("render:cm_fs_passthrough");
|
||||
const bool PHDR = pMonitor->imageDescription.transferFunction == CM_TRANSFER_FUNCTION_ST2084_PQ;
|
||||
|
||||
const bool SUPPORTSPQ = pMonitor->output->parsedEDID.hdrMetadata.has_value() ? pMonitor->output->parsedEDID.hdrMetadata->supportsPQ : false;
|
||||
Debug::log(TRACE, "ColorManagement supportsBT2020 {}, supportsPQ {}", pMonitor->output->parsedEDID.supportsBT2020, SUPPORTSPQ);
|
||||
|
||||
if (pMonitor->output->parsedEDID.supportsBT2020 && SUPPORTSPQ) {
|
||||
if (pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
|
||||
if (*PPASS && pMonitor->activeWorkspace && pMonitor->activeWorkspace->m_bHasFullscreenWindow && pMonitor->activeWorkspace->m_efFullscreenMode == FSMODE_FULLSCREEN) {
|
||||
const auto WINDOW = pMonitor->activeWorkspace->getFullscreenWindow();
|
||||
const auto ROOT_SURF = WINDOW->m_pWLSurface->resource();
|
||||
const auto SURF =
|
||||
|
|
@ -1512,18 +1476,17 @@ bool CHyprRenderer::commitPendingAndDoExplicitSync(PHLMONITOR pMonitor) {
|
|||
SURF->colorManagement->setHDRMetadata(createHDRMetadata(SURF->colorManagement->imageDescription(), pMonitor->output->parsedEDID));
|
||||
if (needsHdrMetadataUpdate)
|
||||
pMonitor->output->state->setHDRMetadata(SURF->colorManagement->hdrMetadata());
|
||||
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != *PHDR)
|
||||
pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID));
|
||||
} else if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
|
||||
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
|
||||
pMonitor->m_previousFSWindow = WINDOW;
|
||||
} else {
|
||||
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != *PHDR)
|
||||
pMonitor->output->state->setHDRMetadata(*PHDR ? createHDRMetadata(2, pMonitor->output->parsedEDID) : createHDRMetadata(0, pMonitor->output->parsedEDID));
|
||||
if ((pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2) != PHDR)
|
||||
pMonitor->output->state->setHDRMetadata(PHDR ? createHDRMetadata(pMonitor->imageDescription, pMonitor->output->parsedEDID) : NO_HDR_METADATA);
|
||||
pMonitor->m_previousFSWindow.reset();
|
||||
}
|
||||
}
|
||||
|
||||
static auto PWIDE = CConfigValue<Hyprlang::INT>("experimental:wide_color_gamut");
|
||||
const bool needsWCG = *PWIDE || pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2;
|
||||
const bool needsWCG = pMonitor->output->state->state().hdrMetadata.hdmi_metadata_type1.eotf == 2 || pMonitor->imageDescription.primariesNamed == CM_PRIMARIES_BT2020;
|
||||
if (pMonitor->output->state->state().wideColorGamut != needsWCG) {
|
||||
Debug::log(TRACE, "Setting wide color gamut {}", needsWCG ? "on" : "off");
|
||||
pMonitor->output->state->setWideColorGamut(needsWCG);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,17 @@ class CShader {
|
|||
GLint proj = -1;
|
||||
GLint color = -1;
|
||||
GLint alphaMatte = -1;
|
||||
GLint texType = -1;
|
||||
GLint skipCM = -1;
|
||||
GLint sourceTF = -1;
|
||||
GLint targetTF = -1;
|
||||
GLint sourcePrimaries = -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 tex = -1;
|
||||
GLint alpha = -1;
|
||||
GLint posAttrib = -1;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ CSurfacePassElement::CSurfacePassElement(const CSurfacePassElement::SRenderData&
|
|||
|
||||
void CSurfacePassElement::draw(const CRegion& damage) {
|
||||
g_pHyprOpenGL->m_RenderData.currentWindow = data.pWindow;
|
||||
g_pHyprOpenGL->m_RenderData.surface = data.surface;
|
||||
g_pHyprOpenGL->m_RenderData.currentLS = data.pLS;
|
||||
g_pHyprOpenGL->m_RenderData.clipBox = data.clipBox;
|
||||
g_pHyprOpenGL->m_RenderData.discardMode = data.discardMode;
|
||||
|
|
@ -36,6 +37,7 @@ void CSurfacePassElement::draw(const CRegion& damage) {
|
|||
g_pHyprOpenGL->m_RenderData.useNearestNeighbor = false;
|
||||
g_pHyprOpenGL->m_bEndFrame = false;
|
||||
g_pHyprOpenGL->m_RenderData.currentWindow.reset();
|
||||
g_pHyprOpenGL->m_RenderData.surface.reset();
|
||||
g_pHyprOpenGL->m_RenderData.currentLS.reset();
|
||||
}};
|
||||
|
||||
|
|
|
|||
439
src/render/shaders/CM.frag
Normal file
439
src/render/shaders/CM.frag
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
R"#(
|
||||
#version 320 es
|
||||
//#extension GL_OES_EGL_image_external : require
|
||||
|
||||
precision highp float;
|
||||
in vec2 v_texcoord;
|
||||
uniform sampler2D tex;
|
||||
//uniform samplerExternalOES texture0;
|
||||
|
||||
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 maxLuminance;
|
||||
uniform float dstMaxLuminance;
|
||||
uniform float dstRefLuminance;
|
||||
uniform float sdrSaturation;
|
||||
uniform float sdrBrightnessMultiplier;
|
||||
|
||||
uniform float alpha;
|
||||
|
||||
uniform vec2 topLeft;
|
||||
uniform vec2 fullSize;
|
||||
uniform float radius;
|
||||
uniform float roundingPower;
|
||||
|
||||
uniform int discardOpaque;
|
||||
uniform int discardAlpha;
|
||||
uniform float discardAlphaValue;
|
||||
|
||||
uniform int applyTint;
|
||||
uniform vec3 tint;
|
||||
|
||||
//enum eTransferFunction
|
||||
#define CM_TRANSFER_FUNCTION_BT1886 1
|
||||
#define CM_TRANSFER_FUNCTION_GAMMA22 2
|
||||
#define CM_TRANSFER_FUNCTION_GAMMA28 3
|
||||
#define CM_TRANSFER_FUNCTION_ST240 4
|
||||
#define CM_TRANSFER_FUNCTION_EXT_LINEAR 5
|
||||
#define CM_TRANSFER_FUNCTION_LOG_100 6
|
||||
#define CM_TRANSFER_FUNCTION_LOG_316 7
|
||||
#define CM_TRANSFER_FUNCTION_XVYCC 8
|
||||
#define CM_TRANSFER_FUNCTION_SRGB 9
|
||||
#define CM_TRANSFER_FUNCTION_EXT_SRGB 10
|
||||
#define CM_TRANSFER_FUNCTION_ST2084_PQ 11
|
||||
#define CM_TRANSFER_FUNCTION_ST428 12
|
||||
#define CM_TRANSFER_FUNCTION_HLG 13
|
||||
|
||||
// sRGB constants
|
||||
#define SRGB_POW 2.4
|
||||
#define SRGB_INV_POW (1.0 / SRGB_POW)
|
||||
#define SRGB_D_CUT 0.04045
|
||||
#define SRGB_E_CUT 0.0031308
|
||||
#define SRGB_LO 12.92
|
||||
#define SRGB_HI 1.055
|
||||
#define SRGB_HI_ADD 0.055
|
||||
|
||||
// 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 (sqrt(3.0) * pow(HLG_D_CUT, 0.5))
|
||||
#define HLG_A 0.17883277
|
||||
#define HLG_B 0.28466892
|
||||
#define HLG_C 0.55991073
|
||||
|
||||
#define SDR_MIN_LUMINANCE 0.2
|
||||
#define SDR_MAX_LUMINANCE 80.0
|
||||
#define HDR_MIN_LUMINANCE 0.005
|
||||
#define HDR_MAX_LUMINANCE 10000.0
|
||||
#define HLG_MAX_LUMINANCE 1000.0
|
||||
|
||||
// smoothing constant for the edge: more = blurrier, but smoother
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#define M_E 2.718281828459045
|
||||
#define SMOOTHING_CONSTANT (M_PI / 5.34665792551)
|
||||
|
||||
vec4 rounding(vec4 color) {
|
||||
highp vec2 pixCoord = vec2(gl_FragCoord);
|
||||
pixCoord -= topLeft + fullSize * 0.5;
|
||||
pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0;
|
||||
pixCoord -= fullSize * 0.5 - radius;
|
||||
pixCoord += vec2(1.0, 1.0) / fullSize; // center the pix dont make it top-left
|
||||
|
||||
if (pixCoord.x + pixCoord.y > radius) {
|
||||
float dist = pow(pow(pixCoord.x, roundingPower) + pow(pixCoord.y, roundingPower), 1.0/roundingPower);
|
||||
|
||||
if (dist > radius + SMOOTHING_CONSTANT)
|
||||
discard;
|
||||
|
||||
float normalized = 1.0 - smoothstep(0.0, 1.0, (dist - radius + SMOOTHING_CONSTANT) / (SMOOTHING_CONSTANT * 2.0));
|
||||
|
||||
color *= normalized;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 xy2xyz(vec2 xy) {
|
||||
if (xy.y == 0.0)
|
||||
return vec3(0.0, 0.0, 0.0);
|
||||
|
||||
return vec3(xy.x / xy.y, 1.0, (1.0 - xy.x - xy.y) / xy.y);
|
||||
}
|
||||
|
||||
vec4 saturate(vec4 color, mat3 primaries, float saturation) {
|
||||
if (saturation == 1.0)
|
||||
return color;
|
||||
vec3 brightness = vec3(primaries[1][0], primaries[1][1], primaries[1][2]);
|
||||
float Y = dot(color.rgb, brightness);
|
||||
return vec4(mix(vec3(Y), color.rgb, saturation), color[3]);
|
||||
}
|
||||
|
||||
vec3 toLinearRGB(vec3 color, int tf) {
|
||||
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||
return color;
|
||||
|
||||
bvec3 isLow;
|
||||
vec3 lo;
|
||||
vec3 hi;
|
||||
switch (tf) {
|
||||
case CM_TRANSFER_FUNCTION_ST2084_PQ:
|
||||
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_INV_M2));
|
||||
return pow(
|
||||
(max(E - PQ_C1, vec3(0.0))) / (PQ_C2 - PQ_C3 * E),
|
||||
vec3(PQ_INV_M1)
|
||||
);
|
||||
case CM_TRANSFER_FUNCTION_GAMMA22:
|
||||
return pow(max(color.rgb, vec3(0.0)), vec3(2.2));
|
||||
case CM_TRANSFER_FUNCTION_GAMMA28:
|
||||
return pow(max(color.rgb, vec3(0.0)), vec3(2.8));
|
||||
case CM_TRANSFER_FUNCTION_HLG:
|
||||
isLow = lessThanEqual(color.rgb, vec3(HLG_D_CUT));
|
||||
lo = sqrt(3.0) * pow(color.rgb, vec3(0.5));
|
||||
hi = HLG_A * log(12.0 * color.rgb - HLG_B) + HLG_C;
|
||||
return mix(hi, lo, isLow);
|
||||
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:
|
||||
isLow = lessThanEqual(color.rgb, vec3(SRGB_D_CUT));
|
||||
lo = color.rgb / SRGB_LO;
|
||||
hi = pow((color.rgb + SRGB_HI_ADD) / SRGB_HI, vec3(SRGB_POW));
|
||||
return mix(hi, lo, isLow);
|
||||
}
|
||||
}
|
||||
|
||||
vec4 toLinear(vec4 color, int tf) {
|
||||
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||
return color;
|
||||
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = toLinearRGB(color.rgb, tf);
|
||||
color.rgb *= color.a;
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 toNit(vec4 color, int tf) {
|
||||
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||
color.rgb = color.rgb * SDR_MAX_LUMINANCE;
|
||||
else {
|
||||
|
||||
switch (tf) {
|
||||
case CM_TRANSFER_FUNCTION_ST2084_PQ:
|
||||
color.rgb = color.rgb * (HDR_MAX_LUMINANCE - HDR_MIN_LUMINANCE) + HDR_MIN_LUMINANCE; break;
|
||||
case CM_TRANSFER_FUNCTION_HLG:
|
||||
color.rgb = color.rgb * (HLG_MAX_LUMINANCE - HDR_MIN_LUMINANCE) + HDR_MIN_LUMINANCE; break;
|
||||
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:
|
||||
color.rgb = color.rgb * (SDR_MAX_LUMINANCE - SDR_MIN_LUMINANCE) + SDR_MIN_LUMINANCE; break;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 fromLinearRGB(vec3 color, int tf) {
|
||||
bvec3 isLow;
|
||||
vec3 lo;
|
||||
vec3 hi;
|
||||
|
||||
switch (tf) {
|
||||
case CM_TRANSFER_FUNCTION_EXT_LINEAR:
|
||||
return color;
|
||||
case CM_TRANSFER_FUNCTION_ST2084_PQ:
|
||||
vec3 E = pow(clamp(color.rgb, vec3(0.0), vec3(1.0)), vec3(PQ_M1));
|
||||
return pow(
|
||||
(vec3(PQ_C1) + PQ_C2 * E) / (vec3(1.0) + PQ_C3 * E),
|
||||
vec3(PQ_M2)
|
||||
);
|
||||
break;
|
||||
case CM_TRANSFER_FUNCTION_GAMMA22:
|
||||
return pow(max(color.rgb, vec3(0.0)), vec3(1.0 / 2.2));
|
||||
case CM_TRANSFER_FUNCTION_GAMMA28:
|
||||
return pow(max(color.rgb, vec3(0.0)), vec3(1.0 / 2.8));
|
||||
case CM_TRANSFER_FUNCTION_HLG:
|
||||
isLow = lessThanEqual(color.rgb, vec3(HLG_E_CUT));
|
||||
lo = pow(color.rgb / sqrt(3.0), vec3(2.0));
|
||||
hi = (pow(vec3(M_E), (color.rgb - HLG_C) / HLG_A) + HLG_B) / 12.0;
|
||||
return mix(hi, lo, isLow);
|
||||
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:
|
||||
isLow = lessThanEqual(color.rgb, vec3(SRGB_E_CUT));
|
||||
lo = color.rgb * SRGB_LO;
|
||||
hi = pow(color.rgb, vec3(SRGB_INV_POW)) * SRGB_HI - SRGB_HI_ADD;
|
||||
return mix(hi, lo, isLow);
|
||||
}
|
||||
}
|
||||
|
||||
vec4 fromLinear(vec4 color, int tf) {
|
||||
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||
return color;
|
||||
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
color.rgb = fromLinearRGB(color.rgb, tf);
|
||||
color.rgb *= color.a;
|
||||
return color;
|
||||
}
|
||||
|
||||
vec4 fromLinearNit(vec4 color, int tf) {
|
||||
if (tf == CM_TRANSFER_FUNCTION_EXT_LINEAR)
|
||||
color.rgb = color.rgb / SDR_MAX_LUMINANCE;
|
||||
else {
|
||||
color.rgb /= max(color.a, 0.001);
|
||||
|
||||
switch (tf) {
|
||||
case CM_TRANSFER_FUNCTION_ST2084_PQ:
|
||||
color.rgb = (color.rgb - HDR_MIN_LUMINANCE) / (HDR_MAX_LUMINANCE - HDR_MIN_LUMINANCE); break;
|
||||
case CM_TRANSFER_FUNCTION_HLG:
|
||||
color.rgb = (color.rgb - HDR_MIN_LUMINANCE) / (HLG_MAX_LUMINANCE - HDR_MIN_LUMINANCE); break;
|
||||
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:
|
||||
color.rgb = (color.rgb - SDR_MIN_LUMINANCE) / (SDR_MAX_LUMINANCE - SDR_MIN_LUMINANCE); break;
|
||||
}
|
||||
|
||||
color.rgb = fromLinearRGB(color.rgb, tf);
|
||||
|
||||
color.rgb *= color.a;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
mat3 primaries2xyz(mat4x2 primaries) {
|
||||
vec3 r = xy2xyz(primaries[0]);
|
||||
vec3 g = xy2xyz(primaries[1]);
|
||||
vec3 b = xy2xyz(primaries[2]);
|
||||
vec3 w = xy2xyz(primaries[3]);
|
||||
|
||||
mat3 invMat = inverse(
|
||||
mat3(
|
||||
r.x, r.y, r.z,
|
||||
g.x, g.y, g.z,
|
||||
b.x, b.y, b.z
|
||||
)
|
||||
);
|
||||
|
||||
vec3 s = invMat * w;
|
||||
|
||||
return mat3(
|
||||
s.r * r.x, s.r * r.y, s.r * r.z,
|
||||
s.g * g.x, s.g * g.y, s.g * g.z,
|
||||
s.b * b.x, s.b * b.y, s.b * b.z
|
||||
);
|
||||
}
|
||||
|
||||
// const vec2 D65 = vec2(0.3127, 0.3290);
|
||||
const mat3 Bradford = mat3(
|
||||
0.8951, 0.2664, -0.1614,
|
||||
-0.7502, 1.7135, 0.0367,
|
||||
0.0389, -0.0685, 1.0296
|
||||
);
|
||||
const mat3 BradfordInv = inverse(Bradford);
|
||||
|
||||
mat3 adaptWhite(vec2 src, vec2 dst) {
|
||||
if (src == dst)
|
||||
return mat3(
|
||||
1.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 1.0
|
||||
);
|
||||
|
||||
vec3 srcXYZ = xy2xyz(src);
|
||||
vec3 dstXYZ = xy2xyz(dst);
|
||||
vec3 factors = (Bradford * dstXYZ) / (Bradford * srcXYZ);
|
||||
|
||||
return BradfordInv * mat3(
|
||||
factors.x, 0.0, 0.0,
|
||||
0.0, factors.y, 0.0,
|
||||
0.0, 0.0, factors.z
|
||||
) * Bradford;
|
||||
}
|
||||
|
||||
vec4 convertPrimaries(vec4 color, mat3 src, vec2 srcWhite, mat3 dst, vec2 dstWhite) {
|
||||
mat3 convMat = inverse(dst) * adaptWhite(srcWhite, dstWhite) * src;
|
||||
return vec4(convMat * color.rgb, color[3]);
|
||||
}
|
||||
|
||||
const mat3 BT2020toLMS = mat3(
|
||||
0.3592, 0.6976, -0.0358,
|
||||
-0.1922, 1.1004, 0.0755,
|
||||
0.0070, 0.0749, 0.8434
|
||||
);
|
||||
const mat3 LMStoBT2020 = inverse(BT2020toLMS);
|
||||
|
||||
const mat3 ICtCpPQ = transpose(mat3(
|
||||
2048.0, 2048.0, 0.0,
|
||||
6610.0, -13613.0, 7003.0,
|
||||
17933.0, -17390.0, -543.0
|
||||
) / 4096.0);
|
||||
const mat3 ICtCpPQInv = inverse(ICtCpPQ);
|
||||
|
||||
const mat3 ICtCpHLG = transpose(mat3(
|
||||
2048.0, 2048.0, 0.0,
|
||||
3625.0, -7465.0, 3840.0,
|
||||
9500.0, -9212.0, -288.0
|
||||
) / 4096.0);
|
||||
const mat3 ICtCpHLGInv = inverse(ICtCpHLG);
|
||||
|
||||
vec4 tonemap(vec4 color, mat3 dstXYZ) {
|
||||
if (maxLuminance < dstMaxLuminance * 1.01)
|
||||
return vec4(clamp(color.rgb, vec3(0.0), vec3(dstMaxLuminance)), color[3]);
|
||||
|
||||
mat3 toLMS = BT2020toLMS * dstXYZ;
|
||||
mat3 fromLMS = inverse(dstXYZ) * LMStoBT2020;
|
||||
|
||||
vec3 lms = fromLinear(vec4((toLMS * color.rgb) / HDR_MAX_LUMINANCE, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb;
|
||||
vec3 ICtCp = ICtCpPQ * lms;
|
||||
|
||||
float E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_INV_M2);
|
||||
float luminance = pow(
|
||||
(max(E - PQ_C1, 0.0)) / (PQ_C2 - PQ_C3 * E),
|
||||
PQ_INV_M1
|
||||
) * HDR_MAX_LUMINANCE;
|
||||
|
||||
float srcScale = maxLuminance / dstRefLuminance;
|
||||
float dstScale = dstMaxLuminance / dstRefLuminance;
|
||||
|
||||
float minScale = min(srcScale, 1.5);
|
||||
float dimming = 1.0 / clamp(minScale / dstScale, 1.0, minScale);
|
||||
float refLuminance = dstRefLuminance * dimming;
|
||||
|
||||
float low = min(luminance * dimming, refLuminance);
|
||||
float highlight = clamp((luminance / dstRefLuminance - 1.0) / (srcScale - 1.0), 0.0, 1.0);
|
||||
float high = log(highlight * (M_E - 1.0) + 1.0) * (dstMaxLuminance - refLuminance);
|
||||
luminance = low + high;
|
||||
|
||||
E = pow(clamp(ICtCp[0], 0.0, 1.0), PQ_M1);
|
||||
ICtCp[0] = pow(
|
||||
(PQ_C1 + PQ_C2 * E) / (1.0 + PQ_C3 * E),
|
||||
PQ_M2
|
||||
) / HDR_MAX_LUMINANCE;
|
||||
return vec4(fromLMS * toLinear(vec4(ICtCpPQInv * ICtCp, 1.0), CM_TRANSFER_FUNCTION_ST2084_PQ).rgb * HDR_MAX_LUMINANCE, color[3]);
|
||||
}
|
||||
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
void main() {
|
||||
vec4 pixColor;
|
||||
if (texType == 1)
|
||||
pixColor = vec4(texture(tex, v_texcoord).rgb, 1.0);
|
||||
// else if (texType == 2)
|
||||
// pixColor = texture(texture0, v_texcoord);
|
||||
else // assume rgba
|
||||
pixColor = texture(tex, v_texcoord);
|
||||
|
||||
if (discardOpaque == 1 && pixColor[3] * alpha == 1.0)
|
||||
discard;
|
||||
|
||||
if (discardAlpha == 1 && pixColor[3] <= discardAlphaValue)
|
||||
discard;
|
||||
|
||||
if (skipCM == 0) {
|
||||
pixColor.rgb /= max(pixColor.a, 0.001);
|
||||
pixColor.rgb = toLinearRGB(pixColor.rgb, sourceTF);
|
||||
mat3 srcxyz = primaries2xyz(sourcePrimaries);
|
||||
mat3 dstxyz;
|
||||
if (sourcePrimaries == targetPrimaries)
|
||||
dstxyz = srcxyz;
|
||||
else {
|
||||
dstxyz = primaries2xyz(targetPrimaries);
|
||||
pixColor = convertPrimaries(pixColor, srcxyz, sourcePrimaries[3], dstxyz, targetPrimaries[3]);
|
||||
}
|
||||
pixColor = toNit(pixColor, sourceTF);
|
||||
pixColor.rgb *= pixColor.a;
|
||||
pixColor = tonemap(pixColor, dstxyz);
|
||||
if (sourceTF == CM_TRANSFER_FUNCTION_SRGB && targetTF == CM_TRANSFER_FUNCTION_ST2084_PQ)
|
||||
pixColor = saturate(pixColor, srcxyz, sdrSaturation);
|
||||
pixColor *= sdrBrightnessMultiplier;
|
||||
pixColor = fromLinearNit(pixColor, targetTF);
|
||||
}
|
||||
|
||||
if (applyTint == 1)
|
||||
pixColor = vec4(pixColor.rgb * tint.rgb, pixColor[3]);
|
||||
|
||||
if (radius > 0.0)
|
||||
pixColor = rounding(pixColor);
|
||||
|
||||
fragColor = pixColor * alpha;
|
||||
}
|
||||
)#"
|
||||
|
|
@ -94,6 +94,10 @@ void main() {
|
|||
v_texcoord = texcoord;
|
||||
})#";
|
||||
|
||||
inline const std::string TEXFRAGSRCCM =
|
||||
#include "CM.frag"
|
||||
;
|
||||
|
||||
inline const std::string TEXFRAGSRCRGBA = R"#(
|
||||
precision highp float;
|
||||
varying vec2 v_texcoord; // is in 0-1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue