Implement initial support for color management and HDR
This is done by converting from the sRGB + gamma 2.2 input from clients to linear with the color space of the output (BT.709 or BT2020 atm) in a shadow buffer, and then convert from the shadow buffer to the transfer function the output needs (sRGB or PQ).
This commit is contained in:
parent
644e31f389
commit
afc5567651
42 changed files with 787 additions and 123 deletions
|
@ -10,6 +10,7 @@
|
|||
#include "drm_buffer.h"
|
||||
#include "drm_egl_backend.h"
|
||||
#include "drm_gpu.h"
|
||||
#include "drm_output.h"
|
||||
#include "drm_pipeline.h"
|
||||
|
||||
#include <gbm.h>
|
||||
|
@ -45,7 +46,7 @@ EglGbmCursorLayer::EglGbmCursorLayer(EglGbmBackend *eglBackend, DrmPipeline *pip
|
|||
|
||||
std::optional<OutputLayerBeginFrameInfo> EglGbmCursorLayer::beginFrame()
|
||||
{
|
||||
return m_surface.startRendering(m_pipeline->gpu()->cursorSize(), drmToTextureRotation(m_pipeline) | TextureTransform::MirrorY, m_pipeline->cursorFormats());
|
||||
return m_surface.startRendering(m_pipeline->gpu()->cursorSize(), drmToTextureRotation(m_pipeline) | TextureTransform::MirrorY, m_pipeline->cursorFormats(), Colorspace(m_pipeline->colorimetry(), m_pipeline->transferFunction()), m_pipeline->output()->sdrBrightness(), m_pipeline->output()->channelFactors());
|
||||
}
|
||||
|
||||
bool EglGbmCursorLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
|
||||
|
|
|
@ -60,7 +60,7 @@ std::optional<OutputLayerBeginFrameInfo> EglGbmLayer::beginFrame()
|
|||
m_scanoutBuffer.reset();
|
||||
m_dmabufFeedback.renderingSurface();
|
||||
|
||||
return m_surface.startRendering(m_pipeline->mode()->size(), drmToTextureRotation(m_pipeline) | TextureTransform::MirrorY, m_pipeline->formats());
|
||||
return m_surface.startRendering(m_pipeline->mode()->size(), drmToTextureRotation(m_pipeline) | TextureTransform::MirrorY, m_pipeline->formats(), Colorspace(m_pipeline->colorimetry(), m_pipeline->transferFunction()), m_pipeline->output()->sdrBrightness(), m_pipeline->output()->channelFactors());
|
||||
}
|
||||
|
||||
bool EglGbmLayer::endFrame(const QRegion &renderedRegion, const QRegion &damagedRegion)
|
||||
|
@ -100,6 +100,10 @@ bool EglGbmLayer::scanout(SurfaceItem *surfaceItem)
|
|||
if (directScanoutDisabled) {
|
||||
return false;
|
||||
}
|
||||
// TODO use GAMMA_LUT, CTM and DEGAMMA_LUT to allow direct scanout with HDR
|
||||
if (m_pipeline->colorimetry() != NamedColorimetry::BT709 || m_pipeline->transferFunction() != NamedTransferFunction::sRGB) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SurfaceItemWayland *item = qobject_cast<SurfaceItemWayland *>(surfaceItem);
|
||||
if (!item || !item->surface()) {
|
||||
|
|
|
@ -60,7 +60,7 @@ void EglGbmLayerSurface::destroyResources()
|
|||
m_oldSurface = {};
|
||||
}
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(const QSize &bufferSize, TextureTransforms transformation, const QMap<uint32_t, QVector<uint64_t>> &formats)
|
||||
std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(const QSize &bufferSize, TextureTransforms transformation, const QMap<uint32_t, QVector<uint64_t>> &formats, const Colorspace &colorspace, uint32_t sdrBrightness, const QVector3D &channelFactors)
|
||||
{
|
||||
if (!checkSurface(bufferSize, formats)) {
|
||||
return std::nullopt;
|
||||
|
@ -69,7 +69,7 @@ std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(cons
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto [buffer, repaint] = m_surface.gbmSwapchain->acquire();
|
||||
auto [buffer, repaint] = m_surface.gbmSwapchain->acquire();
|
||||
if (!buffer) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -90,14 +90,49 @@ std::optional<OutputLayerBeginFrameInfo> EglGbmLayerSurface::startRendering(cons
|
|||
}
|
||||
m_surface.currentBuffer = buffer;
|
||||
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(fbo.get()),
|
||||
.repaint = repaint,
|
||||
};
|
||||
if (m_surface.colorspace != colorspace || m_surface.channelFactors != channelFactors || m_surface.sdrBrightness != sdrBrightness) {
|
||||
m_surface.gbmSwapchain->resetDamage();
|
||||
repaint = infiniteRegion();
|
||||
m_surface.colorspace = colorspace;
|
||||
m_surface.channelFactors = channelFactors;
|
||||
m_surface.sdrBrightness = sdrBrightness;
|
||||
}
|
||||
if (colorspace != Colorspace::sRGB) {
|
||||
if (!m_surface.shadowBuffer) {
|
||||
m_surface.shadowTexture = std::make_shared<GLTexture>(GL_RGBA16F, m_surface.gbmSwapchain->size());
|
||||
m_surface.shadowBuffer = std::make_shared<GLFramebuffer>(m_surface.shadowTexture.get());
|
||||
}
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(m_surface.shadowBuffer.get(), Colorspace(colorspace.colorimetry(), NamedTransferFunction::linear), sdrBrightness),
|
||||
.repaint = repaint,
|
||||
};
|
||||
} else {
|
||||
return OutputLayerBeginFrameInfo{
|
||||
.renderTarget = RenderTarget(fbo.get(), colorspace),
|
||||
.repaint = repaint,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool EglGbmLayerSurface::endRendering(const QRegion &damagedRegion)
|
||||
{
|
||||
if (m_surface.colorspace != Colorspace::sRGB) {
|
||||
const auto &[texture, fbo] = m_surface.textureCache[m_surface.currentBuffer->bo()];
|
||||
GLFramebuffer::pushFramebuffer(fbo.get());
|
||||
ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
|
||||
QMatrix4x4 mat = texture->contentTransformMatrix();
|
||||
mat.ortho(QRectF(QPointF(), fbo->size()));
|
||||
binder.shader()->setUniform(GLShader::MatrixUniform::ModelViewProjectionMatrix, mat);
|
||||
QMatrix3x3 ctm;
|
||||
ctm(0, 0) = m_surface.channelFactors.x();
|
||||
ctm(1, 1) = m_surface.channelFactors.y();
|
||||
ctm(2, 2) = m_surface.channelFactors.z();
|
||||
binder.shader()->setUniform(GLShader::MatrixUniform::ColorimetryTransformation, ctm);
|
||||
binder.shader()->setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(NamedTransferFunction::linear));
|
||||
binder.shader()->setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(m_surface.colorspace.transferFunction()));
|
||||
m_surface.shadowTexture->render(m_surface.gbmSwapchain->size(), 1);
|
||||
GLFramebuffer::popFramebuffer();
|
||||
}
|
||||
m_surface.gbmSwapchain->damage(damagedRegion);
|
||||
glFlush();
|
||||
const auto buffer = importBuffer(m_surface, m_surface.currentBuffer);
|
||||
|
|
|
@ -54,7 +54,7 @@ public:
|
|||
EglGbmLayerSurface(DrmGpu *gpu, EglGbmBackend *eglBackend, BufferTarget target = BufferTarget::Normal, FormatOption formatOption = FormatOption::PreferAlpha);
|
||||
~EglGbmLayerSurface();
|
||||
|
||||
std::optional<OutputLayerBeginFrameInfo> startRendering(const QSize &bufferSize, TextureTransforms transformation, const QMap<uint32_t, QVector<uint64_t>> &formats);
|
||||
std::optional<OutputLayerBeginFrameInfo> startRendering(const QSize &bufferSize, TextureTransforms transformation, const QMap<uint32_t, QVector<uint64_t>> &formats, const Colorspace &colorspace, uint32_t sdrBrightness, const QVector3D &channelFactors);
|
||||
bool endRendering(const QRegion &damagedRegion);
|
||||
|
||||
bool doesSurfaceFit(const QSize &size, const QMap<uint32_t, QVector<uint64_t>> &formats) const;
|
||||
|
@ -74,6 +74,11 @@ private:
|
|||
};
|
||||
struct Surface
|
||||
{
|
||||
std::shared_ptr<GLTexture> shadowTexture;
|
||||
std::shared_ptr<GLFramebuffer> shadowBuffer;
|
||||
Colorspace colorspace = Colorspace::sRGB;
|
||||
QVector3D channelFactors = {1, 1, 1};
|
||||
float sdrBrightness = 200;
|
||||
std::shared_ptr<GbmSwapchain> gbmSwapchain;
|
||||
std::shared_ptr<DumbSwapchain> importDumbSwapchain;
|
||||
std::shared_ptr<GbmSwapchain> importGbmSwapchain;
|
||||
|
|
|
@ -72,6 +72,11 @@ void GbmSwapchain::damage(const QRegion &damage)
|
|||
m_damageJournal.add(damage);
|
||||
}
|
||||
|
||||
void GbmSwapchain::resetDamage()
|
||||
{
|
||||
m_damageJournal.clear();
|
||||
}
|
||||
|
||||
void GbmSwapchain::releaseBuffer(GbmBuffer *buffer)
|
||||
{
|
||||
if (m_buffers.size() < 3) {
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
|
||||
std::pair<std::shared_ptr<GbmBuffer>, QRegion> acquire();
|
||||
void damage(const QRegion &damage);
|
||||
void resetDamage();
|
||||
void releaseBuffer(GbmBuffer *buffer);
|
||||
|
||||
DrmGpu *gpu() const;
|
||||
|
|
|
@ -67,6 +67,13 @@ DrmOutput::DrmOutput(const std::shared_ptr<DrmConnector> &conn)
|
|||
capabilities |= Capability::RgbRange;
|
||||
initialState.rgbRange = DrmConnector::broadcastRgbToRgbRange(conn->broadcastRGB.enumValue());
|
||||
}
|
||||
if (m_connector->hdrMetadata.isValid() && m_connector->edid() && m_connector->edid()->hdrMetadata() && m_connector->edid()->hdrMetadata()->supportsPQ) {
|
||||
capabilities |= Capability::HighDynamicRange;
|
||||
}
|
||||
if (m_connector->colorspace.isValid() && m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_RGB)
|
||||
&& m_connector->edid() && m_connector->edid()->hdrMetadata() && m_connector->edid()->hdrMetadata()->supportsBT2020) {
|
||||
capabilities |= Capability::WideColorGamut;
|
||||
}
|
||||
|
||||
const Edid *edid = conn->edid();
|
||||
|
||||
|
@ -409,6 +416,8 @@ bool DrmOutput::queueChanges(const std::shared_ptr<OutputChangeSet> &props)
|
|||
m_pipeline->setRgbRange(props->rgbRange.value_or(m_pipeline->rgbRange()));
|
||||
m_pipeline->setRenderOrientation(outputToPlaneTransform(props->transform.value_or(transform())));
|
||||
m_pipeline->setEnable(props->enabled.value_or(m_pipeline->enabled()));
|
||||
m_pipeline->setColorimetry(props->wideColorGamut.value_or(m_state.wideColorGamut) ? NamedColorimetry::BT2020 : NamedColorimetry::BT709);
|
||||
m_pipeline->setNamedTransferFunction(props->highDynamicRange.value_or(m_state.highDynamicRange) ? NamedTransferFunction::PerceptualQuantizer : NamedTransferFunction::sRGB);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -428,6 +437,12 @@ void DrmOutput::applyQueuedChanges(const std::shared_ptr<OutputChangeSet> &props
|
|||
next.currentMode = m_pipeline->mode();
|
||||
next.overscan = m_pipeline->overscan();
|
||||
next.rgbRange = m_pipeline->rgbRange();
|
||||
next.highDynamicRange = props->highDynamicRange.value_or(m_state.highDynamicRange);
|
||||
next.sdrBrightness = props->sdrBrightness.value_or(m_state.sdrBrightness);
|
||||
next.wideColorGamut = props->wideColorGamut.value_or(m_state.wideColorGamut);
|
||||
if (m_state.highDynamicRange != next.highDynamicRange || m_state.sdrBrightness != next.sdrBrightness || m_state.wideColorGamut != next.wideColorGamut) {
|
||||
m_renderLoop->scheduleRepaint();
|
||||
}
|
||||
|
||||
setState(next);
|
||||
setVrrPolicy(props->vrrPolicy.value_or(vrrPolicy()));
|
||||
|
@ -463,7 +478,7 @@ DrmOutputLayer *DrmOutput::cursorLayer() const
|
|||
|
||||
bool DrmOutput::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
if (!m_pipeline->active()) {
|
||||
if (!m_pipeline->active() || m_pipeline->colorimetry() != NamedColorimetry::BT709 || m_pipeline->transferFunction() != NamedTransferFunction::sRGB) {
|
||||
return false;
|
||||
}
|
||||
m_pipeline->setGammaRamp(transformation);
|
||||
|
@ -478,19 +493,37 @@ bool DrmOutput::setGammaRamp(const std::shared_ptr<ColorTransformation> &transfo
|
|||
}
|
||||
}
|
||||
|
||||
bool DrmOutput::setCTM(const QMatrix3x3 &ctm)
|
||||
bool DrmOutput::setChannelFactors(const QVector3D &rgb)
|
||||
{
|
||||
if (!m_pipeline->active()) {
|
||||
return false;
|
||||
if (m_channelFactors == rgb) {
|
||||
return true;
|
||||
}
|
||||
m_pipeline->setCTM(ctm);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_channelFactors = rgb;
|
||||
if (m_pipeline->colorimetry() == NamedColorimetry::BT709 && m_pipeline->transferFunction() == NamedTransferFunction::sRGB) {
|
||||
if (!m_pipeline->active()) {
|
||||
return false;
|
||||
}
|
||||
QMatrix3x3 ctm;
|
||||
ctm(0, 0) = rgb.x();
|
||||
ctm(1, 1) = rgb.y();
|
||||
ctm(2, 2) = rgb.z();
|
||||
m_pipeline->setCTM(ctm);
|
||||
if (DrmPipeline::commitPipelines({m_pipeline}, DrmPipeline::CommitMode::Test) == DrmPipeline::Error::None) {
|
||||
m_pipeline->applyPendingChanges();
|
||||
m_renderLoop->scheduleRepaint();
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->revertPendingChanges();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
m_renderLoop->scheduleRepaint();
|
||||
return true;
|
||||
} else {
|
||||
m_pipeline->revertPendingChanges();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QVector3D DrmOutput::channelFactors() const
|
||||
{
|
||||
return m_channelFactors;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,8 @@ public:
|
|||
void leaseEnded();
|
||||
|
||||
bool setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation) override;
|
||||
bool setCTM(const QMatrix3x3 &ctm) override;
|
||||
bool setChannelFactors(const QVector3D &rgb) override;
|
||||
QVector3D channelFactors() const;
|
||||
|
||||
private:
|
||||
bool setDrmDpmsMode(DpmsMode mode);
|
||||
|
@ -80,6 +81,7 @@ private:
|
|||
QPointer<CursorSource> source;
|
||||
QPointF position;
|
||||
} m_cursor;
|
||||
QVector3D m_channelFactors = {1, 1, 1};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -108,7 +108,9 @@ DrmPipeline::Error DrmPipeline::commitPipelinesAtomic(const QVector<DrmPipeline
|
|||
return Error::InvalidArguments;
|
||||
}
|
||||
if (mode == CommitMode::TestAllowModeset || mode == CommitMode::CommitModeset) {
|
||||
pipeline->prepareAtomicModeset(commit.get());
|
||||
if (!pipeline->prepareAtomicModeset(commit.get())) {
|
||||
return Error::InvalidArguments;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pipeline->prepareAtomicDisable(commit.get());
|
||||
|
@ -229,7 +231,7 @@ void DrmPipeline::prepareAtomicDisable(DrmAtomicCommit *commit)
|
|||
}
|
||||
}
|
||||
|
||||
void DrmPipeline::prepareAtomicModeset(DrmAtomicCommit *commit)
|
||||
bool DrmPipeline::prepareAtomicModeset(DrmAtomicCommit *commit)
|
||||
{
|
||||
commit->addProperty(m_connector->crtcId, m_pending.crtc->id());
|
||||
if (m_connector->broadcastRGB.isValid()) {
|
||||
|
@ -254,10 +256,14 @@ void DrmPipeline::prepareAtomicModeset(DrmAtomicCommit *commit)
|
|||
commit->addProperty(m_connector->maxBpc, preferred);
|
||||
}
|
||||
if (m_connector->hdrMetadata.isValid()) {
|
||||
commit->addProperty(m_connector->hdrMetadata, 0);
|
||||
commit->addBlob(m_connector->hdrMetadata, createHdrMetadata(m_pending.transferFunction));
|
||||
} else if (m_pending.transferFunction != NamedTransferFunction::sRGB) {
|
||||
return false;
|
||||
}
|
||||
if (m_connector->colorspace.isValid()) {
|
||||
commit->addEnum(m_connector->colorspace, DrmConnector::Colorspace::Default);
|
||||
if (m_connector->colorspace.isValid() && m_connector->colorspace.hasEnum(DrmConnector::Colorspace::BT2020_RGB)) {
|
||||
commit->addEnum(m_connector->colorspace, m_pending.colorimetry == NamedColorimetry::BT2020 ? DrmConnector::Colorspace::BT2020_RGB : DrmConnector::Colorspace::Default);
|
||||
} else if (m_pending.colorimetry != NamedColorimetry::BT709) {
|
||||
return false;
|
||||
}
|
||||
if (m_connector->scalingMode.isValid() && m_connector->scalingMode.hasEnum(DrmConnector::ScalingMode::None)) {
|
||||
commit->addEnum(m_connector->scalingMode, DrmConnector::ScalingMode::None);
|
||||
|
@ -291,6 +297,7 @@ void DrmPipeline::prepareAtomicModeset(DrmAtomicCommit *commit)
|
|||
commit->addEnum(cursor->pixelBlendMode, DrmPlane::PixelBlendMode::PreMultiplied);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t DrmPipeline::calculateUnderscan()
|
||||
|
@ -571,6 +578,16 @@ DrmConnector::DrmContentType DrmPipeline::contentType() const
|
|||
return m_pending.contentType;
|
||||
}
|
||||
|
||||
NamedColorimetry DrmPipeline::colorimetry() const
|
||||
{
|
||||
return m_pending.colorimetry;
|
||||
}
|
||||
|
||||
NamedTransferFunction DrmPipeline::transferFunction() const
|
||||
{
|
||||
return m_pending.transferFunction;
|
||||
}
|
||||
|
||||
void DrmPipeline::setCrtc(DrmCrtc *crtc)
|
||||
{
|
||||
if (crtc && m_pending.crtc && crtc->gammaRampSize() != m_pending.crtc->gammaRampSize() && m_pending.colorTransformation) {
|
||||
|
@ -660,4 +677,62 @@ void DrmPipeline::setContentType(DrmConnector::DrmContentType type)
|
|||
{
|
||||
m_pending.contentType = type;
|
||||
}
|
||||
|
||||
void DrmPipeline::setColorimetry(NamedColorimetry name)
|
||||
{
|
||||
m_pending.colorimetry = name;
|
||||
}
|
||||
|
||||
void DrmPipeline::setNamedTransferFunction(NamedTransferFunction tf)
|
||||
{
|
||||
m_pending.transferFunction = tf;
|
||||
}
|
||||
|
||||
std::shared_ptr<DrmBlob> DrmPipeline::createHdrMetadata(NamedTransferFunction transferFunction) const
|
||||
{
|
||||
if (transferFunction != NamedTransferFunction::PerceptualQuantizer) {
|
||||
// for sRGB / gamma 2.2, don't send any metadata, to ensure the non-HDR experience stays the same
|
||||
return nullptr;
|
||||
}
|
||||
if (!m_connector->edid() || !m_connector->edid()->hdrMetadata()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto metadata = *m_connector->edid()->hdrMetadata();
|
||||
if (!metadata.supportsPQ) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto colorimetry = m_connector->edid()->colorimetry();
|
||||
const auto to16Bit = [](float value) {
|
||||
return uint16_t(std::round(value / 0.00002));
|
||||
};
|
||||
hdr_output_metadata data{
|
||||
.metadata_type = 0,
|
||||
.hdmi_metadata_type1 = hdr_metadata_infoframe{
|
||||
// eotf types (from CTA-861-G page 85):
|
||||
// - 0: traditional gamma, SDR
|
||||
// - 1: traditional gamma, HDR
|
||||
// - 2: SMPTE ST2084
|
||||
// - 3: hybrid Log-Gamma based on BT.2100-0
|
||||
// - 4-7: reserved
|
||||
.eotf = uint8_t(2),
|
||||
// there's only one type. 1-7 are reserved for future use
|
||||
.metadata_type = 0,
|
||||
// in 0.00002 nits
|
||||
.display_primaries = {
|
||||
{to16Bit(colorimetry.redPrimary.x()), to16Bit(colorimetry.redPrimary.y())},
|
||||
{to16Bit(colorimetry.greenPrimary.x()), to16Bit(colorimetry.greenPrimary.y())},
|
||||
{to16Bit(colorimetry.bluePrimary.x()), to16Bit(colorimetry.bluePrimary.y())},
|
||||
},
|
||||
.white_point = {to16Bit(colorimetry.whitePoint.x()), to16Bit(colorimetry.whitePoint.y())},
|
||||
// in nits
|
||||
.max_display_mastering_luminance = uint16_t(std::round(metadata.desiredContentMaxLuminance)),
|
||||
// in 0.0001 nits
|
||||
.min_display_mastering_luminance = uint16_t(std::round(metadata.desiredContentMinLuminance * 10000)),
|
||||
// in nits
|
||||
.max_cll = uint16_t(std::round(metadata.desiredContentMaxLuminance)),
|
||||
.max_fall = uint16_t(std::round(metadata.desiredMaxFrameAverageLuminance)),
|
||||
},
|
||||
};
|
||||
return DrmBlob::create(gpu(), &data, sizeof(data));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "drm_blob.h"
|
||||
#include "drm_connector.h"
|
||||
#include "drm_plane.h"
|
||||
#include "libkwineffects/colorspace.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -107,6 +108,8 @@ public:
|
|||
uint32_t overscan() const;
|
||||
Output::RgbRange rgbRange() const;
|
||||
DrmConnector::DrmContentType contentType() const;
|
||||
NamedColorimetry colorimetry() const;
|
||||
NamedTransferFunction transferFunction() const;
|
||||
|
||||
void setCrtc(DrmCrtc *crtc);
|
||||
void setMode(const std::shared_ptr<DrmConnectorMode> &mode);
|
||||
|
@ -120,6 +123,8 @@ public:
|
|||
void setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
void setCTM(const QMatrix3x3 &ctm);
|
||||
void setContentType(DrmConnector::DrmContentType type);
|
||||
void setColorimetry(NamedColorimetry name);
|
||||
void setNamedTransferFunction(NamedTransferFunction tf);
|
||||
|
||||
enum class CommitMode {
|
||||
Test,
|
||||
|
@ -134,6 +139,7 @@ private:
|
|||
bool isBufferForDirectScanout() const;
|
||||
uint32_t calculateUnderscan();
|
||||
static Error errnoToError();
|
||||
std::shared_ptr<DrmBlob> createHdrMetadata(NamedTransferFunction transferFunction) const;
|
||||
|
||||
// legacy only
|
||||
Error presentLegacy();
|
||||
|
@ -146,7 +152,7 @@ private:
|
|||
// atomic modesetting only
|
||||
void atomicCommitSuccessful();
|
||||
void atomicModesetSuccessful();
|
||||
void prepareAtomicModeset(DrmAtomicCommit *commit);
|
||||
bool prepareAtomicModeset(DrmAtomicCommit *commit);
|
||||
bool prepareAtomicPresentation(DrmAtomicCommit *commit);
|
||||
void prepareAtomicDisable(DrmAtomicCommit *commit);
|
||||
static Error commitPipelinesAtomic(const QVector<DrmPipeline *> &pipelines, CommitMode mode, const QVector<DrmObject *> &unusedObjects);
|
||||
|
@ -172,6 +178,8 @@ private:
|
|||
std::shared_ptr<DrmGammaRamp> gamma;
|
||||
std::shared_ptr<DrmBlob> ctm;
|
||||
DrmConnector::DrmContentType contentType = DrmConnector::DrmContentType::Graphics;
|
||||
NamedColorimetry colorimetry = NamedColorimetry::BT709;
|
||||
NamedTransferFunction transferFunction = NamedTransferFunction::sRGB;
|
||||
|
||||
std::shared_ptr<DrmPipelineLayer> layer;
|
||||
std::shared_ptr<DrmOverlayLayer> cursorLayer;
|
||||
|
|
|
@ -319,11 +319,7 @@ void ColorDevice::update()
|
|||
{
|
||||
d->rebuildPipeline();
|
||||
if (!d->output->setGammaRamp(d->transformation)) {
|
||||
QMatrix3x3 ctm;
|
||||
ctm(0, 0) = d->simpleTransformation.x();
|
||||
ctm(1, 1) = d->simpleTransformation.y();
|
||||
ctm(2, 2) = d->simpleTransformation.z();
|
||||
d->output->setCTM(ctm);
|
||||
d->output->setChannelFactors(d->simpleTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -297,6 +297,15 @@ void Output::setState(const State &state)
|
|||
if (oldState.rgbRange != state.rgbRange) {
|
||||
Q_EMIT rgbRangeChanged();
|
||||
}
|
||||
if (oldState.highDynamicRange != state.highDynamicRange) {
|
||||
Q_EMIT highDynamicRangeChanged();
|
||||
}
|
||||
if (oldState.sdrBrightness != state.sdrBrightness) {
|
||||
Q_EMIT sdrBrightnessChanged();
|
||||
}
|
||||
if (oldState.wideColorGamut != state.wideColorGamut) {
|
||||
Q_EMIT wideColorGamutChanged();
|
||||
}
|
||||
if (oldState.enabled != state.enabled) {
|
||||
Q_EMIT enabledChanged();
|
||||
}
|
||||
|
@ -400,12 +409,12 @@ Output::RgbRange Output::rgbRange() const
|
|||
return m_state.rgbRange;
|
||||
}
|
||||
|
||||
bool Output::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
bool Output::setChannelFactors(const QVector3D &rgb)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Output::setCTM(const QMatrix3x3 &ctm)
|
||||
bool Output::setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -425,6 +434,21 @@ Output::Transform Output::panelOrientation() const
|
|||
return m_information.panelOrientation;
|
||||
}
|
||||
|
||||
bool Output::wideColorGamut() const
|
||||
{
|
||||
return m_state.wideColorGamut;
|
||||
}
|
||||
|
||||
bool Output::highDynamicRange() const
|
||||
{
|
||||
return m_state.highDynamicRange;
|
||||
}
|
||||
|
||||
uint32_t Output::sdrBrightness() const
|
||||
{
|
||||
return m_state.sdrBrightness;
|
||||
}
|
||||
|
||||
bool Output::setCursor(CursorSource *source)
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -76,10 +76,12 @@ public:
|
|||
Q_ENUM(DpmsMode)
|
||||
|
||||
enum class Capability : uint {
|
||||
Dpms = 0x1,
|
||||
Overscan = 0x2,
|
||||
Vrr = 0x4,
|
||||
RgbRange = 0x8,
|
||||
Dpms = 1,
|
||||
Overscan = 1 << 1,
|
||||
Vrr = 1 << 2,
|
||||
RgbRange = 1 << 3,
|
||||
HighDynamicRange = 1 << 4,
|
||||
WideColorGamut = 1 << 5,
|
||||
};
|
||||
Q_DECLARE_FLAGS(Capabilities, Capability)
|
||||
|
||||
|
@ -254,9 +256,12 @@ public:
|
|||
bool isPlaceholder() const;
|
||||
bool isNonDesktop() const;
|
||||
Transform panelOrientation() const;
|
||||
bool wideColorGamut() const;
|
||||
bool highDynamicRange() const;
|
||||
uint32_t sdrBrightness() const;
|
||||
|
||||
virtual bool setGammaRamp(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
virtual bool setCTM(const QMatrix3x3 &ctm);
|
||||
virtual bool setChannelFactors(const QVector3D &rgb);
|
||||
|
||||
virtual bool setCursor(CursorSource *source);
|
||||
virtual bool moveCursor(const QPointF &position);
|
||||
|
@ -314,6 +319,9 @@ Q_SIGNALS:
|
|||
void overscanChanged();
|
||||
void vrrPolicyChanged();
|
||||
void rgbRangeChanged();
|
||||
void wideColorGamutChanged();
|
||||
void sdrBrightnessChanged();
|
||||
void highDynamicRangeChanged();
|
||||
|
||||
protected:
|
||||
struct Information
|
||||
|
@ -345,6 +353,9 @@ protected:
|
|||
bool enabled = false;
|
||||
uint32_t overscan = 0;
|
||||
RgbRange rgbRange = RgbRange::Automatic;
|
||||
bool wideColorGamut = false;
|
||||
bool highDynamicRange = false;
|
||||
uint32_t sdrBrightness = 200;
|
||||
};
|
||||
|
||||
void setInformation(const Information &information);
|
||||
|
|
|
@ -29,6 +29,9 @@ public:
|
|||
std::optional<uint32_t> overscan;
|
||||
std::optional<Output::RgbRange> rgbRange;
|
||||
std::optional<RenderLoop::VrrPolicy> vrrPolicy;
|
||||
std::optional<bool> highDynamicRange;
|
||||
std::optional<uint32_t> sdrBrightness;
|
||||
std::optional<bool> wideColorGamut;
|
||||
};
|
||||
|
||||
class KWIN_EXPORT OutputConfiguration
|
||||
|
|
|
@ -1627,7 +1627,7 @@ void EffectsHandlerImpl::renderOffscreenQuickView(const RenderTarget &renderTarg
|
|||
return;
|
||||
}
|
||||
|
||||
ShaderTraits traits = ShaderTrait::MapTexture;
|
||||
ShaderTraits traits = ShaderTrait::MapTexture | ShaderTrait::TransformColorspace;
|
||||
const qreal a = w->opacity();
|
||||
if (a != 1.0) {
|
||||
traits |= ShaderTrait::Modulate;
|
||||
|
@ -1643,6 +1643,7 @@ void EffectsHandlerImpl::renderOffscreenQuickView(const RenderTarget &renderTarg
|
|||
if (a != 1.0) {
|
||||
shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
|
||||
}
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
|
|
@ -17,8 +17,6 @@ set(kwin_EFFECTSLIB_SRCS
|
|||
kwinoffscreenquickview.cpp
|
||||
kwinquickeffect.cpp
|
||||
logging.cpp
|
||||
rendertarget.cpp
|
||||
renderviewport.cpp
|
||||
)
|
||||
|
||||
add_library(kwineffects SHARED ${kwin_EFFECTSLIB_SRCS})
|
||||
|
@ -45,6 +43,7 @@ install(TARGETS kwineffects EXPORT KWinEffectsTargets ${KDE_INSTALL_TARGETS_DEFA
|
|||
|
||||
# kwingl(es)utils library
|
||||
set(kwin_GLUTILSLIB_SRCS
|
||||
colorspace.cpp
|
||||
kwineglimagetexture.cpp
|
||||
kwinglplatform.cpp
|
||||
kwingltexture.cpp
|
||||
|
@ -71,6 +70,7 @@ install(FILES
|
|||
${CMAKE_CURRENT_BINARY_DIR}/kwinconfig.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kwineffects_export.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kwinglutils_export.h
|
||||
colorspace.h
|
||||
kwinanimationeffect.h
|
||||
kwineffects.h
|
||||
kwinglobals.h
|
||||
|
@ -81,6 +81,8 @@ install(FILES
|
|||
kwinoffscreeneffect.h
|
||||
kwinoffscreenquickview.h
|
||||
kwinquickeffect.h
|
||||
rendertarget.h
|
||||
renderviewport.h
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kwin/libkwineffects COMPONENT Devel)
|
||||
|
||||
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KWinEffects")
|
||||
|
|
129
src/libkwineffects/colorspace.cpp
Normal file
129
src/libkwineffects/colorspace.cpp
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "colorspace.h"
|
||||
|
||||
#include <qassert.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
static QMatrix3x3 inverse(const QMatrix3x3 &m)
|
||||
{
|
||||
const double determinant = m(0, 0) * (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) - m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0)) + m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0));
|
||||
QMatrix3x3 ret;
|
||||
ret(0, 0) = (m(1, 1) * m(2, 2) - m(2, 1) * m(1, 2)) / determinant;
|
||||
ret(0, 1) = (m(0, 2) * m(2, 1) - m(0, 1) * m(2, 2)) / determinant;
|
||||
ret(0, 2) = (m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1)) / determinant;
|
||||
ret(1, 0) = (m(1, 2) * m(2, 0) - m(1, 0) * m(2, 2)) / determinant;
|
||||
ret(1, 1) = (m(0, 0) * m(2, 2) - m(0, 2) * m(2, 0)) / determinant;
|
||||
ret(1, 2) = (m(1, 0) * m(0, 2) - m(0, 0) * m(1, 2)) / determinant;
|
||||
ret(2, 0) = (m(1, 0) * m(2, 1) - m(2, 0) * m(1, 1)) / determinant;
|
||||
ret(2, 1) = (m(2, 0) * m(0, 1) - m(0, 0) * m(2, 1)) / determinant;
|
||||
ret(2, 2) = (m(0, 0) * m(1, 1) - m(1, 0) * m(0, 1)) / determinant;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QMatrix3x3 matrixFromColumns(const QVector3D &first, const QVector3D &second, const QVector3D &third)
|
||||
{
|
||||
QMatrix3x3 ret;
|
||||
ret(0, 0) = first.x();
|
||||
ret(1, 0) = first.y();
|
||||
ret(2, 0) = first.z();
|
||||
ret(0, 1) = second.x();
|
||||
ret(1, 1) = second.y();
|
||||
ret(2, 1) = second.z();
|
||||
ret(0, 2) = third.x();
|
||||
ret(1, 2) = third.y();
|
||||
ret(2, 2) = third.z();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static QVector3D operator*(const QMatrix3x3 &mat, const QVector3D &v)
|
||||
{
|
||||
return QVector3D(
|
||||
mat(0, 0) * v.x() + mat(0, 1) * v.y() + mat(0, 2) * v.z(),
|
||||
mat(1, 0) * v.x() + mat(1, 1) * v.y() + mat(1, 2) * v.z(),
|
||||
mat(2, 0) * v.x() + mat(2, 1) * v.y() + mat(2, 2) * v.z());
|
||||
}
|
||||
|
||||
static QVector3D xyToXYZ(QVector2D xy)
|
||||
{
|
||||
return QVector3D(xy.x() / xy.y(), 1, (1 - xy.x() - xy.y()) / xy.y());
|
||||
}
|
||||
|
||||
QMatrix3x3 Colorimetry::toXYZ() const
|
||||
{
|
||||
const auto r_xyz = xyToXYZ(red);
|
||||
const auto g_xyz = xyToXYZ(blue);
|
||||
const auto b_xyz = xyToXYZ(green);
|
||||
const auto w_xyz = xyToXYZ(white);
|
||||
const auto component_scale = inverse(matrixFromColumns(r_xyz, g_xyz, b_xyz)) * w_xyz;
|
||||
return matrixFromColumns(r_xyz * component_scale.x(), g_xyz * component_scale.y(), b_xyz * component_scale.z());
|
||||
}
|
||||
|
||||
QMatrix3x3 Colorimetry::toOther(const Colorimetry &other) const
|
||||
{
|
||||
return toXYZ() * inverse(other.toXYZ());
|
||||
}
|
||||
|
||||
bool Colorimetry::operator==(const Colorimetry &other) const
|
||||
{
|
||||
return (name || other.name) ? (name == other.name)
|
||||
: (red == other.red && green == other.green && blue == other.blue && white == other.white);
|
||||
}
|
||||
|
||||
constexpr Colorimetry Colorimetry::createFromName(NamedColorimetry name)
|
||||
{
|
||||
switch (name) {
|
||||
case NamedColorimetry::BT709:
|
||||
return Colorimetry{
|
||||
.red = {0.64, 0.33},
|
||||
.green = {0.30, 0.60},
|
||||
.blue = {0.15, 0.06},
|
||||
.white = {0.3127, 0.3290},
|
||||
.name = name,
|
||||
};
|
||||
case NamedColorimetry::BT2020:
|
||||
return Colorimetry{
|
||||
.red = {0.708, 0.292},
|
||||
.green = {0.170, 0.797},
|
||||
.blue = {0.131, 0.046},
|
||||
.white = {0.3127, 0.3290},
|
||||
.name = name,
|
||||
};
|
||||
}
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
const Colorspace Colorspace::sRGB(Colorimetry::createFromName(NamedColorimetry::BT709), NamedTransferFunction::sRGB);
|
||||
|
||||
Colorspace::Colorspace(NamedColorimetry colorimetry, NamedTransferFunction tf)
|
||||
: m_colorimetry(Colorimetry::createFromName(colorimetry))
|
||||
, m_transferFunction(tf)
|
||||
{
|
||||
}
|
||||
|
||||
Colorspace::Colorspace(const Colorimetry &colorimetry, NamedTransferFunction tf)
|
||||
: m_colorimetry(colorimetry)
|
||||
, m_transferFunction(tf)
|
||||
{
|
||||
}
|
||||
|
||||
const Colorimetry &Colorspace::colorimetry() const
|
||||
{
|
||||
return m_colorimetry;
|
||||
}
|
||||
|
||||
NamedTransferFunction Colorspace::transferFunction() const
|
||||
{
|
||||
return m_transferFunction;
|
||||
}
|
||||
|
||||
bool Colorspace::operator==(const Colorspace &other) const
|
||||
{
|
||||
return m_colorimetry == other.m_colorimetry && m_transferFunction == other.m_transferFunction;
|
||||
}
|
||||
}
|
73
src/libkwineffects/colorspace.h
Normal file
73
src/libkwineffects/colorspace.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2023 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
#include <QMatrix3x3>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "libkwineffects/kwineffects_export.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
enum class NamedColorimetry {
|
||||
BT709,
|
||||
BT2020,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the definition of colors in a color space.
|
||||
* Red, green and blue define the chromaticities ("absolute colors") of the red, green and blue LEDs on a display in xy coordinates
|
||||
* White defines the the chromaticity of the reference white in xy coordinates
|
||||
*/
|
||||
class KWINEFFECTS_EXPORT Colorimetry
|
||||
{
|
||||
public:
|
||||
static constexpr Colorimetry createFromName(NamedColorimetry name);
|
||||
|
||||
QMatrix3x3 toXYZ() const;
|
||||
QMatrix3x3 toOther(const Colorimetry &colorimetry) const;
|
||||
bool operator==(const Colorimetry &other) const;
|
||||
|
||||
QVector2D red;
|
||||
QVector2D green;
|
||||
QVector2D blue;
|
||||
QVector2D white;
|
||||
std::optional<NamedColorimetry> name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes an EOTF, that is, how encoded brightness values are converted to light
|
||||
*/
|
||||
enum class NamedTransferFunction {
|
||||
sRGB = 0,
|
||||
linear = 1,
|
||||
PerceptualQuantizer = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the meaning of encoded color values
|
||||
*/
|
||||
class KWINEFFECTS_EXPORT Colorspace
|
||||
{
|
||||
public:
|
||||
explicit Colorspace(NamedColorimetry colorimetry, NamedTransferFunction tf);
|
||||
explicit Colorspace(const Colorimetry &colorimety, NamedTransferFunction tf);
|
||||
|
||||
bool operator==(const Colorspace &other) const;
|
||||
|
||||
const Colorimetry &colorimetry() const;
|
||||
NamedTransferFunction transferFunction() const;
|
||||
|
||||
static const Colorspace sRGB;
|
||||
|
||||
private:
|
||||
Colorimetry m_colorimetry;
|
||||
NamedTransferFunction m_transferFunction;
|
||||
};
|
||||
|
||||
}
|
|
@ -426,6 +426,7 @@ void GLShader::resolveLocations()
|
|||
mMatrixLocation[ModelViewProjectionMatrix] = uniformLocation("modelViewProjectionMatrix");
|
||||
mMatrixLocation[WindowTransformation] = uniformLocation("windowTransformation");
|
||||
mMatrixLocation[ScreenTransformation] = uniformLocation("screenTransformation");
|
||||
mMatrixLocation[ColorimetryTransformation] = uniformLocation("colorimetryTransform");
|
||||
|
||||
mVec2Location[Offset] = uniformLocation("offset");
|
||||
|
||||
|
@ -437,6 +438,9 @@ void GLShader::resolveLocations()
|
|||
|
||||
mIntLocation[TextureWidth] = uniformLocation("textureWidth");
|
||||
mIntLocation[TextureHeight] = uniformLocation("textureHeight");
|
||||
mIntLocation[SourceNamedTransferFunction] = uniformLocation("sourceNamedTransferFunction");
|
||||
mIntLocation[DestinationNamedTransferFunction] = uniformLocation("destinationNamedTransferFunction");
|
||||
mIntLocation[SdrBrightness] = uniformLocation("sdrBrightness");
|
||||
|
||||
mLocationsResolved = true;
|
||||
}
|
||||
|
@ -447,6 +451,12 @@ int GLShader::uniformLocation(const char *name)
|
|||
return location;
|
||||
}
|
||||
|
||||
bool GLShader::setUniform(MatrixUniform uniform, const QMatrix3x3 &value)
|
||||
{
|
||||
resolveLocations();
|
||||
return setUniform(mMatrixLocation[uniform], value);
|
||||
}
|
||||
|
||||
bool GLShader::setUniform(GLShader::MatrixUniform uniform, const QMatrix4x4 &matrix)
|
||||
{
|
||||
resolveLocations();
|
||||
|
@ -571,6 +581,14 @@ bool GLShader::setUniform(int location, const QVector4D &value)
|
|||
return (location >= 0);
|
||||
}
|
||||
|
||||
bool GLShader::setUniform(int location, const QMatrix3x3 &value)
|
||||
{
|
||||
if (location >= 0) {
|
||||
glUniformMatrix3fv(location, 1, GL_FALSE, value.constData());
|
||||
}
|
||||
return location >= 0;
|
||||
}
|
||||
|
||||
bool GLShader::setUniform(int location, const QMatrix4x4 &value)
|
||||
{
|
||||
if (location >= 0) {
|
||||
|
@ -619,6 +637,19 @@ QMatrix4x4 GLShader::getUniformMatrix4x4(const char *name)
|
|||
}
|
||||
}
|
||||
|
||||
bool GLShader::setColorspaceUniforms(const Colorspace &src, const Colorspace &dst)
|
||||
{
|
||||
return setUniform(GLShader::MatrixUniform::ColorimetryTransformation, src.colorimetry().toOther(dst.colorimetry()))
|
||||
&& setUniform(GLShader::IntUniform::SourceNamedTransferFunction, int(src.transferFunction()))
|
||||
&& setUniform(GLShader::IntUniform::DestinationNamedTransferFunction, int(dst.transferFunction()));
|
||||
}
|
||||
|
||||
bool GLShader::setColorspaceUniforms(const Colorspace &src, const RenderTarget &renderTarget)
|
||||
{
|
||||
return setColorspaceUniforms(src, renderTarget.colorspace())
|
||||
&& setUniform(IntUniform::SdrBrightness, renderTarget.sdrBrightness());
|
||||
}
|
||||
|
||||
//****************************************
|
||||
// ShaderManager
|
||||
//****************************************
|
||||
|
@ -749,6 +780,38 @@ QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const
|
|||
} else if (traits & ShaderTrait::UniformColor) {
|
||||
stream << "uniform vec4 geometryColor;\n";
|
||||
}
|
||||
if (traits & ShaderTrait::TransformColorspace) {
|
||||
stream << "uniform mat3 colorimetryTransform;\n";
|
||||
stream << "uniform int sourceNamedTransferFunction;\n";
|
||||
stream << "uniform int destinationNamedTransferFunction;\n";
|
||||
stream << "uniform int sdrBrightness;// in nits\n";
|
||||
stream << "\n";
|
||||
stream << "vec3 nitsToPq(vec3 nits) {\n";
|
||||
stream << " vec3 normalized = clamp(nits / 10000.0, vec3(0), vec3(1));\n";
|
||||
stream << " float c1 = 0.8359375;\n";
|
||||
stream << " float c2 = 18.8515625;\n";
|
||||
stream << " float c3 = 18.6875;\n";
|
||||
stream << " float m1 = 0.1593017578125;\n";
|
||||
stream << " float m2 = 78.84375;\n";
|
||||
stream << " vec3 num = vec3(c1) + c2 * pow(normalized, vec3(m1));\n";
|
||||
stream << " vec3 denum = vec3(1.0) + c3 * pow(normalized, vec3(m1));\n";
|
||||
stream << " return pow(num / denum, vec3(m2));\n";
|
||||
stream << "}\n";
|
||||
stream << "vec3 srgbToLinear(vec3 color) {\n";
|
||||
stream << " bvec3 isLow = lessThanEqual(color, vec3(0.04045f));\n";
|
||||
stream << " vec3 loPart = color / 12.92f;\n";
|
||||
stream << " vec3 hiPart = pow((color + 0.055f) / 1.055f, vec3(12.0f / 5.0f));\n";
|
||||
stream << " return mix(hiPart, loPart, isLow);\n";
|
||||
stream << "}\n";
|
||||
stream << "\n";
|
||||
stream << "vec3 linearToSrgb(vec3 color) {\n";
|
||||
stream << " bvec3 isLow = lessThanEqual(color, vec3(0.0031308f));\n";
|
||||
stream << " vec3 loPart = color * 12.92f;\n";
|
||||
stream << " vec3 hiPart = pow(color, vec3(5.0f / 12.0f)) * 1.055f - 0.055f;\n";
|
||||
stream << " return mix(hiPart, loPart, isLow);\n";
|
||||
stream << "}\n";
|
||||
stream << "\n";
|
||||
}
|
||||
|
||||
if (output != QByteArrayLiteral("gl_FragColor")) {
|
||||
stream << "\nout vec4 " << output << ";\n";
|
||||
|
@ -774,6 +837,20 @@ QByteArray ShaderManager::generateFragmentSource(ShaderTraits traits) const
|
|||
} else if (traits & ShaderTrait::UniformColor) {
|
||||
stream << " " << output << " = geometryColor;\n";
|
||||
}
|
||||
if (traits & ShaderTrait::TransformColorspace) {
|
||||
// simple sRGB -> linear
|
||||
stream << "if (sourceNamedTransferFunction == 0) {\n";
|
||||
stream << " " << output << ".rgb = sdrBrightness * srgbToLinear(" << output << ".rgb);\n";
|
||||
stream << "}\n";
|
||||
stream << " " << output << ".rgb = colorimetryTransform * " << output << ".rgb;\n";
|
||||
// nits -> simple sRGB
|
||||
stream << "if (destinationNamedTransferFunction == 0) {\n";
|
||||
stream << " " << output << ".rgb = linearToSrgb(" << output << ".rgb / sdrBrightness);\n";
|
||||
// nits -> PQ
|
||||
stream << "} else if (destinationNamedTransferFunction == 2) {\n";
|
||||
stream << " " << output << ".rgb = nitsToPq(" << output << ".rgb);\n";
|
||||
stream << "}\n";
|
||||
}
|
||||
|
||||
stream << "}";
|
||||
stream.flush();
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#pragma once
|
||||
|
||||
// kwin
|
||||
#include "libkwineffects/colorspace.h"
|
||||
#include "libkwineffects/kwingltexture.h"
|
||||
#include "libkwineffects/kwinglutils_export.h"
|
||||
#include "libkwineffects/kwinglutils_funcs.h"
|
||||
|
@ -90,6 +91,7 @@ public:
|
|||
bool setUniform(int location, const QVector2D &value);
|
||||
bool setUniform(int location, const QVector3D &value);
|
||||
bool setUniform(int location, const QVector4D &value);
|
||||
bool setUniform(int location, const QMatrix3x3 &value);
|
||||
bool setUniform(int location, const QMatrix4x4 &value);
|
||||
bool setUniform(int location, const QColor &value);
|
||||
|
||||
|
@ -108,6 +110,7 @@ public:
|
|||
ModelViewProjectionMatrix,
|
||||
WindowTransformation,
|
||||
ScreenTransformation,
|
||||
ColorimetryTransformation,
|
||||
MatrixCount
|
||||
};
|
||||
|
||||
|
@ -130,6 +133,9 @@ public:
|
|||
AlphaToOne, ///< @deprecated no longer used
|
||||
TextureWidth,
|
||||
TextureHeight,
|
||||
SourceNamedTransferFunction,
|
||||
DestinationNamedTransferFunction,
|
||||
SdrBrightness,
|
||||
IntUniformCount
|
||||
};
|
||||
|
||||
|
@ -138,6 +144,7 @@ public:
|
|||
ColorUniformCount
|
||||
};
|
||||
|
||||
bool setUniform(MatrixUniform uniform, const QMatrix3x3 &value);
|
||||
bool setUniform(MatrixUniform uniform, const QMatrix4x4 &matrix);
|
||||
bool setUniform(Vec2Uniform uniform, const QVector2D &value);
|
||||
bool setUniform(Vec4Uniform uniform, const QVector4D &value);
|
||||
|
@ -146,6 +153,9 @@ public:
|
|||
bool setUniform(ColorUniform uniform, const QVector4D &value);
|
||||
bool setUniform(ColorUniform uniform, const QColor &value);
|
||||
|
||||
bool setColorspaceUniforms(const Colorspace &src, const Colorspace &dst);
|
||||
bool setColorspaceUniforms(const Colorspace &src, const RenderTarget &renderTarget);
|
||||
|
||||
protected:
|
||||
GLShader(unsigned int flags = NoFlags);
|
||||
bool loadFromFiles(const QString &vertexfile, const QString &fragmentfile);
|
||||
|
@ -176,6 +186,7 @@ enum class ShaderTrait {
|
|||
UniformColor = (1 << 1),
|
||||
Modulate = (1 << 2),
|
||||
AdjustSaturation = (1 << 3),
|
||||
TransformColorspace = (1 << 4),
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(ShaderTraits, ShaderTrait)
|
||||
|
|
|
@ -150,8 +150,9 @@ void OffscreenData::setVertexSnappingMode(RenderGeometry::VertexSnappingMode mod
|
|||
void OffscreenData::paint(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *window, const QRegion ®ion,
|
||||
const WindowPaintData &data, const WindowQuadList &quads)
|
||||
{
|
||||
GLShader *shader = m_shader ? m_shader : ShaderManager::instance()->shader(ShaderTrait::MapTexture | ShaderTrait::Modulate | ShaderTrait::AdjustSaturation);
|
||||
GLShader *shader = m_shader ? m_shader : ShaderManager::instance()->shader(ShaderTrait::MapTexture | ShaderTrait::Modulate | ShaderTrait::AdjustSaturation | ShaderTrait::TransformColorspace);
|
||||
ShaderBinder binder(shader);
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
|
||||
const double scale = viewport.scale();
|
||||
|
||||
|
|
|
@ -10,14 +10,18 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
RenderTarget::RenderTarget(GLFramebuffer *fbo)
|
||||
RenderTarget::RenderTarget(GLFramebuffer *fbo, const Colorspace &colorspace, uint32_t brightness)
|
||||
: m_framebuffer(fbo)
|
||||
, m_transformation(fbo->colorAttachment() ? fbo->colorAttachment()->contentTransformMatrix() : QMatrix4x4())
|
||||
, m_colorspace(colorspace)
|
||||
, m_sdrBrightness(brightness)
|
||||
{
|
||||
}
|
||||
|
||||
RenderTarget::RenderTarget(QImage *image)
|
||||
RenderTarget::RenderTarget(QImage *image, const Colorspace &colorspace, uint32_t brightness)
|
||||
: m_image(image)
|
||||
, m_colorspace(colorspace)
|
||||
, m_sdrBrightness(brightness)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -67,4 +71,14 @@ QImage *RenderTarget::image() const
|
|||
return m_image;
|
||||
}
|
||||
|
||||
const Colorspace &RenderTarget::colorspace() const
|
||||
{
|
||||
return m_colorspace;
|
||||
}
|
||||
|
||||
uint32_t RenderTarget::sdrBrightness() const
|
||||
{
|
||||
return m_sdrBrightness;
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "libkwineffects/colorspace.h"
|
||||
#include "libkwineffects/kwinglutils_export.h"
|
||||
|
||||
#include <QImage>
|
||||
|
@ -21,11 +22,13 @@ class GLTexture;
|
|||
class KWINGLUTILS_EXPORT RenderTarget
|
||||
{
|
||||
public:
|
||||
explicit RenderTarget(GLFramebuffer *fbo);
|
||||
explicit RenderTarget(QImage *image);
|
||||
explicit RenderTarget(GLFramebuffer *fbo, const Colorspace &colorspace = Colorspace::sRGB, uint32_t sdrBrightness = 200);
|
||||
explicit RenderTarget(QImage *image, const Colorspace &colorspace = Colorspace::sRGB, uint32_t sdrBrightness = 200);
|
||||
|
||||
QSize size() const;
|
||||
QMatrix4x4 transformation() const;
|
||||
const Colorspace &colorspace() const;
|
||||
uint32_t sdrBrightness() const;
|
||||
QRectF applyTransformation(const QRectF &rect, const QRectF &viewport) const;
|
||||
QRect applyTransformation(const QRect &rect, const QRect &viewport) const;
|
||||
|
||||
|
@ -37,6 +40,8 @@ private:
|
|||
QImage *m_image = nullptr;
|
||||
GLFramebuffer *m_framebuffer = nullptr;
|
||||
QMatrix4x4 m_transformation;
|
||||
const Colorspace m_colorspace;
|
||||
const uint32_t m_sdrBrightness;
|
||||
};
|
||||
|
||||
} // namespace KWin
|
||||
|
|
|
@ -186,11 +186,29 @@ bool EglContext::isValid() const
|
|||
return EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
static GLint glFormatForDrmFormat(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_ARGB16161616:
|
||||
return GL_RGBA16;
|
||||
case DRM_FORMAT_ARGB16161616F:
|
||||
return GL_RGBA16F;
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
return GL_RGB10_A2;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
default:
|
||||
return GL_RGBA8;
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<GLTexture> EglContext::importDmaBufAsTexture(const DmaBufAttributes &attributes) const
|
||||
{
|
||||
EGLImageKHR image = m_display->importDmaBufAsImage(attributes);
|
||||
if (image != EGL_NO_IMAGE_KHR) {
|
||||
return std::make_shared<EGLImageTexture>(m_display->handle(), image, GL_RGBA8, QSize(attributes.width, attributes.height));
|
||||
return std::make_shared<EGLImageTexture>(m_display->handle(), image, glFormatForDrmFormat(attributes.format), QSize(attributes.width, attributes.height));
|
||||
} else {
|
||||
qCWarning(KWIN_OPENGL) << "Failed to record frame: Error creating EGLImageKHR - " << getEglErrorString();
|
||||
return nullptr;
|
||||
|
|
|
@ -58,12 +58,11 @@ BlurEffect::BlurEffect()
|
|||
connect(effects, &EffectsHandler::virtualScreenGeometryChanged, this, [this]() {
|
||||
screenGeometryChanged(nullptr);
|
||||
});
|
||||
updateTexture(nullptr);
|
||||
}
|
||||
|
||||
// ### Hackish way to announce support.
|
||||
// Should be included in _NET_SUPPORTED instead.
|
||||
if (m_shader && m_shader->isValid() && !m_screenData.empty()) {
|
||||
if (m_shader && m_shader->isValid()) {
|
||||
if (effects->xcbConnection()) {
|
||||
net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this);
|
||||
}
|
||||
|
@ -88,7 +87,7 @@ BlurEffect::BlurEffect()
|
|||
connect(effects, &EffectsHandler::windowDecorationChanged, this, &BlurEffect::setupDecorationConnections);
|
||||
connect(effects, &EffectsHandler::propertyNotify, this, &BlurEffect::slotPropertyNotify);
|
||||
connect(effects, &EffectsHandler::xcbConnectionChanged, this, [this]() {
|
||||
if (m_shader && m_shader->isValid() && !m_screenData.empty()) {
|
||||
if (m_shader && m_shader->isValid()) {
|
||||
net_wm_blur_region = effects->announceSupportProperty(s_blurAtomName, this);
|
||||
}
|
||||
});
|
||||
|
@ -113,8 +112,6 @@ void BlurEffect::screenAdded(EffectScreen *screen)
|
|||
connect(screen, &EffectScreen::geometryChanged, this, [this, screen]() {
|
||||
screenGeometryChanged(screen);
|
||||
});
|
||||
effects->makeOpenGLContextCurrent();
|
||||
updateTexture(screen);
|
||||
}
|
||||
|
||||
void BlurEffect::screenRemoved(EffectScreen *screen)
|
||||
|
@ -126,19 +123,54 @@ void BlurEffect::screenRemoved(EffectScreen *screen)
|
|||
|
||||
void BlurEffect::screenGeometryChanged(EffectScreen *screen)
|
||||
{
|
||||
effects->makeOpenGLContextCurrent();
|
||||
updateTexture(screen);
|
||||
|
||||
// Fetch the blur regions for all windows
|
||||
const auto stackingOrder = effects->stackingOrder();
|
||||
for (EffectWindow *window : stackingOrder) {
|
||||
updateBlurRegion(window);
|
||||
}
|
||||
effects->doneOpenGLContextCurrent();
|
||||
}
|
||||
|
||||
void BlurEffect::updateTexture(EffectScreen *screen)
|
||||
bool BlurEffect::updateTexture(EffectScreen *screen, const RenderTarget &renderTarget)
|
||||
{
|
||||
GLenum textureFormat = GL_RGBA8;
|
||||
if (renderTarget.colorspace() == Colorspace::sRGB) {
|
||||
if (!GLPlatform::instance()->isGLES()) {
|
||||
GLuint prevFbo = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&prevFbo));
|
||||
|
||||
if (prevFbo != 0) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
GLenum colorEncoding = GL_LINEAR;
|
||||
glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT,
|
||||
GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
|
||||
reinterpret_cast<GLint *>(&colorEncoding));
|
||||
|
||||
if (prevFbo != 0) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
|
||||
}
|
||||
|
||||
if (colorEncoding == GL_SRGB) {
|
||||
textureFormat = GL_SRGB8_ALPHA8;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
textureFormat = GL_RGBA16F;
|
||||
}
|
||||
|
||||
// Note that we currently render the entire blur effect in logical
|
||||
// coordinates - this means that when using high DPI screens the underlying
|
||||
// texture will be low DPI. This isn't really visible since we're blurring
|
||||
// anyway.
|
||||
const auto screenSize = screen ? screen->geometry().size() : effects->virtualScreenSize();
|
||||
|
||||
if (auto it = m_screenData.find(screen); it != m_screenData.end()) {
|
||||
const auto &texture = it->second.renderTargetTextures.front();
|
||||
if (texture->internalFormat() == textureFormat && texture->size() == screenSize) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
ScreenData data;
|
||||
/* Reserve memory for:
|
||||
* - The original sized texture (1)
|
||||
|
@ -148,36 +180,6 @@ void BlurEffect::updateTexture(EffectScreen *screen)
|
|||
data.renderTargets.reserve(m_downSampleIterations + 2);
|
||||
data.renderTargetTextures.reserve(m_downSampleIterations + 2);
|
||||
|
||||
GLenum textureFormat = GL_RGBA8;
|
||||
|
||||
// Check the color encoding of the default framebuffer
|
||||
if (!GLPlatform::instance()->isGLES()) {
|
||||
GLuint prevFbo = 0;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, reinterpret_cast<GLint *>(&prevFbo));
|
||||
|
||||
if (prevFbo != 0) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
GLenum colorEncoding = GL_LINEAR;
|
||||
glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_BACK_LEFT,
|
||||
GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
|
||||
reinterpret_cast<GLint *>(&colorEncoding));
|
||||
|
||||
if (prevFbo != 0) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prevFbo);
|
||||
}
|
||||
|
||||
if (colorEncoding == GL_SRGB) {
|
||||
textureFormat = GL_SRGB8_ALPHA8;
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we currently render the entire blur effect in logical
|
||||
// coordinates - this means that when using high DPI screens the underlying
|
||||
// texture will be low DPI. This isn't really visible since we're blurring
|
||||
// anyway.
|
||||
const auto screenSize = screen ? screen->geometry().size() : effects->virtualScreenSize();
|
||||
for (int i = 0; i <= m_downSampleIterations; i++) {
|
||||
data.renderTargetTextures.push_back(std::make_unique<GLTexture>(textureFormat, screenSize / (1 << i)));
|
||||
data.renderTargetTextures.back()->setFilter(GL_LINEAR);
|
||||
|
@ -197,7 +199,7 @@ void BlurEffect::updateTexture(EffectScreen *screen)
|
|||
return fbo->valid();
|
||||
});
|
||||
if (!renderTargetsValid) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare the stack for the rendering
|
||||
|
@ -217,6 +219,7 @@ void BlurEffect::updateTexture(EffectScreen *screen)
|
|||
data.renderTargetStack.push(data.renderTargets.front().get());
|
||||
|
||||
m_screenData[screen] = std::move(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BlurEffect::initBlurStrengthValues()
|
||||
|
@ -291,14 +294,6 @@ void BlurEffect::reconfigure(ReconfigureFlags flags)
|
|||
// Invalidate noise texture
|
||||
m_noiseTexture.reset();
|
||||
|
||||
if (effects->waylandDisplay()) {
|
||||
for (const auto screen : effects->screens()) {
|
||||
updateTexture(screen);
|
||||
}
|
||||
} else {
|
||||
updateTexture(nullptr);
|
||||
}
|
||||
|
||||
// Update all windows for the blur to take effect
|
||||
effects->addRepaintFull();
|
||||
}
|
||||
|
@ -609,7 +604,7 @@ void BlurEffect::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::
|
|||
|
||||
bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintData &data) const
|
||||
{
|
||||
if (m_screenData.empty() || !m_shader || !m_shader->isValid()) {
|
||||
if (!m_shader || !m_shader->isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -634,6 +629,9 @@ bool BlurEffect::shouldBlur(const EffectWindow *w, int mask, const WindowPaintDa
|
|||
void BlurEffect::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion ®ion, WindowPaintData &data)
|
||||
{
|
||||
if (shouldBlur(w, mask, data)) {
|
||||
if (!updateTexture(m_currentScreen, renderTarget)) {
|
||||
return;
|
||||
}
|
||||
const QRect screen = viewport.renderRect().toRect();
|
||||
QRegion shape = blurRegion(w).translated(w->pos().toPoint());
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ private:
|
|||
QRect expand(const QRect &rect) const;
|
||||
QRegion expand(const QRegion ®ion) const;
|
||||
void initBlurStrengthValues();
|
||||
void updateTexture(EffectScreen *screen);
|
||||
bool updateTexture(EffectScreen *screen, const RenderTarget &renderTarget);
|
||||
QRegion blurRegion(const EffectWindow *w) const;
|
||||
QRegion decorationBlurRegion(const EffectWindow *w) const;
|
||||
bool decorationSupportsBlurBehind(const EffectWindow *w) const;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
// KConfigSkeleton
|
||||
#include "mouseclickconfig.h"
|
||||
|
||||
#include "libkwineffects/rendertarget.h"
|
||||
#include "libkwineffects/renderviewport.h"
|
||||
|
||||
#include <QAction>
|
||||
|
@ -101,7 +102,7 @@ void MouseClickEffect::paintScreen(const RenderTarget &renderTarget, const Rende
|
|||
effects->paintScreen(renderTarget, viewport, mask, region, screen);
|
||||
|
||||
if (effects->isOpenGLCompositing()) {
|
||||
paintScreenSetupGl(viewport.projectionMatrix());
|
||||
paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
|
||||
}
|
||||
for (const auto &click : m_clicks) {
|
||||
for (int i = 0; i < m_ringCount; ++i) {
|
||||
|
@ -301,10 +302,11 @@ void MouseClickEffect::drawCircleQPainter(const QColor &color, float cx, float c
|
|||
painter->restore();
|
||||
}
|
||||
|
||||
void MouseClickEffect::paintScreenSetupGl(const QMatrix4x4 &projectionMatrix)
|
||||
void MouseClickEffect::paintScreenSetupGl(const RenderTarget &renderTarget, const QMatrix4x4 &projectionMatrix)
|
||||
{
|
||||
GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor);
|
||||
GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace);
|
||||
shader->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix);
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
|
||||
glLineWidth(m_lineWidth);
|
||||
glEnable(GL_BLEND);
|
||||
|
|
|
@ -135,7 +135,7 @@ private:
|
|||
|
||||
void drawCircleGl(const RenderViewport &viewport, const QColor &color, float cx, float cy, float r);
|
||||
void drawCircleQPainter(const QColor &color, float cx, float cy, float r);
|
||||
void paintScreenSetupGl(const QMatrix4x4 &projectionMatrix);
|
||||
void paintScreenSetupGl(const RenderTarget &renderTarget, const QMatrix4x4 &projectionMatrix);
|
||||
void paintScreenFinishGl();
|
||||
|
||||
QColor m_colors[BUTTON_COUNT];
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "libkwineffects/kwinconfig.h"
|
||||
#include "libkwineffects/kwinglplatform.h"
|
||||
#include "libkwineffects/rendertarget.h"
|
||||
#include "libkwineffects/renderviewport.h"
|
||||
#include <KGlobalAccel>
|
||||
#include <KLocalizedString>
|
||||
|
@ -90,8 +91,9 @@ void MouseMarkEffect::paintScreen(const RenderTarget &renderTarget, const Render
|
|||
vbo->setUseColor(true);
|
||||
vbo->setColor(color);
|
||||
const auto scale = viewport.scale();
|
||||
ShaderBinder binder(ShaderTrait::UniformColor);
|
||||
ShaderBinder binder(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace);
|
||||
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, viewport.projectionMatrix());
|
||||
binder.shader()->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
QVector<float> verts;
|
||||
for (const Mark &mark : std::as_const(marks)) {
|
||||
verts.clear();
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
// KWin
|
||||
#include "libkwineffects/kwingltexture.h"
|
||||
#include "libkwineffects/kwinglutils.h"
|
||||
#include "libkwineffects/rendertarget.h"
|
||||
#include "libkwineffects/renderviewport.h"
|
||||
// KDE
|
||||
#include <Plasma/Svg>
|
||||
|
@ -80,7 +81,8 @@ void ScreenEdgeEffect::paintScreen(const RenderTarget &renderTarget, const Rende
|
|||
GLTexture *texture = glow->texture.get();
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::Modulate);
|
||||
ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::Modulate | ShaderTrait::TransformColorspace);
|
||||
binder.shader()->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
const QVector4D constant(opacity, opacity, opacity, opacity);
|
||||
binder.shader()->setUniform(GLShader::ModulationConstant, constant);
|
||||
const auto scale = viewport.scale();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "snaphelper.h"
|
||||
|
||||
#include "libkwineffects/kwinglutils.h"
|
||||
#include "libkwineffects/rendertarget.h"
|
||||
#include "libkwineffects/renderviewport.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
@ -105,8 +106,9 @@ void SnapHelperEffect::paintScreen(const RenderTarget &renderTarget, const Rende
|
|||
GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
|
||||
vbo->reset();
|
||||
vbo->setUseColor(true);
|
||||
ShaderBinder binder(ShaderTrait::UniformColor);
|
||||
ShaderBinder binder(ShaderTrait::UniformColor | ShaderTrait::TransformColorspace);
|
||||
binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, viewport.projectionMatrix());
|
||||
binder.shader()->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
|
|
|
@ -213,17 +213,20 @@ void StartupFeedbackEffect::paintScreen(const RenderTarget &renderTarget, const
|
|||
}
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
GLShader *shader = nullptr;
|
||||
if (m_type == BlinkingFeedback && m_blinkingShader && m_blinkingShader->isValid()) {
|
||||
const QColor &blinkingColor = BLINKING_COLORS[FRAME_TO_BLINKING_COLOR[m_frame]];
|
||||
ShaderManager::instance()->pushShader(m_blinkingShader.get());
|
||||
shader = m_blinkingShader.get();
|
||||
m_blinkingShader->setUniform(GLShader::Color, blinkingColor);
|
||||
} else {
|
||||
ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
|
||||
}
|
||||
const auto scale = viewport.scale();
|
||||
QMatrix4x4 mvp = viewport.projectionMatrix();
|
||||
mvp.translate(m_currentGeometry.x() * scale, m_currentGeometry.y() * scale);
|
||||
ShaderManager::instance()->getBoundShader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
|
||||
shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
texture->render(m_currentGeometry.size(), scale);
|
||||
ShaderManager::instance()->popShader();
|
||||
glDisable(GL_BLEND);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "touchpoints.h"
|
||||
|
||||
#include "libkwineffects/kwinglutils.h"
|
||||
#include "libkwineffects/rendertarget.h"
|
||||
#include "libkwineffects/renderviewport.h"
|
||||
#include <QAction>
|
||||
|
||||
|
@ -123,7 +124,7 @@ void TouchPointsEffect::paintScreen(const RenderTarget &renderTarget, const Rend
|
|||
effects->paintScreen(renderTarget, viewport, mask, region, screen);
|
||||
|
||||
if (effects->isOpenGLCompositing()) {
|
||||
paintScreenSetupGl(viewport.projectionMatrix());
|
||||
paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
|
||||
}
|
||||
for (auto it = m_points.constBegin(), end = m_points.constEnd(); it != end; ++it) {
|
||||
for (int i = 0; i < m_ringCount; ++i) {
|
||||
|
@ -227,10 +228,11 @@ void TouchPointsEffect::drawCircleQPainter(const QColor &color, float cx, float
|
|||
painter->restore();
|
||||
}
|
||||
|
||||
void TouchPointsEffect::paintScreenSetupGl(const QMatrix4x4 &projectionMatrix)
|
||||
void TouchPointsEffect::paintScreenSetupGl(const RenderTarget &renderTarget, const QMatrix4x4 &projectionMatrix)
|
||||
{
|
||||
GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor);
|
||||
shader->setUniform(GLShader::ModelViewProjectionMatrix, projectionMatrix);
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
|
||||
glLineWidth(m_lineWidth);
|
||||
glEnable(GL_BLEND);
|
||||
|
|
|
@ -61,7 +61,7 @@ private:
|
|||
float computeRadius(int time, bool press, int ring);
|
||||
void drawCircleGl(const RenderViewport &viewport, const QColor &color, float cx, float cy, float r);
|
||||
void drawCircleQPainter(const QColor &color, float cx, float cy, float r);
|
||||
void paintScreenSetupGl(const QMatrix4x4 &projectionMatrix);
|
||||
void paintScreenSetupGl(const RenderTarget &renderTarget, const QMatrix4x4 &projectionMatrix);
|
||||
void paintScreenFinishGl();
|
||||
|
||||
Qt::GlobalColor colorForId(quint32 id);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "libkwineffects/kwinconfig.h"
|
||||
#include "libkwineffects/kwinglutils.h"
|
||||
#include "libkwineffects/rendertarget.h"
|
||||
#include "libkwineffects/renderviewport.h"
|
||||
|
||||
#include <KGlobalAccel>
|
||||
|
@ -103,11 +104,12 @@ void TrackMouseEffect::paintScreen(const RenderTarget &renderTarget, const Rende
|
|||
effects->paintScreen(renderTarget, viewport, mask, region, screen); // paint normal screen
|
||||
|
||||
if (effects->isOpenGLCompositing() && m_texture[0] && m_texture[1]) {
|
||||
ShaderBinder binder(ShaderTrait::MapTexture);
|
||||
ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
|
||||
GLShader *shader(binder.shader());
|
||||
if (!shader) {
|
||||
return;
|
||||
}
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
QMatrix4x4 matrix(viewport.projectionMatrix());
|
||||
|
|
|
@ -255,7 +255,7 @@ void ZoomEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseco
|
|||
effects->prePaintScreen(data, presentTime);
|
||||
}
|
||||
|
||||
ZoomEffect::OffscreenData *ZoomEffect::ensureOffscreenData(const RenderViewport &viewport, EffectScreen *screen)
|
||||
ZoomEffect::OffscreenData *ZoomEffect::ensureOffscreenData(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectScreen *screen)
|
||||
{
|
||||
const QRect rect = effects->waylandDisplay() ? screen->geometry() : effects->virtualScreenGeometry();
|
||||
const qreal devicePixelRatio = effects->waylandDisplay() ? screen->devicePixelRatio() : 1;
|
||||
|
@ -264,8 +264,9 @@ ZoomEffect::OffscreenData *ZoomEffect::ensureOffscreenData(const RenderViewport
|
|||
OffscreenData &data = m_offscreenData[effects->waylandDisplay() ? screen : nullptr];
|
||||
data.viewport = rect;
|
||||
|
||||
if (!data.texture || data.texture->size() != nativeSize) {
|
||||
data.texture.reset(new GLTexture(GL_RGBA8, nativeSize));
|
||||
const GLenum textureFormat = renderTarget.colorspace() == Colorspace::sRGB ? GL_RGBA8 : GL_RGBA16F;
|
||||
if (!data.texture || data.texture->size() != nativeSize || data.texture->internalFormat() != textureFormat) {
|
||||
data.texture.reset(new GLTexture(textureFormat, nativeSize));
|
||||
data.texture->setFilter(GL_LINEAR);
|
||||
data.texture->setWrapMode(GL_CLAMP_TO_EDGE);
|
||||
data.framebuffer = std::make_unique<GLFramebuffer>(data.texture.get());
|
||||
|
@ -276,10 +277,10 @@ ZoomEffect::OffscreenData *ZoomEffect::ensureOffscreenData(const RenderViewport
|
|||
|
||||
void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, EffectScreen *screen)
|
||||
{
|
||||
OffscreenData *offscreenData = ensureOffscreenData(viewport, screen);
|
||||
OffscreenData *offscreenData = ensureOffscreenData(renderTarget, viewport, screen);
|
||||
|
||||
// Render the scene in an offscreen texture and then upscale it.
|
||||
RenderTarget offscreenRenderTarget(offscreenData->framebuffer.get());
|
||||
RenderTarget offscreenRenderTarget(offscreenData->framebuffer.get(), renderTarget.colorspace(), renderTarget.sdrBrightness());
|
||||
RenderViewport offscreenViewport(screen->geometry(), screen->devicePixelRatio(), offscreenRenderTarget);
|
||||
GLFramebuffer::pushFramebuffer(offscreenData->framebuffer.get());
|
||||
effects->paintScreen(offscreenRenderTarget, offscreenViewport, mask, region, screen);
|
||||
|
@ -382,7 +383,8 @@ void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewp
|
|||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
|
||||
auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
|
||||
s->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
QMatrix4x4 mvp = viewport.projectionMatrix();
|
||||
mvp.translate(p.x() * scale, p.y() * scale);
|
||||
s->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
|
||||
|
|
|
@ -93,7 +93,7 @@ private:
|
|||
};
|
||||
|
||||
GLTexture *ensureCursorTexture();
|
||||
OffscreenData *ensureOffscreenData(const RenderViewport &viewport, EffectScreen *screen);
|
||||
OffscreenData *ensureOffscreenData(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectScreen *screen);
|
||||
void markCursorTextureDirty();
|
||||
|
||||
#if HAVE_ACCESSIBILITY
|
||||
|
|
|
@ -42,11 +42,11 @@ void CursorDelegateOpenGL::paint(const RenderTarget &renderTarget, const QRegion
|
|||
// Render the cursor scene in an offscreen render target.
|
||||
const QSize bufferSize = (Cursors::self()->currentCursor()->rect().size() * scale).toSize();
|
||||
if (!m_texture || m_texture->size() != bufferSize) {
|
||||
m_texture = std::make_unique<GLTexture>(GL_RGBA8, bufferSize);
|
||||
m_texture = std::make_unique<GLTexture>(renderTarget.framebuffer()->colorAttachment()->internalFormat(), bufferSize);
|
||||
m_framebuffer = std::make_unique<GLFramebuffer>(m_texture.get());
|
||||
}
|
||||
|
||||
RenderTarget offscreenRenderTarget(m_framebuffer.get());
|
||||
RenderTarget offscreenRenderTarget(m_framebuffer.get(), renderTarget.colorspace());
|
||||
|
||||
RenderLayer renderLayer(layer()->loop());
|
||||
renderLayer.setDelegate(std::make_unique<SceneDelegate>(Compositor::self()->cursorScene(), m_output));
|
||||
|
|
|
@ -283,7 +283,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
|||
|
||||
const size_t size = totalVertexCount * sizeof(GLVertex2D);
|
||||
|
||||
ShaderTraits shaderTraits = ShaderTrait::MapTexture;
|
||||
ShaderTraits shaderTraits = ShaderTrait::MapTexture | ShaderTrait::TransformColorspace;
|
||||
|
||||
if (data.brightness() != 1.0) {
|
||||
shaderTraits |= ShaderTrait::Modulate;
|
||||
|
@ -322,6 +322,7 @@ void ItemRendererOpenGL::renderItem(const RenderTarget &renderTarget, const Rend
|
|||
|
||||
GLShader *shader = ShaderManager::instance()->pushShader(shaderTraits);
|
||||
shader->setUniform(GLShader::Saturation, data.saturation());
|
||||
shader->setColorspaceUniforms(Colorspace::sRGB, renderTarget);
|
||||
|
||||
if (renderContext.hardwareClipping) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
|
|
|
@ -24,7 +24,7 @@ using namespace KWin;
|
|||
namespace KWaylandServer
|
||||
{
|
||||
|
||||
static const quint32 s_version = 2;
|
||||
static const quint32 s_version = 3;
|
||||
|
||||
static QtWaylandServer::kde_output_device_v2::transform kwinTransformToOutputDeviceTransform(Output::Transform transform)
|
||||
{
|
||||
|
@ -48,6 +48,12 @@ static uint32_t kwinCapabilitiesToOutputDeviceCapabilities(Output::Capabilities
|
|||
if (caps & Output::Capability::RgbRange) {
|
||||
ret |= QtWaylandServer::kde_output_device_v2::capability_rgb_range;
|
||||
}
|
||||
if (caps & Output::Capability::HighDynamicRange) {
|
||||
ret |= QtWaylandServer::kde_output_device_v2::capability_high_dynamic_range;
|
||||
}
|
||||
if (caps & Output::Capability::WideColorGamut) {
|
||||
ret |= QtWaylandServer::kde_output_device_v2::capability_wide_color_gamut;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -82,6 +88,9 @@ public:
|
|||
void sendOverscan(Resource *resource);
|
||||
void sendVrrPolicy(Resource *resource);
|
||||
void sendRgbRange(Resource *resource);
|
||||
void sendHighDynamicRange(Resource *resource);
|
||||
void sendSdrBrightness(Resource *resource);
|
||||
void sendWideColorGamut(Resource *resource);
|
||||
|
||||
OutputDeviceV2Interface *q;
|
||||
QPointer<Display> m_display;
|
||||
|
@ -105,6 +114,9 @@ public:
|
|||
uint32_t m_overscan = 0;
|
||||
vrr_policy m_vrrPolicy = vrr_policy_automatic;
|
||||
rgb_range m_rgbRange = rgb_range_automatic;
|
||||
bool m_highDynamicRange = false;
|
||||
uint32_t m_sdrBrightness = 200;
|
||||
bool m_wideColorGamut = false;
|
||||
|
||||
protected:
|
||||
void kde_output_device_v2_bind_resource(Resource *resource) override;
|
||||
|
@ -182,6 +194,9 @@ OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, KWin::Output
|
|||
updateRgbRange();
|
||||
updateName();
|
||||
updateModes();
|
||||
updateHighDynamicRange();
|
||||
updateSdrBrightness();
|
||||
updateWideColorGamut();
|
||||
|
||||
connect(handle, &Output::geometryChanged,
|
||||
this, &OutputDeviceV2Interface::updateGlobalPosition);
|
||||
|
@ -203,6 +218,9 @@ OutputDeviceV2Interface::OutputDeviceV2Interface(Display *display, KWin::Output
|
|||
this, &OutputDeviceV2Interface::updateModes);
|
||||
connect(handle, &Output::rgbRangeChanged,
|
||||
this, &OutputDeviceV2Interface::updateRgbRange);
|
||||
connect(handle, &Output::highDynamicRangeChanged, this, &OutputDeviceV2Interface::updateHighDynamicRange);
|
||||
connect(handle, &Output::sdrBrightnessChanged, this, &OutputDeviceV2Interface::updateSdrBrightness);
|
||||
connect(handle, &Output::wideColorGamutChanged, this, &OutputDeviceV2Interface::updateWideColorGamut);
|
||||
}
|
||||
|
||||
OutputDeviceV2Interface::~OutputDeviceV2Interface()
|
||||
|
@ -253,6 +271,9 @@ void OutputDeviceV2InterfacePrivate::kde_output_device_v2_bind_resource(Resource
|
|||
sendOverscan(resource);
|
||||
sendVrrPolicy(resource);
|
||||
sendRgbRange(resource);
|
||||
sendHighDynamicRange(resource);
|
||||
sendSdrBrightness(resource);
|
||||
sendWideColorGamut(resource);
|
||||
sendDone(resource);
|
||||
}
|
||||
|
||||
|
@ -350,6 +371,27 @@ void OutputDeviceV2InterfacePrivate::sendRgbRange(Resource *resource)
|
|||
send_rgb_range(resource->handle, m_rgbRange);
|
||||
}
|
||||
|
||||
void OutputDeviceV2InterfacePrivate::sendHighDynamicRange(Resource *resource)
|
||||
{
|
||||
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_HIGH_DYNAMIC_RANGE_SINCE_VERSION) {
|
||||
send_high_dynamic_range(resource->handle, m_highDynamicRange ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputDeviceV2InterfacePrivate::sendSdrBrightness(Resource *resource)
|
||||
{
|
||||
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_SDR_BRIGHTNESS_SINCE_VERSION) {
|
||||
send_sdr_brightness(resource->handle, m_sdrBrightness);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputDeviceV2InterfacePrivate::sendWideColorGamut(Resource *resource)
|
||||
{
|
||||
if (resource->version() >= KDE_OUTPUT_DEVICE_V2_WIDE_COLOR_GAMUT_SINCE_VERSION) {
|
||||
send_wide_color_gamut(resource->handle, m_wideColorGamut ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void OutputDeviceV2Interface::updateGeometry()
|
||||
{
|
||||
const auto clientResources = d->resourceMap();
|
||||
|
@ -570,6 +612,42 @@ void OutputDeviceV2Interface::updateRgbRange()
|
|||
}
|
||||
}
|
||||
|
||||
void OutputDeviceV2Interface::updateHighDynamicRange()
|
||||
{
|
||||
if (d->m_highDynamicRange != d->m_handle->highDynamicRange()) {
|
||||
d->m_highDynamicRange = d->m_handle->highDynamicRange();
|
||||
const auto clientResources = d->resourceMap();
|
||||
for (const auto &resource : clientResources) {
|
||||
d->sendHighDynamicRange(resource);
|
||||
d->sendDone(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OutputDeviceV2Interface::updateSdrBrightness()
|
||||
{
|
||||
if (d->m_sdrBrightness != d->m_handle->sdrBrightness()) {
|
||||
d->m_sdrBrightness = d->m_handle->sdrBrightness();
|
||||
const auto clientResources = d->resourceMap();
|
||||
for (const auto &resource : clientResources) {
|
||||
d->sendSdrBrightness(resource);
|
||||
d->sendDone(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OutputDeviceV2Interface::updateWideColorGamut()
|
||||
{
|
||||
if (d->m_wideColorGamut != d->m_handle->wideColorGamut()) {
|
||||
d->m_wideColorGamut = d->m_handle->wideColorGamut();
|
||||
const auto clientResources = d->resourceMap();
|
||||
for (const auto &resource : clientResources) {
|
||||
d->sendWideColorGamut(resource);
|
||||
d->sendDone(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutputDeviceV2Interface *OutputDeviceV2Interface::get(wl_resource *native)
|
||||
{
|
||||
if (auto devicePrivate = resource_cast<OutputDeviceV2InterfacePrivate *>(native); devicePrivate && !devicePrivate->isGlobalRemoved()) {
|
||||
|
|
|
@ -73,6 +73,9 @@ private:
|
|||
void updateVrrPolicy();
|
||||
void updateRgbRange();
|
||||
void updateGeometry();
|
||||
void updateHighDynamicRange();
|
||||
void updateSdrBrightness();
|
||||
void updateWideColorGamut();
|
||||
|
||||
std::unique_ptr<OutputDeviceV2InterfacePrivate> d;
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ using namespace KWin;
|
|||
namespace KWaylandServer
|
||||
{
|
||||
|
||||
static const quint32 s_version = 3;
|
||||
static const quint32 s_version = 4;
|
||||
|
||||
class OutputManagementV2InterfacePrivate : public QtWaylandServer::kde_output_management_v2
|
||||
{
|
||||
|
@ -61,6 +61,9 @@ protected:
|
|||
void kde_output_configuration_v2_set_rgb_range(Resource *resource, wl_resource *outputdevice, uint32_t rgbRange) override;
|
||||
void kde_output_configuration_v2_set_primary_output(Resource *resource, struct ::wl_resource *output) override;
|
||||
void kde_output_configuration_v2_set_priority(Resource *resource, wl_resource *output, uint32_t priority) override;
|
||||
void kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr) override;
|
||||
void kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness) override;
|
||||
void kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg) override;
|
||||
};
|
||||
|
||||
OutputManagementV2InterfacePrivate::OutputManagementV2InterfacePrivate(Display *display)
|
||||
|
@ -241,6 +244,36 @@ void OutputConfigurationV2Interface::kde_output_configuration_v2_set_priority(Re
|
|||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_high_dynamic_range(Resource *resource, wl_resource *outputdevice, uint32_t enable_hdr)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->highDynamicRange = enable_hdr == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_sdr_brightness(Resource *resource, wl_resource *outputdevice, uint32_t sdr_brightness)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->sdrBrightness = sdr_brightness;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_set_wide_color_gamut(Resource *resource, wl_resource *outputdevice, uint32_t enable_wcg)
|
||||
{
|
||||
if (invalid) {
|
||||
return;
|
||||
}
|
||||
if (OutputDeviceV2Interface *output = OutputDeviceV2Interface::get(outputdevice)) {
|
||||
config.changeSet(output->handle())->wideColorGamut = enable_wcg == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputConfigurationV2Interface::kde_output_configuration_v2_destroy(Resource *resource)
|
||||
{
|
||||
wl_resource_destroy(resource->handle);
|
||||
|
|
Loading…
Reference in a new issue