System designExplainer

Zero copy image buffer access on GigE: why the pointer trick fails

Why the SDK pointer is not camera memory, what one GigE link actually moves, and the zero copy image buffer access pattern that removes the copy.

LumaSpec Vision11 min read Share on LinkedIn

On this page 9 sections
  1. 01Can you read camera memory directly from the pointer the driver gives you?
  2. 02Why does a 4 ms per MB target outrun a GigE link before any copy runs?
  3. 03How much image data does one GigE link actually carry?
  4. 04What does genuine zero copy image buffer access look like in an acquisition stack?
  5. 05Does the copy itself waste much time once the frame has arrived?
  6. 06Which host-side changes cut copies and latency before you touch the camera choice?
  7. 07Which interface meets 250 MB/s, and what does each step up buy?
  8. 08What should you measure before you rewrite the acquisition path?
  9. 09Questions

You have a live stream, an SDK object that swears it holds the current frame, and a copy-to-array call in the middle that looks like pure waste. The question — can you keep the pointer and read the bytes where they already sit — has two answers, and the second decides your bandwidth budget. This guide separates them: what that pointer actually is, why one GigE link caps any host-side trick at its usable payload, what genuine zero copy image buffer access looks like in a GenICam acquisition stack, and which interface to order when the requirement is 250 MB/s or more. The arithmetic is short enough to run on a whiteboard.

Can you read camera memory directly from the pointer the driver gives you?

No — that pointer is a handle to a driver-owned host buffer or an SDK object, not an address of memory on the camera, and dereferencing it either faults, returns garbage, or hands you a second copy under a different name.

Three separate memories are in play. Inside the camera there is sensor RAM and a staging buffer, and GigE Vision carries the image out as UDP packets, so nothing in the camera is ever mapped into your process. On the PC the driver owns the receive buffers it fills from the network. On top of the driver, the SDK keeps an image object whose fields — width, height, pixel format and a pointer — describe the data. Converting that object reference to an integer and using the value as an address reads the object's own header bytes as pixels, which is exactly why the trick returns noise.

Even when you find the true pixel pointer, the buffer is a loan. Release the frame and the driver writes the next one into it; read it late and you get a torn mix of two frames. The ownership window is the whole game — torn frames in a camera callback: copy the buffer before you queue it shows the failure mode in detail.

Because 4 ms per MB is 250 MB/s, while one GigE link tops out at 125 MB/s gross and roughly 115 MB/s of image data, so the wire fails the target before any copying code runs.

Run the units: 1 MB in 4 ms is 1 ÷ 0.004 s = 250 MB/s, and 250 MB/s × 8 = 2,000 Mbit/s. Gigabit Ethernet is 1,000 Mbit/s, so the target asks for twice the line rate before overhead. Turned around, one link at 115 MB/s of usable payload imposes 1,000 ÷ 115 = 8.7 ms per MB on every megabyte that crosses it — a floor no software can break. Zero-copy, memory mapping and DMA tricks all live on the PC side of the cable; none of them pulls bits through the wire faster than the wire delivers them. If frames arrive late, the fix is a wider link, not a shorter copy.

Plan on about 115 MB/s of usable image payload: 1,000 Mbit/s is 125 MB/s of gross line rate, jumbo frames keep 99% of that on the wire, and a modest reserve for control traffic and resends brings the planning figure down.

Put numbers on it, because the reserve is where optimistic plans die:

gross rate (MB/s) = line rate (Mbit/s) ÷ 8 on-wire bytes per packet = payload + 66 wire efficiency = payload ÷ on-wire bytes usable payload (MB/s) ≈ gross × wire efficiency × 0.95

The 66 bytes are Ethernet framing, IP, UDP, preamble, inter-packet gap and CRC. With 1500-byte packets the efficiency is 1500 ÷ 1566 = 95.8%; with 9000-byte jumbo packets it is 9000 ÷ 9066 = 99.3%. The 5% reserve holds bandwidth back for the GigE Vision control channel — feature commands and heartbeats — and for resend packets when a UDP datagram drops. Jumbo frames on a quiet switched link reach about 124 MB/s best case; treat the planning figure above as the number you plan against.

Frame arithmetic follows directly. A 4,000 × 3,000 Mono8 frame is 4,000 × 3,000 × 1 B = 12 MB. At the planning rate one frame takes 12 ÷ 115 ≈ 104 ms, so the link sustains about 9.6 fps. The catalog proves the ceiling is the link, not the sensor: the same IMX226 sensor appears twice, and the rating moves with the interface. The LS-CMR-0053 lists 9 fps over GigE on that sensor. The LS-CMR-0166 lists 30 fps over USB3 on the same sensor.

Area scan cameras12 MP GigE area scan camera, IMX226, 1/1.7" rolling shutter, 9 fps (LS-CMR-0053)LS-CMR-0053
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
12 MP
$560.00View partBuild a system with it

What does genuine zero copy image buffer access look like in an acquisition stack?

Real zero copy image buffer access means the driver fills a buffer you have already handed it and returns that buffer's address, so your code reads pixels where they arrived instead of calling a copy into a second array.

The GenICam world standardizes the pattern as buffer announcement. You allocate a fixed pool of buffers and hand them to the acquisition stream; the driver writes each incoming frame straight into the next free slot; your wait call returns a container naming that buffer, its size and its timestamp; you process it; you requeue it. The pointer is real and safe, but only between delivery and requeue — finish your read inside the window, because the moment the buffer goes back, its bytes belong to the driver again. GenTL, the GenICam acquisition standard, defines exactly this trio of announce, queue and filled-buffer handover, and frame grabber image acquisition for Camera Link and CoaXPress shows the same model on other transports.

If your SDK exposes no announcement API, you cannot create zero-copy from outside it; the best you can do is reuse one preallocated destination array so the copy you cannot remove at least stops allocating. Working in place rather than copying is not vision-specific — it is the same idea as zero-copy parameter references in PLC function blocks.

Does the copy itself waste much time once the frame has arrived?

Not much: a straight copy of a 12 MB frame costs about a millisecond on a modern desktop because host memory moves many GB/s, so a call that blocks for 48 ms is waiting for the wire, not moving bytes.

A single-threaded memory copy on a current desktop runs at 10-20 GB/s, so 12 MB moves in 12 ÷ 15,000 MB/s ≈ 0.8 ms. Per-call overhead — allocating the destination, zeroing it, crossing from driver to user space — adds roughly another millisecond. A copy-to-array reporting 4 ms per MB, 48 ms for a 12 MB frame, is therefore mostly blocked time: it was called before the frame finished arriving, or the driver had no buffer available and simply waited.

Which host-side changes cut copies and latency before you touch the camera choice?

Three, in order: announce and reuse a fixed ring of buffers so nothing is allocated per frame, set the packet size to jumbo before acquisition starts, and run the NIC's high-performance path so the stack copies once, not three times.

Buffer reuse. A preallocated ring of four to sixteen buffers, each sized to one frame, removes allocator jitter and caps memory use. The buffer-reuse acquisition loop is the reference pattern, and the same ring discipline carries over to saving an image sequence from a frame grabber.

Packet size. Every UDP packet carries 66 bytes of fixed framing, so raising the payload from 1500 to 9000 bytes cuts framing fivefold and lifts usable payload from about 114 to about 118 MB/s, with fewer interrupts per megabyte as a bonus. Set it before acquisition starts, as in setting the GigE packet size before acquisition.

Driver path. Default kernel networking copies packet data several times on the way to user space; the NIC's high-performance GigE Vision path cuts that to about one copy per frame. That wins CPU headroom, not wire speed — what a high-performance NIC driver actually gains gives the measured split, and when buffers still overflow, work through the checklist in troubleshooting GigE camera buffer overflow errors.

On the hardware side, the MV-GE2002P is a dual-port PoE GigE NIC. One camera per port means two cameras never share a single link's payload.

Frame grabbersHikrobot MV-GE2002P Dual-port PoE NIC, full height V2.0MV-GE2002P
Interface
GigE
Ports
2 ports
$249.00View partBuild a system with it

Which interface meets 250 MB/s, and what does each step up buy?

A single 2.5GigE link already meets 250 MB/s: 2,500 Mbit/s is 312 MB/s gross and about 290 MB/s usable, while 10GigE carries roughly 1,180 MB/s and one CXP-12 connection roughly 1,400 MB/s of image payload.

Link Line rate Usable image payload Time per 12 MB frame
1 GigE 1,000 Mbit/s ~115 MB/s 104 ms (9.6 fps)
2.5GigE 2,500 Mbit/s ~290 MB/s 41 ms (24 fps)
10GigE 10,000 Mbit/s ~1,180 MB/s 10 ms (98 fps)
CXP-12 connection 12,500 Mbit/s ~1,400 MB/s 9 ms (117 fps)

The usable column applies the same assumptions as the GigE budget above — jumbo packets and a 5% reserve. CoaXPress adds encoding: CXP-6 keeps 80% of the line rate (8b/10b) and CXP-12 keeps 97% (64b/66b), so after framing a CXP-6 connection yields about 600 MB/s against about 1,400 MB/s for CXP-12.

The smallest step first: a 250 MB/s requirement fits one 2.5GigE link, since 290 ÷ 250 = 1.16, a 16% margin. The MV-GE4004 is a quad 2.5G Ethernet adaptor with four ports, so the host upgrade is one card and the camera and its cabling stay untouched — which is why 2.5GigE is the first rung above GigE, not 10GigE.

Frame grabbersHikrobot MV-GE4004 Quad 2.5G Ethernet AdaptorMV-GE4004
Interface
2.5GigE
Ports
4 ports
$319.00View partBuild a system with it

The camera side repeats the ladder across the catalog: GigE models for payloads within one link's budget, 10GigE when 20-25 MB frames must move at tens of fps, CXP-12 when one connection must carry ten GigE links' worth. The three cameras below sit one rung apart.

Specification MV-CS200-10GC MV-CH250-25TC-F-NF MV-CH250-90YC-M58S-NN
Sensor type CMOS, rolling shutter CMOS, global shutter CMOS, global shutter
Sensor model Sony® IMX183 OnSemi PYTHON25K Gpixel GMAX0505
Pixel size 2.4 μm × 2.4 μm 4.5 μm × 4.5 μm 2.5 μm × 2.5 μm
Sensor size 1" 23 mm × 23 mm 1.1"
Resolution 5472 × 3648 5120 × 5120 5120 × 5120
Max. frame rate 5.9 fps @ 5472 × 3648 40 fps @ 5120 × 5120 Bayer RG 8 150 fps @ 5120 × 5120 Bayer BG 8
SNR 41.5 dB 41 dB 36 dB
Gain 0 dB to 24 dB 0 dB ~ 15 dB 2.5 ×
Exposure time 46 μs to 2.5 sec 45 μs ~ 10 sec UltraShort exposure mode: 3 μs to 8 μs; Standard exposure mode: 10 μs to 10 sec
Exposure mode Off/Once/Continuous exposure auto, supports Timed and Trigger Width exposure mode, and Rolling and Global Reset sensor shutter mode Off/Once/Continuous exposure mode Off/Once/Continuous exposure mode
Pixel format Mono 8/10/12, Bayer GB 8/10/10p/12/12p, YUV422Packed, YUV422_YUYV_Packed, RGB 8, BGR 8 Mono 8/10/12; Bayer RG 8/10/10Packed/12/12Packed; YUV422Packed, YUV422_YUYV_Packed; RGB 8, BGR 8 Bayer BG 8/10/12
Binning Supports 1 × 1, 2 × 2, 4 × 4 Supports 1 × 1, 1 × 2, 1 × 4, 2 × 1, 2 × 2, 2 × 4, 4 × 1, 4 × 2, 4 × 4 Supports 1 × 1, 1 × 2, 1 × 4, 2 ×1, 2 × 2, 2 × 4, 4 × 1, 4 × 2, 4 × 4
Price $739.00 $7,349.00 · quoted $10,979.00 · quoted

All 3 share: Humidity 20% RH to 95% RH (no condensation); Mono/color Color. Values as printed in each product's data sheet; open a part for its drawing and full specifications.

For the host, each rung has its own silicon: the MV-GT2104 is a quad-port copper 10GbE NIC, and four ports carry four separate 10GigE streams.

Frame grabbersHikrobot MV-GT2104 Quad-port copper 10GbE NICMV-GT2104
Interface
10GigE
Ports
4 ports
$1,429.00View partBuild a system with it

Two GigE cameras on separate ports aggregate to about 230 MB/s without sharing wire time; assigning cameras to ports is worked through in assign cameras to ports reliably in a multi-camera system. Above 10GigE, or wherever a switch in the path is unacceptable, terminate CoaXPress links in a frame grabber instead of a NIC — the grabber writes frames to host memory through the same buffer-announcement model as the acquisition stack above.

The decision by requirement, using the table:

  • Total payload within one GigE link's budget: stay on GigE and apply the host-side fixes above.
  • Above one link's budget to about 290 MB/s: move the host to 2.5GigE.
  • 290 to about 1,180 MB/s: a 10GigE camera and 10GbE NIC.
  • Beyond that: one or more CXP-12 connections through a frame grabber.

What should you measure before you rewrite the acquisition path?

Measure three numbers first: the bytes per frame your pixel format actually produces, the end-to-end arrival time of complete frames, and the driver's dropped-frame or resend counters, because those three decide whether the fix is host-side copies or a wider link.

  1. Frame bytes. Take width, height and bytes per pixel from the camera's current pixel format — Mono8 is 1 B, RGB8 is 3 B, Mono12 unpacked is 2 B. A 2,448 × 2,048 Mono12 frame is 2,448 × 2,048 × 2 B = 10.0 MB.
  2. Arrival rate. Log timestamps for 100 complete frames and divide frame bytes by the mean interval. Landing within 10% of the link's planning rate means the link is saturated and no host change will help.
  3. Resend and drop counters. Non-zero resend counts point at packet size, switch MTU or cabling — link-layer work, not application work.

Then apply the requirement test:

required rate (MB/s) = width × height × bytes per pixel × fps headroom = usable link payload ÷ required rate

The worked case: 10.0 MB per frame at 25 fps needs 10.0 × 25 = 251 MB/s. Headroom on GigE is 115 ÷ 251 = 0.46 — the interface changes. On 2.5GigE it is 290 ÷ 251 = 1.15 — the link passes with about 15% margin.

When the link is not saturated and copies measure about a millisecond, the bottleneck is downstream — acquisition faster than processing: fix the frame rate gap covers that case. If the counters are clean and headroom works out under 1, shortlist cameras by interface with the camera finder or start the configurator at the camera step; more acquisition-integration reading sits on the systems topic page. For help turning the headroom math into a parts list, contact the applications team.

Questions

Does enabling jumbo frames double my GigE camera's frame rate?

No. Larger packets lift wire efficiency from about 96% to about 99%, a few percent of payload, not a doubling. The bigger effect is CPU load: five times fewer packets per megabyte means five times fewer interrupts. Every switch and port between camera and host must pass the larger frames.

Is USB3 Vision faster than one GigE link?

Yes, roughly three times over. USB3 Vision carries about 400 MB/s of image payload against the single-link GigE planning figure. The trade is distance: a passive USB3 cable is dependable over a few metres, while GigE reaches 100 m, so USB3 suits bench stations and hosts mounted next to the machine.

Do I need a frame grabber for a single GigE camera?

Usually not. One camera runs on a standard NIC, and the gains that matter come from packet size, buffer reuse and the driver path. Add a multi-port frame grabber or NIC when the second camera arrives, when per-port CPU budget gets tight, or when a switched office network cannot give you predictable timing.

How do I size the link for a color camera?

Multiply width × height × bytes per pixel, then by fps. A 4,000 × 3,000 RGB8 frame is 4,000 × 3,000 × 3 B = 36 MB; at 15 fps the stream needs 36 × 15 = 540 MB/s. That rules out GigE and 2.5GigE (about 290 MB/s) and points to 10GigE. Recompute whenever the pixel format changes.

Can two GigE cameras share one network port?

Only when their combined payload stays within one link's usable budget. One 4 MB camera at 15 fps plus one 2 MB camera at 15 fps totals 60 + 30 = 90 MB/s, which fits with the last quarter of the link in reserve. Above that, give each camera its own port so resend bursts from one never delay the other.