00 Bellek Paylaşım Problemi
Modern SoC'larda kamera, GPU, video encoder ve display birbirinden farklı DMA master'larıdır. Geleneksel yöntemde her aktarım gereksiz bellek kopyaları üretir ve gecikme ile güç tüketimini artırır.
Geleneksel pipeline — sorunlu yaklaşım
Kamera DMA → [Çekirdek tamponu A] → mmap → [Userspace tamponu]
→ write() → [Çekirdek tamponu B] → GPU DMA
→ write() → [Çekirdek tamponu C] → Encoder DMA
→ write() → [Çekirdek tamponu D] → Display DMA
Toplam kopya: 3–4x (4K@30fps için ~3.5 GB/s gereksiz bant genişliği)
Neden bu kadar kopyalama?
Linux'ta her alt sistem (V4L2, DRM, V4L2M2M, ALSA) kendi bellek yönetim modeliyle çalışır. Aralarında doğrudan fiziksel adres paylaşımı yoktur; veri aktarımı ancak userspace tampon aracılığıyla gerçekleşir.
DMA-BUF çözümü
Kamera DMA → [Fiziksel bellek — tek tampon]
↓ dma_buf fd (file descriptor)
GPU DMA ─────────────┤
Encoder DMA ─────────┤
Display DMA ─────────┘
Toplam kopya: 0 (zero-copy) — aynı fiziksel sayfa tüm aygıtlar tarafından paylaşılır
Bu bölümde
- Geleneksel pipeline'da 3–4 gereksiz bellek kopyası oluşur
- DMA-BUF: fiziksel tampon bir kez tahsis edilir, tüm donanım birimleri aynı tampona erişir
- 4K@60fps için sıfır kopya, bant genişliği ve güç tüketimini dramatik azaltır
01 DMA-BUF Mimarisi
DMA-BUF, Linux çekirdeğinde alt sistemler arasında bellek paylaşımı için standart bir çerçevedir. Temel mekanizma: fiziksel bellek sahibi exporter, kullanan importer ve aralarında geçen file descriptor.
Temel kavramlar
DMA-BUF yaşam döngüsü
1. dma_buf_export() — exporter fiziksel belleği sarmaladı, struct dma_buf oluştu 2. dma_buf_fd() — çekirdek nesnesini file descriptor'a dönüştür 3. fd → userspace (ioctl) — V4L2 VIDIOC_QUERYBUF veya özel ioctl ile userspace'e gönder 4. fd → başka sürücü — userspace fd'yi DRM/encoder sürücüsüne iletir 5. dma_buf_get(fd) — importer fd'den struct dma_buf* alır 6. dma_buf_attach() — importer, dma_buf'a eklenir 7. dma_buf_map_attachment() — sg_table alınır, DMA mapping yapılır 8. DMA işlemi — donanım tampona doğrudan erişir 9. dma_buf_unmap_attachment() — mapping serbest bırakılır 10. dma_buf_detach() — importer ayrılır 11. dma_buf_put() — referans sayacı düşer, sıfırda bellek serbest
Kernel header'ları
#include <linux/dma-buf.h> /* dma_buf, dma_buf_ops, dma_buf_export_info */
#include <linux/dma-mapping.h> /* DMA direction flags, dma_map_sg */
#include <linux/scatterlist.h> /* sg_table, sg_alloc_table */
#include <linux/module.h>
Bu bölümde
- DMA-BUF üç taraflı: exporter (sahip), importer (kullanıcı), fd (köprü)
- sg_table fiziksel bellek parçalarını tanımlar; CMA ile genelde tek contiguous parça
- File descriptor userspace üzerinden sürücüler arasında güvenli geçiş sağlar
02 Exporter Implementasyonu
Exporter, fiziksel belleği tahsis eden ve dma_buf_ops tablosunu uygulayan sürücüdür. Kamera sürücüsü tipik exporter örneğidir.
dma_buf_ops tablosu
#include <linux/dma-buf.h>
#include <linux/slab.h>
#include <linux/module.h>
struct camera_buffer {
struct dma_buf *dmabuf;
struct sg_table *sgt;
void *vaddr; /* sanal adres (vmap için) */
dma_addr_t dma_addr; /* fiziksel DMA adresi */
size_t size;
struct device *dev;
};
/* map_dma_buf: importer DMA mapping talep ettiğinde çağrılır */
static struct sg_table *
camera_dmabuf_map(struct dma_buf_attachment *attach,
enum dma_data_direction dir)
{
struct camera_buffer *cbuf = attach->dmabuf->priv;
struct sg_table *sgt;
int ret;
sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return ERR_PTR(-ENOMEM);
/* Tek contiguous parça — CMA belleği için tipik */
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
if (ret) {
kfree(sgt);
return ERR_PTR(ret);
}
sg_set_page(sgt->sgl,
pfn_to_page(PFN_DOWN(cbuf->dma_addr)),
cbuf->size, 0);
ret = dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir);
if (!ret) {
sg_free_table(sgt);
kfree(sgt);
return ERR_PTR(-ENOMEM);
}
return sgt;
}
static void camera_dmabuf_unmap(struct dma_buf_attachment *attach,
struct sg_table *sgt,
enum dma_data_direction dir)
{
dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
sg_free_table(sgt);
kfree(sgt);
}
/* mmap: userspace CPU erişimi için */
static int camera_dmabuf_mmap(struct dma_buf *dmabuf,
struct vm_area_struct *vma)
{
struct camera_buffer *cbuf = dmabuf->priv;
unsigned long size = vma->vm_end - vma->vm_start;
if (size > cbuf->size)
return -EINVAL;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
return remap_pfn_range(vma, vma->vm_start,
PFN_DOWN(cbuf->dma_addr),
size, vma->vm_page_prot);
}
/* vmap: çekirdek içi sanal adres eşlemesi */
static int camera_dmabuf_vmap(struct dma_buf *dmabuf,
struct iosys_map *map)
{
struct camera_buffer *cbuf = dmabuf->priv;
iosys_map_set_vaddr(map, cbuf->vaddr);
return 0;
}
static void camera_dmabuf_release(struct dma_buf *dmabuf)
{
struct camera_buffer *cbuf = dmabuf->priv;
/* Fiziksel belleği serbest bırak (CMA veya DMA coherent) */
dma_free_coherent(cbuf->dev, cbuf->size, cbuf->vaddr, cbuf->dma_addr);
kfree(cbuf);
}
static const struct dma_buf_ops camera_dmabuf_ops = {
.map_dma_buf = camera_dmabuf_map,
.unmap_dma_buf = camera_dmabuf_unmap,
.mmap = camera_dmabuf_mmap,
.vmap = camera_dmabuf_vmap,
.release = camera_dmabuf_release,
};
/* Tampon oluşturma ve dışa aktarma */
int camera_alloc_and_export(struct device *dev, size_t size, int *out_fd)
{
struct camera_buffer *cbuf;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
struct dma_buf *dmabuf;
int fd;
cbuf = kzalloc(sizeof(*cbuf), GFP_KERNEL);
if (!cbuf)
return -ENOMEM;
cbuf->size = size;
cbuf->dev = dev;
/* DMA coherent bellek tahsis et */
cbuf->vaddr = dma_alloc_coherent(dev, size,
&cbuf->dma_addr, GFP_KERNEL);
if (!cbuf->vaddr) {
kfree(cbuf);
return -ENOMEM;
}
exp_info.ops = &camera_dmabuf_ops;
exp_info.size = size;
exp_info.flags = O_CLOEXEC | O_RDWR;
exp_info.priv = cbuf;
dmabuf = dma_buf_export(&exp_info);
if (IS_ERR(dmabuf)) {
dma_free_coherent(dev, size, cbuf->vaddr, cbuf->dma_addr);
kfree(cbuf);
return PTR_ERR(dmabuf);
}
cbuf->dmabuf = dmabuf;
fd = dma_buf_fd(dmabuf, O_CLOEXEC);
if (fd < 0) {
dma_buf_put(dmabuf);
return fd;
}
*out_fd = fd;
return 0;
}
Bu bölümde
dma_buf_ops: map/unmap/mmap/vmap/release callback'leri zorunlu veya isteğe bağlıdma_buf_export()+dma_buf_fd()ile çekirdek nesnesi userspace fd'ye dönüştürülürdma_alloc_coherent()fiziksel ve sanal adresi birlikte tahsis eder
03 Importer Kullanımı
Importer, başka bir sürücünün dışa aktardığı DMA-BUF tampona DMA erişimi gerçekleştiren sürücüdür. GPU veya video encoder tipik importer örnekleridir.
Importer sürücü kodu
#include <linux/dma-buf.h>
struct encoder_job {
struct dma_buf *input_buf;
struct dma_buf_attachment *attachment;
struct sg_table *sgt;
struct device *dev;
};
int encoder_import_dmabuf(struct device *dev, int fd,
struct encoder_job **out_job)
{
struct encoder_job *job;
struct dma_buf *dmabuf;
int ret;
job = kzalloc(sizeof(*job), GFP_KERNEL);
if (!job)
return -ENOMEM;
/* fd'den struct dma_buf* al — referans sayacını artırır */
dmabuf = dma_buf_get(fd);
if (IS_ERR(dmabuf)) {
kfree(job);
return PTR_ERR(dmabuf);
}
job->input_buf = dmabuf;
job->dev = dev;
/* Encoder cihazını tampona ekle */
job->attachment = dma_buf_attach(dmabuf, dev);
if (IS_ERR(job->attachment)) {
ret = PTR_ERR(job->attachment);
goto err_put;
}
/* DMA mapping al — encoder cihazı için scatter-gather tablosu */
job->sgt = dma_buf_map_attachment(job->attachment,
DMA_TO_DEVICE);
if (IS_ERR(job->sgt)) {
ret = PTR_ERR(job->sgt);
goto err_detach;
}
/* İlk sg entry'den DMA adresini al (CMA = tek parça) */
dma_addr_t input_addr = sg_dma_address(job->sgt->sgl);
dev_dbg(dev, "imported DMA-BUF: dma_addr=0x%llx size=%zu\n",
(u64)input_addr, dmabuf->size);
/* Encoder donanım register'ına yaz */
encoder_set_input_addr(dev, input_addr, dmabuf->size);
*out_job = job;
return 0;
err_detach:
dma_buf_detach(dmabuf, job->attachment);
err_put:
dma_buf_put(dmabuf);
kfree(job);
return ret;
}
void encoder_release_job(struct encoder_job *job)
{
dma_buf_unmap_attachment(job->attachment, job->sgt, DMA_TO_DEVICE);
dma_buf_detach(job->input_buf, job->attachment);
dma_buf_put(job->input_buf);
kfree(job);
}
DMA yön bayrakları
| Bayrak | Anlam | Kullanım |
|---|---|---|
DMA_TO_DEVICE | CPU → Cihaz | Encoder giriş tamponu (CPU yazdı, encoder okuyacak) |
DMA_FROM_DEVICE | Cihaz → CPU | Kamera çıkış tamponu (kamera yazdı, CPU okuyacak) |
DMA_BIDIRECTIONAL | Her iki yön | Hem CPU hem cihaz okuyup yazacak |
DMA_NONE | Veri yönü bilinmiyor | Debug/test; mümkünse kullanmayın |
CPU ve DMA aynı belleğe eriştiğinde cache senkronizasyonu kritiktir. DMA_TO_DEVICE mapping sırasında çekirdek otomatik cache flush yapar; DMA_FROM_DEVICE sırasında cache invalidate yapar. dma_alloc_coherent() ile tahsis edilen bellek hardware coherent olduğundan bu işlemler atlanabilir — ancak yavaş olabilir.
Bu bölümde
dma_buf_get() → attach() → map_attachment()standart importer üçlüsüsg_dma_address(sgt->sgl)ile ilk parçanın DMA adresi alınır- Kullanım sonrası ters sırayla serbest bırakma: unmap → detach → put
04 CMA — Contiguous Memory Allocator
CMA, büyük contiguous fiziksel bellek bloklarını garantili tahsis etmek için tasarlanmış bir Linux alt sistemidir. DMA-BUF exporters için en yaygın fiziksel bellek kaynağıdır.
CMA neden gereklidir?
Sistem ayaklandıktan sonra DRAM parçalanır. Normal alloc_pages() çağrıları 10–100 MB büyüklüğünde contiguous bellek bulamaz. CMA bu bölgeyi sistemin başından rezerve ederek kamera/GPU/codec için her zaman contiguous bellek garantisi verir.
Device Tree konfigürasyonu
/ {
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
/* Kamera + video codec için 128 MB CMA bölgesi */
linux,cma {
compatible = "shared-dma-pool";
reusable; /* CMA: sistem tarafından geri kullanılabilir */
size = <0x0 0x8000000>; /* 128 MB */
alignment = <0x0 0x400000>; /* 4 MB hizalama */
linux,cma-default; /* varsayılan CMA havuzu */
};
/* ISP için özel 64 MB sabit CMA */
isp_reserved: isp-memory@80000000 {
compatible = "shared-dma-pool";
reusable;
reg = <0x0 0x80000000 0x0 0x4000000>; /* 0x80000000..0x84000000 */
};
};
/* ISP sürücüsü bu özel havuzu kullanır */
isp@fe800000 {
compatible = "vendor,isp-v2";
memory-region = <&isp_reserved>;
/* ... */
};
};
Kernel konfigürasyon seçenekleri
# CMA için zorunlu kernel seçenekleri
CONFIG_CMA=y
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_MBYTES=128 # Varsayılan CMA boyutu (MB)
CONFIG_CMA_AREAS=7 # Maksimum CMA bölge sayısı
# Kernel boot parametresiyle de ayarlanabilir
# /boot/cmdline.txt veya u-boot bootargs:
# cma=256M@0x40000000 — 256 MB, 1 GB offset'te
# DMA-BUF framework
CONFIG_DMABUF_HEAPS=y
CONFIG_DMABUF_HEAPS_SYSTEM=y # Sistem belleği heap
CONFIG_DMABUF_HEAPS_CMA=y # /dev/dma_heap/linux,cma
Kernel sürücüsünde CMA kullanımı
#include <linux/cma.h>
#include <linux/dma-contiguous.h>
#include <linux/of_reserved_mem.h>
static int mydev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
/* Device Tree memory-region bağlaması (isp_reserved gibi özel bölge) */
int ret = of_reserved_mem_device_init(dev);
if (ret)
dev_warn(dev, "DT reserved-memory init failed: %d\n", ret);
/* CMA'dan contiguous fiziksel sayfa tahsis et */
size_t npages = (FRAME_SIZE + PAGE_SIZE - 1) >> PAGE_SHIFT;
struct page *pages = dma_alloc_from_contiguous(dev, npages, 0, GFP_KERNEL);
if (!pages)
return -ENOMEM;
phys_addr_t phys = page_to_phys(pages);
void *vaddr = page_address(pages);
dev_info(dev, "CMA allocated: phys=0x%llx virt=%p size=%zu\n",
(u64)phys, vaddr, FRAME_SIZE);
/* Kullanım sonrası serbest bırak */
dma_release_from_contiguous(dev, pages, npages);
return 0;
}
Userspace DMA-BUF heap API — /dev/dma_heap
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/dma-heap.h>
int alloc_cma_dmabuf(size_t size)
{
/* CMA heap cihaz dosyasını aç */
int heap_fd = open("/dev/dma_heap/linux,cma", O_RDONLY | O_CLOEXEC);
if (heap_fd < 0) {
/* Sistem heap fallback */
heap_fd = open("/dev/dma_heap/system", O_RDONLY | O_CLOEXEC);
if (heap_fd < 0)
return -1;
}
struct dma_heap_allocation_data alloc = {
.len = size,
.fd_flags = O_RDWR | O_CLOEXEC,
.heap_flags = 0,
};
if (ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &alloc) < 0) {
close(heap_fd);
return -1;
}
close(heap_fd);
return alloc.fd; /* DMA-BUF file descriptor */
}
Bu bölümde
- CMA, sistem başlangıcında büyük contiguous fiziksel bellek bloğunu rezerve eder
- DT
reserved-memory+shared-dma-poolile sürücüye özel bölge tanımlanır CONFIG_DMABUF_HEAPS_CMA=yile userspace/dev/dma_heap/linux,cmaüzerinden erişebilir
05 Sürücüler Arası Paylaşım
V4L2 kamera sürücüsünden DRM display sürücüsüne kadar uzanan sıfır kopyalı pipeline, DMA-BUF'un en güçlü kullanım senaryosudur.
V4L2 DMABUF modu
V4L2 sürücüleri üç tampon modu destekler: MMAP, USERPTR ve DMABUF. DMABUF modunda kernel tamponları doğrudan dışa aktarılır.
#include <linux/videodev2.h>
#include <sys/ioctl.h>
/* V4L2 DMABUF tampon isteği */
struct v4l2_requestbuffers req = {
.count = 4,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.memory = V4L2_MEMORY_DMABUF,
};
ioctl(v4l2_fd, VIDIOC_REQBUFS, &req);
/* Her tampon için DMA-BUF fd al — VIDIOC_EXPBUF */
for (int i = 0; i < req.count; i++) {
struct v4l2_exportbuffer expbuf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.index = i,
.flags = O_CLOEXEC | O_RDWR,
};
ioctl(v4l2_fd, VIDIOC_EXPBUF, &expbuf);
dmabuf_fds[i] = expbuf.fd;
/* dmabuf_fds[i] artık DRM/encoder'a iletilebilir */
}
/* Tamponu kuyruğa ekle */
struct v4l2_buffer buf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.memory = V4L2_MEMORY_DMABUF,
.index = 0,
.m.fd = dmabuf_fds[0],
};
ioctl(v4l2_fd, VIDIOC_QBUF, &buf);
DRM — DMA-BUF importer (display)
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm/drm_fourcc.h>
/* V4L2'den alınan DMA-BUF fd'yi DRM'e aktar */
uint32_t gem_handle;
drmPrimeFDToHandle(drm_fd, dmabuf_fd, &gem_handle);
/* GEM handle'dan framebuffer oluştur */
uint32_t fb_id;
uint32_t pitches[4] = { width * 4 };
uint32_t offsets[4] = { 0 };
uint64_t modifiers[4]= { DRM_FORMAT_MOD_LINEAR };
uint32_t handles[4] = { gem_handle };
drmModeAddFB2WithModifiers(drm_fd, width, height,
DRM_FORMAT_ARGB8888,
handles, pitches, offsets,
modifiers, &fb_id,
DRM_MODE_FB_MODIFIERS);
/* Framebuffer'ı doğrudan display plane'e ata */
drmModeSetPlane(drm_fd, plane_id, crtc_id, fb_id, 0,
0, 0, width, height,
0, 0, width << 16, height << 16);
Kamera → Display pipeline özeti
V4L2 kamera sürücüsü
│ VIDIOC_EXPBUF → dmabuf_fd
│
▼ userspace (fd geçişi — kopya YOK)
│
DRM display sürücüsü
│ drmPrimeFDToHandle → gem_handle
│ drmModeAddFB2 → fb_id
│ drmModeSetPlane → direkt display
Kamera DMA → Fiziksel tampon → Display DMA
(tek fiziksel adres, sıfır kopya)
Bu bölümde
- V4L2 DMABUF modu:
VIDIOC_REQBUFS(DMABUF)+VIDIOC_EXPBUFile fd alınır - DRM import:
drmPrimeFDToHandle()fd → GEM handle dönüşümü - Userspace sadece fd iletir — gerçek veri kopyası hiç gerçekleşmez
06 Userspace Erişimi
Userspace, DMA-BUF tamponlarına hem CPU erişimi (mmap) hem de senkronizasyon mekanizması (DMA_BUF_IOCTL_SYNC) aracılığıyla ulaşabilir.
mmap ile CPU erişimi
#include <sys/mman.h>
#include <linux/dma-buf.h>
/* DMA-BUF'u CPU'dan okumak için mmap */
void *cpu_access_dmabuf(int dmabuf_fd, size_t size)
{
/* DMA_BUF_IOCTL_SYNC: CPU erişimi öncesi cache flush */
struct dma_buf_sync sync_start = {
.flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ,
};
ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_start);
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED, dmabuf_fd, 0);
if (ptr == MAP_FAILED)
return NULL;
/* Tampon içeriğini CPU'dan oku */
uint8_t first_pixel[4];
memcpy(first_pixel, ptr, 4);
printf("R=%u G=%u B=%u A=%u\n",
first_pixel[0], first_pixel[1],
first_pixel[2], first_pixel[3]);
/* CPU erişimi bitti — cache sync */
struct dma_buf_sync sync_end = {
.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ,
};
ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, &sync_end);
munmap(ptr, size);
return ptr; /* gerçekte munmap sonrası geçersiz */
}
DMA_BUF_IOCTL_SYNC bayrakları
| Bayrak kombinasyonu | Anlam |
|---|---|
SYNC_START | SYNC_READ | CPU okuyacak — DMA→CPU cache invalidate |
SYNC_START | SYNC_WRITE | CPU yazacak — DMA→CPU cache invalidate |
SYNC_START | SYNC_RW | CPU hem okuyup hem yazacak |
SYNC_END | SYNC_WRITE | CPU yazmayı bitirdi — CPU→DMA cache flush |
SYNC_END | SYNC_READ | CPU okumayı bitirdi |
DMA_BUF_IOCTL_SYNC'i atlamak cache inkoherens sorunlarına yol açar: GPU'nun yazdığı veri CPU'ya görünmez veya CPU'nun yazdığı veri DMA cihazına ulaşmaz. ARM big.LITTLE SoC'larda bu hata aralıklı, tekrarlanamaz hatalara neden olur.
Bu bölümde
mmap(dmabuf_fd, ...)ile CPU'dan doğrudan DMA tamponuna erişim sağlanırDMA_BUF_IOCTL_SYNCSYNC_START ve SYNC_END etrafında zorunludur- Cache inkoherens sorunlarını önlemek için erişim yönü (READ/WRITE) doğru belirtilmeli
07 Pratik: Kamera → GPU Sıfır Kopya Pipeline
V4L2 kamera yakalama → DMA-BUF fd → OpenGL ES texture yükleme. Fiziksel tampon boyunca tek kopya gerçekleşmez.
Sistem konfigürasyonu
Raspberry Pi 5 — kamera modülü + OpenGL ES (Mesa V3D)
[libcamera/V4L2] → DMA-BUF fd → [EGL/OpenGL ES]
│ │
└── aynı fiziksel tampon ───────────┘
(DRAM üzerinde tek kopya)
Adım 1 — V4L2 DMABUF tamponu kur
#include <linux/videodev2.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define WIDTH 1920
#define HEIGHT 1080
#define NUM_BUFS 4
int dmabuf_fds[NUM_BUFS];
int v4l2_fd;
void setup_camera_dmabuf(void)
{
v4l2_fd = open("/dev/video0", O_RDWR | O_NONBLOCK);
struct v4l2_format fmt = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.fmt.pix = {
.width = WIDTH,
.height = HEIGHT,
.pixelformat = V4L2_PIX_FMT_NV12,
.field = V4L2_FIELD_NONE,
},
};
ioctl(v4l2_fd, VIDIOC_S_FMT, &fmt);
struct v4l2_requestbuffers req = {
.count = NUM_BUFS,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.memory = V4L2_MEMORY_MMAP,
};
ioctl(v4l2_fd, VIDIOC_REQBUFS, &req);
for (int i = 0; i < NUM_BUFS; i++) {
struct v4l2_exportbuffer expbuf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.index = i,
.flags = O_CLOEXEC | O_RDONLY,
};
ioctl(v4l2_fd, VIDIOC_EXPBUF, &expbuf);
dmabuf_fds[i] = expbuf.fd;
struct v4l2_buffer buf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.memory = V4L2_MEMORY_MMAP,
.index = i,
};
ioctl(v4l2_fd, VIDIOC_QBUF, &buf);
}
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(v4l2_fd, VIDIOC_STREAMON, &type);
}
Adım 2 — EGL DMA-BUF texture import
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
GLuint import_dmabuf_as_texture(EGLDisplay dpy, int dmabuf_fd,
int width, int height)
{
EGLint attribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_NV12,
EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf_fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, 0,
EGL_DMA_BUF_PLANE0_PITCH_EXT, width,
EGL_DMA_BUF_PLANE1_FD_EXT, dmabuf_fd,
EGL_DMA_BUF_PLANE1_OFFSET_EXT, width * height,
EGL_DMA_BUF_PLANE1_PITCH_EXT, width,
EGL_NONE
};
EGLImage image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT,
NULL, attribs);
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
/* DMA-BUF fiziksel bellek GPU texture'ına bağlandı — sıfır kopya */
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
eglDestroyImageKHR(dpy, image);
return texture;
}
Adım 3 — Render döngüsü
void render_loop(EGLDisplay dpy)
{
GLuint textures[NUM_BUFS] = {0};
while (1) {
struct v4l2_buffer buf = {
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
.memory = V4L2_MEMORY_MMAP,
};
ioctl(v4l2_fd, VIDIOC_DQBUF, &buf);
if (!textures[buf.index])
textures[buf.index] = import_dmabuf_as_texture(
dpy, dmabuf_fds[buf.index], WIDTH, HEIGHT);
/* Render — kamera verisi doğrudan GPU'da, kopya yok */
draw_fullscreen_quad(textures[buf.index]);
eglSwapBuffers(dpy, egl_surface);
ioctl(v4l2_fd, VIDIOC_QBUF, &buf);
}
}
1920x1080 NV12 frame = 3.1 MB. 60fps'de geleneksel kopya yaklaşımı 186 MB/s gereksiz bant genişliği tüketir. DMA-BUF pipeline'da kamera → GPU veri hareketi sıfırdır; aynı fiziksel sayfa her iki donanım biriminden erişilir.
Bu bölümde
- V4L2
VIDIOC_EXPBUFile kamera tampon fd'si alınır EGL_LINUX_DMA_BUF_EXTile DMA-BUF doğrudan GPU texture'ına import edilirglEGLImageTargetTexture2DOESsıfır kopyalı bağlamayı tamamlar- 60fps@1080p için 186 MB/s bant genişliği tasarrufu sağlanır