ref:a4abf81219b311b19ff5dc4808e021970dc065d7

fix(linux): don't copy or re-log per DMABUF scanout in the qemu frame store

Guests that flip buffers send a DMABUF scanout for every frame. Keep no system memory copy of the frame for GPU encoders, reallocate the frame only when its size changes, and log each readback problem once instead of on every scanout. Refs #3 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPNw4PCgkEfhyCjQT19wsb
SHA: a4abf81219b311b19ff5dc4808e021970dc065d7
Author: Cole Christensen <cole.christensen@gmail.com>
Date: 2026-09-12 23:08
Parents: 3e0c14f
2 files changed +31 -5
Type
src/platform/linux/qemu/frame_store.cpp +29 −5
@@ -9,6 +9,8 @@
#include <algorithm>
#include <array>
#include <cstring>
#include <sstream>
#include <string>
#include <string_view>
// platform includes
@@ -245,7 +247,14 @@
resize_locked((int) dmabuf.width, (int) dmabuf.height);
if (readback) {
map_dmabuf_locked();
if (dmabuf_addr) {
read_dmabuf_locked(0, 0, frame_width, frame_height);
} else {
std::ranges::fill(pixels, 0);
}
} else {
// GPU encoders take the descriptors; don't keep a system memory copy of the frame
read_dmabuf_locked(0, 0, frame_width, frame_height);
pixels.clear();
}
touch_locked();
}
@@ -441,7 +450,16 @@
void frame_store_t::map_dmabuf_locked() {
const auto layout = pixman_from_drm_fourcc(dmabuf.fourcc).and_then(pixman_layout);
const bool linear = dmabuf.modifier == drm_fourcc::mod_linear || dmabuf.modifier == drm_fourcc::mod_invalid;
// guests that flip buffers send a scanout per frame: warn once per kind of problem
auto warn_once = [this](const std::string &message) {
if (message != readback_warning) {
BOOST_LOG(warning) << "qemu: "sv << message;
readback_warning = message;
}
};
if (!layout || !linear || dmabuf.num_planes != 1) {
std::ostringstream message;
message << "can't read DMABUF fourcc 0x" << std::hex << dmabuf.fourcc << " modifier 0x" << dmabuf.modifier << std::dec << " with " << dmabuf.num_planes << " plane(s) from system memory; use a hardware encoder";
BOOST_LOG(warning) << "qemu: can't read DMABUF fourcc 0x"sv << std::hex << dmabuf.fourcc << " modifier 0x"sv << dmabuf.modifier << std::dec << " with "sv << dmabuf.num_planes << " plane(s) from system memory; use a hardware encoder"sv;
warn_once(message.str());
return;
}
@@ -450,13 +468,15 @@
const auto size = lseek(fd, 0, SEEK_END);
const std::uint64_t needed = (std::uint64_t) dmabuf.offsets[0] + (std::uint64_t) dmabuf.strides[0] * (dmabuf.backing_height - 1) + (std::uint64_t) dmabuf.backing_width * layout->bytes_per_pixel;
if (size <= 0 || (std::uint64_t) size < needed || dmabuf.strides[0] < dmabuf.backing_width * layout->bytes_per_pixel) {
BOOST_LOG(warning) << "qemu: DMABUF of "sv << size << " bytes doesn't cover a "sv << dmabuf.backing_width << 'x' << dmabuf.backing_height << " buffer with stride "sv << dmabuf.strides[0];
std::ostringstream message;
message << "DMABUF of " << size << " bytes doesn't cover a " << dmabuf.backing_width << 'x' << dmabuf.backing_height << " buffer with stride " << dmabuf.strides[0];
warn_once(message.str());
return;
}
auto addr = mmap(nullptr, (std::size_t) size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
BOOST_LOG(warning) << "qemu: couldn't map DMABUF for readback: "sv << std::strerror(errno) << "; use a hardware encoder"sv;
warn_once(std::string {"couldn't map DMABUF for readback: "} + std::strerror(errno) + "; use a hardware encoder");
return;
}
dmabuf_addr = (const std::uint8_t *) addr;
@@ -509,7 +529,11 @@
}
frame_width = new_width;
frame_height = new_height;
pixels.assign((std::size_t) new_width * new_height * 4, 0);
// every caller overwrites the whole frame, so only a size change needs new memory
const auto size = (std::size_t) new_width * new_height * 4;
if (pixels.size() != size) {
pixels.assign(size, 0);
}
}
void frame_store_t::blit_locked(int x, int y, int w, int h, const std::uint8_t *src, std::uint32_t src_stride, const pixel_layout_t &layout) {
src/platform/linux/qemu/frame_store.h +2 −0
@@ -11,6 +11,7 @@
#include <mutex>
#include <optional>
#include <span>
#include <string>
#include <vector>
// local includes
@@ -284,5 +285,6 @@
const std::uint8_t *dmabuf_addr {nullptr}; ///< Read-only mapping of the DMABUF's only plane, for readback.
std::size_t dmabuf_size {0}; ///< Length of the DMABUF mapping.
pixel_layout_t dmabuf_layout; ///< Pixel layout of the mapped DMABUF.
std::string readback_warning; ///< Last DMABUF readback problem logged, to log each one once.
};
} // namespace qemu