-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Question: Copy framebuffer /dev/fb0 to main memory #2
Comments
Thank you for the interest! There are two types of memory address: virtual address and bus address, which must not be confused. Yes, because the framebuffer is essentially a memory area allocated by firmware or kernel, this library can be used to copy it. An example program that inverts the colors in the framebuffer with DMA copies is here: Click to expand#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <rpicopy.h>
#include <rpimemmgr.h>
int main(void) {
int err;
int fd;
uint8_t *image;
uint32_t image_bus;
struct fb_fix_screeninfo fscr;
struct fb_var_screeninfo vscr;
struct rpimemmgr memmgr;
fd = open("/dev/fb0", O_RDONLY);
if (fd == -1) {
perror("open(/dev/fb0)");
exit(EXIT_FAILURE);
}
err = ioctl(fd, FBIOGET_FSCREENINFO, &fscr);
if (err) {
perror("ioctl(FBIOGET_FSCREENINFO)");
exit(EXIT_FAILURE);
}
printf("ID: %.16s\n", fscr.id);
printf("smem_start: %#010lx\n", fscr.smem_start);
printf("smem_len: %u\n", fscr.smem_len);
printf("line_length: %u\n", fscr.line_length);
err = ioctl(fd, FBIOGET_VSCREENINFO, &vscr);
if (err) {
perror("ioctl(FBIOGET_VSCREENINFO)");
exit(EXIT_FAILURE);
}
printf("xres: %u\n", vscr.xres);
printf("yres: %u\n", vscr.yres);
err = rpimemmgr_init(&memmgr);
if (err) {
printf("error: rpimemmgr_init: %d\n", err);
exit(EXIT_FAILURE);
}
err = rpimemmgr_alloc_vcsm(fscr.smem_len, 4096, VCSM_CACHE_TYPE_NONE,
(void **)&image, &image_bus, &memmgr);
if (err) {
printf("error: rpimemmgr_alloc_vcsm: %d\n", err);
exit(EXIT_FAILURE);
}
err = rpicopy_init();
if (err) {
printf("error: rpicopy_init: %d\n", err);
exit(EXIT_FAILURE);
}
/* Capture the framebuffer. */
(void) memcpy_dma(image_bus, fscr.smem_start, fscr.smem_len);
/* Invert the colors. */
for (uint32_t i = 0; i < fscr.smem_len; ++i) image[i] = ~image[i];
/* Copy the image back to framebuffer. */
(void) memcpy_dma(fscr.smem_start, image_bus, fscr.smem_len);
err = rpicopy_finalize();
if (err) {
printf("error: rpicopy_finalize: %d\n", err);
exit(EXIT_FAILURE);
}
err = rpimemmgr_free_by_usraddr(image, &memmgr);
if (err) {
printf("error: rpimemmgr_free_by_usraddr: %d\n", err);
exit(EXIT_FAILURE);
}
err = rpimemmgr_finalize(&memmgr);
if (err) {
printf("error: rpimemmgr_finalize: %d\n", err);
exit(EXIT_FAILURE);
}
return 0;
} As another option, you can use the Click to expand#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <rpimemmgr.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h>
int main(void) {
int err;
const int fd = open("/dev/fb0", O_RDONLY);
if (fd == -1) {
perror("open(/dev/fb0)");
exit(EXIT_FAILURE);
}
struct fb_fix_screeninfo fscr;
err = ioctl(fd, FBIOGET_FSCREENINFO, &fscr);
if (err) {
perror("ioctl(FBIOGET_FSCREENINFO)");
exit(EXIT_FAILURE);
}
printf("ID: %.16s\n", fscr.id);
printf("smem_start: %#010lx\n", fscr.smem_start);
printf("smem_len: %u\n", fscr.smem_len);
printf("line_length: %u\n", fscr.line_length);
struct fb_var_screeninfo vscr;
err = ioctl(fd, FBIOGET_VSCREENINFO, &vscr);
if (err) {
perror("ioctl(FBIOGET_VSCREENINFO)");
exit(EXIT_FAILURE);
}
printf("xres: %u\n", vscr.xres);
printf("yres: %u\n", vscr.yres);
struct rpimemmgr memmgr;
err = rpimemmgr_init(&memmgr);
if (err) {
printf("error: rpimemmgr_init: %d\n", err);
exit(EXIT_FAILURE);
}
uint8_t *image;
uint32_t image_bus;
err = rpimemmgr_alloc_vcsm(fscr.smem_len, 4096, VCSM_CACHE_TYPE_NONE,
(void **)&image, &image_bus, &memmgr);
if (err) {
printf("error: rpimemmgr_alloc_vcsm: %d\n", err);
exit(EXIT_FAILURE);
}
/* Capture the framebuffer. */
const struct fb_dmacopy dma = {
.dst = image,
.src = fscr.smem_start,
.length = fscr.smem_len,
};
err = ioctl(fd, FBIODMACOPY, &dma);
if (err) {
perror("ioctl(FBIODMACOPY)");
exit(EXIT_FAILURE);
}
stbi_write_png("out.png", vscr.xres, vscr.yres, 4, image, fscr.line_length);
err = rpimemmgr_free_by_usraddr(image, &memmgr);
if (err) {
printf("error: rpimemmgr_free_by_usraddr: %d\n", err);
exit(EXIT_FAILURE);
}
err = rpimemmgr_finalize(&memmgr);
if (err) {
printf("error: rpimemmgr_finalize: %d\n", err);
exit(EXIT_FAILURE);
}
return 0;
} Please note that the framebuffer is placed onto the DispmanX layer, so some contents (e.g. omxplayer) cannot be captured through it. In summary, if you want to write to the framebuffer, then take the first option. |
Hi, I've tried several approaches to get the best performance:
I think the following could work, what do you think? |
Thank you for the explanation! I surprised that librpicopy is the fastest option. Yes, I think your idea is feasible.
I'll try the above if it really works. |
I've managed to setup the DMA transfer with librpicopy. I'm now capturing the X11 desktop by copying the framebuffer contents and dma'ing them to the mmal input buffer for h264 compression. I think I've found exactly the forum post you're referencing and implemented it - after searching alot! You're right - it seems there are many things interfering with the DMA transfers: with default settings (channels {1, 4, 5, 6, 0}) I couldn't even log in to the desktop while capturing. After fiddling alot I've found a configuration which works mostly (channels 5&6 with burst = 1). The capturing runs at around 1080p25fps. htop tells me one ARM core is at 99% during the dma transfer - which I don't really understand because DMA is supposed to work without CPU, right? I'm still trying to improve this but haven't managed to get better than this. I'd still like to find a zero-copy path for this setup: both framebuffer and mmal input are on the VC - so maybe it is possible to pass the framebuffer address to mmal? There is an allocator function in MMAL_PORT_PRIVATE_T which allocates shared memory for the zero-copy path. Could this perhaps be used as entry point to return the framebuffer memory directly instead of allocating shared memory? |
Good! I realized that the snapshot achieves 359 frame/sec @ 1366x768 on my Raspberry Pi 3 (182 frame/sec even with a copy to CPU side) with this code: Click to expand#include <cassert>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <vector>
#include <bcm_host.h>
int main(void) {
const std::size_t count = 100;
bcm_host_init();
const DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(0);
DISPMANX_MODEINFO_T modeinfo;
assert(vc_dispmanx_display_get_info(display, &modeinfo) == 0);
std::cout << "width: " << modeinfo.width << std::endl;
std::cout << "height: " << modeinfo.height << std::endl;
const DISPMANX_RESOURCE_HANDLE_T resource = vc_dispmanx_resource_create(
VC_IMAGE_RGBA32, modeinfo.width, modeinfo.height, (std::uint32_t[1]){});
VC_RECT_T rect;
vc_dispmanx_rect_set(&rect, 0, 0, modeinfo.width, modeinfo.height);
std::vector<std::uint32_t> data;
data.resize(modeinfo.width * modeinfo.height);
const auto start = std::chrono::steady_clock::now();
for (auto i(count); i != 0; --i) {
assert(vc_dispmanx_snapshot(display, resource, DISPMANX_NO_ROTATE) ==
0);
assert(vc_dispmanx_resource_read_data(resource, &rect, data.data(),
modeinfo.width) == 0);
}
const auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> t = end - start;
std::cout << t.count() << " seconds" << std::endl;
std::cout << count / t.count() << " frame/second" << std::endl;
assert(vc_dispmanx_resource_delete(resource) == 0);
assert(vc_dispmanx_display_close(display) == 0);
bcm_host_deinit();
return 0;
} How about the performance on your Pi? I did not notice the |
Hi, I wonder how you achieve these transfer rates with dispmanX: This is the output of your benchmark:
My pi is "Pi 3 model B V1.2". I'll see if it is possible to inject the framebuffer's address into MMAL through the private api mentioned. I'm still trying to figure out what kind of address is returned by pf_payload_alloc and how to get such an address for dispmanX/framebuffer. |
Thank you for the report. |
wow, you're right - seems my pi is throttled:
I'll try benchmarking again :) |
If X11 is not running I'm getting better readout rates - but this will unfortunately not help as I want to read out X11 desktop. I've examined several other ways of capturing X11 in the meantime (https://www.raspberrypi.org/forums/viewtopic.php?f=67&t=310179) - what looks most promising to me is the EXT_image_dma_buf_import extension which can read the desktop into a GL texture really quickly. Exporting should work as well. Thanks for your help! |
Hi,
I'd like to copy the framebuffer contents (/dev/fb0) to main memory on raspbian using
memcpy_dma_config
.Could you please give me a hint on if this library is appropriate for the task and how to set it up?
I've figured there are different methods of copying (vcsm, mailbox,...) and different addresses.
I've tried to set it up but I could not figure out how to use the api correctly. Do I need to allocate using
rpimemmgr_alloc_vcsm
? Which addresses do I provide at which places?Kind regards,
Martin
The text was updated successfully, but these errors were encountered: