mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-15 13:22:19 +01:00
On a host whose GPU can encode H.264 (a Windows driver through d3d12 on WSL 2 does), KRDP in
the demo guest encoded on the guest's CPUs. The host side was fine (vainfo on the host lists
H.264 and HEVC EncSlice), and virgl can pass a guest's VA-API video to it, but a chain of seven
problems stood between the two. Each is a patch, and the guest takes them all:
QEMU (nix/dev/qemu-virgl-video.patch)
QEMU never asks virglrenderer for video: virglrenderer turns it on only for an embedder that
passes VIRGL_RENDERER_USE_VIDEO and a get_drm_fd callback to open the host's video device on.
The flag is defined only with VIRGL_RENDERER_UNSTABLE_APIS, which the build sets.
virglrenderer 1.3.0, four patches in this order
virglrenderer-chroma-format.patch
the guest's Mesa and virglrenderer number the chroma format of a codec differently: 4:2:0
was read as 4:2:2, and the host's encoder refused every codec it was asked for
virglrenderer-video-memory-upload.patch
the d3d12 driver has no dma-bufs: vaExportSurfaceHandle fails, nothing was uploaded and
the encoder returned an empty bitstream. The planes of the guest's picture are read back
(glReadPixels) and written to the surface through a derived image (vaDeriveImage, with
vaCreateImage and vaPutImage as a fallback)
virglrenderer-h264-packed-sps.patch
Mesa's VA-API takes the level of the stream from a packed SPS, which virglrenderer never
sent: level 0 reached UNREACHABLE in the d3d12 encoder and crashed QEMU
virglrenderer-ubo0-constants.patch
a constant buffer 0 bound as a buffer was never read by the shader, so the colour matrix
of an RGB to YUV conversion was zero and the picture was a solid green
Mesa 26.2.5 in the guest, two patches (hardware.graphics.package)
mesa-virgl-rgb-yuv-csc.patch
the graphics compositor never loaded an RGB to YUV matrix, and multiplied by the YUV to
RGB one of its initial state
mesa-virgl-fence-leak.patch
a surface kept the out-fence (a sync_file) of its last flush and the next flush stored a
new one over it: one file descriptor leaked per converted frame, 44 a second under motion,
and KRDP froze when it reached the limit of 1024, about 28 seconds in
KRDP (krdp.nix) asks for H264Baseline and the guest's driver lists Main and High, so ffmpeg's
h264_vaapi found no profile it could use: it is built with H264Main.
What was checked: a luma ramp and a colour-bar pattern encoded through the guest to the 4090
decode to the right values; the real desktop shows over RDP in Windows' own client with the
right colours; under es2gears motion the descriptors of krdpserver stay flat (3 sync_file, 78
of 1024) where they were 949 of 1024 after 28 seconds.
run-kde sets what the host needs for Mesa and for virglrenderer's libva to find the Windows
driver, on WSL 2 with the GPU on and unless it is set already: GALLIUM_DRIVER=d3d12,
LD_LIBRARY_PATH with /usr/lib/wsl/lib first, LIBVA_DRIVER_NAME=d3d12 and LIBVA_DRIVERS_PATH
(/run/opengl-driver/lib/dri, for the d3d12 video driver, which it looks for). --doctor checks
for the libraries and for that driver, and the tests have a scenario for each.
Known: the software decoder warns "illegal short term buffer state detected" on the stream; the
d3d12 encoder writes Constrained Baseline whatever profile is asked for. There is no binary
cache: the first run builds QEMU and Mesa, which takes about 20 minutes. QEMU, virglrenderer and
Mesa are the runner's and the guest's only, and the VM suites build the Mesa as well.
Not measured yet: whether it is smoother than the CPU encoder under the same load.
106 lines
4.8 KiB
Diff
106 lines
4.8 KiB
Diff
vrend: read constant buffer 0 bound as a buffer into the default uniforms
|
|
|
|
A guest binds the constant buffer 0 of a shader either as inline constants
|
|
(SET_CONSTANT_BUFFER) or as a buffer resource (SET_UNIFORM_BUFFER). A shader reads its
|
|
constant buffer 0 from the default uniform block (fsconst0[] and the like), which vrend
|
|
fills only from the inline constants, and vrend binds UBOs only for the buffers from 1:
|
|
a buffer bound at index 0 was never seen by the shader, and its constants stayed zero.
|
|
|
|
Mesa's vl_compositor binds the colour matrix of an RGB to YUV conversion that way, so
|
|
with the virgl driver the conversion (scale_vaapi from RGB to NV12 in FFmpeg, which
|
|
KRDP uses) multiplied by a zero matrix and wrote zeros: a solid green picture.
|
|
|
|
Remember when the constant buffer 0 is set whether it is a buffer, and at the draw read
|
|
it (glGetBufferSubData) into the uniforms with glUniform4uiv. It is read at every draw,
|
|
since the guest can write to the buffer without binding it again. The conversion then
|
|
writes what Mesa's matrix gives: the luma of a ramp goes from 0 0 0 to 0 124 255. What
|
|
is left is Mesa's: the graphics path of vl_compositor_convert_rgb_to_yuv() never loads
|
|
an RGB to YUV matrix, and the one that is sent is the YUV to RGB matrix of the
|
|
compositor's initial state.
|
|
|
|
diff --git a/src/vrend/vrend_renderer.c b/src/vrend/vrend_renderer.c
|
|
index 4bf88e0..a5166c4 100644
|
|
--- a/src/vrend/vrend_renderer.c
|
|
+++ b/src/vrend/vrend_renderer.c
|
|
@@ -735,6 +735,8 @@ struct vrend_sub_context {
|
|
|
|
struct vrend_constants consts[PIPE_SHADER_TYPES];
|
|
bool const_dirty[PIPE_SHADER_TYPES];
|
|
+ /* constant buffer 0 was last bound as a buffer resource, not as inline constants */
|
|
+ bool const0_from_buffer[PIPE_SHADER_TYPES];
|
|
|
|
struct pipe_constant_buffer cbs[PIPE_SHADER_TYPES][PIPE_MAX_CONSTANT_BUFFERS];
|
|
uint32_t const_bufs_used_mask[PIPE_SHADER_TYPES];
|
|
@@ -3486,6 +3488,7 @@ void vrend_set_constants(struct vrend_context *ctx,
|
|
|
|
consts = &ctx->sub->consts[shader];
|
|
ctx->sub->const_dirty[shader] = true;
|
|
+ ctx->sub->const0_from_buffer[shader] = false;
|
|
|
|
/* avoid reallocations by only growing the buffer */
|
|
if (consts->num_allocated_consts < num_constant) {
|
|
@@ -3517,6 +3520,9 @@ void vrend_set_uniform_buffer(struct vrend_context *ctx,
|
|
struct pipe_constant_buffer *cbs = &ctx->sub->cbs[shader][index];
|
|
const uint32_t mask = 1u << index;
|
|
|
|
+ if (index == 0)
|
|
+ ctx->sub->const0_from_buffer[shader] = res_handle != 0;
|
|
+
|
|
if (res_handle) {
|
|
res = vrend_renderer_ctx_res_lookup(ctx, res_handle);
|
|
|
|
@@ -5352,9 +5358,53 @@ static GLuint vrend_draw_bind_ubo_shader(struct vrend_sub_context *sub_ctx,
|
|
return next_ubo_id;
|
|
}
|
|
|
|
+/*
|
|
+ * A guest can bind constant buffer 0 as a buffer resource (SET_UNIFORM_BUFFER) instead of as
|
|
+ * inline constants (SET_CONSTANT_BUFFER): Mesa's vl_compositor does for the colour matrix of an
|
|
+ * RGB to YUV conversion. A shader reads its constant buffer 0 as the default uniform block
|
|
+ * (fsconst0[]), which only the inline constants fill, and the UBOs are for the buffers from 1:
|
|
+ * the matrix stayed zero, and the conversion wrote zeros. Read the buffer into the uniforms. It
|
|
+ * is read at every draw, as the guest can write to the buffer without binding it again. Returns
|
|
+ * whether the constants came from the buffer.
|
|
+ */
|
|
+static bool vrend_draw_bind_const0_buffer(struct vrend_sub_context *sub_ctx,
|
|
+ int shader_type)
|
|
+{
|
|
+ const struct vrend_shader_selector *sel = sub_ctx->shaders[shader_type];
|
|
+ const struct pipe_constant_buffer *cb = &sub_ctx->cbs[shader_type][0];
|
|
+ const struct vrend_resource *res = (const struct vrend_resource *)cb->buffer;
|
|
+ unsigned num_consts, bytes;
|
|
+ uint32_t *data;
|
|
+
|
|
+ if (!sub_ctx->const0_from_buffer[shader_type] || !sel || !sel->sinfo.num_consts ||
|
|
+ sub_ctx->prog->const_location[shader_type] == -1 ||
|
|
+ !(sub_ctx->const_bufs_used_mask[shader_type] & 1) ||
|
|
+ !res || !has_bit(res->storage_bits, VREND_STORAGE_GL_BUFFER))
|
|
+ return false;
|
|
+
|
|
+ num_consts = sel->sinfo.num_consts;
|
|
+ bytes = num_consts * 4 * sizeof(uint32_t);
|
|
+ data = calloc(1, bytes);
|
|
+ if (!data)
|
|
+ return true;
|
|
+
|
|
+ glBindBuffer(GL_COPY_READ_BUFFER, res->gl_id);
|
|
+ glGetBufferSubData(GL_COPY_READ_BUFFER, cb->buffer_offset,
|
|
+ MIN2(bytes, cb->buffer_size), data);
|
|
+ glBindBuffer(GL_COPY_READ_BUFFER, 0);
|
|
+
|
|
+ glUniform4uiv(sub_ctx->prog->const_location[shader_type], num_consts, data);
|
|
+ free(data);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
static void vrend_draw_bind_const_shader(struct vrend_sub_context *sub_ctx,
|
|
int shader_type, bool new_program)
|
|
{
|
|
+ if (vrend_draw_bind_const0_buffer(sub_ctx, shader_type))
|
|
+ return;
|
|
+
|
|
if (sub_ctx->consts[shader_type].consts &&
|
|
sub_ctx->shaders[shader_type] &&
|
|
(sub_ctx->prog->const_location[shader_type] != -1) &&
|