DRM/KMS defaults to limited color range (16-235) for RGB planes, should default to full (0-255) #1105
Replies: 1 comment
-
|
Hi Diego, I was the developer who implemented our COLOR_ENCODING and COLOR_RANGE support. When I added our COLOR_RANGE support, I designed it to be a no-op for non-YUV planes. In #if defined(NV_DRM_PLANE_CREATE_COLOR_PROPERTIES_PRESENT)
if ((nv_drm_plane_state->input_colorspace == NV_DRM_INPUT_COLOR_SPACE_NONE) &&
nv_drm_format_is_yuv(plane_state->fb->format->format)) {
req_config->config.inputColorSpace =
nv_drm_color_encoding_to_nvkms_colorspace(plane_state->color_encoding);
req_config->config.inputColorRange =
nv_drm_color_range_to_nvkms_color_range(plane_state->color_range);
req_config->config.inputTf = NVKMS_INPUT_TF_LINEAR;
} else {
#endif
req_config->config.inputColorSpace =
nv_get_nvkms_input_colorspace(nv_drm_plane_state->input_colorspace);
req_config->config.inputColorRange = NVKMS_INPUT_COLOR_RANGE_DEFAULT;
req_config->config.inputTf =
nv_get_nvkms_input_tf(nv_drm_plane_state->input_colorspace);
#if defined(NV_DRM_PLANE_CREATE_COLOR_PROPERTIES_PRESENT)
}
#endifWhat this means is that so long as the deprecated NV_INPUT_COLORSPACE is unused and the plane's framebuffer color format is a YUV format, we send the requested color encoding and range to NvKms, converted to equivalent NvKms enum values. Otherwise, we send the NV_INPUT_COLORSPACE value (which is likely NONE) and NVKMS_INPUT_COLOR_RANGE_DEFAULT to NvKms. In other words, no matter what the client specifies to COLOR_ENCODING, so long as the plane's framebuffer is in an RGB color format, NVKMS_INPUT_COLOR_RANGE_DEFAULT will always be given to NvKms. Next, if we trace the plumbing of the inputColorSpace parameter down into NvKms, we eventually arrive at this function in static enum FMTCoeffType EvoGetFMTCoeffType(
NvBool isYUV,
enum NvKmsInputColorSpace colorSpace,
NvU8 depthPerComponent,
enum NvKmsInputColorRange colorRange)
{
#define FMT(nvkms_space, coeff_space, depth, nvkms_range, coeff_range) \
if ((colorSpace == NVKMS_INPUT_COLOR_SPACE_##nvkms_space) && \
(depthPerComponent == depth) && \
(colorRange == NVKMS_INPUT_COLOR_RANGE_##nvkms_range)) { \
return FMT_COEFF_TYPE_##coeff_space##_YUV_##depth##BPC_##coeff_range##_TO_RGB_16BPC_FULL; \
}
// RGB colorspaces use identity FMT
if (!isYUV) {
return FMT_COEFF_TYPE_IDENTITY;
}
if (colorRange == NVKMS_INPUT_COLOR_RANGE_DEFAULT) {
// For YUV, default to limited color range
colorRange = NVKMS_INPUT_COLOR_RANGE_LIMITED;
}
FMT(BT601, REC601, 8, LIMITED, LTD);
FMT(BT601, REC601, 8, FULL, FULL);
FMT(BT601, REC601, 10, LIMITED, LTD);
FMT(BT601, REC601, 10, FULL, FULL);
FMT(BT601, REC601, 12, LIMITED, LTD);
FMT(BT601, REC601, 12, FULL, FULL);
FMT(BT709, REC709, 8, LIMITED, LTD);
FMT(BT709, REC709, 8, FULL, FULL);
FMT(BT709, REC709, 10, LIMITED, LTD);
FMT(BT709, REC709, 10, FULL, FULL);
FMT(BT709, REC709, 12, LIMITED, LTD);
FMT(BT709, REC709, 12, FULL, FULL);
FMT(BT2100, REC2020, 8, LIMITED, LTD);
FMT(BT2100, REC2020, 8, FULL, FULL);
FMT(BT2100, REC2020, 10, LIMITED, LTD);
FMT(BT2100, REC2020, 10, FULL, FULL);
FMT(BT2100, REC2020, 12, LIMITED, LTD);
FMT(BT2100, REC2020, 12, FULL, FULL);
// Unsupported formats also use identity FMT
return FMT_COEFF_TYPE_IDENTITY;
#undef FMT
}This function is responsible for using the input color space, color format, depth per component, and color range to pick a set of parameters for the FMT matrix. The first thing this function does is simply return the identity matrix for any non-YUV planes. As a result, even if we somehow pass an arbitrary color range value to NvKms with an RGB surface, it will have no effect. I have verified that there is nowhere else where the input color range is used to change any values sent to the hardware. As a result, I'd be surprised if the workaround had an effect. That said, could you supply a photograph with and without your workaround enabled? An nvidia-bug-report.log.gz both with and without the workaround would also be very helpful. Thanks, Jacob |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
The NVIDIA DRM/KMS output path defaults to limited color range (16-235) for RGB content when the
COLOR_RANGEplane property is not explicitly set. The X11 DDX driver defaults to full range (0-255) for the same hardware. This causes every Wayland compositor to produce washed-out output compared to X11.How to reproduce
The difference can be confirmed with
nvidia-settingson X11:ColorRange = 0(Full): correct outputColorRange = 1(Limited): identical to Wayland appearanceRoot cause
In
kernel-open/nvidia-drm/nvidia-drm-crtc.c, the functionnv_drm_color_range_to_nvkms_color_range()maps:DRM_COLOR_YCBCR_FULL_RANGE→NVKMS_INPUT_COLOR_RANGE_FULLDRM_COLOR_YCBCR_LIMITED_RANGE→NVKMS_INPUT_COLOR_RANGE_LIMITEDNVKMS_INPUT_COLOR_RANGE_DEFAULTWhen compositors don't set
COLOR_RANGE(which the DRM spec says is for YCbCr only), the driver usesNVKMS_INPUT_COLOR_RANGE_DEFAULT, which results in limited range output on DisplayPort connections.Additionally, the standard DRM connector property Broadcast RGB (which controls full vs limited RGB range) is not exposed by the NVIDIA driver. Both Intel and AMD expose this: https://drmdb.emersion.fr/properties/3233857728/Broadcast%20RGB
This is likely the correct property for this use case, as pointed out by a wlroots maintainer. Without it, there is no proper way to control RGB output range on the DRM path.
Expected behavior
For RGB content, the DRM/KMS path should default to full range (0-255), matching the X11 DDX behavior.
Workaround
Compositors can explicitly set
COLOR_RANGE = DRM_COLOR_YCBCR_FULL_RANGEon RGB planes in their atomic commits. This has been submitted to KWin: https://invent.kde.org/plasma/kwin/-/merge_requests/9075Environment
Beta Was this translation helpful? Give feedback.
All reactions