parent
71ead9adea
commit
368a956b96
41 changed files with 3876 additions and 0 deletions
@ -0,0 +1,36 @@ |
||||
Import('env', 'arch', 'SHARED') |
||||
|
||||
if SHARED: |
||||
fxn = env.SharedLibrary |
||||
else: |
||||
fxn = env.Library |
||||
|
||||
_common = fxn('common', ['params.cc', 'swaglog.c', 'util.c', 'cqueue.c'], LIBS="json") |
||||
_visionipc = fxn('visionipc', ['visionipc.c', 'ipc.c']) |
||||
|
||||
files = [ |
||||
'buffering.c', |
||||
'clutil.c', |
||||
'efd.c', |
||||
'glutil.c', |
||||
'visionimg.cc', |
||||
] |
||||
|
||||
if arch == "aarch64": |
||||
defines = {} |
||||
files += [ |
||||
'framebuffer.cc', |
||||
'touch.c', |
||||
'visionbuf_ion.c', |
||||
] |
||||
_gpu_libs = ['gui', 'adreno_utils'] |
||||
else: |
||||
defines = {"CLU_NO_CACHE": None} |
||||
files += [ |
||||
'visionbuf_cl.c', |
||||
] |
||||
_gpu_libs = ["GL"] |
||||
|
||||
_gpucommon = fxn('gpucommon', files, CPPDEFINES=defines, LIBS=_gpu_libs) |
||||
Export('_common', '_visionipc', '_gpucommon', '_gpu_libs') |
||||
|
@ -0,0 +1,438 @@ |
||||
#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); |
||||
} |
@ -0,0 +1,123 @@ |
||||
#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 |
@ -0,0 +1,418 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <assert.h> |
||||
#include <string.h> |
||||
#include <unistd.h> |
||||
#include <inttypes.h> |
||||
#include <sys/stat.h> |
||||
|
||||
#ifdef __APPLE__ |
||||
#include <OpenCL/cl.h> |
||||
#else |
||||
#include <CL/cl.h> |
||||
#endif |
||||
|
||||
#include "common/util.h" |
||||
|
||||
#include "clutil.h" |
||||
|
||||
typedef struct CLUProgramIndex { |
||||
uint64_t index_hash; |
||||
const uint8_t* bin_data; |
||||
const uint8_t* bin_end; |
||||
} CLUProgramIndex; |
||||
|
||||
#ifdef CLU_NO_SRC |
||||
#include "clcache_bins.h" |
||||
#else |
||||
static const CLUProgramIndex clu_index[] = {}; |
||||
#endif |
||||
|
||||
void clu_init(void) { |
||||
#ifndef CLU_NO_SRC |
||||
mkdir("/tmp/clcache", 0777); |
||||
unlink("/tmp/clcache/index.cli"); |
||||
#endif |
||||
} |
||||
|
||||
cl_program cl_create_program_from_file(cl_context ctx, const char* path) { |
||||
char* src_buf = read_file(path, NULL); |
||||
assert(src_buf); |
||||
|
||||
int err = 0; |
||||
cl_program ret = clCreateProgramWithSource(ctx, 1, (const char**)&src_buf, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
free(src_buf); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static char* get_version_string(cl_platform_id platform) { |
||||
size_t size = 0; |
||||
int err; |
||||
err = clGetPlatformInfo(platform, CL_PLATFORM_VERSION, 0, NULL, &size); |
||||
assert(err == 0); |
||||
char *str = malloc(size); |
||||
assert(str); |
||||
err = clGetPlatformInfo(platform, CL_PLATFORM_VERSION, size, str, NULL); |
||||
assert(err == 0); |
||||
return str; |
||||
} |
||||
|
||||
void cl_print_info(cl_platform_id platform, cl_device_id device) { |
||||
char str[4096]; |
||||
|
||||
clGetPlatformInfo(platform, CL_PLATFORM_VENDOR, sizeof(str), str, NULL); |
||||
printf("vendor: '%s'\n", str); |
||||
|
||||
char* version = get_version_string(platform); |
||||
printf("platform version: '%s'\n", version); |
||||
free(version); |
||||
|
||||
clGetPlatformInfo(platform, CL_PLATFORM_PROFILE, sizeof(str), str, NULL); |
||||
printf("profile: '%s'\n", str); |
||||
|
||||
clGetPlatformInfo(platform, CL_PLATFORM_EXTENSIONS, sizeof(str), str, NULL); |
||||
printf("extensions: '%s'\n", str); |
||||
|
||||
clGetDeviceInfo(device, CL_DEVICE_NAME, sizeof(str), str, NULL); |
||||
printf("name: '%s'\n", str); |
||||
|
||||
clGetDeviceInfo(device, CL_DEVICE_VERSION, sizeof(str), str, NULL); |
||||
printf("device version: '%s'\n", str); |
||||
|
||||
size_t sz; |
||||
clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(sz), &sz, NULL); |
||||
printf("max work group size: %u\n", sz); |
||||
|
||||
cl_device_type type; |
||||
clGetDeviceInfo(device, CL_DEVICE_TYPE, sizeof(type), &type, NULL); |
||||
printf("type = 0x%04x = ", (unsigned int)type); |
||||
switch(type) { |
||||
case CL_DEVICE_TYPE_CPU: |
||||
printf("CL_DEVICE_TYPE_CPU\n"); |
||||
break; |
||||
case CL_DEVICE_TYPE_GPU: |
||||
printf("CL_DEVICE_TYPE_GPU\n"); |
||||
break; |
||||
case CL_DEVICE_TYPE_ACCELERATOR: |
||||
printf("CL_DEVICE_TYPE_ACCELERATOR\n"); |
||||
break; |
||||
default: |
||||
printf("Other...\n" ); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
void cl_print_build_errors(cl_program program, cl_device_id device) { |
||||
cl_build_status status; |
||||
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_STATUS, |
||||
sizeof(cl_build_status), &status, NULL); |
||||
|
||||
size_t log_size; |
||||
clGetProgramBuildInfo(program, device, |
||||
CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size); |
||||
|
||||
char* log = calloc(log_size+1, 1); |
||||
assert(log); |
||||
|
||||
clGetProgramBuildInfo(program, device, |
||||
CL_PROGRAM_BUILD_LOG, log_size+1, log, NULL); |
||||
|
||||
printf("build failed; status=%d, log:\n%s\n", |
||||
status, log); |
||||
|
||||
free(log); |
||||
} |
||||
|
||||
uint64_t clu_index_hash(const char* s) { |
||||
size_t sl = strlen(s); |
||||
assert(sl < 128); |
||||
uint64_t x = 0; |
||||
for (int i=127; i>=0; i--) { |
||||
x *= 65599ULL; |
||||
x += (uint8_t)s[i<sl ? sl-1-i : sl]; |
||||
} |
||||
return x ^ (x >> 32); |
||||
} |
||||
|
||||
uint64_t clu_fnv_hash(const uint8_t *data, size_t len) { |
||||
/* 64 bit Fowler/Noll/Vo FNV-1a hash code */ |
||||
uint64_t hval = 0xcbf29ce484222325ULL; |
||||
const uint8_t *dp = data; |
||||
const uint8_t *de = data + len; |
||||
while (dp < de) { |
||||
hval ^= (uint64_t) *dp++; |
||||
hval += (hval << 1) + (hval << 4) + (hval << 5) + |
||||
(hval << 7) + (hval << 8) + (hval << 40); |
||||
} |
||||
|
||||
return hval; |
||||
} |
||||
|
||||
cl_program cl_cached_program_from_hash(cl_context ctx, cl_device_id device_id, uint64_t hash) { |
||||
int err; |
||||
|
||||
char cache_path[1024]; |
||||
snprintf(cache_path, sizeof(cache_path), "/tmp/clcache/%016" PRIx64 ".clb", hash); |
||||
|
||||
size_t bin_size; |
||||
uint8_t *bin = read_file(cache_path, &bin_size); |
||||
if (!bin) { |
||||
return NULL; |
||||
} |
||||
|
||||
cl_program prg = clCreateProgramWithBinary(ctx, 1, &device_id, &bin_size, (const uint8_t**)&bin, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
free(bin); |
||||
|
||||
err = clBuildProgram(prg, 1, &device_id, NULL, NULL, NULL); |
||||
assert(err == 0); |
||||
|
||||
return prg; |
||||
} |
||||
|
||||
static uint8_t* get_program_binary(cl_program prg, size_t *out_size) { |
||||
int err; |
||||
|
||||
cl_uint num_devices; |
||||
err = clGetProgramInfo(prg, CL_PROGRAM_NUM_DEVICES, sizeof(num_devices), &num_devices, NULL); |
||||
assert(err == 0); |
||||
assert(num_devices == 1); |
||||
|
||||
size_t binary_size = 0; |
||||
err = clGetProgramInfo(prg, CL_PROGRAM_BINARY_SIZES, sizeof(binary_size), &binary_size, NULL); |
||||
assert(err == 0); |
||||
assert(binary_size > 0); |
||||
|
||||
uint8_t *binary_buf = malloc(binary_size); |
||||
assert(binary_buf); |
||||
|
||||
uint8_t* bufs[1] = { binary_buf, }; |
||||
|
||||
err = clGetProgramInfo(prg, CL_PROGRAM_BINARIES, sizeof(bufs), &bufs, NULL); |
||||
assert(err == 0); |
||||
|
||||
*out_size = binary_size; |
||||
return binary_buf; |
||||
} |
||||
|
||||
cl_program cl_cached_program_from_string(cl_context ctx, cl_device_id device_id, |
||||
const char* src, const char* args, |
||||
uint64_t *out_hash) { |
||||
int err; |
||||
|
||||
cl_platform_id platform; |
||||
err = clGetDeviceInfo(device_id, CL_DEVICE_PLATFORM, sizeof(platform), &platform, NULL); |
||||
assert(err == 0); |
||||
|
||||
const char* platform_version = get_version_string(platform); |
||||
|
||||
const size_t hash_len = strlen(platform_version)+1+strlen(src)+1+strlen(args)+1; |
||||
char* hash_buf = malloc(hash_len); |
||||
assert(hash_buf); |
||||
memset(hash_buf, 0, hash_len); |
||||
snprintf(hash_buf, hash_len, "%s%c%s%c%s", platform_version, 1, src, 1, args); |
||||
free((void*)platform_version); |
||||
|
||||
uint64_t hash = clu_fnv_hash((uint8_t*)hash_buf, hash_len); |
||||
free(hash_buf); |
||||
|
||||
cl_program prg = NULL; |
||||
#ifndef CLU_NO_CACHE |
||||
prg = cl_cached_program_from_hash(ctx, device_id, hash); |
||||
#endif |
||||
if (prg == NULL) { |
||||
prg = clCreateProgramWithSource(ctx, 1, (const char**)&src, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); |
||||
if (err != 0) { |
||||
cl_print_build_errors(prg, device_id); |
||||
} |
||||
assert(err == 0); |
||||
|
||||
#ifndef CLU_NO_CACHE |
||||
// write program binary to cache
|
||||
|
||||
size_t binary_size; |
||||
uint8_t *binary_buf = get_program_binary(prg, &binary_size); |
||||
|
||||
char cache_path[1024]; |
||||
snprintf(cache_path, sizeof(cache_path), "/tmp/clcache/%016" PRIx64 ".clb", hash); |
||||
FILE* of = fopen(cache_path, "wb"); |
||||
assert(of); |
||||
fwrite(binary_buf, 1, binary_size, of); |
||||
fclose(of); |
||||
|
||||
free(binary_buf); |
||||
#endif |
||||
} |
||||
|
||||
if (out_hash) *out_hash = hash; |
||||
return prg; |
||||
} |
||||
|
||||
cl_program cl_cached_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args, |
||||
uint64_t *out_hash) { |
||||
char* src_buf = read_file(path, NULL); |
||||
assert(src_buf); |
||||
cl_program ret = cl_cached_program_from_string(ctx, device_id, src_buf, args, out_hash); |
||||
free(src_buf); |
||||
return ret; |
||||
} |
||||
|
||||
static void add_index(uint64_t index_hash, uint64_t src_hash) { |
||||
FILE *f = fopen("/tmp/clcache/index.cli", "a"); |
||||
assert(f); |
||||
fprintf(f, "%016" PRIx64 " %016" PRIx64 "\n", index_hash, src_hash); |
||||
fclose(f); |
||||
} |
||||
|
||||
|
||||
cl_program cl_program_from_index(cl_context ctx, cl_device_id device_id, uint64_t index_hash) { |
||||
int err; |
||||
|
||||
int i; |
||||
for (i=0; i<ARRAYSIZE(clu_index); i++) { |
||||
if (clu_index[i].index_hash == index_hash) { |
||||
break; |
||||
} |
||||
} |
||||
if (i >= ARRAYSIZE(clu_index)) { |
||||
assert(false); |
||||
} |
||||
|
||||
size_t bin_size = clu_index[i].bin_end - clu_index[i].bin_data; |
||||
const uint8_t *bin_data = clu_index[i].bin_data; |
||||
|
||||
cl_program prg = clCreateProgramWithBinary(ctx, 1, &device_id, &bin_size, (const uint8_t**)&bin_data, NULL, &err); |
||||
assert(err == 0); |
||||
|
||||
err = clBuildProgram(prg, 1, &device_id, NULL, NULL, NULL); |
||||
assert(err == 0); |
||||
|
||||
return prg; |
||||
} |
||||
|
||||
cl_program cl_index_program_from_string(cl_context ctx, cl_device_id device_id, |
||||
const char* src, const char* args, |
||||
uint64_t index_hash) { |
||||
uint64_t src_hash = 0; |
||||
cl_program ret = cl_cached_program_from_string(ctx, device_id, src, args, &src_hash); |
||||
#ifndef CLU_NO_CACHE |
||||
add_index(index_hash, src_hash); |
||||
#endif |
||||
return ret; |
||||
} |
||||
|
||||
cl_program cl_index_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args, |
||||
uint64_t index_hash) { |
||||
uint64_t src_hash = 0; |
||||
cl_program ret = cl_cached_program_from_file(ctx, device_id, path, args, &src_hash); |
||||
#ifndef CLU_NO_CACHE |
||||
add_index(index_hash, src_hash); |
||||
#endif |
||||
return ret; |
||||
} |
||||
|
||||
/*
|
||||
* Given a cl code and return a string represenation |
||||
*/ |
||||
const char* cl_get_error_string(int err) { |
||||
switch (err) { |
||||
case 0: return "CL_SUCCESS"; |
||||
case -1: return "CL_DEVICE_NOT_FOUND"; |
||||
case -2: return "CL_DEVICE_NOT_AVAILABLE"; |
||||
case -3: return "CL_COMPILER_NOT_AVAILABLE"; |
||||
case -4: return "CL_MEM_OBJECT_ALLOCATION_FAILURE"; |
||||
case -5: return "CL_OUT_OF_RESOURCES"; |
||||
case -6: return "CL_OUT_OF_HOST_MEMORY"; |
||||
case -7: return "CL_PROFILING_INFO_NOT_AVAILABLE"; |
||||
case -8: return "CL_MEM_COPY_OVERLAP"; |
||||
case -9: return "CL_IMAGE_FORMAT_MISMATCH"; |
||||
case -10: return "CL_IMAGE_FORMAT_NOT_SUPPORTED"; |
||||
case -12: return "CL_MAP_FAILURE"; |
||||
case -13: return "CL_MISALIGNED_SUB_BUFFER_OFFSET"; |
||||
case -14: return "CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST"; |
||||
case -15: return "CL_COMPILE_PROGRAM_FAILURE"; |
||||
case -16: return "CL_LINKER_NOT_AVAILABLE"; |
||||
case -17: return "CL_LINK_PROGRAM_FAILURE"; |
||||
case -18: return "CL_DEVICE_PARTITION_FAILED"; |
||||
case -19: return "CL_KERNEL_ARG_INFO_NOT_AVAILABLE"; |
||||
case -30: return "CL_INVALID_VALUE"; |
||||
case -31: return "CL_INVALID_DEVICE_TYPE"; |
||||
case -32: return "CL_INVALID_PLATFORM"; |
||||
case -33: return "CL_INVALID_DEVICE"; |
||||
case -34: return "CL_INVALID_CONTEXT"; |
||||
case -35: return "CL_INVALID_QUEUE_PROPERTIES"; |
||||
case -36: return "CL_INVALID_COMMAND_QUEUE"; |
||||
case -37: return "CL_INVALID_HOST_PTR"; |
||||
case -38: return "CL_INVALID_MEM_OBJECT"; |
||||
case -39: return "CL_INVALID_IMAGE_FORMAT_DESCRIPTOR"; |
||||
case -40: return "CL_INVALID_IMAGE_SIZE"; |
||||
case -41: return "CL_INVALID_SAMPLER"; |
||||
case -42: return "CL_INVALID_BINARY"; |
||||
case -43: return "CL_INVALID_BUILD_OPTIONS"; |
||||
case -44: return "CL_INVALID_PROGRAM"; |
||||
case -45: return "CL_INVALID_PROGRAM_EXECUTABLE"; |
||||
case -46: return "CL_INVALID_KERNEL_NAME"; |
||||
case -47: return "CL_INVALID_KERNEL_DEFINITION"; |
||||
case -48: return "CL_INVALID_KERNEL"; |
||||
case -49: return "CL_INVALID_ARG_INDEX"; |
||||
case -50: return "CL_INVALID_ARG_VALUE"; |
||||
case -51: return "CL_INVALID_ARG_SIZE"; |
||||
case -52: return "CL_INVALID_KERNEL_ARGS"; |
||||
case -53: return "CL_INVALID_WORK_DIMENSION"; |
||||
case -54: return "CL_INVALID_WORK_GROUP_SIZE"; |
||||
case -55: return "CL_INVALID_WORK_ITEM_SIZE"; |
||||
case -56: return "CL_INVALID_GLOBAL_OFFSET"; |
||||
case -57: return "CL_INVALID_EVENT_WAIT_LIST"; |
||||
case -58: return "CL_INVALID_EVENT"; |
||||
case -59: return "CL_INVALID_OPERATION"; |
||||
case -60: return "CL_INVALID_GL_OBJECT"; |
||||
case -61: return "CL_INVALID_BUFFER_SIZE"; |
||||
case -62: return "CL_INVALID_MIP_LEVEL"; |
||||
case -63: return "CL_INVALID_GLOBAL_WORK_SIZE"; |
||||
case -64: return "CL_INVALID_PROPERTY"; |
||||
case -65: return "CL_INVALID_IMAGE_DESCRIPTOR"; |
||||
case -66: return "CL_INVALID_COMPILER_OPTIONS"; |
||||
case -67: return "CL_INVALID_LINKER_OPTIONS"; |
||||
case -68: return "CL_INVALID_DEVICE_PARTITION_COUNT"; |
||||
case -69: return "CL_INVALID_PIPE_SIZE"; |
||||
case -70: return "CL_INVALID_DEVICE_QUEUE"; |
||||
case -71: return "CL_INVALID_SPEC_ID"; |
||||
case -72: return "CL_MAX_SIZE_RESTRICTION_EXCEEDED"; |
||||
case -1002: return "CL_INVALID_D3D10_DEVICE_KHR"; |
||||
case -1003: return "CL_INVALID_D3D10_RESOURCE_KHR"; |
||||
case -1004: return "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR"; |
||||
case -1005: return "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR"; |
||||
case -1006: return "CL_INVALID_D3D11_DEVICE_KHR"; |
||||
case -1007: return "CL_INVALID_D3D11_RESOURCE_KHR"; |
||||
case -1008: return "CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR"; |
||||
case -1009: return "CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR"; |
||||
case -1010: return "CL_INVALID_DX9_MEDIA_ADAPTER_KHR"; |
||||
case -1011: return "CL_INVALID_DX9_MEDIA_SURFACE_KHR"; |
||||
case -1012: return "CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR"; |
||||
case -1013: return "CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR"; |
||||
case -1093: return "CL_INVALID_EGL_OBJECT_KHR"; |
||||
case -1092: return "CL_EGL_RESOURCE_NOT_ACQUIRED_KHR"; |
||||
case -1001: return "CL_PLATFORM_NOT_FOUND_KHR"; |
||||
case -1057: return "CL_DEVICE_PARTITION_FAILED_EXT"; |
||||
case -1058: return "CL_INVALID_PARTITION_COUNT_EXT"; |
||||
case -1059: return "CL_INVALID_PARTITION_NAME_EXT"; |
||||
case -1094: return "CL_INVALID_ACCELERATOR_INTEL"; |
||||
case -1095: return "CL_INVALID_ACCELERATOR_TYPE_INTEL"; |
||||
case -1096: return "CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL"; |
||||
case -1097: return "CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL"; |
||||
case -1000: return "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR"; |
||||
case -1098: return "CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL"; |
||||
case -1099: return "CL_INVALID_VA_API_MEDIA_SURFACE_INTEL"; |
||||
case -1100: return "CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL"; |
||||
case -1101: return "CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL"; |
||||
default: return "CL_UNKNOWN_ERROR"; |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
#ifndef CLUTIL_H |
||||
#define CLUTIL_H |
||||
|
||||
#include <stdint.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
|
||||
#ifdef __APPLE__ |
||||
#include <OpenCL/cl.h> |
||||
#else |
||||
#include <CL/cl.h> |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
void clu_init(void); |
||||
|
||||
cl_program cl_create_program_from_file(cl_context ctx, const char* path); |
||||
void cl_print_info(cl_platform_id platform, cl_device_id device); |
||||
void cl_print_build_errors(cl_program program, cl_device_id device); |
||||
void cl_print_build_errors(cl_program program, cl_device_id device); |
||||
|
||||
cl_program cl_cached_program_from_hash(cl_context ctx, cl_device_id device_id, uint64_t hash); |
||||
cl_program cl_cached_program_from_string(cl_context ctx, cl_device_id device_id, |
||||
const char* src, const char* args, |
||||
uint64_t *out_hash); |
||||
cl_program cl_cached_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args, |
||||
uint64_t *out_hash); |
||||
|
||||
cl_program cl_program_from_index(cl_context ctx, cl_device_id device_id, uint64_t index_hash); |
||||
|
||||
cl_program cl_index_program_from_string(cl_context ctx, cl_device_id device_id, |
||||
const char* src, const char* args, |
||||
uint64_t index_hash); |
||||
cl_program cl_index_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args, |
||||
uint64_t index_hash); |
||||
|
||||
uint64_t clu_index_hash(const char *s); |
||||
uint64_t clu_fnv_hash(const uint8_t *data, size_t len); |
||||
|
||||
const char* cl_get_error_string(int err); |
||||
|
||||
static inline int cl_check_error(int err) { |
||||
if (err != 0) { |
||||
fprintf(stderr, "%s\n", cl_get_error_string(err)); |
||||
exit(1); |
||||
} |
||||
return err; |
||||
} |
||||
|
||||
|
||||
// // string hash macro. compiler, I'm so sorry.
|
||||
#define CLU_H1(s,i,x) (x*65599ULL+(uint8_t)s[(i)<strlen(s)?strlen(s)-1-(i):strlen(s)]) |
||||
#define CLU_H4(s,i,x) CLU_H1(s,i,CLU_H1(s,i+1,CLU_H1(s,i+2,CLU_H1(s,i+3,x)))) |
||||
#define CLU_H16(s,i,x) CLU_H4(s,i,CLU_H4(s,i+4,CLU_H4(s,i+8,CLU_H4(s,i+12,x)))) |
||||
#define CLU_H64(s,i,x) CLU_H16(s,i,CLU_H16(s,i+16,CLU_H16(s,i+32,CLU_H16(s,i+48,x)))) |
||||
// #define CLU_H256(s,i,x) CLU_H64(s,i,CLU_H64(s,i+64,CLU_H64(s,i+128,CLU_H64(s,i+192,x))))
|
||||
#define CLU_H128(s,i,x) CLU_H64(s,i,CLU_H64(s,i+64,x)) |
||||
#define CLU_HASH(s) ((uint64_t)(CLU_H128(s,0,0)^(CLU_H128(s,0,0)>>32))) |
||||
|
||||
#define CLU_STRINGIFY(x) #x |
||||
#define CLU_STRINGIFY2(x) CLU_STRINGIFY(x) |
||||
#define CLU_LINESTR CLU_STRINGIFY2(__LINE__) |
||||
|
||||
#ifdef CLU_NO_SRC |
||||
|
||||
#define CLU_LOAD_FROM_STRING(ctx, device_id, src, args) \ |
||||
cl_program_from_index(ctx, device_id, CLU_HASH("\1" __FILE__ "\1" CLU_LINESTR) ^ clu_fnv_hash((const uint8_t*)__func__, strlen(__func__)) ^ clu_fnv_hash((const uint8_t*)args, strlen(args))) |
||||
#define CLU_LOAD_FROM_FILE(ctx, device_id, path, args) \ |
||||
cl_program_from_index(ctx, device_id, CLU_HASH("\2" path) ^ clu_fnv_hash((const uint8_t*)args, strlen(args))) |
||||
|
||||
#else |
||||
|
||||
#define CLU_LOAD_FROM_STRING(ctx, device_id, src, args) \ |
||||
cl_index_program_from_string(ctx, device_id, src, args, clu_index_hash("\1" __FILE__ "\1" CLU_LINESTR) ^ clu_fnv_hash((const uint8_t*)__func__, strlen(__func__)) ^ clu_fnv_hash((const uint8_t*)args, strlen(args))) |
||||
#define CLU_LOAD_FROM_FILE(ctx, device_id, path, args) \ |
||||
cl_index_program_from_file(ctx, device_id, path, args, clu_index_hash("\2" path) ^ clu_fnv_hash((const uint8_t*)args, strlen(args))) |
||||
|
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,52 @@ |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
|
||||
#include "cqueue.h" |
||||
|
||||
void queue_init(Queue *q) { |
||||
memset(q, 0, sizeof(*q)); |
||||
TAILQ_INIT(&q->q); |
||||
pthread_mutex_init(&q->lock, NULL); |
||||
pthread_cond_init(&q->cv, NULL); |
||||
} |
||||
|
||||
void* queue_pop(Queue *q) { |
||||
pthread_mutex_lock(&q->lock); |
||||
while (TAILQ_EMPTY(&q->q)) { |
||||
pthread_cond_wait(&q->cv, &q->lock); |
||||
} |
||||
QueueEntry *entry = TAILQ_FIRST(&q->q); |
||||
TAILQ_REMOVE(&q->q, entry, entries); |
||||
pthread_mutex_unlock(&q->lock); |
||||
|
||||
void* r = entry->data; |
||||
free(entry); |
||||
return r; |
||||
} |
||||
|
||||
void* queue_try_pop(Queue *q) { |
||||
pthread_mutex_lock(&q->lock); |
||||
|
||||
void* r = NULL; |
||||
if (!TAILQ_EMPTY(&q->q)) { |
||||
QueueEntry *entry = TAILQ_FIRST(&q->q); |
||||
TAILQ_REMOVE(&q->q, entry, entries); |
||||
r = entry->data; |
||||
free(entry); |
||||
} |
||||
|
||||
pthread_mutex_unlock(&q->lock); |
||||
return r; |
||||
} |
||||
|
||||
void queue_push(Queue *q, void *data) { |
||||
QueueEntry *entry = calloc(1, sizeof(QueueEntry)); |
||||
assert(entry); |
||||
entry->data = data; |
||||
|
||||
pthread_mutex_lock(&q->lock); |
||||
TAILQ_INSERT_TAIL(&q->q, entry, entries); |
||||
pthread_cond_signal(&q->cv); |
||||
pthread_mutex_unlock(&q->lock); |
||||
} |
@ -0,0 +1,33 @@ |
||||
#ifndef COMMON_CQUEUE_H |
||||
#define COMMON_CQUEUE_H |
||||
|
||||
#include <sys/queue.h> |
||||
#include <pthread.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
// a blocking queue
|
||||
|
||||
typedef struct QueueEntry { |
||||
TAILQ_ENTRY(QueueEntry) entries; |
||||
void *data; |
||||
} QueueEntry; |
||||
|
||||
typedef struct Queue { |
||||
TAILQ_HEAD(queue, QueueEntry) q; |
||||
pthread_mutex_t lock; |
||||
pthread_cond_t cv; |
||||
} Queue; |
||||
|
||||
void queue_init(Queue *q); |
||||
void* queue_pop(Queue *q); |
||||
void* queue_try_pop(Queue *q); |
||||
void queue_push(Queue *q, void *data); |
||||
|
||||
#ifdef __cplusplus |
||||
} // extern "C"
|
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,56 @@ |
||||
#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 |
||||
} |
@ -0,0 +1,17 @@ |
||||
#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 |
@ -0,0 +1,142 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <cassert> |
||||
|
||||
#include <ui/DisplayInfo.h> |
||||
|
||||
#include <gui/ISurfaceComposer.h> |
||||
#include <gui/Surface.h> |
||||
#include <gui/SurfaceComposerClient.h> |
||||
|
||||
|
||||
#include <GLES2/gl2.h> |
||||
#include <EGL/eglext.h> |
||||
|
||||
#define BACKLIGHT_CONTROL "/sys/class/leds/lcd-backlight/brightness" |
||||
#define BACKLIGHT_LEVEL "205" |
||||
|
||||
using namespace android; |
||||
|
||||
struct FramebufferState { |
||||
sp<SurfaceComposerClient> session; |
||||
sp<IBinder> dtoken; |
||||
DisplayInfo dinfo; |
||||
sp<SurfaceControl> control; |
||||
|
||||
sp<Surface> s; |
||||
EGLDisplay display; |
||||
|
||||
EGLint egl_major, egl_minor; |
||||
EGLConfig config; |
||||
EGLSurface surface; |
||||
EGLContext context; |
||||
}; |
||||
|
||||
extern "C" void framebuffer_set_power(FramebufferState *s, int mode) { |
||||
SurfaceComposerClient::setDisplayPowerMode(s->dtoken, mode); |
||||
} |
||||
|
||||
extern "C" FramebufferState* framebuffer_init( |
||||
const char* name, int32_t layer, int alpha, |
||||
int *out_w, int *out_h) { |
||||
status_t status; |
||||
int success; |
||||
|
||||
FramebufferState *s = new FramebufferState; |
||||
|
||||
s->session = new SurfaceComposerClient(); |
||||
assert(s->session != NULL); |
||||
|
||||
s->dtoken = SurfaceComposerClient::getBuiltInDisplay( |
||||
ISurfaceComposer::eDisplayIdMain); |
||||
assert(s->dtoken != NULL); |
||||
|
||||
status = SurfaceComposerClient::getDisplayInfo(s->dtoken, &s->dinfo); |
||||
assert(status == 0); |
||||
|
||||
//int orientation = 3; // rotate framebuffer 270 degrees
|
||||
int orientation = 1; // rotate framebuffer 90 degrees
|
||||
if(orientation == 1 || orientation == 3) { |
||||
int temp = s->dinfo.h; |
||||
s->dinfo.h = s->dinfo.w; |
||||
s->dinfo.w = temp; |
||||
} |
||||
|
||||
printf("dinfo %dx%d\n", s->dinfo.w, s->dinfo.h); |
||||
|
||||
Rect destRect(s->dinfo.w, s->dinfo.h); |
||||
s->session->setDisplayProjection(s->dtoken, orientation, destRect, destRect); |
||||
|
||||
s->control = s->session->createSurface(String8(name), |
||||
s->dinfo.w, s->dinfo.h, PIXEL_FORMAT_RGBX_8888); |
||||
assert(s->control != NULL); |
||||
|
||||
SurfaceComposerClient::openGlobalTransaction(); |
||||
status = s->control->setLayer(layer); |
||||
SurfaceComposerClient::closeGlobalTransaction(); |
||||
assert(status == 0); |
||||
|
||||
s->s = s->control->getSurface(); |
||||
assert(s->s != NULL); |
||||
|
||||
// init opengl and egl
|
||||
const EGLint attribs[] = { |
||||
EGL_RED_SIZE, 8, |
||||
EGL_GREEN_SIZE, 8, |
||||
EGL_BLUE_SIZE, 8, |
||||
EGL_ALPHA_SIZE, alpha ? 8 : 0, |
||||
EGL_DEPTH_SIZE, 0, |
||||
EGL_STENCIL_SIZE, 8, |
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, |
||||
EGL_NONE, |
||||
}; |
||||
|
||||
s->display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
||||
assert(s->display != EGL_NO_DISPLAY); |
||||
|
||||
success = eglInitialize(s->display, &s->egl_major, &s->egl_minor); |
||||
assert(success); |
||||
|
||||
printf("egl version %d.%d\n", s->egl_major, s->egl_minor); |
||||
|
||||
EGLint num_configs; |
||||
success = eglChooseConfig(s->display, attribs, &s->config, 1, &num_configs); |
||||
assert(success); |
||||
|
||||
s->surface = eglCreateWindowSurface(s->display, s->config, s->s.get(), NULL); |
||||
assert(s->surface != EGL_NO_SURFACE); |
||||
|
||||
const EGLint context_attribs[] = { |
||||
EGL_CONTEXT_CLIENT_VERSION, 3, |
||||
EGL_NONE, |
||||
}; |
||||
s->context = eglCreateContext(s->display, s->config, NULL, context_attribs); |
||||
assert(s->context != EGL_NO_CONTEXT); |
||||
|
||||
EGLint w, h; |
||||
eglQuerySurface(s->display, s->surface, EGL_WIDTH, &w); |
||||
eglQuerySurface(s->display, s->surface, EGL_HEIGHT, &h); |
||||
printf("egl w %d h %d\n", w, h); |
||||
|
||||
success = eglMakeCurrent(s->display, s->surface, s->surface, s->context); |
||||
assert(success); |
||||
|
||||
printf("gl version %s\n", glGetString(GL_VERSION)); |
||||
|
||||
|
||||
// set brightness
|
||||
int brightness_fd = open(BACKLIGHT_CONTROL, O_RDWR); |
||||
const char brightness_level[] = BACKLIGHT_LEVEL; |
||||
write(brightness_fd, brightness_level, strlen(brightness_level)); |
||||
|
||||
if (out_w) *out_w = w; |
||||
if (out_h) *out_h = h; |
||||
|
||||
return s; |
||||
} |
||||
|
||||
extern "C" void framebuffer_swap(FramebufferState *s) { |
||||
eglSwapBuffers(s->display, s->surface); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
} |
||||
|
@ -0,0 +1,50 @@ |
||||
#ifndef FRAMEBUFFER_H |
||||
#define FRAMEBUFFER_H |
||||
|
||||
#include <EGL/eglext.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct FramebufferState FramebufferState; |
||||
|
||||
FramebufferState* framebuffer_init( |
||||
const char* name, int32_t layer, int alpha, |
||||
int *out_w, int *out_h); |
||||
|
||||
void framebuffer_set_power(FramebufferState *s, int mode); |
||||
void framebuffer_swap(FramebufferState *s); |
||||
|
||||
/* Display power modes */ |
||||
enum { |
||||
/* The display is turned off (blanked). */ |
||||
HWC_POWER_MODE_OFF = 0, |
||||
/* The display is turned on and configured in a low power state
|
||||
* that is suitable for presenting ambient information to the user, |
||||
* possibly with lower fidelity than normal but greater efficiency. */ |
||||
HWC_POWER_MODE_DOZE = 1, |
||||
/* The display is turned on normally. */ |
||||
HWC_POWER_MODE_NORMAL = 2, |
||||
/* The display is configured as in HWC_POWER_MODE_DOZE but may
|
||||
* stop applying frame buffer updates from the graphics subsystem. |
||||
* This power mode is effectively a hint from the doze dream to |
||||
* tell the hardware that it is done drawing to the display for the |
||||
* time being and that the display should remain on in a low power |
||||
* state and continue showing its current contents indefinitely |
||||
* until the mode changes. |
||||
* |
||||
* This mode may also be used as a signal to enable hardware-based doze |
||||
* functionality. In this case, the doze dream is effectively |
||||
* indicating that the hardware is free to take over the display |
||||
* and manage it autonomously to implement low power always-on display |
||||
* functionality. */ |
||||
HWC_POWER_MODE_DOZE_SUSPEND = 3, |
||||
}; |
||||
|
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,71 @@ |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
|
||||
#include <GLES3/gl3.h> |
||||
|
||||
#include "glutil.h" |
||||
|
||||
GLuint load_shader(GLenum shaderType, const char *src) { |
||||
GLint status = 0, len = 0; |
||||
GLuint shader; |
||||
|
||||
if (!(shader = glCreateShader(shaderType))) |
||||
return 0; |
||||
|
||||
glShaderSource(shader, 1, &src, NULL); |
||||
glCompileShader(shader); |
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); |
||||
|
||||
if (status) |
||||
return shader; |
||||
|
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); |
||||
if (len) { |
||||
char *msg = (char*)malloc(len); |
||||
if (msg) { |
||||
glGetShaderInfoLog(shader, len, NULL, msg); |
||||
msg[len-1] = 0; |
||||
fprintf(stderr, "error compiling shader:\n%s\n", msg); |
||||
free(msg); |
||||
} |
||||
} |
||||
glDeleteShader(shader); |
||||
return 0; |
||||
} |
||||
|
||||
GLuint load_program(const char *vert_src, const char *frag_src) { |
||||
GLuint vert, frag, prog; |
||||
GLint status = 0, len = 0; |
||||
|
||||
if (!(vert = load_shader(GL_VERTEX_SHADER, vert_src))) |
||||
return 0; |
||||
if (!(frag = load_shader(GL_FRAGMENT_SHADER, frag_src))) |
||||
goto fail_frag; |
||||
if (!(prog = glCreateProgram())) |
||||
goto fail_prog; |
||||
|
||||
glAttachShader(prog, vert); |
||||
glAttachShader(prog, frag); |
||||
glLinkProgram(prog); |
||||
|
||||
glGetProgramiv(prog, GL_LINK_STATUS, &status); |
||||
if (status) |
||||
return prog; |
||||
|
||||
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); |
||||
if (len) { |
||||
char *buf = (char*) malloc(len); |
||||
if (buf) { |
||||
glGetProgramInfoLog(prog, len, NULL, buf); |
||||
buf[len-1] = 0; |
||||
fprintf(stderr, "error linking program:\n%s\n", buf); |
||||
free(buf); |
||||
} |
||||
} |
||||
glDeleteProgram(prog); |
||||
fail_prog: |
||||
glDeleteShader(frag); |
||||
fail_frag: |
||||
glDeleteShader(vert); |
||||
return 0; |
||||
} |
@ -0,0 +1,8 @@ |
||||
#ifndef COMMON_GLUTIL_H |
||||
#define COMMON_GLUTIL_H |
||||
|
||||
#include <GLES3/gl3.h> |
||||
GLuint load_shader(GLenum shaderType, const char *src); |
||||
GLuint load_program(const char *vert_src, const char *frag_src); |
||||
|
||||
#endif |
@ -0,0 +1,119 @@ |
||||
#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; |
||||
|
||||
int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); |
||||
assert(sock >= 0); |
||||
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); |
||||
|
||||
int sock = socket(AF_UNIX, SOCK_SEQPACKET, 0); |
||||
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) { |
||||
int err; |
||||
|
||||
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; |
||||
} |
||||
} |
@ -0,0 +1,19 @@ |
||||
#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 |
@ -0,0 +1,88 @@ |
||||
#ifndef COMMON_MAT_H |
||||
#define COMMON_MAT_H |
||||
|
||||
typedef struct vec3 { |
||||
float v[3]; |
||||
} vec3; |
||||
|
||||
typedef struct vec4 { |
||||
float v[4]; |
||||
} vec4; |
||||
|
||||
typedef struct mat3 { |
||||
float v[3*3]; |
||||
} mat3; |
||||
|
||||
typedef struct mat4 { |
||||
float v[4*4]; |
||||
} mat4; |
||||
|
||||
static inline mat3 matmul3(const mat3 a, const mat3 b) { |
||||
mat3 ret = {{0.0}}; |
||||
for (int r=0; r<3; r++) { |
||||
for (int c=0; c<3; c++) { |
||||
float v = 0.0; |
||||
for (int k=0; k<3; k++) { |
||||
v += a.v[r*3+k] * b.v[k*3+c]; |
||||
} |
||||
ret.v[r*3+c] = v; |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
static inline vec3 matvecmul3(const mat3 a, const vec3 b) { |
||||
vec3 ret = {{0.0}}; |
||||
for (int r=0; r<3; r++) { |
||||
for (int c=0; c<3; c++) { |
||||
ret.v[r] += a.v[r*3+c] * b.v[c]; |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
static inline mat4 matmul(const mat4 a, const mat4 b) { |
||||
mat4 ret = {{0.0}}; |
||||
for (int r=0; r<4; r++) { |
||||
for (int c=0; c<4; c++) { |
||||
float v = 0.0; |
||||
for (int k=0; k<4; k++) { |
||||
v += a.v[r*4+k] * b.v[k*4+c]; |
||||
} |
||||
ret.v[r*4+c] = v; |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
static inline vec4 matvecmul(const mat4 a, const vec4 b) { |
||||
vec4 ret = {{0.0}}; |
||||
for (int r=0; r<4; r++) { |
||||
for (int c=0; c<4; c++) { |
||||
ret.v[r] += a.v[r*4+c] * b.v[c]; |
||||
} |
||||
} |
||||
return ret; |
||||
} |
||||
|
||||
// scales the input and output space of a transformation matrix
|
||||
// that assumes pixel-center origin.
|
||||
static inline mat3 transform_scale_buffer(const mat3 in, float s) { |
||||
// in_pt = ( transform(out_pt/s + 0.5) - 0.5) * s
|
||||
|
||||
mat3 transform_out = {{ |
||||
1.0f/s, 0.0f, 0.5f, |
||||
0.0f, 1.0f/s, 0.5f, |
||||
0.0f, 0.0f, 1.0f, |
||||
}}; |
||||
|
||||
mat3 transform_in = {{ |
||||
s, 0.0f, -0.5f*s, |
||||
0.0f, s, -0.5f*s, |
||||
0.0f, 0.0f, 1.0f, |
||||
}}; |
||||
|
||||
return matmul3(transform_in, matmul3(in, transform_out)); |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,15 @@ |
||||
// the c version of cereal/messaging.py
|
||||
|
||||
#include <zmq.h> |
||||
|
||||
// TODO: refactor to take in service instead of endpoint?
|
||||
void *sub_sock(void *ctx, const char *endpoint) { |
||||
void* sock = zmq_socket(ctx, ZMQ_SUB); |
||||
assert(sock); |
||||
zmq_setsockopt(sock, ZMQ_SUBSCRIBE, "", 0); |
||||
int reconnect_ivl = 500; |
||||
zmq_setsockopt(sock, ZMQ_RECONNECT_IVL_MAX, &reconnect_ivl, sizeof(reconnect_ivl)); |
||||
zmq_connect(sock, endpoint); |
||||
return sock; |
||||
} |
||||
|
@ -0,0 +1,40 @@ |
||||
#ifndef MODELDATA_H |
||||
#define MODELDATA_H |
||||
|
||||
#define MODEL_PATH_DISTANCE 192 |
||||
#define POLYFIT_DEGREE 4 |
||||
#define SPEED_PERCENTILES 10 |
||||
#define DESIRE_PRED_SIZE 32 |
||||
#define OTHER_META_SIZE 4 |
||||
|
||||
typedef struct PathData { |
||||
float points[MODEL_PATH_DISTANCE]; |
||||
float prob; |
||||
float std; |
||||
float stds[MODEL_PATH_DISTANCE]; |
||||
float poly[POLYFIT_DEGREE]; |
||||
} PathData; |
||||
|
||||
typedef struct LeadData { |
||||
float dist; |
||||
float prob; |
||||
float std; |
||||
float rel_y; |
||||
float rel_y_std; |
||||
float rel_v; |
||||
float rel_v_std; |
||||
float rel_a; |
||||
float rel_a_std; |
||||
} LeadData; |
||||
|
||||
typedef struct ModelData { |
||||
PathData path; |
||||
PathData left_lane; |
||||
PathData right_lane; |
||||
LeadData lead; |
||||
LeadData lead_future; |
||||
float meta[OTHER_META_SIZE + DESIRE_PRED_SIZE]; |
||||
float speed[SPEED_PERCENTILES]; |
||||
} ModelData; |
||||
|
||||
#endif |
@ -0,0 +1,13 @@ |
||||
#ifndef COMMON_MUTEX_H |
||||
#define COMMON_MUTEX_H |
||||
|
||||
#include <pthread.h> |
||||
|
||||
static inline void mutex_init_reentrant(pthread_mutex_t *mutex) { |
||||
pthread_mutexattr_t attr; |
||||
pthread_mutexattr_init(&attr); |
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); |
||||
pthread_mutex_init(mutex, &attr); |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,307 @@ |
||||
#include "common/params.h" |
||||
|
||||
#ifndef _GNU_SOURCE |
||||
#define _GNU_SOURCE |
||||
#endif // _GNU_SOURCE
|
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include <dirent.h> |
||||
#include <sys/file.h> |
||||
#include <sys/stat.h> |
||||
|
||||
#include <map> |
||||
#include <string> |
||||
|
||||
#include "common/util.h" |
||||
#include "common/utilpp.h" |
||||
|
||||
|
||||
namespace { |
||||
|
||||
template <typename T> |
||||
T* null_coalesce(T* a, T* b) { |
||||
return a != NULL ? a : b; |
||||
} |
||||
|
||||
static const char* default_params_path = null_coalesce( |
||||
const_cast<const char*>(getenv("PARAMS_PATH")), "/data/params"); |
||||
|
||||
} // namespace
|
||||
|
||||
static int fsync_dir(const char* path){ |
||||
int result = 0; |
||||
int fd = open(path, O_RDONLY); |
||||
|
||||
if (fd < 0){ |
||||
result = -1; |
||||
goto cleanup; |
||||
} |
||||
|
||||
result = fsync(fd); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
cleanup: |
||||
int result_close = 0; |
||||
if (fd >= 0){ |
||||
result_close = close(fd); |
||||
} |
||||
|
||||
if (result_close < 0) { |
||||
return result_close; |
||||
} else { |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
int write_db_value(const char* params_path, const char* key, const char* value, |
||||
size_t value_size) { |
||||
// Information about safely and atomically writing a file: https://lwn.net/Articles/457667/
|
||||
// 1) Create temp file
|
||||
// 2) Write data to temp file
|
||||
// 3) fsync() the temp file
|
||||
// 4) rename the temp file to the real name
|
||||
// 5) fsync() the containing directory
|
||||
|
||||
int lock_fd = -1; |
||||
int tmp_fd = -1; |
||||
int result; |
||||
char tmp_path[1024]; |
||||
char path[1024]; |
||||
ssize_t bytes_written; |
||||
|
||||
if (params_path == NULL) { |
||||
params_path = default_params_path; |
||||
} |
||||
|
||||
// Write value to temp.
|
||||
result = |
||||
snprintf(tmp_path, sizeof(tmp_path), "%s/.tmp_value_XXXXXX", params_path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
tmp_fd = mkstemp(tmp_path); |
||||
bytes_written = write(tmp_fd, value, value_size); |
||||
if (bytes_written != value_size) { |
||||
result = -20; |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Build lock path
|
||||
result = snprintf(path, sizeof(path), "%s/.lock", params_path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
lock_fd = open(path, 0); |
||||
|
||||
// Build key path
|
||||
result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Take lock.
|
||||
result = flock(lock_fd, LOCK_EX); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// change permissions to 0666 for apks
|
||||
result = fchmod(tmp_fd, 0666); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// fsync to force persist the changes.
|
||||
result = fsync(tmp_fd); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Move temp into place.
|
||||
result = rename(tmp_path, path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// fsync parent directory
|
||||
result = snprintf(path, sizeof(path), "%s/d", params_path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
result = fsync_dir(path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
cleanup: |
||||
// Release lock.
|
||||
if (lock_fd >= 0) { |
||||
close(lock_fd); |
||||
} |
||||
if (tmp_fd >= 0) { |
||||
if (result < 0) { |
||||
remove(tmp_path); |
||||
} |
||||
close(tmp_fd); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
int delete_db_value(const char* params_path, const char* key) { |
||||
int lock_fd = -1; |
||||
int result; |
||||
char path[1024]; |
||||
|
||||
if (params_path == NULL) { |
||||
params_path = default_params_path; |
||||
} |
||||
|
||||
// Build lock path, and open lockfile
|
||||
result = snprintf(path, sizeof(path), "%s/.lock", params_path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
lock_fd = open(path, 0); |
||||
|
||||
// Take lock.
|
||||
result = flock(lock_fd, LOCK_EX); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Build key path
|
||||
result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Delete value.
|
||||
result = remove(path); |
||||
if (result != 0) { |
||||
result = ERR_NO_VALUE; |
||||
goto cleanup; |
||||
} |
||||
|
||||
// fsync parent directory
|
||||
result = snprintf(path, sizeof(path), "%s/d", params_path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
result = fsync_dir(path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
cleanup: |
||||
// Release lock.
|
||||
if (lock_fd >= 0) { |
||||
close(lock_fd); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
int read_db_value(const char* params_path, const char* key, char** value, |
||||
size_t* value_sz) { |
||||
int lock_fd = -1; |
||||
int result; |
||||
char path[1024]; |
||||
|
||||
if (params_path == NULL) { |
||||
params_path = default_params_path; |
||||
} |
||||
|
||||
result = snprintf(path, sizeof(path), "%s/.lock", params_path); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
lock_fd = open(path, 0); |
||||
|
||||
result = snprintf(path, sizeof(path), "%s/d/%s", params_path, key); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Take lock.
|
||||
result = flock(lock_fd, LOCK_EX); |
||||
if (result < 0) { |
||||
goto cleanup; |
||||
} |
||||
|
||||
// Read value.
|
||||
// TODO(mgraczyk): If there is a lot of contention, we can release the lock
|
||||
// after opening the file, before reading.
|
||||
*value = static_cast<char*>(read_file(path, value_sz)); |
||||
if (*value == NULL) { |
||||
result = -22; |
||||
goto cleanup; |
||||
} |
||||
|
||||
result = 0; |
||||
|
||||
cleanup: |
||||
// Release lock.
|
||||
if (lock_fd >= 0) { |
||||
close(lock_fd); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
void read_db_value_blocking(const char* params_path, const char* key, |
||||
char** value, size_t* value_sz) { |
||||
while (1) { |
||||
const int result = read_db_value(params_path, key, value, value_sz); |
||||
if (result == 0) { |
||||
return; |
||||
} else { |
||||
// Sleep for 0.1 seconds.
|
||||
usleep(100000); |
||||
} |
||||
} |
||||
} |
||||
|
||||
int read_db_all(const char* params_path, std::map<std::string, std::string> *params) { |
||||
int err = 0; |
||||
|
||||
if (params_path == NULL) { |
||||
params_path = default_params_path; |
||||
} |
||||
|
||||
std::string lock_path = util::string_format("%s/.lock", params_path); |
||||
|
||||
int lock_fd = open(lock_path.c_str(), 0); |
||||
if (lock_fd < 0) return -1; |
||||
|
||||
err = flock(lock_fd, LOCK_EX); |
||||
if (err < 0) return err; |
||||
|
||||
std::string key_path = util::string_format("%s/d", params_path); |
||||
DIR *d = opendir(key_path.c_str()); |
||||
if (!d) { |
||||
close(lock_fd); |
||||
return -1; |
||||
} |
||||
|
||||
struct dirent *de = NULL; |
||||
while ((de = readdir(d))) { |
||||
if (!isalnum(de->d_name[0])) continue; |
||||
std::string key = std::string(de->d_name); |
||||
|
||||
if (key == "AccessToken") continue; |
||||
|
||||
std::string value = util::read_file(util::string_format("%s/%s", key_path.c_str(), key.c_str())); |
||||
|
||||
(*params)[key] = value; |
||||
} |
||||
|
||||
closedir(d); |
||||
|
||||
close(lock_fd); |
||||
return 0; |
||||
} |
@ -0,0 +1,47 @@ |
||||
#ifndef _SELFDRIVE_COMMON_PARAMS_H_ |
||||
#define _SELFDRIVE_COMMON_PARAMS_H_ |
||||
|
||||
#include <stddef.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define ERR_NO_VALUE -33 |
||||
|
||||
int write_db_value(const char* params_path, const char* key, const char* value, |
||||
size_t value_size); |
||||
|
||||
// Reads a value from the params database.
|
||||
// Inputs:
|
||||
// params_path: The path of the database, or NULL to use the default.
|
||||
// key: The key to read.
|
||||
// value: A pointer where a newly allocated string containing the db value will
|
||||
// be written.
|
||||
// value_sz: A pointer where the size of value will be written. Does not
|
||||
// include the NULL terminator.
|
||||
//
|
||||
// Returns: Negative on failure, otherwise 0.
|
||||
int read_db_value(const char* params_path, const char* key, char** value, |
||||
size_t* value_sz); |
||||
|
||||
// Delete a value from the params database.
|
||||
// Inputs are the same as read_db_value, without value and value_sz.
|
||||
int delete_db_value(const char* params_path, const char* key); |
||||
|
||||
// Reads a value from the params database, blocking until successful.
|
||||
// Inputs are the same as read_db_value.
|
||||
void read_db_value_blocking(const char* params_path, const char* key, |
||||
char** value, size_t* value_sz); |
||||
|
||||
#ifdef __cplusplus |
||||
} // extern "C"
|
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
#include <map> |
||||
#include <string> |
||||
int read_db_all(const char* params_path, std::map<std::string, std::string> *params); |
||||
#endif |
||||
|
||||
#endif // _SELFDRIVE_COMMON_PARAMS_H_
|
@ -0,0 +1,182 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <math.h> |
||||
#include <unistd.h> |
||||
#include <assert.h> |
||||
#include <ctype.h> |
||||
|
||||
#include <GLES3/gl3.h> |
||||
#include <EGL/egl.h> |
||||
#include <EGL/eglext.h> |
||||
|
||||
#include "nanovg.h" |
||||
#define NANOVG_GLES3_IMPLEMENTATION |
||||
#include "nanovg_gl.h" |
||||
#include "nanovg_gl_utils.h" |
||||
|
||||
#include "framebuffer.h" |
||||
#include "spinner.h" |
||||
|
||||
#define SPINTEXT_LENGTH 128 |
||||
|
||||
// external resources linked in
|
||||
extern const unsigned char _binary_opensans_semibold_ttf_start[]; |
||||
extern const unsigned char _binary_opensans_semibold_ttf_end[]; |
||||
|
||||
extern const unsigned char _binary_img_spinner_track_png_start[]; |
||||
extern const unsigned char _binary_img_spinner_track_png_end[]; |
||||
|
||||
extern const unsigned char _binary_img_spinner_comma_png_start[]; |
||||
extern const unsigned char _binary_img_spinner_comma_png_end[]; |
||||
|
||||
bool stdin_input_available() { |
||||
struct timeval timeout; |
||||
timeout.tv_sec = 0; |
||||
timeout.tv_usec = 0; |
||||
|
||||
fd_set fds; |
||||
FD_ZERO(&fds); |
||||
FD_SET(STDIN_FILENO, &fds); |
||||
select(STDIN_FILENO+1, &fds, NULL, NULL, &timeout); |
||||
return (FD_ISSET(0, &fds)); |
||||
} |
||||
|
||||
int spin(int argc, char** argv) { |
||||
int err; |
||||
|
||||
bool draw_progress = false; |
||||
float progress_val = 0.0; |
||||
|
||||
char spintext[SPINTEXT_LENGTH]; |
||||
spintext[0] = 0; |
||||
|
||||
const char* spintext_arg = NULL; |
||||
if (argc >= 2) { |
||||
strncpy(spintext, argv[1], SPINTEXT_LENGTH); |
||||
} |
||||
|
||||
// spinner
|
||||
int fb_w, fb_h; |
||||
FramebufferState *fb = framebuffer_init("spinner", 0x00001000, false, |
||||
&fb_w, &fb_h); |
||||
assert(fb); |
||||
|
||||
NVGcontext *vg = nvgCreateGLES3(NVG_ANTIALIAS | NVG_STENCIL_STROKES); |
||||
assert(vg); |
||||
|
||||
int font = nvgCreateFontMem(vg, "Bold", (unsigned char*)_binary_opensans_semibold_ttf_start, _binary_opensans_semibold_ttf_end-_binary_opensans_semibold_ttf_start, 0); |
||||
assert(font >= 0); |
||||
|
||||
int spinner_img = nvgCreateImageMem(vg, 0, (unsigned char*)_binary_img_spinner_track_png_start, _binary_img_spinner_track_png_end - _binary_img_spinner_track_png_start); |
||||
assert(spinner_img >= 0); |
||||
int spinner_img_s = 360; |
||||
int spinner_img_x = ((fb_w/2)-(spinner_img_s/2)); |
||||
int spinner_img_y = 260; |
||||
int spinner_img_xc = (fb_w/2); |
||||
int spinner_img_yc = (fb_h/2)-100; |
||||
int spinner_comma_img = nvgCreateImageMem(vg, 0, (unsigned char*)_binary_img_spinner_comma_png_start, _binary_img_spinner_comma_png_end - _binary_img_spinner_comma_png_start); |
||||
assert(spinner_comma_img >= 0); |
||||
|
||||
for (int cnt = 0; ; cnt++) { |
||||
// Check stdin for new text
|
||||
if (stdin_input_available()){ |
||||
fgets(spintext, SPINTEXT_LENGTH, stdin); |
||||
spintext[strcspn(spintext, "\n")] = 0; |
||||
|
||||
// Check if number (update progress bar)
|
||||
size_t len = strlen(spintext); |
||||
bool is_number = len > 0; |
||||
for (int i = 0; i < len; i++){ |
||||
if (!isdigit(spintext[i])){ |
||||
is_number = false; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (is_number) { |
||||
progress_val = (float)(atoi(spintext)) / 100.0; |
||||
progress_val = fmin(1.0, progress_val); |
||||
progress_val = fmax(0.0, progress_val); |
||||
} |
||||
|
||||
draw_progress = is_number; |
||||
} |
||||
|
||||
glClearColor(0.1, 0.1, 0.1, 1.0); |
||||
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); |
||||
glEnable(GL_BLEND); |
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||
nvgBeginFrame(vg, fb_w, fb_h, 1.0f); |
||||
|
||||
// background
|
||||
nvgBeginPath(vg); |
||||
NVGpaint bg = nvgLinearGradient(vg, fb_w, 0, fb_w, fb_h, |
||||
nvgRGBA(0, 0, 0, 175), nvgRGBA(0, 0, 0, 255)); |
||||
nvgFillPaint(vg, bg); |
||||
nvgRect(vg, 0, 0, fb_w, fb_h); |
||||
nvgFill(vg); |
||||
|
||||
// spin track
|
||||
nvgSave(vg); |
||||
nvgTranslate(vg, spinner_img_xc, spinner_img_yc); |
||||
nvgRotate(vg, (3.75*M_PI * cnt/120.0)); |
||||
nvgTranslate(vg, -spinner_img_xc, -spinner_img_yc); |
||||
NVGpaint spinner_imgPaint = nvgImagePattern(vg, spinner_img_x, spinner_img_y, |
||||
spinner_img_s, spinner_img_s, 0, spinner_img, 0.6f); |
||||
nvgBeginPath(vg); |
||||
nvgFillPaint(vg, spinner_imgPaint); |
||||
nvgRect(vg, spinner_img_x, spinner_img_y, spinner_img_s, spinner_img_s); |
||||
nvgFill(vg); |
||||
nvgRestore(vg); |
||||
|
||||
// comma
|
||||
NVGpaint comma_imgPaint = nvgImagePattern(vg, spinner_img_x, spinner_img_y, |
||||
spinner_img_s, spinner_img_s, 0, spinner_comma_img, 1.0f); |
||||
nvgBeginPath(vg); |
||||
nvgFillPaint(vg, comma_imgPaint); |
||||
nvgRect(vg, spinner_img_x, spinner_img_y, spinner_img_s, spinner_img_s); |
||||
nvgFill(vg); |
||||
|
||||
if (draw_progress){ |
||||
// draw progress bar
|
||||
int progress_width = 1000; |
||||
int progress_x = fb_w/2-progress_width/2; |
||||
int progress_y = 775; |
||||
int progress_height = 25; |
||||
|
||||
NVGpaint paint = nvgBoxGradient( |
||||
vg, progress_x + 1, progress_y + 1, |
||||
progress_width - 2, progress_height, 3, 4, nvgRGB(27, 27, 27), nvgRGB(27, 27, 27)); |
||||
nvgBeginPath(vg); |
||||
nvgRoundedRect(vg, progress_x, progress_y, progress_width, progress_height, 12); |
||||
nvgFillPaint(vg, paint); |
||||
nvgFill(vg); |
||||
|
||||
int bar_pos = ((progress_width - 2) * progress_val); |
||||
|
||||
paint = nvgBoxGradient( |
||||
vg, progress_x, progress_y, |
||||
bar_pos+1.5f, progress_height-1, 3, 4, |
||||
nvgRGB(245, 245, 245), nvgRGB(105, 105, 105)); |
||||
|
||||
nvgBeginPath(vg); |
||||
nvgRoundedRect( |
||||
vg, progress_x+1, progress_y+1, |
||||
bar_pos, progress_height-2, 12); |
||||
nvgFillPaint(vg, paint); |
||||
nvgFill(vg); |
||||
} else { |
||||
// message
|
||||
nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_TOP); |
||||
nvgFontSize(vg, 96.0f); |
||||
nvgText(vg, fb_w/2, (fb_h*2/3)+24, spintext, NULL); |
||||
} |
||||
|
||||
nvgEndFrame(vg); |
||||
framebuffer_swap(fb); |
||||
assert(glGetError() == GL_NO_ERROR); |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,14 @@ |
||||
#ifndef COMMON_SPINNER_H |
||||
#define COMMON_SPINNER_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
int spin(int argc, char** argv); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,119 @@ |
||||
#define _GNU_SOURCE |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdarg.h> |
||||
#include <stdbool.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
|
||||
#include <pthread.h> |
||||
#include <zmq.h> |
||||
#include <json.h> |
||||
|
||||
#include "common/timing.h" |
||||
#include "common/version.h" |
||||
|
||||
#include "swaglog.h" |
||||
|
||||
typedef struct LogState { |
||||
pthread_mutex_t lock; |
||||
bool inited; |
||||
JsonNode *ctx_j; |
||||
void *zctx; |
||||
void *sock; |
||||
int print_level; |
||||
} LogState; |
||||
|
||||
static LogState s = { |
||||
.lock = PTHREAD_MUTEX_INITIALIZER, |
||||
}; |
||||
|
||||
static void cloudlog_bind_locked(const char* k, const char* v) { |
||||
json_append_member(s.ctx_j, k, json_mkstring(v)); |
||||
} |
||||
|
||||
static void cloudlog_init() { |
||||
if (s.inited) return; |
||||
s.ctx_j = json_mkobject(); |
||||
s.zctx = zmq_ctx_new(); |
||||
s.sock = zmq_socket(s.zctx, ZMQ_PUSH); |
||||
zmq_connect(s.sock, "ipc:///tmp/logmessage"); |
||||
|
||||
s.print_level = CLOUDLOG_WARNING; |
||||
const char* print_level = getenv("LOGPRINT"); |
||||
if (print_level) { |
||||
if (strcmp(print_level, "debug") == 0) { |
||||
s.print_level = CLOUDLOG_DEBUG; |
||||
} else if (strcmp(print_level, "info") == 0) { |
||||
s.print_level = CLOUDLOG_INFO; |
||||
} else if (strcmp(print_level, "warning") == 0) { |
||||
s.print_level = CLOUDLOG_WARNING; |
||||
} |
||||
} |
||||
|
||||
// openpilot bindings
|
||||
char* dongle_id = getenv("DONGLE_ID"); |
||||
if (dongle_id) { |
||||
cloudlog_bind_locked("dongle_id", dongle_id); |
||||
} |
||||
cloudlog_bind_locked("version", COMMA_VERSION); |
||||
bool dirty = !getenv("CLEAN"); |
||||
json_append_member(s.ctx_j, "dirty", json_mkbool(dirty)); |
||||
|
||||
s.inited = true; |
||||
} |
||||
|
||||
void cloudlog_e(int levelnum, const char* filename, int lineno, const char* func, |
||||
const char* fmt, ...) { |
||||
pthread_mutex_lock(&s.lock); |
||||
cloudlog_init(); |
||||
|
||||
char* msg_buf = NULL; |
||||
va_list args; |
||||
va_start(args, fmt); |
||||
vasprintf(&msg_buf, fmt, args); |
||||
va_end(args); |
||||
|
||||
if (!msg_buf) { |
||||
pthread_mutex_unlock(&s.lock); |
||||
return; |
||||
} |
||||
|
||||
if (levelnum >= s.print_level) { |
||||
printf("%s: %s\n", filename, msg_buf); |
||||
} |
||||
|
||||
JsonNode *log_j = json_mkobject(); |
||||
assert(log_j); |
||||
|
||||
json_append_member(log_j, "msg", json_mkstring(msg_buf)); |
||||
json_append_member(log_j, "ctx", s.ctx_j); |
||||
json_append_member(log_j, "levelnum", json_mknumber(levelnum)); |
||||
json_append_member(log_j, "filename", json_mkstring(filename)); |
||||
json_append_member(log_j, "lineno", json_mknumber(lineno)); |
||||
json_append_member(log_j, "funcname", json_mkstring(func)); |
||||
json_append_member(log_j, "created", json_mknumber(seconds_since_epoch())); |
||||
|
||||
char* log_s = json_encode(log_j); |
||||
assert(log_s); |
||||
|
||||
json_remove_from_parent(s.ctx_j);
|
||||
|
||||
json_delete(log_j); |
||||
free(msg_buf); |
||||
|
||||
char levelnum_c = levelnum; |
||||
zmq_send(s.sock, &levelnum_c, 1, ZMQ_NOBLOCK | ZMQ_SNDMORE); |
||||
zmq_send(s.sock, log_s, strlen(log_s), ZMQ_NOBLOCK); |
||||
free(log_s); |
||||
|
||||
pthread_mutex_unlock(&s.lock); |
||||
} |
||||
|
||||
void cloudlog_bind(const char* k, const char* v) { |
||||
pthread_mutex_lock(&s.lock); |
||||
cloudlog_init(); |
||||
cloudlog_bind_locked(k, v); |
||||
pthread_mutex_unlock(&s.lock); |
||||
} |
@ -0,0 +1,68 @@ |
||||
#ifndef SWAGLOG_H |
||||
#define SWAGLOG_H |
||||
|
||||
#include "selfdrive/common/timing.h" |
||||
|
||||
#define CLOUDLOG_DEBUG 10 |
||||
#define CLOUDLOG_INFO 20 |
||||
#define CLOUDLOG_WARNING 30 |
||||
#define CLOUDLOG_ERROR 40 |
||||
#define CLOUDLOG_CRITICAL 50 |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
void cloudlog_e(int levelnum, const char* filename, int lineno, const char* func, |
||||
const char* fmt, ...) /*__attribute__ ((format (printf, 6, 7)))*/; |
||||
|
||||
void cloudlog_bind(const char* k, const char* v); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#define cloudlog(lvl, fmt, ...) cloudlog_e(lvl, __FILE__, __LINE__, \ |
||||
__func__, \
|
||||
fmt, ## __VA_ARGS__) |
||||
|
||||
#define cloudlog_rl(burst, millis, lvl, fmt, ...) \ |
||||
{ \
|
||||
static uint64_t __begin = 0; \
|
||||
static int __printed = 0; \
|
||||
static int __missed = 0; \
|
||||
\
|
||||
int __burst = (burst); \
|
||||
int __millis = (millis); \
|
||||
uint64_t __ts = nanos_since_boot(); \
|
||||
\
|
||||
if (!__begin) __begin = __ts; \
|
||||
\
|
||||
if (__begin + __millis*1000000ULL < __ts) { \
|
||||
if (__missed) { \
|
||||
cloudlog(CLOUDLOG_WARNING, "cloudlog: %d messages supressed", __missed); \
|
||||
} \
|
||||
__begin = 0; \
|
||||
__printed = 0; \
|
||||
__missed = 0; \
|
||||
} \
|
||||
\
|
||||
if (__printed < __burst) { \
|
||||
cloudlog(lvl, fmt, ## __VA_ARGS__); \
|
||||
__printed++; \
|
||||
} else { \
|
||||
__missed++; \
|
||||
} \
|
||||
} |
||||
|
||||
#define LOGD(fmt, ...) cloudlog(CLOUDLOG_DEBUG, fmt, ## __VA_ARGS__) |
||||
#define LOG(fmt, ...) cloudlog(CLOUDLOG_INFO, fmt, ## __VA_ARGS__) |
||||
#define LOGW(fmt, ...) cloudlog(CLOUDLOG_WARNING, fmt, ## __VA_ARGS__) |
||||
#define LOGE(fmt, ...) cloudlog(CLOUDLOG_ERROR, fmt, ## __VA_ARGS__) |
||||
|
||||
#define LOGD_100(fmt, ...) cloudlog_rl(2, 100, CLOUDLOG_DEBUG, fmt, ## __VA_ARGS__) |
||||
#define LOG_100(fmt, ...) cloudlog_rl(2, 100, CLOUDLOG_INFO, fmt, ## __VA_ARGS__) |
||||
#define LOGW_100(fmt, ...) cloudlog_rl(2, 100, CLOUDLOG_WARNING, fmt, ## __VA_ARGS__) |
||||
#define LOGE_100(fmt, ...) cloudlog_rl(2, 100, CLOUDLOG_ERROR, fmt, ## __VA_ARGS__) |
||||
|
||||
#endif |
@ -0,0 +1,64 @@ |
||||
#include "selfdrive/common/params.h" |
||||
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
static const char* const kUsage = "%s: read|write|read_block params_path key [value]\n"; |
||||
|
||||
int main(int argc, const char* argv[]) { |
||||
if (argc < 4) { |
||||
printf(kUsage, argv[0]); |
||||
return 0; |
||||
} |
||||
|
||||
const char* params_path = argv[2]; |
||||
const char* key = argv[3]; |
||||
if (strcmp(argv[1], "read") == 0) { |
||||
char* value; |
||||
size_t value_size; |
||||
int result = read_db_value(params_path, key, &value, &value_size); |
||||
if (result >= 0) { |
||||
fprintf(stdout, "Read %zu bytes: ", value_size); |
||||
fwrite(value, 1, value_size, stdout); |
||||
fprintf(stdout, "\n"); |
||||
free(value); |
||||
} else { |
||||
fprintf(stderr, "Error reading: %d\n", result); |
||||
return -1; |
||||
} |
||||
} else if (strcmp(argv[1], "write") == 0) { |
||||
if (argc < 5) { |
||||
fprintf(stderr, "Error: write value required\n"); |
||||
return 1; |
||||
} |
||||
|
||||
const char* value = argv[4]; |
||||
const size_t value_size = strlen(value); |
||||
int result = write_db_value(params_path, key, value, value_size); |
||||
if (result >= 0) { |
||||
fprintf(stdout, "Wrote %s to %s\n", value, key); |
||||
} else { |
||||
fprintf(stderr, "Error writing: %d\n", result); |
||||
return -1; |
||||
} |
||||
} else if (strcmp(argv[1], "read_block") == 0) { |
||||
char* value; |
||||
size_t value_size; |
||||
read_db_value_blocking(params_path, key, &value, &value_size); |
||||
fprintf(stdout, "Read %zu bytes: ", value_size); |
||||
fwrite(value, 1, value_size, stdout); |
||||
fprintf(stdout, "\n"); |
||||
free(value); |
||||
} else { |
||||
printf(kUsage, argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
// BUILD:
|
||||
// $ gcc -I$HOME/one selfdrive/common/test_params.c selfdrive/common/params.c selfdrive/common/util.c -o ./test_params
|
||||
// $ seq 0 100000 | xargs -P20 -I{} ./test_params write /data/params DongleId {} && sleep 0.1 &
|
||||
// $ while ./test_params read /data/params DongleId; do sleep 0.05; done
|
@ -0,0 +1,54 @@ |
||||
#ifndef COMMON_TIMING_H |
||||
#define COMMON_TIMING_H |
||||
|
||||
#include <stdint.h> |
||||
#include <time.h> |
||||
|
||||
#ifdef __APPLE__ |
||||
#define CLOCK_BOOTTIME CLOCK_MONOTONIC |
||||
#endif |
||||
|
||||
static inline uint64_t nanos_since_boot() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_BOOTTIME, &t); |
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec; |
||||
} |
||||
|
||||
static inline double millis_since_boot() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_BOOTTIME, &t); |
||||
return t.tv_sec * 1000.0 + t.tv_nsec * 1e-6; |
||||
} |
||||
|
||||
static inline double seconds_since_boot() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_BOOTTIME, &t); |
||||
return (double)t.tv_sec + t.tv_nsec * 1e-9;; |
||||
} |
||||
|
||||
static inline uint64_t nanos_since_epoch() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_REALTIME, &t); |
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec; |
||||
} |
||||
|
||||
static inline double seconds_since_epoch() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_REALTIME, &t); |
||||
return (double)t.tv_sec + t.tv_nsec * 1e-9; |
||||
} |
||||
|
||||
// you probably should use nanos_since_boot instead
|
||||
static inline uint64_t nanos_monotonic() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_MONOTONIC, &t); |
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec; |
||||
} |
||||
|
||||
static inline uint64_t nanos_monotonic_raw() { |
||||
struct timespec t; |
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &t); |
||||
return t.tv_sec * 1000000000ULL + t.tv_nsec; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,120 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <stdbool.h> |
||||
#include <assert.h> |
||||
#include <unistd.h> |
||||
#include <fcntl.h> |
||||
#include <dirent.h> |
||||
#include <sys/poll.h> |
||||
#include <linux/input.h> |
||||
|
||||
#include "touch.h" |
||||
|
||||
static int find_dev() { |
||||
int err; |
||||
|
||||
int ret = -1; |
||||
|
||||
DIR *dir = opendir("/dev/input"); |
||||
assert(dir); |
||||
struct dirent* de = NULL; |
||||
while ((de = readdir(dir))) { |
||||
if (strncmp(de->d_name, "event", 5)) continue; |
||||
|
||||
int fd = openat(dirfd(dir), de->d_name, O_RDONLY); |
||||
assert(fd >= 0); |
||||
|
||||
unsigned char ev_bits[KEY_MAX / 8 + 1]; |
||||
err = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(ev_bits)), ev_bits); |
||||
assert(err >= 0); |
||||
|
||||
const int x_key = ABS_MT_POSITION_X / 8; |
||||
const int y_key = ABS_MT_POSITION_Y / 8; |
||||
if ((ev_bits[x_key] & (ABS_MT_POSITION_X - x_key)) && |
||||
(ev_bits[y_key] & (ABS_MT_POSITION_Y - y_key))) { |
||||
ret = fd; |
||||
break; |
||||
} |
||||
close(fd); |
||||
} |
||||
closedir(dir); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
void touch_init(TouchState *s) { |
||||
s->fd = find_dev(); |
||||
assert(s->fd >= 0); |
||||
} |
||||
|
||||
int touch_poll(TouchState *s, int* out_x, int* out_y, int timeout) { |
||||
assert(out_x && out_y); |
||||
bool up = false; |
||||
while (true) { |
||||
struct pollfd polls[] = {{ |
||||
.fd = s->fd, |
||||
.events = POLLIN, |
||||
}}; |
||||
int err = poll(polls, 1, timeout); |
||||
if (err < 0) { |
||||
return -1; |
||||
} |
||||
if (!(polls[0].revents & POLLIN)) { |
||||
break; |
||||
} |
||||
|
||||
struct input_event event; |
||||
err = read(polls[0].fd, &event, sizeof(event)); |
||||
if (err < sizeof(event)) { |
||||
return -1; |
||||
} |
||||
|
||||
switch (event.type) { |
||||
case EV_ABS: |
||||
if (event.code == ABS_MT_POSITION_X) { |
||||
s->last_x = event.value; |
||||
} else if (event.code == ABS_MT_POSITION_Y) { |
||||
s->last_y = event.value; |
||||
} else if (event.code == ABS_MT_TRACKING_ID && event.value != -1) { |
||||
up = true; |
||||
} |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
if (up) { |
||||
// adjust for flippening
|
||||
*out_x = s->last_y; |
||||
*out_y = 1080 - s->last_x; |
||||
} |
||||
return up; |
||||
} |
||||
|
||||
int touch_read(TouchState *s, int* out_x, int* out_y) { |
||||
assert(out_x && out_y); |
||||
struct input_event event; |
||||
int err = read(s->fd, &event, sizeof(event)); |
||||
if (err < sizeof(event)) { |
||||
return -1; |
||||
} |
||||
bool up = false; |
||||
switch (event.type) { |
||||
case EV_ABS: |
||||
if (event.code == ABS_MT_POSITION_X) { |
||||
s->last_x = event.value; |
||||
} else if (event.code == ABS_MT_POSITION_Y) { |
||||
s->last_y = event.value; |
||||
} |
||||
up = true; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
if (up) { |
||||
// adjust for flippening
|
||||
*out_x = s->last_y; |
||||
*out_y = 1080 - s->last_x; |
||||
} |
||||
return up; |
||||
} |
@ -0,0 +1,21 @@ |
||||
#ifndef TOUCH_H |
||||
#define TOUCH_H |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct TouchState { |
||||
int fd; |
||||
int last_x, last_y; |
||||
} TouchState; |
||||
|
||||
void touch_init(TouchState *s); |
||||
int touch_poll(TouchState *s, int *out_x, int *out_y, int timeout); |
||||
int touch_read(TouchState *s, int* out_x, int* out_y); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,61 @@ |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
#include <unistd.h> |
||||
|
||||
#ifdef __linux__ |
||||
#include <sys/prctl.h> |
||||
#include <sys/syscall.h> |
||||
#include <sched.h> |
||||
#endif |
||||
|
||||
void* read_file(const char* path, size_t* out_len) { |
||||
FILE* f = fopen(path, "r"); |
||||
if (!f) { |
||||
return NULL; |
||||
} |
||||
fseek(f, 0, SEEK_END); |
||||
long f_len = ftell(f); |
||||
rewind(f); |
||||
|
||||
char* buf = (char*)calloc(f_len, 1); |
||||
assert(buf); |
||||
|
||||
size_t num_read = fread(buf, f_len, 1, f); |
||||
fclose(f); |
||||
|
||||
if (num_read != 1) { |
||||
free(buf); |
||||
return NULL; |
||||
} |
||||
|
||||
if (out_len) { |
||||
*out_len = f_len; |
||||
} |
||||
|
||||
return buf; |
||||
} |
||||
|
||||
void set_thread_name(const char* name) { |
||||
#ifdef __linux__ |
||||
// pthread_setname_np is dumb (fails instead of truncates)
|
||||
prctl(PR_SET_NAME, (unsigned long)name, 0, 0, 0); |
||||
#endif |
||||
} |
||||
|
||||
int set_realtime_priority(int level) { |
||||
#ifdef __linux__ |
||||
|
||||
long tid = syscall(SYS_gettid); |
||||
|
||||
// should match python using chrt
|
||||
struct sched_param sa; |
||||
memset(&sa, 0, sizeof(sa)); |
||||
sa.sched_priority = level; |
||||
return sched_setscheduler(tid, SCHED_FIFO, &sa); |
||||
#else |
||||
return -1; |
||||
#endif |
||||
} |
||||
|
@ -0,0 +1,48 @@ |
||||
#ifndef COMMON_UTIL_H |
||||
#define COMMON_UTIL_H |
||||
|
||||
#include <stdio.h> |
||||
|
||||
#ifndef __cplusplus |
||||
|
||||
#define min(a,b) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a < _b ? _a : _b; }) |
||||
|
||||
#define max(a,b) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
_a > _b ? _a : _b; }) |
||||
|
||||
#endif |
||||
|
||||
#define clamp(a,b,c) \ |
||||
({ __typeof__ (a) _a = (a); \
|
||||
__typeof__ (b) _b = (b); \
|
||||
__typeof__ (c) _c = (c); \
|
||||
_a < _b ? _b : (_a > _c ? _c : _a); }) |
||||
|
||||
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0])) |
||||
|
||||
#define ALIGN(x, align) (((x) + (align)-1) & ~((align)-1)) |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
// Reads a file into a newly allocated buffer.
|
||||
//
|
||||
// Returns NULL on failure, otherwise the NULL-terminated file contents.
|
||||
// The result must be freed by the caller.
|
||||
void* read_file(const char* path, size_t* out_len); |
||||
|
||||
void set_thread_name(const char* name); |
||||
|
||||
int set_realtime_priority(int level); |
||||
|
||||
#ifdef __cplusplus |
||||
} |
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,66 @@ |
||||
#ifndef UTILPP_H |
||||
#define UTILPP_H |
||||
|
||||
#include <cstdio> |
||||
#include <unistd.h> |
||||
|
||||
#include <string> |
||||
#include <memory> |
||||
#include <sstream> |
||||
#include <fstream> |
||||
|
||||
namespace util { |
||||
|
||||
inline bool starts_with(std::string s, std::string prefix) { |
||||
return s.compare(0, prefix.size(), prefix) == 0; |
||||
} |
||||
|
||||
template<typename ... Args> |
||||
inline std::string string_format( const std::string& format, Args ... args ) { |
||||
size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; |
||||
std::unique_ptr<char[]> buf( new char[ size ] ); |
||||
snprintf( buf.get(), size, format.c_str(), args ... ); |
||||
return std::string( buf.get(), buf.get() + size - 1 ); |
||||
} |
||||
|
||||
inline std::string read_file(std::string fn) { |
||||
std::ifstream t(fn); |
||||
std::stringstream buffer; |
||||
buffer << t.rdbuf(); |
||||
return buffer.str(); |
||||
} |
||||
|
||||
inline std::string tohex(const uint8_t* buf, size_t buf_size) { |
||||
std::unique_ptr<char[]> hexbuf(new char[buf_size*2+1]); |
||||
for (size_t i=0; i < buf_size; i++) { |
||||
sprintf(&hexbuf[i*2], "%02x", buf[i]); |
||||
} |
||||
hexbuf[buf_size*2] = 0; |
||||
return std::string(hexbuf.get(), hexbuf.get() + buf_size*2); |
||||
} |
||||
|
||||
inline std::string base_name(std::string const & path) { |
||||
size_t pos = path.find_last_of("/"); |
||||
if (pos == std::string::npos) return path; |
||||
return path.substr(pos + 1); |
||||
} |
||||
|
||||
inline std::string dir_name(std::string const & path) { |
||||
size_t pos = path.find_last_of("/"); |
||||
if (pos == std::string::npos) return ""; |
||||
return path.substr(0, pos); |
||||
} |
||||
|
||||
inline std::string readlink(std::string path) { |
||||
char buff[4096]; |
||||
ssize_t len = ::readlink(path.c_str(), buff, sizeof(buff)-1); |
||||
if (len != -1) { |
||||
buff[len] = '\0'; |
||||
return std::string(buff); |
||||
} |
||||
return ""; |
||||
} |
||||
|
||||
} |
||||
|
||||
#endif |
@ -0,0 +1 @@ |
||||
#define COMMA_VERSION "0.7.2" |
@ -0,0 +1,39 @@ |
||||
#ifndef IONBUF_H |
||||
#define IONBUF_H |
||||
|
||||
#ifdef __APPLE__ |
||||
#include <OpenCL/cl.h> |
||||
#else |
||||
#include <CL/cl.h> |
||||
#endif |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct VisionBuf { |
||||
size_t 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, cl_mem *out_mem); |
||||
cl_mem visionbuf_to_cl(const VisionBuf* buf, 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 |
||||
|
||||
#endif |
@ -0,0 +1,110 @@ |
||||
#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> |
||||
|
||||
#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]; |
||||
snprintf(full_path, sizeof(full_path)-1, "/dev/shm/visionbuf_%d_%d", getpid(), offset++); |
||||
*fd = open(full_path, O_RDWR | O_CREAT, 0777); |
||||
unlink(full_path); |
||||
ftruncate(*fd, len); |
||||
void *addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0); |
||||
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, |
||||
}; |
||||
} |
||||
|
||||
cl_mem visionbuf_to_cl(const VisionBuf* buf, cl_device_id device_id, cl_context ctx) { |
||||
// HACK because this platform is just for convenience
|
||||
VisionBuf *w_buf = (VisionBuf*)buf; |
||||
cl_mem ret; |
||||
*w_buf = visionbuf_allocate_cl(buf->len, device_id, ctx, &ret); |
||||
return ret; |
||||
} |
||||
|
||||
VisionBuf visionbuf_allocate_cl(size_t len, cl_device_id device_id, cl_context ctx, cl_mem *out_mem) { |
||||
int err; |
||||
assert(out_mem); |
||||
|
||||
#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 = clCreateCommandQueue(ctx, device_id, 0, &err); |
||||
assert(err == 0); |
||||
#endif |
||||
|
||||
cl_mem mem = clCreateBuffer(ctx, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, len, host_ptr, &err); |
||||
assert(err == 0); |
||||
|
||||
*out_mem = mem; |
||||
|
||||
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) { |
||||
int err = 0; |
||||
if (!buf->buf_cl) return; |
||||
|
||||
#if __OPENCL_VERSION__ < 200 |
||||
if (dir == VISIONBUF_SYNC_FROM_DEVICE) { |
||||
err = clEnqueueReadBuffer(buf->copy_q, buf->buf_cl, CL_FALSE, 0, buf->len, buf->addr, 0, NULL, NULL); |
||||
} else { |
||||
err = clEnqueueWriteBuffer(buf->copy_q, buf->buf_cl, CL_FALSE, 0, buf->len, buf->addr, 0, NULL, NULL); |
||||
} |
||||
assert(err == 0); |
||||
clFinish(buf->copy_q); |
||||
#endif |
||||
} |
||||
|
||||
void visionbuf_free(const VisionBuf* buf) { |
||||
if (buf->handle) { |
||||
munmap(buf->addr, buf->len); |
||||
close(buf->fd); |
||||
} else { |
||||
int err = clReleaseMemObject(buf->buf_cl); |
||||
assert(err == 0); |
||||
#if __OPENCL_VERSION__ >= 200 |
||||
clSVMFree(buf->ctx, buf->addr); |
||||
#else |
||||
munmap(buf->addr, buf->len); |
||||
close(buf->fd); |
||||
#endif |
||||
} |
||||
} |
@ -0,0 +1,141 @@ |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <assert.h> |
||||
#include <sys/mman.h> |
||||
#include <sys/ioctl.h> |
||||
|
||||
#include <linux/ion.h> |
||||
#include <CL/cl_ext.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, |
||||
.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, cl_mem *out_mem) { |
||||
VisionBuf r = visionbuf_allocate(len); |
||||
*out_mem = visionbuf_to_cl(&r, device_id, ctx); |
||||
return r; |
||||
} |
||||
|
||||
cl_mem visionbuf_to_cl(const VisionBuf* buf, cl_device_id device_id, cl_context ctx) { |
||||
int err = 0; |
||||
|
||||
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; |
||||
|
||||
cl_mem mem = clCreateBuffer(ctx, |
||||
CL_MEM_USE_HOST_PTR | CL_MEM_EXT_HOST_PTR_QCOM, |
||||
buf->len, &ion_cl, &err); |
||||
assert(err == 0); |
||||
|
||||
return mem; |
||||
} |
||||
|
||||
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) { |
||||
struct ion_handle_data handle_data = { |
||||
.handle = buf->handle, |
||||
}; |
||||
int ret = ioctl(ion_fd, ION_IOC_FREE, &handle_data); |
||||
assert(ret == 0); |
||||
} |
@ -0,0 +1,122 @@ |
||||
#include <cassert> |
||||
|
||||
#ifdef QCOM |
||||
#include <system/graphics.h> |
||||
#include <ui/GraphicBuffer.h> |
||||
#include <ui/PixelFormat.h> |
||||
#include <gralloc_priv.h> |
||||
|
||||
#include <GLES3/gl3.h> |
||||
#define GL_GLEXT_PROTOTYPES |
||||
#include <GLES2/gl2ext.h> |
||||
|
||||
#include <EGL/egl.h> |
||||
#define EGL_EGLEXT_PROTOTYPES |
||||
#include <EGL/eglext.h> |
||||
|
||||
#endif |
||||
|
||||
#include "common/util.h" |
||||
#include "common/visionbuf.h" |
||||
|
||||
#include "common/visionimg.h" |
||||
|
||||
#ifdef QCOM |
||||
|
||||
using namespace android; |
||||
|
||||
// from libadreno_utils.so
|
||||
extern "C" void compute_aligned_width_and_height(int width, |
||||
int height, |
||||
int bpp, |
||||
int tile_mode, |
||||
int raster_mode, |
||||
int padding_threshold, |
||||
int *aligned_w, |
||||
int *aligned_h); |
||||
#endif |
||||
|
||||
void visionimg_compute_aligned_width_and_height(int width, int height, int *aligned_w, int *aligned_h) { |
||||
#ifdef QCOM |
||||
compute_aligned_width_and_height(ALIGN(width, 32), ALIGN(height, 32), 3, 0, 0, 512, aligned_w, aligned_h); |
||||
#else |
||||
*aligned_w = width; *aligned_h = height; |
||||
#endif |
||||
} |
||||
|
||||
VisionImg visionimg_alloc_rgb24(int width, int height, VisionBuf *out_buf) { |
||||
int aligned_w = 0, aligned_h = 0; |
||||
visionimg_compute_aligned_width_and_height(width, height, &aligned_w, &aligned_h); |
||||
|
||||
int stride = aligned_w * 3; |
||||
size_t size = aligned_w * aligned_h * 3; |
||||
|
||||
VisionBuf buf = visionbuf_allocate(size); |
||||
|
||||
*out_buf = buf; |
||||
|
||||
return (VisionImg){ |
||||
.fd = buf.fd, |
||||
.format = VISIONIMG_FORMAT_RGB24, |
||||
.width = width, |
||||
.height = height, |
||||
.stride = stride, |
||||
.size = size, |
||||
.bpp = 3, |
||||
}; |
||||
} |
||||
|
||||
#ifdef QCOM |
||||
|
||||
EGLClientBuffer visionimg_to_egl(const VisionImg *img, void **pph) { |
||||
assert((img->size % img->stride) == 0); |
||||
assert((img->stride % img->bpp) == 0); |
||||
|
||||
int format = 0; |
||||
if (img->format == VISIONIMG_FORMAT_RGB24) { |
||||
format = HAL_PIXEL_FORMAT_RGB_888; |
||||
} else { |
||||
assert(false); |
||||
} |
||||
|
||||
private_handle_t* hnd = new private_handle_t(img->fd, img->size, |
||||
private_handle_t::PRIV_FLAGS_USES_ION|private_handle_t::PRIV_FLAGS_FRAMEBUFFER, |
||||
0, format, |
||||
img->stride/img->bpp, img->size/img->stride, |
||||
img->width, img->height); |
||||
|
||||
GraphicBuffer* gb = new GraphicBuffer(img->width, img->height, (PixelFormat)format, |
||||
GraphicBuffer::USAGE_HW_TEXTURE, img->stride/img->bpp, hnd, false); |
||||
// GraphicBuffer is ref counted by EGLClientBuffer(ANativeWindowBuffer), no need and not possible to release.
|
||||
*pph = hnd; |
||||
return (EGLClientBuffer) gb->getNativeBuffer(); |
||||
} |
||||
|
||||
GLuint visionimg_to_gl(const VisionImg *img, EGLImageKHR *pkhr, void **pph) { |
||||
|
||||
EGLClientBuffer buf = visionimg_to_egl(img, pph); |
||||
|
||||
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
||||
assert(display != EGL_NO_DISPLAY); |
||||
|
||||
EGLint img_attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; |
||||
EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, |
||||
EGL_NATIVE_BUFFER_ANDROID, buf, img_attrs); |
||||
assert(image != EGL_NO_IMAGE_KHR); |
||||
|
||||
GLuint tex = 0; |
||||
glGenTextures(1, &tex); |
||||
glBindTexture(GL_TEXTURE_2D, tex); |
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); |
||||
*pkhr = image; |
||||
return tex; |
||||
} |
||||
|
||||
void visionimg_destroy_gl(EGLImageKHR khr, void *ph) { |
||||
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
||||
assert(display != EGL_NO_DISPLAY); |
||||
eglDestroyImageKHR(display, khr); |
||||
delete (private_handle_t*)ph; |
||||
} |
||||
|
||||
#endif |
@ -0,0 +1,36 @@ |
||||
#ifndef VISIONIMG_H |
||||
#define VISIONIMG_H |
||||
|
||||
#include "common/visionbuf.h" |
||||
|
||||
#include <GLES3/gl3.h> |
||||
#include <EGL/egl.h> |
||||
#include <EGL/eglext.h> |
||||
#undef Status |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define VISIONIMG_FORMAT_RGB24 1 |
||||
|
||||
typedef struct VisionImg { |
||||
int fd; |
||||
int format; |
||||
int width, height, stride; |
||||
int bpp; |
||||
size_t size; |
||||
} VisionImg; |
||||
|
||||
void visionimg_compute_aligned_width_and_height(int width, int height, int *aligned_w, int *aligned_h); |
||||
VisionImg visionimg_alloc_rgb24(int width, int height, VisionBuf *out_buf); |
||||
|
||||
EGLClientBuffer visionimg_to_egl(const VisionImg *img, void **pph); |
||||
GLuint visionimg_to_gl(const VisionImg *img, EGLImageKHR *pkhr, void **pph); |
||||
void visionimg_destroy_gl(EGLImageKHR khr, void *ph); |
||||
|
||||
#ifdef __cplusplus |
||||
} // extern "C"
|
||||
#endif |
||||
|
||||
#endif |
@ -0,0 +1,194 @@ |
||||
#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 %u\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 %u\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); |
||||
return -1; |
||||
} |
||||
|
||||
VisionPacket rp; |
||||
err = vipc_recv(s->ipc_fd, &rp); |
||||
if (err <= 0) { |
||||
close(s->ipc_fd); |
||||
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); |
||||
close(s->ipc_fd); |
||||
} |
@ -0,0 +1,113 @@ |
||||
#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_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 VIPCBufExtra { |
||||
// only for yuv
|
||||
uint32_t frame_id; |
||||
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 |
@ -0,0 +1,124 @@ |
||||
|
||||
|
||||
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); |
||||
return -1; |
||||
} |
||||
|
||||
VisionPacket rp; |
||||
err = vipc_recv(s->ipc_fd, &rp); |
||||
if (err <= 0) { |
||||
close(s->ipc_fd); |
||||
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); |
||||
close(s->ipc_fd); |
||||
} |
Loading…
Reference in new issue