VisionIPC 2.0 (#19641)
* Squashed vipc * Update release files * Remove else * add visionipc to release files * use poller in vipc receive * opencl framework instead of lib on macos * Fix camera webcam * Fix opencl on mac in ui * more webcam fixes * typo in ui sconsfile * Use cur_yuv_buf * visionbuf c++ class * Camera qcom was still using visionbuf_allocate * Turn loggerd back on * fix snapshot * No build needed * update test camerad * no more release callback * make encoder c++ * Revert "no more release callback" This reverts commit e5707b07002fee665d0483d90713154efc2d70d4. * fix exit handlers * No need to check errno * move release callback call * s/VIPCBufExtra/VisionIpcBufExtra/g * use non blocking connect * ui use non blocking connect * Lower condition variable wait time * Snapshot cleanup * bump cereal * bump cerealpull/19698/head
parent
206d072bb4
commit
fb496c692a
46 changed files with 398 additions and 2180 deletions
@ -1 +1 @@ |
|||||||
Subproject commit 2220a4f10019d0e192f9cdad3bb388790e59f25b |
Subproject commit 266fb9b204cdf4edf5fb8ff4584a4b3eaa35cee2 |
@ -1,93 +0,0 @@ |
|||||||
#!/usr/bin/env python3 |
|
||||||
import os |
|
||||||
from cffi import FFI |
|
||||||
|
|
||||||
import numpy as np |
|
||||||
|
|
||||||
gf_dir = os.path.dirname(os.path.abspath(__file__)) |
|
||||||
|
|
||||||
ffi = FFI() |
|
||||||
ffi.cdef(""" |
|
||||||
|
|
||||||
typedef enum VisionStreamType { |
|
||||||
VISION_STREAM_RGB_BACK, |
|
||||||
VISION_STREAM_RGB_FRONT, |
|
||||||
VISION_STREAM_YUV, |
|
||||||
VISION_STREAM_YUV_FRONT, |
|
||||||
VISION_STREAM_MAX, |
|
||||||
} VisionStreamType; |
|
||||||
|
|
||||||
typedef struct VisionUIInfo { |
|
||||||
int big_box_x, big_box_y; |
|
||||||
int big_box_width, big_box_height; |
|
||||||
int transformed_width, transformed_height; |
|
||||||
|
|
||||||
int front_box_x, front_box_y; |
|
||||||
int front_box_width, front_box_height; |
|
||||||
} VisionUIInfo; |
|
||||||
|
|
||||||
typedef struct VisionStreamBufs { |
|
||||||
VisionStreamType type; |
|
||||||
|
|
||||||
int width, height, stride; |
|
||||||
size_t buf_len; |
|
||||||
|
|
||||||
union { |
|
||||||
VisionUIInfo ui_info; |
|
||||||
} buf_info; |
|
||||||
} VisionStreamBufs; |
|
||||||
|
|
||||||
typedef struct VIPCBuf { |
|
||||||
int fd; |
|
||||||
size_t len; |
|
||||||
void* addr; |
|
||||||
} VIPCBuf; |
|
||||||
|
|
||||||
typedef struct VIPCBufExtra { |
|
||||||
// only for yuv |
|
||||||
uint32_t frame_id; |
|
||||||
uint64_t timestamp_eof; |
|
||||||
} VIPCBufExtra; |
|
||||||
|
|
||||||
typedef struct VisionStream { |
|
||||||
int ipc_fd; |
|
||||||
int last_idx; |
|
||||||
int last_type; |
|
||||||
int num_bufs; |
|
||||||
VisionStreamBufs bufs_info; |
|
||||||
VIPCBuf *bufs; |
|
||||||
} VisionStream; |
|
||||||
|
|
||||||
int visionstream_init(VisionStream *s, VisionStreamType type, bool tbuffer, VisionStreamBufs *out_bufs_info); |
|
||||||
VIPCBuf* visionstream_get(VisionStream *s, VIPCBufExtra *out_extra); |
|
||||||
void visionstream_destroy(VisionStream *s); |
|
||||||
|
|
||||||
""" |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
class VisionIPCError(Exception): |
|
||||||
pass |
|
||||||
|
|
||||||
|
|
||||||
class VisionIPC(): |
|
||||||
def __init__(self, front=False): |
|
||||||
self.clib = ffi.dlopen(os.path.join(gf_dir, "libvisionipc.so")) |
|
||||||
|
|
||||||
self.s = ffi.new("VisionStream*") |
|
||||||
self.buf_info = ffi.new("VisionStreamBufs*") |
|
||||||
|
|
||||||
err = self.clib.visionstream_init(self.s, self.clib.VISION_STREAM_RGB_FRONT if front else self.clib.VISION_STREAM_RGB_BACK, True, self.buf_info) |
|
||||||
|
|
||||||
if err != 0: |
|
||||||
self.clib.visionstream_destroy(self.s) |
|
||||||
raise VisionIPCError |
|
||||||
|
|
||||||
def __del__(self): |
|
||||||
self.clib.visionstream_destroy(self.s) |
|
||||||
|
|
||||||
def get(self): |
|
||||||
buf = self.clib.visionstream_get(self.s, ffi.NULL) |
|
||||||
pbuf = ffi.buffer(buf.addr, buf.len) |
|
||||||
ret = np.frombuffer(pbuf, dtype=np.uint8).reshape((-1, self.buf_info.stride//3, 3)) |
|
||||||
return ret[:self.buf_info.height, :self.buf_info.width, [2, 1, 0]] |
|
@ -1,438 +0,0 @@ |
|||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdbool.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include "common/efd.h" |
|
||||||
|
|
||||||
#include "buffering.h" |
|
||||||
|
|
||||||
void tbuffer_init(TBuffer *tb, int num_bufs, const char* name) { |
|
||||||
assert(num_bufs >= 3); |
|
||||||
|
|
||||||
memset(tb, 0, sizeof(TBuffer)); |
|
||||||
tb->reading = (bool*)calloc(num_bufs, sizeof(bool)); |
|
||||||
assert(tb->reading); |
|
||||||
tb->pending_idx = -1; |
|
||||||
tb->num_bufs = num_bufs; |
|
||||||
tb->name = name; |
|
||||||
|
|
||||||
pthread_mutex_init(&tb->lock, NULL); |
|
||||||
pthread_cond_init(&tb->cv, NULL); |
|
||||||
tb->efd = efd_init(); |
|
||||||
assert(tb->efd >= 0); |
|
||||||
} |
|
||||||
|
|
||||||
void tbuffer_init2(TBuffer *tb, int num_bufs, const char* name, |
|
||||||
void (*release_cb)(void* c, int idx), |
|
||||||
void* cb_cookie) { |
|
||||||
|
|
||||||
tbuffer_init(tb, num_bufs, name); |
|
||||||
|
|
||||||
tb->release_cb = release_cb; |
|
||||||
tb->cb_cookie = cb_cookie; |
|
||||||
} |
|
||||||
|
|
||||||
int tbuffer_efd(TBuffer *tb) { |
|
||||||
return tb->efd; |
|
||||||
} |
|
||||||
|
|
||||||
int tbuffer_select(TBuffer *tb) { |
|
||||||
pthread_mutex_lock(&tb->lock); |
|
||||||
|
|
||||||
int i; |
|
||||||
for (i=0; i<tb->num_bufs; i++) { |
|
||||||
if (!tb->reading[i] && i != tb->pending_idx) { |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
assert(i < tb->num_bufs); |
|
||||||
|
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
return i; |
|
||||||
} |
|
||||||
|
|
||||||
void tbuffer_dispatch(TBuffer *tb, int idx) { |
|
||||||
pthread_mutex_lock(&tb->lock); |
|
||||||
|
|
||||||
if (tb->pending_idx != -1) { |
|
||||||
//printf("tbuffer (%s) dropped!\n", tb->name ? tb->name : "?");
|
|
||||||
if (tb->release_cb) { |
|
||||||
tb->release_cb(tb->cb_cookie, tb->pending_idx); |
|
||||||
} |
|
||||||
tb->pending_idx = -1; |
|
||||||
} |
|
||||||
|
|
||||||
tb->pending_idx = idx; |
|
||||||
|
|
||||||
efd_write(tb->efd); |
|
||||||
pthread_cond_signal(&tb->cv); |
|
||||||
|
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
} |
|
||||||
|
|
||||||
int tbuffer_acquire(TBuffer *tb) { |
|
||||||
pthread_mutex_lock(&tb->lock); |
|
||||||
|
|
||||||
if (tb->stopped) { |
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
while (tb->pending_idx == -1) { |
|
||||||
pthread_cond_wait(&tb->cv, &tb->lock); |
|
||||||
|
|
||||||
if (tb->stopped) { |
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
efd_clear(tb->efd); |
|
||||||
|
|
||||||
int ret = tb->pending_idx; |
|
||||||
assert(ret < tb->num_bufs); |
|
||||||
|
|
||||||
tb->reading[ret] = true; |
|
||||||
tb->pending_idx = -1; |
|
||||||
|
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
|
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
static void tbuffer_release_locked(TBuffer *tb, int idx) { |
|
||||||
assert(idx < tb->num_bufs); |
|
||||||
if (!tb->reading[idx]) { |
|
||||||
printf("!! releasing tbuffer we aren't reading %d\n", idx); |
|
||||||
} |
|
||||||
|
|
||||||
if (tb->release_cb) { |
|
||||||
tb->release_cb(tb->cb_cookie, idx); |
|
||||||
} |
|
||||||
|
|
||||||
tb->reading[idx] = false; |
|
||||||
} |
|
||||||
|
|
||||||
void tbuffer_release(TBuffer *tb, int idx) { |
|
||||||
pthread_mutex_lock(&tb->lock); |
|
||||||
tbuffer_release_locked(tb, idx); |
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
} |
|
||||||
|
|
||||||
void tbuffer_release_all(TBuffer *tb) { |
|
||||||
pthread_mutex_lock(&tb->lock); |
|
||||||
for (int i=0; i<tb->num_bufs; i++) { |
|
||||||
if (tb->reading[i]) { |
|
||||||
tbuffer_release_locked(tb, i); |
|
||||||
} |
|
||||||
} |
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
} |
|
||||||
|
|
||||||
void tbuffer_stop(TBuffer *tb) { |
|
||||||
pthread_mutex_lock(&tb->lock); |
|
||||||
tb->stopped = true; |
|
||||||
efd_write(tb->efd); |
|
||||||
pthread_cond_signal(&tb->cv); |
|
||||||
pthread_mutex_unlock(&tb->lock); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void pool_init(Pool *s, int num_bufs) { |
|
||||||
assert(num_bufs > 3); |
|
||||||
|
|
||||||
memset(s, 0, sizeof(*s)); |
|
||||||
s->num_bufs = num_bufs; |
|
||||||
|
|
||||||
s->refcnt = (int*)calloc(num_bufs, sizeof(int)); |
|
||||||
s->ts = (int*)calloc(num_bufs, sizeof(int)); |
|
||||||
|
|
||||||
s->counter = 1; |
|
||||||
|
|
||||||
pthread_mutex_init(&s->lock, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
void pool_init2(Pool *s, int num_bufs, |
|
||||||
void (*release_cb)(void* c, int idx), void* cb_cookie) { |
|
||||||
|
|
||||||
pool_init(s, num_bufs); |
|
||||||
s->cb_cookie = cb_cookie; |
|
||||||
s->release_cb = release_cb; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
void pool_acquire(Pool *s, int idx) { |
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
|
|
||||||
assert(idx >= 0 && idx < s->num_bufs); |
|
||||||
|
|
||||||
s->refcnt[idx]++; |
|
||||||
|
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
} |
|
||||||
|
|
||||||
static void pool_release_locked(Pool *s, int idx) { |
|
||||||
// printf("release %d refcnt %d\n", idx, s->refcnt[idx]);
|
|
||||||
|
|
||||||
assert(idx >= 0 && idx < s->num_bufs); |
|
||||||
|
|
||||||
assert(s->refcnt[idx] > 0); |
|
||||||
s->refcnt[idx]--; |
|
||||||
|
|
||||||
// printf("release %d -> %d, %p\n", idx, s->refcnt[idx], s->release_cb);
|
|
||||||
if (s->refcnt[idx] == 0 && s->release_cb) { |
|
||||||
// printf("call %p\b", s->release_cb);
|
|
||||||
s->release_cb(s->cb_cookie, idx); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void pool_release(Pool *s, int idx) { |
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
pool_release_locked(s, idx); |
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
} |
|
||||||
|
|
||||||
TBuffer* pool_get_tbuffer(Pool *s) { |
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
|
|
||||||
assert(s->num_tbufs < POOL_MAX_TBUFS); |
|
||||||
TBuffer* tbuf = &s->tbufs[s->num_tbufs]; |
|
||||||
s->num_tbufs++; |
|
||||||
tbuffer_init2(tbuf, s->num_bufs, |
|
||||||
"pool", (void (*)(void *, int))pool_release, s); |
|
||||||
|
|
||||||
bool stopped = s->stopped; |
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
|
|
||||||
// Stop the tbuffer so we can return a valid object.
|
|
||||||
// We must stop here because the pool_stop may have already been called,
|
|
||||||
// in which case tbuffer_stop may never be called again.
|
|
||||||
if (stopped) { |
|
||||||
tbuffer_stop(tbuf); |
|
||||||
} |
|
||||||
return tbuf; |
|
||||||
} |
|
||||||
|
|
||||||
PoolQueue* pool_get_queue(Pool *s) { |
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
|
|
||||||
int i; |
|
||||||
for (i = 0; i < POOL_MAX_QUEUES; i++) { |
|
||||||
if (!s->queues[i].inited) { |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
assert(i < POOL_MAX_QUEUES); |
|
||||||
|
|
||||||
PoolQueue *c = &s->queues[i]; |
|
||||||
memset(c, 0, sizeof(*c)); |
|
||||||
|
|
||||||
c->pool = s; |
|
||||||
c->inited = true; |
|
||||||
|
|
||||||
c->efd = efd_init(); |
|
||||||
assert(c->efd >= 0); |
|
||||||
|
|
||||||
c->num_bufs = s->num_bufs; |
|
||||||
c->num = c->num_bufs+1; |
|
||||||
c->idx = (int*)malloc(sizeof(int)*c->num); |
|
||||||
memset(c->idx, -1, sizeof(int)*c->num); |
|
||||||
|
|
||||||
pthread_mutex_init(&c->lock, NULL); |
|
||||||
pthread_cond_init(&c->cv, NULL); |
|
||||||
|
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
return c; |
|
||||||
} |
|
||||||
|
|
||||||
void pool_release_queue(PoolQueue *c) { |
|
||||||
Pool *s = c->pool; |
|
||||||
|
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
pthread_mutex_lock(&c->lock); |
|
||||||
|
|
||||||
for (int i=0; i<c->num; i++) { |
|
||||||
if (c->idx[i] != -1) { |
|
||||||
pool_release_locked(s, c->idx[i]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
close(c->efd); |
|
||||||
free(c->idx); |
|
||||||
|
|
||||||
c->inited = false; |
|
||||||
|
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
|
|
||||||
pthread_mutex_destroy(&c->lock); |
|
||||||
pthread_cond_destroy(&c->cv); |
|
||||||
|
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
} |
|
||||||
|
|
||||||
int pool_select(Pool *s) { |
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
|
|
||||||
int i; |
|
||||||
for (i=0; i<s->num_bufs; i++) { |
|
||||||
if (s->refcnt[i] == 0) { |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (i >= s->num_bufs) { |
|
||||||
// overwrite the oldest
|
|
||||||
// still being using in a queue or tbuffer :/
|
|
||||||
|
|
||||||
int min_k = 0; |
|
||||||
int min_ts = s->ts[0]; |
|
||||||
for (int k=1; k<s->num_bufs; k++) { |
|
||||||
if (s->ts[k] < min_ts) { |
|
||||||
min_ts = s->ts[k]; |
|
||||||
min_k = k; |
|
||||||
} |
|
||||||
} |
|
||||||
i = min_k; |
|
||||||
printf("pool is full! evicted %d\n", min_k); |
|
||||||
|
|
||||||
// might be really bad if the user is doing pointery stuff
|
|
||||||
if (s->release_cb) { |
|
||||||
s->release_cb(s->cb_cookie, min_k); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
s->refcnt[i]++; |
|
||||||
|
|
||||||
s->ts[i] = s->counter; |
|
||||||
s->counter++; |
|
||||||
|
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
|
|
||||||
return i; |
|
||||||
} |
|
||||||
|
|
||||||
void pool_push(Pool *s, int idx) { |
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
|
|
||||||
// printf("push %d head %d tail %d\n", idx, s->head, s->tail);
|
|
||||||
|
|
||||||
assert(idx >= 0 && idx < s->num_bufs); |
|
||||||
|
|
||||||
s->ts[idx] = s->counter; |
|
||||||
s->counter++; |
|
||||||
|
|
||||||
assert(s->refcnt[idx] > 0); |
|
||||||
s->refcnt[idx]--; //push is a implcit release
|
|
||||||
|
|
||||||
int num_tbufs = s->num_tbufs; |
|
||||||
s->refcnt[idx] += num_tbufs; |
|
||||||
|
|
||||||
// dispatch pool queues
|
|
||||||
for (int i=0; i<POOL_MAX_QUEUES; i++) { |
|
||||||
PoolQueue *c = &s->queues[i]; |
|
||||||
if (!c->inited) continue; |
|
||||||
|
|
||||||
pthread_mutex_lock(&c->lock); |
|
||||||
if (((c->head+1) % c->num) == c->tail) { |
|
||||||
// queue is full. skip for now
|
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
s->refcnt[idx]++; |
|
||||||
|
|
||||||
c->idx[c->head] = idx; |
|
||||||
c->head = (c->head+1) % c->num; |
|
||||||
assert(c->head != c->tail); |
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
|
|
||||||
efd_write(c->efd); |
|
||||||
pthread_cond_signal(&c->cv); |
|
||||||
} |
|
||||||
|
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
|
|
||||||
for (int i=0; i<num_tbufs; i++) { |
|
||||||
tbuffer_dispatch(&s->tbufs[i], idx); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int poolq_pop(PoolQueue *c) { |
|
||||||
pthread_mutex_lock(&c->lock); |
|
||||||
|
|
||||||
if (c->stopped) { |
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
while (c->head == c->tail) { |
|
||||||
pthread_cond_wait(&c->cv, &c->lock); |
|
||||||
|
|
||||||
if (c->stopped) { |
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// printf("pop head %d tail %d\n", s->head, s->tail);
|
|
||||||
|
|
||||||
assert(c->head != c->tail); |
|
||||||
|
|
||||||
int r = c->idx[c->tail]; |
|
||||||
c->idx[c->tail] = -1; |
|
||||||
c->tail = (c->tail+1) % c->num; |
|
||||||
|
|
||||||
// queue event is level triggered
|
|
||||||
if (c->head == c->tail) { |
|
||||||
efd_clear(c->efd); |
|
||||||
} |
|
||||||
|
|
||||||
// printf("pop %d head %d tail %d\n", r, s->head, s->tail);
|
|
||||||
|
|
||||||
assert(r >= 0 && r < c->num_bufs); |
|
||||||
|
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
|
|
||||||
return r; |
|
||||||
} |
|
||||||
|
|
||||||
int poolq_efd(PoolQueue *c) { |
|
||||||
return c->efd; |
|
||||||
} |
|
||||||
|
|
||||||
void poolq_release(PoolQueue *c, int idx) { |
|
||||||
pool_release(c->pool, idx); |
|
||||||
} |
|
||||||
|
|
||||||
void pool_stop(Pool *s) { |
|
||||||
for (int i=0; i<s->num_tbufs; i++) { |
|
||||||
tbuffer_stop(&s->tbufs[i]); |
|
||||||
} |
|
||||||
|
|
||||||
pthread_mutex_lock(&s->lock); |
|
||||||
s->stopped = true; |
|
||||||
for (int i=0; i<POOL_MAX_QUEUES; i++) { |
|
||||||
PoolQueue *c = &s->queues[i]; |
|
||||||
if (!c->inited) continue; |
|
||||||
|
|
||||||
pthread_mutex_lock(&c->lock); |
|
||||||
c->stopped = true; |
|
||||||
pthread_mutex_unlock(&c->lock); |
|
||||||
efd_write(c->efd); |
|
||||||
pthread_cond_signal(&c->cv); |
|
||||||
} |
|
||||||
pthread_mutex_unlock(&s->lock); |
|
||||||
} |
|
@ -1,123 +0,0 @@ |
|||||||
#ifndef BUFFERING_H |
|
||||||
#define BUFFERING_H |
|
||||||
|
|
||||||
#include <stdbool.h> |
|
||||||
#include <pthread.h> |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
// Tripple buffering helper
|
|
||||||
|
|
||||||
typedef struct TBuffer { |
|
||||||
pthread_mutex_t lock; |
|
||||||
pthread_cond_t cv; |
|
||||||
int efd; |
|
||||||
|
|
||||||
bool* reading; |
|
||||||
int pending_idx; |
|
||||||
|
|
||||||
int num_bufs; |
|
||||||
const char* name; |
|
||||||
|
|
||||||
void (*release_cb)(void* c, int idx); |
|
||||||
void *cb_cookie; |
|
||||||
|
|
||||||
bool stopped; |
|
||||||
} TBuffer; |
|
||||||
|
|
||||||
// num_bufs must be at least the number of buffers that can be acquired simultaniously plus two
|
|
||||||
void tbuffer_init(TBuffer *tb, int num_bufs, const char* name); |
|
||||||
|
|
||||||
void tbuffer_init2(TBuffer *tb, int num_bufs, const char* name, |
|
||||||
void (*release_cb)(void* c, int idx), |
|
||||||
void* cb_cookie); |
|
||||||
|
|
||||||
// returns an eventfd that signals if a buffer is ready and tbuffer_acquire shouldn't to block.
|
|
||||||
// useful to polling on multiple tbuffers.
|
|
||||||
int tbuffer_efd(TBuffer *tb); |
|
||||||
|
|
||||||
// Chooses a buffer that's not reading or pending
|
|
||||||
int tbuffer_select(TBuffer *tb); |
|
||||||
|
|
||||||
// Called when the writer is done with their buffer
|
|
||||||
// - Wakes up the reader if it's waiting
|
|
||||||
// - releases the pending buffer if the reader's too slow
|
|
||||||
void tbuffer_dispatch(TBuffer *tb, int idx); |
|
||||||
|
|
||||||
// Called when the reader wants a new buffer, will return -1 when stopped
|
|
||||||
int tbuffer_acquire(TBuffer *tb); |
|
||||||
|
|
||||||
// Called when the reader is done with their buffer
|
|
||||||
void tbuffer_release(TBuffer *tb, int idx); |
|
||||||
|
|
||||||
void tbuffer_release_all(TBuffer *tb); |
|
||||||
|
|
||||||
void tbuffer_stop(TBuffer *tb); |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// pool: buffer pool + queue thing...
|
|
||||||
|
|
||||||
#define POOL_MAX_TBUFS 8 |
|
||||||
#define POOL_MAX_QUEUES 8 |
|
||||||
|
|
||||||
typedef struct Pool Pool; |
|
||||||
|
|
||||||
typedef struct PoolQueue { |
|
||||||
pthread_mutex_t lock; |
|
||||||
pthread_cond_t cv; |
|
||||||
Pool* pool; |
|
||||||
bool inited; |
|
||||||
bool stopped; |
|
||||||
int efd; |
|
||||||
int num_bufs; |
|
||||||
int num; |
|
||||||
int head, tail; |
|
||||||
int* idx; |
|
||||||
} PoolQueue; |
|
||||||
|
|
||||||
int poolq_pop(PoolQueue *s); |
|
||||||
int poolq_efd(PoolQueue *s); |
|
||||||
void poolq_release(PoolQueue *c, int idx); |
|
||||||
|
|
||||||
typedef struct Pool { |
|
||||||
pthread_mutex_t lock; |
|
||||||
bool stopped; |
|
||||||
int num_bufs; |
|
||||||
int counter; |
|
||||||
|
|
||||||
int* ts; |
|
||||||
int* refcnt; |
|
||||||
|
|
||||||
void (*release_cb)(void* c, int idx); |
|
||||||
void *cb_cookie; |
|
||||||
|
|
||||||
int num_tbufs; |
|
||||||
TBuffer tbufs[POOL_MAX_TBUFS]; |
|
||||||
PoolQueue queues[POOL_MAX_QUEUES]; |
|
||||||
} Pool; |
|
||||||
|
|
||||||
void pool_init(Pool *s, int num_bufs); |
|
||||||
void pool_init2(Pool *s, int num_bufs, |
|
||||||
void (*release_cb)(void* c, int idx), void* cb_cookie); |
|
||||||
|
|
||||||
TBuffer* pool_get_tbuffer(Pool *s); |
|
||||||
|
|
||||||
PoolQueue* pool_get_queue(Pool *s); |
|
||||||
void pool_release_queue(PoolQueue *q); |
|
||||||
|
|
||||||
int pool_select(Pool *s); |
|
||||||
void pool_push(Pool *s, int idx); |
|
||||||
void pool_acquire(Pool *s, int idx); |
|
||||||
void pool_release(Pool *s, int idx); |
|
||||||
void pool_stop(Pool *s); |
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} // extern "C"
|
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,56 +0,0 @@ |
|||||||
#include <stdlib.h> |
|
||||||
#include <assert.h> |
|
||||||
|
|
||||||
#ifdef __linux__ |
|
||||||
#include <sys/eventfd.h> |
|
||||||
#else |
|
||||||
#include <sys/time.h> |
|
||||||
#include <sys/event.h> |
|
||||||
#define EVENT_IDENT 42 |
|
||||||
#endif |
|
||||||
|
|
||||||
#include "efd.h" |
|
||||||
|
|
||||||
|
|
||||||
int efd_init() { |
|
||||||
#ifdef __linux__ |
|
||||||
return eventfd(0, EFD_CLOEXEC); |
|
||||||
#else |
|
||||||
int fd = kqueue(); |
|
||||||
assert(fd >= 0); |
|
||||||
|
|
||||||
struct kevent kev; |
|
||||||
EV_SET(&kev, EVENT_IDENT, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL); |
|
||||||
|
|
||||||
struct timespec timeout = {0, 0}; |
|
||||||
int err = kevent(fd, &kev, 1, NULL, 0, &timeout); |
|
||||||
assert(err != -1); |
|
||||||
|
|
||||||
return fd; |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
void efd_write(int fd) { |
|
||||||
#ifdef __linux__ |
|
||||||
eventfd_write(fd, 1); |
|
||||||
#else |
|
||||||
struct kevent kev; |
|
||||||
EV_SET(&kev, EVENT_IDENT, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); |
|
||||||
|
|
||||||
struct timespec timeout = {0, 0}; |
|
||||||
int err = kevent(fd, &kev, 1, NULL, 0, &timeout); |
|
||||||
assert(err != -1); |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
void efd_clear(int fd) { |
|
||||||
#ifdef __linux__ |
|
||||||
eventfd_t efd_cnt; |
|
||||||
eventfd_read(fd, &efd_cnt); |
|
||||||
#else |
|
||||||
struct kevent kev; |
|
||||||
struct timespec timeout = {0, 0}; |
|
||||||
int nfds = kevent(fd, NULL, 0, &kev, 1, &timeout); |
|
||||||
assert(nfds != -1); |
|
||||||
#endif |
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
#ifndef EFD_H |
|
||||||
#define EFD_H |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
// event fd: a semaphore that can be poll()'d
|
|
||||||
int efd_init(); |
|
||||||
void efd_write(int fd); |
|
||||||
void efd_clear(int fd); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,125 +0,0 @@ |
|||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdbool.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <errno.h> |
|
||||||
|
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/un.h> |
|
||||||
|
|
||||||
#include "ipc.h" |
|
||||||
|
|
||||||
int ipc_connect(const char* socket_path) { |
|
||||||
int err; |
|
||||||
|
|
||||||
#ifdef __APPLE__ |
|
||||||
int sock = socket(AF_UNIX, SOCK_STREAM, 0); |
|
||||||
#else |
|
||||||
int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); |
|
||||||
#endif |
|
||||||
if (sock < 0) return -1; |
|
||||||
struct sockaddr_un addr = { |
|
||||||
.sun_family = AF_UNIX, |
|
||||||
}; |
|
||||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); |
|
||||||
err = connect(sock, (struct sockaddr*)&addr, sizeof(addr)); |
|
||||||
if (err != 0) { |
|
||||||
close(sock); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return sock; |
|
||||||
} |
|
||||||
|
|
||||||
int ipc_bind(const char* socket_path) { |
|
||||||
int err; |
|
||||||
|
|
||||||
unlink(socket_path); |
|
||||||
|
|
||||||
#ifdef __APPLE__ |
|
||||||
int sock = socket(AF_UNIX, SOCK_STREAM, 0); |
|
||||||
#else |
|
||||||
int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); |
|
||||||
#endif |
|
||||||
struct sockaddr_un addr = { |
|
||||||
.sun_family = AF_UNIX, |
|
||||||
}; |
|
||||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); |
|
||||||
err = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); |
|
||||||
assert(err == 0); |
|
||||||
|
|
||||||
err = listen(sock, 3); |
|
||||||
assert(err == 0); |
|
||||||
|
|
||||||
return sock; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int ipc_sendrecv_with_fds(bool send, int fd, void *buf, size_t buf_size, int* fds, int num_fds, |
|
||||||
int *out_num_fds) { |
|
||||||
char control_buf[CMSG_SPACE(sizeof(int) * num_fds)]; |
|
||||||
memset(control_buf, 0, CMSG_SPACE(sizeof(int) * num_fds)); |
|
||||||
|
|
||||||
struct iovec iov = { |
|
||||||
.iov_base = buf, |
|
||||||
.iov_len = buf_size, |
|
||||||
}; |
|
||||||
struct msghdr msg = { |
|
||||||
.msg_iov = &iov, |
|
||||||
.msg_iovlen = 1, |
|
||||||
}; |
|
||||||
|
|
||||||
if (num_fds > 0) { |
|
||||||
assert(fds); |
|
||||||
|
|
||||||
msg.msg_control = control_buf; |
|
||||||
msg.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds); |
|
||||||
} |
|
||||||
|
|
||||||
if (send) { |
|
||||||
if (num_fds) { |
|
||||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); |
|
||||||
assert(cmsg); |
|
||||||
cmsg->cmsg_level = SOL_SOCKET; |
|
||||||
cmsg->cmsg_type = SCM_RIGHTS; |
|
||||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); |
|
||||||
memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * num_fds); |
|
||||||
// printf("send clen %d -> %d\n", num_fds, cmsg->cmsg_len);
|
|
||||||
} |
|
||||||
return sendmsg(fd, &msg, 0); |
|
||||||
} else { |
|
||||||
int r = recvmsg(fd, &msg, 0); |
|
||||||
if (r < 0) return r; |
|
||||||
|
|
||||||
int recv_fds = 0; |
|
||||||
if (msg.msg_controllen > 0) { |
|
||||||
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); |
|
||||||
assert(cmsg); |
|
||||||
assert(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS); |
|
||||||
recv_fds = (cmsg->cmsg_len - CMSG_LEN(0)); |
|
||||||
assert(recv_fds > 0 && (recv_fds % sizeof(int)) == 0); |
|
||||||
recv_fds /= sizeof(int); |
|
||||||
// printf("recv clen %d -> %d\n", cmsg->cmsg_len, recv_fds);
|
|
||||||
// assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int) * num_fds));
|
|
||||||
|
|
||||||
assert(fds && recv_fds <= num_fds); |
|
||||||
memcpy(fds, CMSG_DATA(cmsg), sizeof(int) * recv_fds); |
|
||||||
} |
|
||||||
|
|
||||||
if (msg.msg_flags) { |
|
||||||
for (int i=0; i<recv_fds; i++) { |
|
||||||
close(fds[i]); |
|
||||||
} |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
if (fds) { |
|
||||||
assert(out_num_fds); |
|
||||||
*out_num_fds = recv_fds; |
|
||||||
} |
|
||||||
return r; |
|
||||||
} |
|
||||||
} |
|
@ -1,19 +0,0 @@ |
|||||||
#ifndef IPC_H |
|
||||||
#define IPC_H |
|
||||||
|
|
||||||
#include <stdbool.h> |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
int ipc_connect(const char* socket_path); |
|
||||||
int ipc_bind(const char* socket_path); |
|
||||||
int ipc_sendrecv_with_fds(bool send, int fd, void *buf, size_t buf_size, int* fds, int num_fds, |
|
||||||
int *out_num_fds); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} // extern "C"
|
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,37 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS |
|
||||||
#ifdef __APPLE__ |
|
||||||
#include <OpenCL/cl.h> |
|
||||||
#else |
|
||||||
#include <CL/cl.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
typedef struct VisionBuf { |
|
||||||
size_t len; |
|
||||||
size_t mmap_len; |
|
||||||
void* addr; |
|
||||||
int handle; |
|
||||||
int fd; |
|
||||||
|
|
||||||
cl_context ctx; |
|
||||||
cl_device_id device_id; |
|
||||||
cl_mem buf_cl; |
|
||||||
cl_command_queue copy_q; |
|
||||||
} VisionBuf; |
|
||||||
|
|
||||||
#define VISIONBUF_SYNC_FROM_DEVICE 0 |
|
||||||
#define VISIONBUF_SYNC_TO_DEVICE 1 |
|
||||||
|
|
||||||
VisionBuf visionbuf_allocate(size_t len); |
|
||||||
VisionBuf visionbuf_allocate_cl(size_t len, cl_device_id device_id, cl_context ctx); |
|
||||||
void visionbuf_sync(const VisionBuf* buf, int dir); |
|
||||||
void visionbuf_free(const VisionBuf* buf); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
@ -1,101 +0,0 @@ |
|||||||
#include "visionbuf.h" |
|
||||||
|
|
||||||
#include <stdio.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include "common/clutil.h" |
|
||||||
|
|
||||||
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS |
|
||||||
#ifdef __APPLE__ |
|
||||||
#include <OpenCL/cl.h> |
|
||||||
#else |
|
||||||
#include <CL/cl.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
int offset = 0; |
|
||||||
void *malloc_with_fd(size_t len, int *fd) { |
|
||||||
char full_path[0x100]; |
|
||||||
#ifdef __APPLE__ |
|
||||||
snprintf(full_path, sizeof(full_path)-1, "/tmp/visionbuf_%d_%d", getpid(), offset++); |
|
||||||
#else |
|
||||||
snprintf(full_path, sizeof(full_path)-1, "/dev/shm/visionbuf_%d_%d", getpid(), offset++); |
|
||||||
#endif |
|
||||||
*fd = open(full_path, O_RDWR | O_CREAT, 0777); |
|
||||||
assert(*fd >= 0); |
|
||||||
unlink(full_path); |
|
||||||
ftruncate(*fd, len); |
|
||||||
void *addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0); |
|
||||||
assert(addr != MAP_FAILED); |
|
||||||
return addr; |
|
||||||
} |
|
||||||
|
|
||||||
VisionBuf visionbuf_allocate(size_t len) { |
|
||||||
// const size_t alignment = 4096;
|
|
||||||
// void* addr = aligned_alloc(alignment, alignment * ((len - 1) / alignment + 1));
|
|
||||||
//void* addr = calloc(1, len);
|
|
||||||
|
|
||||||
int fd; |
|
||||||
void *addr = malloc_with_fd(len, &fd); |
|
||||||
|
|
||||||
return (VisionBuf){ |
|
||||||
.len = len, .addr = addr, .handle = 1, .fd = fd, |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
VisionBuf visionbuf_allocate_cl(size_t len, cl_device_id device_id, cl_context ctx) { |
|
||||||
#if __OPENCL_VERSION__ >= 200 |
|
||||||
void* host_ptr = |
|
||||||
clSVMAlloc(ctx, CL_MEM_READ_WRITE | CL_MEM_SVM_FINE_GRAIN_BUFFER, len, 0); |
|
||||||
assert(host_ptr); |
|
||||||
#else |
|
||||||
int fd; |
|
||||||
void* host_ptr = malloc_with_fd(len, &fd); |
|
||||||
|
|
||||||
cl_command_queue q = CL_CHECK_ERR(clCreateCommandQueue(ctx, device_id, 0, &err)); |
|
||||||
#endif |
|
||||||
|
|
||||||
cl_mem mem = CL_CHECK_ERR(clCreateBuffer(ctx, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, len, host_ptr, &err)); |
|
||||||
|
|
||||||
return (VisionBuf){ |
|
||||||
.len = len, .addr = host_ptr, .handle = 0, .fd = fd, |
|
||||||
.device_id = device_id, .ctx = ctx, .buf_cl = mem, |
|
||||||
|
|
||||||
#if __OPENCL_VERSION__ < 200 |
|
||||||
.copy_q = q, |
|
||||||
#endif |
|
||||||
|
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
void visionbuf_sync(const VisionBuf* buf, int dir) { |
|
||||||
if (!buf->buf_cl) return; |
|
||||||
|
|
||||||
#if __OPENCL_VERSION__ < 200 |
|
||||||
if (dir == VISIONBUF_SYNC_FROM_DEVICE) { |
|
||||||
CL_CHECK(clEnqueueReadBuffer(buf->copy_q, buf->buf_cl, CL_FALSE, 0, buf->len, buf->addr, 0, NULL, NULL)); |
|
||||||
} else { |
|
||||||
CL_CHECK(clEnqueueWriteBuffer(buf->copy_q, buf->buf_cl, CL_FALSE, 0, buf->len, buf->addr, 0, NULL, NULL)); |
|
||||||
} |
|
||||||
clFinish(buf->copy_q); |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
void visionbuf_free(const VisionBuf* buf) { |
|
||||||
if (buf->handle) { |
|
||||||
munmap(buf->addr, buf->len); |
|
||||||
close(buf->fd); |
|
||||||
} else { |
|
||||||
CL_CHECK(clReleaseMemObject(buf->buf_cl)); |
|
||||||
#if __OPENCL_VERSION__ >= 200 |
|
||||||
clSVMFree(buf->ctx, buf->addr); |
|
||||||
#else |
|
||||||
CL_CHECK(clReleaseCommandQueue(buf->copy_q)); |
|
||||||
munmap(buf->addr, buf->len); |
|
||||||
close(buf->fd); |
|
||||||
#endif |
|
||||||
} |
|
||||||
} |
|
@ -1,144 +0,0 @@ |
|||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/ioctl.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <sys/stat.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <linux/ion.h> |
|
||||||
#include <CL/cl_ext.h> |
|
||||||
#include "common/clutil.h" |
|
||||||
#include <msm_ion.h> |
|
||||||
|
|
||||||
#include "visionbuf.h" |
|
||||||
|
|
||||||
|
|
||||||
// just hard-code these for convenience
|
|
||||||
// size_t device_page_size = 0;
|
|
||||||
// clGetDeviceInfo(device_id, CL_DEVICE_PAGE_SIZE_QCOM,
|
|
||||||
// sizeof(device_page_size), &device_page_size,
|
|
||||||
// NULL);
|
|
||||||
|
|
||||||
// size_t padding_cl = 0;
|
|
||||||
// clGetDeviceInfo(device_id, CL_DEVICE_EXT_MEM_PADDING_IN_BYTES_QCOM,
|
|
||||||
// sizeof(padding_cl), &padding_cl,
|
|
||||||
// NULL);
|
|
||||||
#define DEVICE_PAGE_SIZE_CL 4096 |
|
||||||
#define PADDING_CL 0 |
|
||||||
|
|
||||||
static int ion_fd = -1; |
|
||||||
static void ion_init() { |
|
||||||
if (ion_fd == -1) { |
|
||||||
ion_fd = open("/dev/ion", O_RDWR | O_NONBLOCK); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
VisionBuf visionbuf_allocate(size_t len) { |
|
||||||
int err; |
|
||||||
|
|
||||||
ion_init(); |
|
||||||
|
|
||||||
struct ion_allocation_data ion_alloc = {0}; |
|
||||||
ion_alloc.len = len + PADDING_CL; |
|
||||||
ion_alloc.align = 4096; |
|
||||||
ion_alloc.heap_id_mask = 1 << ION_IOMMU_HEAP_ID; |
|
||||||
ion_alloc.flags = ION_FLAG_CACHED; |
|
||||||
|
|
||||||
err = ioctl(ion_fd, ION_IOC_ALLOC, &ion_alloc); |
|
||||||
assert(err == 0); |
|
||||||
|
|
||||||
struct ion_fd_data ion_fd_data = {0}; |
|
||||||
ion_fd_data.handle = ion_alloc.handle; |
|
||||||
err = ioctl(ion_fd, ION_IOC_SHARE, &ion_fd_data); |
|
||||||
assert(err == 0); |
|
||||||
|
|
||||||
void *addr = mmap(NULL, ion_alloc.len, |
|
||||||
PROT_READ | PROT_WRITE, |
|
||||||
MAP_SHARED, ion_fd_data.fd, 0); |
|
||||||
assert(addr != MAP_FAILED); |
|
||||||
|
|
||||||
memset(addr, 0, ion_alloc.len); |
|
||||||
|
|
||||||
return (VisionBuf){ |
|
||||||
.len = len, |
|
||||||
.mmap_len = ion_alloc.len, |
|
||||||
.addr = addr, |
|
||||||
.handle = ion_alloc.handle, |
|
||||||
.fd = ion_fd_data.fd, |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
VisionBuf visionbuf_allocate_cl(size_t len, cl_device_id device_id, cl_context ctx) { |
|
||||||
VisionBuf buf = visionbuf_allocate(len); |
|
||||||
|
|
||||||
assert(((uintptr_t)buf.addr % DEVICE_PAGE_SIZE_CL) == 0); |
|
||||||
|
|
||||||
cl_mem_ion_host_ptr ion_cl = {0}; |
|
||||||
ion_cl.ext_host_ptr.allocation_type = CL_MEM_ION_HOST_PTR_QCOM; |
|
||||||
ion_cl.ext_host_ptr.host_cache_policy = CL_MEM_HOST_UNCACHED_QCOM; |
|
||||||
ion_cl.ion_filedesc = buf.fd; |
|
||||||
ion_cl.ion_hostptr = buf.addr; |
|
||||||
|
|
||||||
buf.buf_cl = CL_CHECK_ERR(clCreateBuffer(ctx, |
|
||||||
CL_MEM_USE_HOST_PTR | CL_MEM_EXT_HOST_PTR_QCOM, |
|
||||||
buf.len, &ion_cl, &err)); |
|
||||||
return buf; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
void visionbuf_sync(const VisionBuf* buf, int dir) { |
|
||||||
int err; |
|
||||||
|
|
||||||
struct ion_fd_data fd_data = {0}; |
|
||||||
fd_data.fd = buf->fd; |
|
||||||
err = ioctl(ion_fd, ION_IOC_IMPORT, &fd_data); |
|
||||||
assert(err == 0); |
|
||||||
|
|
||||||
struct ion_flush_data flush_data = {0}; |
|
||||||
flush_data.handle = fd_data.handle; |
|
||||||
flush_data.vaddr = buf->addr; |
|
||||||
flush_data.offset = 0; |
|
||||||
flush_data.length = buf->len; |
|
||||||
|
|
||||||
// ION_IOC_INV_CACHES ~= DMA_FROM_DEVICE
|
|
||||||
// ION_IOC_CLEAN_CACHES ~= DMA_TO_DEVICE
|
|
||||||
// ION_IOC_CLEAN_INV_CACHES ~= DMA_BIDIRECTIONAL
|
|
||||||
|
|
||||||
struct ion_custom_data custom_data = {0}; |
|
||||||
|
|
||||||
switch (dir) { |
|
||||||
case VISIONBUF_SYNC_FROM_DEVICE: |
|
||||||
custom_data.cmd = ION_IOC_INV_CACHES; |
|
||||||
break; |
|
||||||
case VISIONBUF_SYNC_TO_DEVICE: |
|
||||||
custom_data.cmd = ION_IOC_CLEAN_CACHES; |
|
||||||
break; |
|
||||||
default: |
|
||||||
assert(0); |
|
||||||
} |
|
||||||
|
|
||||||
custom_data.arg = (unsigned long)&flush_data; |
|
||||||
err = ioctl(ion_fd, ION_IOC_CUSTOM, &custom_data); |
|
||||||
assert(err == 0); |
|
||||||
|
|
||||||
struct ion_handle_data handle_data = {0}; |
|
||||||
handle_data.handle = fd_data.handle; |
|
||||||
err = ioctl(ion_fd, ION_IOC_FREE, &handle_data); |
|
||||||
assert(err == 0); |
|
||||||
} |
|
||||||
|
|
||||||
void visionbuf_free(const VisionBuf* buf) { |
|
||||||
if (buf->buf_cl) { |
|
||||||
CL_CHECK(clReleaseMemObject(buf->buf_cl)); |
|
||||||
} |
|
||||||
munmap(buf->addr, buf->mmap_len); |
|
||||||
close(buf->fd); |
|
||||||
struct ion_handle_data handle_data = { |
|
||||||
.handle = buf->handle, |
|
||||||
}; |
|
||||||
int ret = ioctl(ion_fd, ION_IOC_FREE, &handle_data); |
|
||||||
assert(ret == 0); |
|
||||||
} |
|
@ -1,196 +0,0 @@ |
|||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdbool.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <errno.h> |
|
||||||
|
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/un.h> |
|
||||||
|
|
||||||
#include "ipc.h" |
|
||||||
|
|
||||||
#include "visionipc.h" |
|
||||||
|
|
||||||
typedef struct VisionPacketWire { |
|
||||||
int type; |
|
||||||
VisionPacketData d; |
|
||||||
} VisionPacketWire; |
|
||||||
|
|
||||||
int vipc_connect() { |
|
||||||
return ipc_connect(VIPC_SOCKET_PATH); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int vipc_recv(int fd, VisionPacket *out_p) { |
|
||||||
VisionPacketWire p = {0}; |
|
||||||
VisionPacket p2 = {0}; |
|
||||||
int ret = ipc_sendrecv_with_fds(false, fd, &p, sizeof(p), (int*)p2.fds, VIPC_MAX_FDS, &p2.num_fds); |
|
||||||
if (ret < 0) { |
|
||||||
printf("vipc_recv err: %s\n", strerror(errno)); |
|
||||||
} else { |
|
||||||
p2.type = p.type; |
|
||||||
p2.d = p.d; |
|
||||||
*out_p = p2; |
|
||||||
} |
|
||||||
//printf("%d = vipc_recv(%d, %d): %d %d %d %zu\n", ret, fd, p2.num_fds, out_p->d.stream_bufs.type, out_p->d.stream_bufs.width, out_p->d.stream_bufs.height, out_p->d.stream_bufs.buf_len);
|
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
int vipc_send(int fd, const VisionPacket *p2) { |
|
||||||
assert(p2->num_fds <= VIPC_MAX_FDS); |
|
||||||
|
|
||||||
VisionPacketWire p = { |
|
||||||
.type = p2->type, |
|
||||||
.d = p2->d, |
|
||||||
}; |
|
||||||
int ret = ipc_sendrecv_with_fds(true, fd, (void*)&p, sizeof(p), (int*)p2->fds, p2->num_fds, NULL); |
|
||||||
//printf("%d = vipc_send(%d, %d): %d %d %d %zu\n", ret, fd, p2->num_fds, p2->d.stream_bufs.type, p2->d.stream_bufs.width, p2->d.stream_bufs.height, p2->d.stream_bufs.buf_len);
|
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
void vipc_bufs_load(VIPCBuf *bufs, const VisionStreamBufs *stream_bufs, |
|
||||||
int num_fds, const int* fds) { |
|
||||||
for (int i=0; i<num_fds; i++) { |
|
||||||
if (bufs[i].addr) { |
|
||||||
munmap(bufs[i].addr, bufs[i].len); |
|
||||||
bufs[i].addr = NULL; |
|
||||||
close(bufs[i].fd); |
|
||||||
} |
|
||||||
bufs[i].fd = fds[i]; |
|
||||||
bufs[i].len = stream_bufs->buf_len; |
|
||||||
bufs[i].addr = mmap(NULL, bufs[i].len, |
|
||||||
PROT_READ | PROT_WRITE, |
|
||||||
MAP_SHARED, bufs[i].fd, 0); |
|
||||||
// printf("b %d %zu -> %p\n", bufs[i].fd, bufs[i].len, bufs[i].addr);
|
|
||||||
assert(bufs[i].addr != MAP_FAILED); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int visionstream_init(VisionStream *s, VisionStreamType type, bool tbuffer, VisionStreamBufs *out_bufs_info) { |
|
||||||
int err; |
|
||||||
|
|
||||||
memset(s, 0, sizeof(*s)); |
|
||||||
|
|
||||||
s->last_idx = -1; |
|
||||||
|
|
||||||
s->ipc_fd = vipc_connect(); |
|
||||||
if (s->ipc_fd < 0) return -1; |
|
||||||
|
|
||||||
VisionPacket p = { |
|
||||||
.type = VIPC_STREAM_SUBSCRIBE, |
|
||||||
.d = { .stream_sub = { |
|
||||||
.type = type, |
|
||||||
.tbuffer = tbuffer, |
|
||||||
}, }, |
|
||||||
}; |
|
||||||
err = vipc_send(s->ipc_fd, &p); |
|
||||||
if (err < 0) { |
|
||||||
close(s->ipc_fd); |
|
||||||
s->ipc_fd = -1; |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
VisionPacket rp; |
|
||||||
err = vipc_recv(s->ipc_fd, &rp); |
|
||||||
if (err <= 0) { |
|
||||||
close(s->ipc_fd); |
|
||||||
s->ipc_fd = -1; |
|
||||||
return -1; |
|
||||||
} |
|
||||||
assert(rp.type == VIPC_STREAM_BUFS); |
|
||||||
assert(rp.d.stream_bufs.type == type); |
|
||||||
|
|
||||||
s->bufs_info = rp.d.stream_bufs; |
|
||||||
|
|
||||||
s->num_bufs = rp.num_fds; |
|
||||||
s->bufs = calloc(s->num_bufs, sizeof(VIPCBuf)); |
|
||||||
assert(s->bufs); |
|
||||||
|
|
||||||
vipc_bufs_load(s->bufs, &rp.d.stream_bufs, s->num_bufs, rp.fds); |
|
||||||
|
|
||||||
if (out_bufs_info) { |
|
||||||
*out_bufs_info = s->bufs_info; |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
void visionstream_release(VisionStream *s) { |
|
||||||
int err; |
|
||||||
if (s->last_idx >= 0) { |
|
||||||
VisionPacket rep = { |
|
||||||
.type = VIPC_STREAM_RELEASE, |
|
||||||
.d = { .stream_rel = { |
|
||||||
.type = s->last_type, |
|
||||||
.idx = s->last_idx, |
|
||||||
}} |
|
||||||
}; |
|
||||||
err = vipc_send(s->ipc_fd, &rep); |
|
||||||
s->last_idx = -1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
VIPCBuf* visionstream_get(VisionStream *s, VIPCBufExtra *out_extra) { |
|
||||||
int err; |
|
||||||
|
|
||||||
VisionPacket rp; |
|
||||||
err = vipc_recv(s->ipc_fd, &rp); |
|
||||||
if (err <= 0) { |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
assert(rp.type == VIPC_STREAM_ACQUIRE); |
|
||||||
|
|
||||||
if (s->last_idx >= 0) { |
|
||||||
VisionPacket rep = { |
|
||||||
.type = VIPC_STREAM_RELEASE, |
|
||||||
.d = { .stream_rel = { |
|
||||||
.type = s->last_type, |
|
||||||
.idx = s->last_idx, |
|
||||||
}} |
|
||||||
}; |
|
||||||
err = vipc_send(s->ipc_fd, &rep); |
|
||||||
if (err <= 0) { |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
s->last_type = rp.d.stream_acq.type; |
|
||||||
s->last_idx = rp.d.stream_acq.idx; |
|
||||||
assert(s->last_idx < s->num_bufs); |
|
||||||
|
|
||||||
if (out_extra) { |
|
||||||
*out_extra = rp.d.stream_acq.extra; |
|
||||||
} |
|
||||||
|
|
||||||
return &s->bufs[s->last_idx]; |
|
||||||
} |
|
||||||
|
|
||||||
void visionstream_destroy(VisionStream *s) { |
|
||||||
int err; |
|
||||||
|
|
||||||
if (s->last_idx >= 0) { |
|
||||||
VisionPacket rep = { |
|
||||||
.type = VIPC_STREAM_RELEASE, |
|
||||||
.d = { .stream_rel = { |
|
||||||
.type = s->last_type, |
|
||||||
.idx = s->last_idx, |
|
||||||
}} |
|
||||||
}; |
|
||||||
err = vipc_send(s->ipc_fd, &rep); |
|
||||||
s->last_idx = -1; |
|
||||||
} |
|
||||||
|
|
||||||
for (int i=0; i<s->num_bufs; i++) { |
|
||||||
if (s->bufs[i].addr) { |
|
||||||
munmap(s->bufs[i].addr, s->bufs[i].len); |
|
||||||
s->bufs[i].addr = NULL; |
|
||||||
close(s->bufs[i].fd); |
|
||||||
} |
|
||||||
} |
|
||||||
if (s->bufs) free(s->bufs); |
|
||||||
if (s->ipc_fd >= 0) close(s->ipc_fd); |
|
||||||
} |
|
@ -1,120 +0,0 @@ |
|||||||
#ifndef VISIONIPC_H |
|
||||||
#define VISIONIPC_H |
|
||||||
|
|
||||||
#include <stddef.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stdbool.h> |
|
||||||
|
|
||||||
#define VIPC_SOCKET_PATH "/tmp/vision_socket" |
|
||||||
#define VIPC_MAX_FDS 64 |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
typedef enum VisionIPCPacketType { |
|
||||||
VIPC_INVALID = 0, |
|
||||||
VIPC_STREAM_SUBSCRIBE, |
|
||||||
VIPC_STREAM_BUFS, |
|
||||||
VIPC_STREAM_ACQUIRE, |
|
||||||
VIPC_STREAM_RELEASE, |
|
||||||
} VisionIPCPacketType; |
|
||||||
|
|
||||||
typedef enum VisionStreamType { |
|
||||||
VISION_STREAM_RGB_BACK, |
|
||||||
VISION_STREAM_RGB_FRONT, |
|
||||||
VISION_STREAM_RGB_WIDE, |
|
||||||
VISION_STREAM_YUV, |
|
||||||
VISION_STREAM_YUV_FRONT, |
|
||||||
VISION_STREAM_YUV_WIDE, |
|
||||||
VISION_STREAM_MAX, |
|
||||||
} VisionStreamType; |
|
||||||
|
|
||||||
typedef struct VisionUIInfo { |
|
||||||
int big_box_x, big_box_y; |
|
||||||
int big_box_width, big_box_height; |
|
||||||
int transformed_width, transformed_height; |
|
||||||
|
|
||||||
int front_box_x, front_box_y; |
|
||||||
int front_box_width, front_box_height; |
|
||||||
|
|
||||||
int wide_box_x, wide_box_y; |
|
||||||
int wide_box_width, wide_box_height; |
|
||||||
|
|
||||||
} VisionUIInfo; |
|
||||||
|
|
||||||
typedef struct VisionStreamBufs { |
|
||||||
VisionStreamType type; |
|
||||||
|
|
||||||
int width, height, stride; |
|
||||||
size_t buf_len; |
|
||||||
|
|
||||||
union { |
|
||||||
VisionUIInfo ui_info; |
|
||||||
} buf_info; |
|
||||||
} VisionStreamBufs; |
|
||||||
|
|
||||||
typedef struct VIPCBufExtra { |
|
||||||
// only for yuv
|
|
||||||
uint32_t frame_id; |
|
||||||
uint64_t timestamp_sof; |
|
||||||
uint64_t timestamp_eof; |
|
||||||
} VIPCBufExtra; |
|
||||||
|
|
||||||
typedef union VisionPacketData { |
|
||||||
struct { |
|
||||||
VisionStreamType type; |
|
||||||
bool tbuffer; |
|
||||||
} stream_sub; |
|
||||||
VisionStreamBufs stream_bufs; |
|
||||||
struct { |
|
||||||
VisionStreamType type; |
|
||||||
int idx; |
|
||||||
VIPCBufExtra extra; |
|
||||||
} stream_acq; |
|
||||||
struct { |
|
||||||
VisionStreamType type; |
|
||||||
int idx; |
|
||||||
} stream_rel; |
|
||||||
} VisionPacketData; |
|
||||||
|
|
||||||
typedef struct VisionPacket { |
|
||||||
int type; |
|
||||||
VisionPacketData d; |
|
||||||
int num_fds; |
|
||||||
int fds[VIPC_MAX_FDS]; |
|
||||||
} VisionPacket; |
|
||||||
|
|
||||||
int vipc_connect(void); |
|
||||||
int vipc_recv(int fd, VisionPacket *out_p); |
|
||||||
int vipc_send(int fd, const VisionPacket *p); |
|
||||||
|
|
||||||
typedef struct VIPCBuf { |
|
||||||
int fd; |
|
||||||
size_t len; |
|
||||||
void* addr; |
|
||||||
} VIPCBuf; |
|
||||||
void vipc_bufs_load(VIPCBuf *bufs, const VisionStreamBufs *stream_bufs, |
|
||||||
int num_fds, const int* fds); |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct VisionStream { |
|
||||||
int ipc_fd; |
|
||||||
int last_idx; |
|
||||||
int last_type; |
|
||||||
int num_bufs; |
|
||||||
VisionStreamBufs bufs_info; |
|
||||||
VIPCBuf *bufs; |
|
||||||
} VisionStream; |
|
||||||
|
|
||||||
int visionstream_init(VisionStream *s, VisionStreamType type, bool tbuffer, VisionStreamBufs *out_bufs_info); |
|
||||||
void visionstream_release(VisionStream *s); |
|
||||||
VIPCBuf* visionstream_get(VisionStream *s, VIPCBufExtra *out_extra); |
|
||||||
void visionstream_destroy(VisionStream *s); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
Loading…
Reference in new issue