博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
当我们在谈论色温调节的时候,我们在谈论什么
阅读量:4103 次
发布时间:2019-05-25

本文共 7561 字,大约阅读时间需要 25 分钟。

总述

前段时间在一个项目里,做了一个色温调节的东西。发现这部分的资料有点少,所幸研究了一段时间,记录一下,免得后面又忘记了。

色温调节是上层发出,最终由显卡驱动处理的一个事件。桌面一般都是采用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函数。然而不是所有得到显卡都实现了这个功能,因此给用户看到的现象是:有的卡能调色温,有的卡不支持。

转载地址:http://gsbsi.baihongyu.com/

你可能感兴趣的文章
vue 遍历对象并动态绑定在下拉列表中
查看>>
Vue动态生成el-checkbox点击无法选中的解决方法
查看>>
python __future__
查看>>
MySQL Tricks1
查看>>
python 变量作用域问题(经典坑)
查看>>
pytorch
查看>>
pytorch(二)
查看>>
pytorch(三)
查看>>
pytorch(四)
查看>>
pytorch(5)
查看>>
pytorch(6)
查看>>
opencv 指定版本下载
查看>>
ubuntu相关
查看>>
C++ 调用json
查看>>
nano中设置脚本开机自启动
查看>>
动态库调动态库
查看>>
Kubernetes集群搭建之CNI-Flanneld部署篇
查看>>
k8s web终端连接工具
查看>>
手绘VS码绘(一):静态图绘制(码绘使用P5.js)
查看>>
手绘VS码绘(二):动态图绘制(码绘使用Processing)
查看>>