当我们在谈论色温调节的时候,我们在谈论什么

总述

前段时间在一个项目里,做了一个色温调节的东西。发现这部分的资料有点少,所幸研究了一段时间,记录一下,免得后面又忘记了。
色温调节是上层发出,最终由显卡驱动处理的一个事件。桌面一般都是采用redshift 调节色温,到xserver,xserver进行一些自己的处理,转由libdrm用drm ioctl陷入内核drm模块,最终到实际的显卡驱动。redshift这边没怎么研究过,本文就先从xserver讲起吧。涉及到具体的显卡驱动,就用radeon驱动为例。

xserver

用户可以直接用xrand命令调节色温:

xrandr --output XXXX --gamma 1:1:1

可以用这个命令看看色温调节是否正常。
gamma参数后面第一个1是红色,第二个1是绿色,第三个1是蓝色。通过调三原色达到调色温的目的。先拉一份xserver的代码来看看xserver是怎么处理参数的。从debian的仓库里直接拉下,非常方便。apt source xorg-server(不要用sudo拉代码,不信你可以试试看)。经验之谈:代码拉下来之后,先打个包替换本地安装的xserver,验证代码的正确性。
dpkg-buildpackage -sa -nc -j40,第一次打包会很慢,稍微等一会。完成之后会在父目录下生成很多个deb包,暂时没分辨分别是什么什么包,所以统统安装,之后重启xserver。重启之后x依然运行正常,那就没什么问题了。
对于xserver而言,上层可以通过两种方式调节色温:一种是redshift;另一种是xrandr提供的gamma参数。这两种方式一开始调用的接口不同,殊途同归,最终通过同一个接口进到libdrm中去。

redshift调节色温xserver函数调用链

ProcVidModeSetGamma
{
 if (!pVidMode->SetGamma(pScreen, ((float) stuff->red) / 10000.,
                         ((float) stuff->green) / 10000.,
                         ((float) stuff->blue) / 10000.))
        return BadValue;
    return Success;
}
                ->xf86VidModeSetGamma
                        ->xf86ChangeGamma
                                ->CMapChangeGamma
                                        ->CMapReinstallMap
                                                ->CMapRefreshColors
                                                        ->xf86RandR12LoadPalette
                                                           {
                                                                for (c = 0; c < config->num_crtc; c++) {
                                                                    xf86CrtcPtr crtc = config->crtc[c];
                                                                    RRCrtcPtr randr_crtc = crtc->randr_crtc;

                                                                    if (randr_crtc) {
                                                                            xf86RandR12CrtcComputeGamma(crtc, colors, reds, greens, blues,
                                                                                                                            randr_crtc->gammaRed,
                                                                                                                            randr_crtc->gammaGreen,
                                                                                                                            randr_crtc->gammaBlue,
                                                                                                                            randr_crtc->gammaSize);
                                                                       } else {
                                                                                xf86RandR12CrtcComputeGamma(crtc, colors, reds, greens, blues,
                                                                                                                            NULL, NULL, NULL,
                                                                                                                            xf86GetGammaRampSize(pScreen));
                                                                       }
                                                                    xf86RandR12CrtcReloadGamma(crtc);
                                                                }
                                                         }
                                                                    ->xf86RandR12CrtcReloadGamma
                                                                            ->drmmode_crtc_gamma_set
                                                                                {
                                                                                     drmModeCrtcSetGamma(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
                                                                                                                                                size, red, green, blue);
                                                                                }

xf86VidModeSetGammaProcVidModeSetGamma接口实现在x扩展中,应该和x协议 xclient有关(细节待补充)。这个函数根据传入的参数,调用实现在xf86中的setgamma钩子函数,钩子上挂的是xf86VidModeSetGamma,这个函数没什么实际功能是个框架:转换参数,调用xf86ChangeGamma->CMapChangeGamma->``CMapReinstallMap->CMapRefreshColors->xf86RandR12LoadPalettexf86RandR12LoadPalette遍历每个crtc,分别计算gamma和load gamma。 用户可能修改一个或者多个gamma参数,在这里全部扫过之后,可以节省调用时间。 load gamma直接调用crtc的gamma set接口——drmmode_crtc_gamma_set,从这里正式进入libdrm里。
xrandr方式调节色温的调用链简单多了,从randr调用带xf86中,余下的和上面一样。

RRCrtcGammaSet
    ->xf86RandR12CrtcSetGamma
        ->xf86RandR12CrtcReloadGamma
            ->drmmode_crtc_gamma_set

libdrm

令人迷惑的xserver终于大概理清楚了,主要是能力有限,分析不清楚。libdrm的代码一般都很简单,做一个基本的判非操作之后,用ioctl陷入内核中。比如刚刚找不到的 函数,他的实现是这样的:

drm_public int drmModeCrtcSetGamma(int fd, uint32_t crtc_id, uint32_t size,

                                   uint16_t *red, uint16_t *green,

                                   uint16_t *blue)

{

    struct drm_mode_crtc_lut l;

    memclear(l);

    l.crtc_id = crtc_id;

    l.gamma_size = size;

    l.red = VOID2U64(red);

    l.green = VOID2U64(green);

    l.blue = VOID2U64(blue);

    return DRM_IOCTL(fd, DRM_IOCTL_MODE_SETGAMMA, &l);

}

熟悉内核代码的童鞋都知道,从ioctl开始就陷入内核了。接下来需要去drm驱动里找到对DRM_IOCTL_MODE_SETGAMMAflag的处理即可。

内核drm模块

drm 的ioctl接口都是实现在文件drivers/gpu/drm/drm_ioctl.c中,DRM_IOCTL_MODE_SETGAMMA对应的接口是drm_mode_gamma_set_ioctl

int drm_mode_gamma_set_ioctl(struct drm_device *dev,
                 void *data, struct drm_file *file_priv)
{
    struct drm_mode_crtc_lut *crtc_lut = data;
    struct drm_crtc *crtc;
    void *r_base, *g_base, *b_base;
    int size;
    struct drm_modeset_acquire_ctx ctx;
    int ret = 0;

    if (!drm_core_check_feature(dev, DRIVER_MODESET))
        return -EINVAL;
    crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id);
    if (!crtc)
        return -ENOENT;
    if (crtc->funcs->gamma_set == NULL)
        return -ENOSYS;

    drm_modeset_acquire_init(&ctx, 0);
retry:
    ret = drm_modeset_lock_all_ctx(dev, &ctx);
    if (ret)
        goto out;
    size = crtc_lut->gamma_size * (sizeof(uint16_t));
    r_base = crtc->gamma_store;
    if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
        ret = -EFAULT;
        goto out;
    }
    g_base = r_base + size;
    if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
        ret = -EFAULT;
        goto out;
    }
    b_base = g_base + size;
    if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
        ret = -EFAULT;
        goto out;
    }
    ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,
                     crtc->gamma_size, &ctx);
......
    return ret;
}

按照惯例,drm只实现框架代码并不care具体的功能,代码通常比较简单,没什么复杂的调用链。drm进来先判断操作的合法性,检查feature标志、根据用户参数获取crtc,判断显卡是否实现了gamma_set接口。全部正常之后,根据用户传入的参数计算红绿蓝三色的基准值,然后调用显卡自己实现的gamma_set接口。以radeon为例,大致调用链如下所示:

radeon_crtc_gamma_set
    ->radeon_crtc_load_lut
        {
#define ASIC_IS_DCE5(rdev) ((rdev->family >= CHIP_BARTS))
                if (ASIC_IS_DCE5(rdev))
                    dce5_crtc_load_lut(crtc);
                else if (ASIC_IS_DCE4(rdev))
                    dce4_crtc_load_lut(crtc);
                else if (ASIC_IS_AVIVO(rdev))
                    avivo_crtc_load_lut(crtc);
                else
                    legacy_crtc_load_lut(crtc);
        }
                 ->device dependence write registers

radeon gamma_set调用radeon_crtc_load_lut,load lut根据显卡芯片类型确定调用哪个,在load lut中实现写寄存器,这部分就和硬件相关了。不同的显卡芯片、不同给厂家的显卡,这部分实现都是不一样的。

Q & A

    1. 为什么有的设备能调节色温,有的设备不能调节色温呢?
      色温调节是要显卡支持的,如上分析,色温调节最终从drm 的gamma_set调用到显卡自身实现的gamma_set中去,真正执行调节功能的是显卡自身的gamma set函数。然而不是所有得到显卡都实现了这个功能,因此给用户看到的现象是:有的卡能调色温,有的卡不支持。
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
高通QDCM工具是高通公司开发的一款用于调节色温的工具。它可以用来调整显示设备的色温,使其更符合用户的使用需求和个人喜好。 使用QDCM工具进行色温调节时,首先需要了解色温的概念。色温是用来描述光源颜色特性的一个参数,通常用单位“开尔文”来表示。较低的色温(如2700K)表示较暖的黄色光,而较高的色温(如6500K)表示较冷的蓝色光。 在进行色温调节时,可以通过QDCM工具中的调节选项来改变显示设备的色温。一般情况下,这些选项包括冷色调、暖色调和正常色调。选择冷色调会使显示屏呈现较高的色温,显示出冷感,适用于需要较鲜艳色彩表现的场景。选择暖色调会使显示屏呈现较低的色温,显示出暖和舒适的感觉,适用于需要较柔和色彩表现的场景。选择正常色调会使显示屏展示默认设置的色温。 为了达到更准确的色温调节效果,建议使用QDCM工具中的高级选项。该选项允许用户自定义调整红、绿、蓝三个原色的亮度和色彩饱和度,以及整体色温的偏移量。通过微调这些参数,用户可以实现对色温的更加精细的调节。 需要注意的是,色温调节不仅仅影响显示设备的色彩表现,也会对眼睛产生一定的影响。较高的色温可能导致眼睛疲劳和干涩,而较低的色温可能导致视觉疲劳和暗部细节丢失。因此,在进行色温调节时,建议根据实际场景和个人使用需求进行合理的选择。 总结而言,高通QDCM工具是一款用于调节显示设备色温的工具,通过选择冷色调、暖色调和正常色调,以及使用高级选项进行精细调节,用户可以实现更符合个人需求和喜好的色温设置。但是,在进行调节时需要注意眼睛的舒适度和实际使用需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值