From: Oarora Etimis <oaroraetimis@gmail.com>
To: "Louis Chauvet" <louis.chauvet@bootlin.com>,
"Haneen Mohammed" <hamohammed.sa@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Melissa Wen" <melissa.srw@gmail.com>,
"Maarten Lankhorst" <maarten.lankhorst@linux.intel.com>,
"Maxime Ripard" <mripard@kernel.org>,
"Thomas Zimmermann" <tzimmermann@suse.de>,
"David Airlie" <airlied@gmail.com>,
"Maíra Canal" <mairacanal@riseup.net>,
"Harry Wentland" <harry.wentland@amd.com>,
"Pekka Paalanen" <pekka.paalanen@collabora.com>,
"Arthur Grillo" <arthurgrillo@riseup.net>,
"Helen Koike" <helen.fornazier@gmail.com>,
"Vignesh Raman" <vignesh.raman@collabora.com>
Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
Oarora Etimis <oaroraetimis@gmail.com>
Subject: [PATCH 2/4] drm/vkms: precompute the gamma LUT for every channel value
Date: Sun, 08 Nov 2026 19:33:10 +0000 [thread overview]
Message-ID: <20261108-lut-v1-2-f762d70ae4cd@gmail.com> (raw)
In-Reply-To: <20261108-lut-v1-0-f762d70ae4cd@gmail.com>
apply_lut() interpolates every channel of every pixel: three calls to
apply_lut_to_channel_value(), each computing a fixed-point LUT index and
lerping the two neighbouring entries. In virtme-ng with two CPUs, that
makes kms_plane@pixel-format-source-clamping take 78.9-83.8 s and
kms_plane@pixel-format 160.1-167.8 s over ten runs.
A channel value is a u16, so a channel has only 65536 possible inputs.
Compute them once per LUT change into three tables of 65536 u16, in one
allocation of 384 KiB, and let apply_lut() index them by the channel value
itself; composing a pixel then costs three reads. The two subtests drop to
4.87-4.90 s and 9.70-9.84 s.
Entries come from the same apply_lut_to_channel_value(), so the composed
output stays bit-identical to the per-pixel path.
The tables are built in vkms_composer_worker(), outside the composer lock,
on a frame with a CRC pending whose blob differs from the one they were
built from. Holding a reference on that blob keeps its address from being
recycled, so comparing pointers stays a valid identity test for contents
that are immutable once published. They are allocated with kvmalloc() in
vkms_crtc_init() and freed by a device action that also drops the blob
reference - registered before the workqueue, so the workqueue is destroyed
first.
When there is no LUT the tables keep their previous contents; apply_lut()
returns on the NULL base before indexing them.
This patch depends on patch 1: it declares gamma_lut and clears
lut_length when there is no LUT. Applied alone, this patch does not
compile, and the rebuild check would build from a NULL base.
Signed-off-by: Oarora Etimis <oaroraetimis@gmail.com>
---
drivers/gpu/drm/vkms/vkms_composer.c | 37 ++++++++++++++++++++++++++++++------
drivers/gpu/drm/vkms/vkms_composer.h | 3 +++
drivers/gpu/drm/vkms/vkms_crtc.c | 24 +++++++++++++++++++++++
drivers/gpu/drm/vkms/vkms_drv.h | 6 ++++++
4 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 1d5658bbd493..5bbc34113250 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -111,21 +111,37 @@ VISIBLE_IF_KUNIT u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut
}
EXPORT_SYMBOL_IF_KUNIT(apply_lut_to_channel_value);
+/*
+ * Precompute the LUT for every 16-bit channel value, so that composing a
+ * pixel becomes one table lookup instead of an interpolation.
+ */
+VISIBLE_IF_KUNIT void build_lut_table(const struct vkms_color_lut *lut, u16 *lut_table)
+{
+ enum lut_channel channel;
+ int i;
+
+ for (channel = LUT_RED; channel < LUT_RESERVED; channel++) {
+ for (i = 0; i < LUT_TABLE_ENTRIES; i++)
+ lut_table[channel * LUT_TABLE_ENTRIES + i] =
+ apply_lut_to_channel_value(lut, i, channel);
+ }
+}
+EXPORT_SYMBOL_IF_KUNIT(build_lut_table);
static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buffer *output_buffer)
{
- if (!crtc_state->gamma_lut.base)
- return;
+ const struct vkms_output *out = drm_crtc_to_vkms_output(crtc_state->base.crtc);
+ const u16 *table = out->gamma_lut_table;
- if (!crtc_state->gamma_lut.lut_length)
+ if (!crtc_state->gamma_lut.base || !crtc_state->gamma_lut.lut_length)
return;
for (size_t x = 0; x < output_buffer->n_pixels; x++) {
struct pixel_argb_u16 *pixel = &output_buffer->pixels[x];
- pixel->r = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->r, LUT_RED);
- pixel->g = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->g, LUT_GREEN);
- pixel->b = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->b, LUT_BLUE);
+ pixel->r = table[LUT_RED * LUT_TABLE_ENTRIES + pixel->r];
+ pixel->g = table[LUT_GREEN * LUT_TABLE_ENTRIES + pixel->g];
+ pixel->b = table[LUT_BLUE * LUT_TABLE_ENTRIES + pixel->b];
}
}
@@ -653,6 +669,15 @@ void vkms_composer_worker(struct work_struct *work)
if (!crc_pending)
return;
+ /* Rebuild only when the LUT differs from the one the table was built from. */
+ if (gamma_lut != out->gamma_lut_blob) {
+ if (crtc_state->gamma_lut.lut_length)
+ build_lut_table(&crtc_state->gamma_lut, out->gamma_lut_table);
+
+ drm_property_blob_put(out->gamma_lut_blob);
+ out->gamma_lut_blob = gamma_lut ? drm_property_blob_get(gamma_lut) : NULL;
+ }
+
if (wb_pending)
ret = compose_active_planes(active_wb, crtc_state, &crc32);
else
diff --git a/drivers/gpu/drm/vkms/vkms_composer.h b/drivers/gpu/drm/vkms/vkms_composer.h
index 04dd5646f672..b0ee93ba6dbe 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.h
+++ b/drivers/gpu/drm/vkms/vkms_composer.h
@@ -17,11 +17,14 @@ enum lut_channel {
LUT_RESERVED
};
+#define LUT_TABLE_ENTRIES (1U << 16)
+
#if IS_ENABLED(CONFIG_KUNIT)
u16 lerp_u16(u16 a, u16 b, s64 t);
s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value);
u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value,
enum lut_channel channel);
+void build_lut_table(const struct vkms_color_lut *lut, u16 *lut_table);
void apply_3x4_matrix(struct pixel_argb_s32 *pixel, const struct drm_color_ctm_3x4 *matrix);
#endif
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index fe17f49305a9..c6ac90fbca2b 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -11,6 +11,7 @@
#include <drm/drm_vblank.h>
#include <drm/drm_vblank_helper.h>
+#include "vkms_composer.h"
#include "vkms_drv.h"
static bool vkms_crtc_handle_vblank_timeout(struct drm_crtc *crtc)
@@ -203,6 +204,14 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
.handle_vblank_timeout = vkms_crtc_handle_vblank_timeout,
};
+static void vkms_release_gamma_lut(struct drm_device *dev, void *data)
+{
+ struct vkms_output *out = data;
+
+ kvfree(out->gamma_lut_table);
+ drm_property_blob_put(out->gamma_lut_blob);
+}
+
struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *primary,
struct drm_plane *cursor)
{
@@ -235,6 +244,21 @@ struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *pri
spin_lock_init(&vkms_out->lock);
spin_lock_init(&vkms_out->composer_lock);
+ /*
+ * Allocate before composer_workq: drmm frees in reverse order, so the
+ * workqueue (and any running worker) is destroyed before this table is
+ * freed.
+ */
+ vkms_out->gamma_lut_table = kvmalloc_array(LUT_RESERVED * LUT_TABLE_ENTRIES,
+ sizeof(*vkms_out->gamma_lut_table),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!vkms_out->gamma_lut_table)
+ return ERR_PTR(-ENOMEM);
+
+ ret = drmm_add_action_or_reset(dev, vkms_release_gamma_lut, vkms_out);
+ if (ret)
+ return ERR_PTR(ret);
+
vkms_out->composer_workq = drmm_alloc_ordered_workqueue(dev, "vkms_composer", 0);
if (IS_ERR(vkms_out->composer_workq))
return ERR_CAST(vkms_out->composer_workq);
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 381483aa4fb0..065a7b07b4ab 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -214,6 +214,10 @@ struct vkms_crtc_state {
* writeback)
* @composer_state: Protected by @lock, current state of this VKMS output
* @composer_lock: Lock used internally to protect @composer_state members
+ * @gamma_lut_table: Precomputed gamma LUT indexed by the 16-bit channel value. Allocated
+ * in vkms_crtc_init() and freed with the device.
+ * @gamma_lut_blob: Blob @gamma_lut_table was built from. Holds a reference so that the
+ * blob cannot be freed and reused at the same address with other contents.
*/
struct vkms_output {
struct drm_crtc crtc;
@@ -226,6 +230,8 @@ struct vkms_output {
struct vkms_crtc_state *composer_state;
spinlock_t composer_lock;
+ u16 *gamma_lut_table;
+ struct drm_property_blob *gamma_lut_blob;
};
struct vkms_config;
--
2.55.0
next prev parent reply other threads:[~2026-11-08 19:33 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-08 19:33 [PATCH 0/4] drm/vkms: speed up composition with a precomputed gamma LUT Oarora Etimis
2026-11-08 19:33 ` [PATCH 1/4] drm/vkms: use the gamma LUT of the state being composed Oarora Etimis
2026-11-08 19:33 ` Oarora Etimis [this message]
2026-11-08 19:33 ` [PATCH 3/4] drm/vkms: add a KUnit test for the precomputed LUT table Oarora Etimis
2026-11-08 19:33 ` [PATCH 4/4] drm/ci: drop the kms_plane pixel-format xfails for vkms Oarora Etimis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261108-lut-v1-2-f762d70ae4cd@gmail.com \
--to=oaroraetimis@gmail.com \
--cc=airlied@gmail.com \
--cc=arthurgrillo@riseup.net \
--cc=dri-devel@lists.freedesktop.org \
--cc=hamohammed.sa@gmail.com \
--cc=harry.wentland@amd.com \
--cc=helen.fornazier@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=louis.chauvet@bootlin.com \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mairacanal@riseup.net \
--cc=melissa.srw@gmail.com \
--cc=mripard@kernel.org \
--cc=pekka.paalanen@collabora.com \
--cc=simona@ffwll.ch \
--cc=tzimmermann@suse.de \
--cc=vignesh.raman@collabora.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®