@@ -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) {