Skip to content

Commit

Permalink
Refactor dispmanx code for threaded vsync
Browse files Browse the repository at this point in the history
All Dispmanx specific rendering code is now shared between teletext
and cea608.

To use it, first call render_start with the width and height, and
with two functions: the init function will be called once before
the render loop starts up and should fill in the first field of
data. The draw function will be called after every vsync to get
next data. Both functions first argument is a pointer to a uint8
buffer. The draw function also takes a parameter indicating whether
to draw the odd or even field (although it is not possible to know
which one will actually be displayed next.)

The render_start function returns a void *. When you want to stop
rendering, call render_stop and pass the void * back in.
  • Loading branch information
ali1234 committed Sep 4, 2019
1 parent 4bcb778 commit 5b972dc
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 250 deletions.
6 changes: 3 additions & 3 deletions Makefile
@@ -1,12 +1,12 @@
CFLAGS+=-DSTANDALONE -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -DTARGET_POSIX -D_LINUX -fPIC -DPIC -D_REENTRANT -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -U_FORTIFY_SOURCE -Wall -g -DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -Wno-psabi

LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbcm_host
LDFLAGS+=-L$(SDKSTAGE)/opt/vc/lib/ -lbcm_host -pthread

INCLUDES+=-I$(SDKSTAGE)/opt/vc/include/ -I./

TELETEXT_OFILES=teletext.o buffer.o hamming.o demo.o
TELETEXT_OFILES=teletext.o render.o buffer.o hamming.o demo.o

CEA608_OFILES=cea608.o cea608buffer.o
CEA608_OFILES=cea608.o render.o cea608buffer.o

all: tvctl teletext cea608

Expand Down
126 changes: 17 additions & 109 deletions cea608.c
Expand Up @@ -29,27 +29,12 @@

#include "bcm_host.h"

#include "render.h"
#include "cea608buffer.h"

#ifndef ALIGN_UP
#define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1))
#endif

static DISPMANX_DISPLAY_HANDLE_T display;
static DISPMANX_ELEMENT_HANDLE_T element;
static DISPMANX_RESOURCE_HANDLE_T resource[3];
static int next_resource = 0;

static unsigned short palette[256] = { 0x0, 0xffff, 0xf000 };

uint8_t *image;
VC_RECT_T image_rect;
#define TYPE (VC_IMAGE_8BPP)
#define WIDTH (53)
#define HEIGHT (2)
#define OFFSET (1)
#define PITCH (ALIGN_UP(WIDTH, 32))
#define ROW(n) (image+(PITCH*(n))+OFFSET)
#define ROW(i, n) (i+(PITCH(WIDTH)*(n))+OFFSET)


static uint8_t hello[] = {
Expand All @@ -61,123 +46,46 @@ static uint8_t hello[] = {
};


void vsync(DISPMANX_UPDATE_HANDLE_T u, void* arg)
void draw(uint8_t *image, int next_resource)
{
static int toggle = 0;
int ret;
DISPMANX_UPDATE_HANDLE_T update;

// We need to show the buffer in both fields so skip
// every other vsync.
toggle = !toggle;
if(toggle) return;

update = vc_dispmanx_update_start( 10 );
assert( update );
ret = vc_dispmanx_element_change_source( update, element, resource[next_resource]);
assert( ret == 0 );
ret = vc_dispmanx_update_submit_sync( update );
assert( ret == 0 );

if(next_resource != 2) {

int real_next_resource = next_resource ^ 1;
next_resource = 2; // use filler if next callback called before this one ends

// fill image
get_packet(ROW(0)+19, ROW(1)+19); // +19 because clock never changes

// write to resource
ret = vc_dispmanx_resource_write_data( resource[real_next_resource], TYPE, PITCH, image, &image_rect );
assert( ret == 0 );

next_resource = real_next_resource; // queue up next real resource

// fill image
if(next_resource == 0) {
get_packet(ROW(image, 0)+19, ROW(image, 1)+19); // +19 because clock never changes
}
}

int main(int argc, char *argv[])
{
int ret;
VC_RECT_T src_rect;
VC_RECT_T dst_rect;

DISPMANX_UPDATE_HANDLE_T update;
uint32_t vc_image_ptr;

bcm_host_init();

display = vc_dispmanx_display_open( 0 );

image = calloc( 1, PITCH * HEIGHT ); // buffer 0
assert(image);

void init(uint8_t *image)
{
// initialize image buffer with clock run in
int n, m, clock = 0x61555;
for (m=0; m<19; m++) {
for (n=0; n<HEIGHT; n++) {
ROW(n)[m] = clock&1;
ROW(image, n)[m] = clock&1;
}
clock = clock >> 1;
}
draw(image, 0); // load first field in to image
}

// set up some resources
vc_dispmanx_rect_set( &image_rect, 0, 0, WIDTH, HEIGHT);
for (n=0;n<3;n++) {
resource[n] = vc_dispmanx_resource_create( TYPE, WIDTH, HEIGHT, &vc_image_ptr );
assert( resource[n] );
ret = vc_dispmanx_resource_set_palette( resource[n], palette, 0, sizeof palette );
assert( ret == 0 );
ret = vc_dispmanx_resource_write_data( resource[n], TYPE, PITCH, image, &image_rect );
assert( ret == 0 );
}
vc_dispmanx_rect_set( &image_rect, OFFSET+24, 0, 336, HEIGHT); // from now on, only copy the parts that change

update = vc_dispmanx_update_start( 10 );
assert( update );

vc_dispmanx_rect_set( &src_rect, 0, 0, WIDTH << 16, HEIGHT << 16 );
vc_dispmanx_rect_set( &dst_rect, 0, 1, 720, HEIGHT );
element = vc_dispmanx_element_add( update, display, 2000,
&dst_rect, resource[2], &src_rect,
DISPMANX_PROTECTION_NONE,
NULL, NULL, VC_IMAGE_ROT0 );

ret = vc_dispmanx_update_submit_sync( update );
assert( ret == 0 );

// BUG: clear any existing callbacks, even to other apps.
// https://github.com/raspberrypi/userland/issues/218
vc_dispmanx_vsync_callback(display, NULL, NULL);

vc_dispmanx_vsync_callback(display, vsync, NULL);
int main(int argc, char *argv[])
{
void *render_handle = render_start(WIDTH, HEIGHT, init, draw, -1);

if (argc == 2 && argv[1][0] == '-') {
if (argc >= 2 && strlen(argv[argc-1])==1 && argv[argc-1][0] == '-') { // last argument is a single '-'
while(read_packets()) {
;
}
} else {
while(1) {
for(n=0;n<20;n+=4) {
for(int n=0;n<20;n+=4) {
push_packet(hello+n);
}
}
}

vc_dispmanx_vsync_callback(display, NULL, NULL); // disable callback

update = vc_dispmanx_update_start( 10 );
assert( update );
ret = vc_dispmanx_element_remove( update, element );
assert( ret == 0 );
ret = vc_dispmanx_update_submit_sync( update );
assert( ret == 0 );
for (n=0; n<3; n++) {
ret = vc_dispmanx_resource_delete( resource[n] );
assert( ret == 0 );
}
ret = vc_dispmanx_display_close( display );
assert( ret == 0 );
render_stop(render_handle);

return 0;
}
162 changes: 162 additions & 0 deletions render.c
@@ -0,0 +1,162 @@
#include <assert.h>
#include <pthread.h>

#include "bcm_host.h"

#include "render.h"


typedef struct render_shared {
DISPMANX_DISPLAY_HANDLE_T display;
DISPMANX_ELEMENT_HANDLE_T element;
DISPMANX_RESOURCE_HANDLE_T resource[2];

pthread_cond_t cond;
pthread_mutex_t lock;
pthread_t thread;
int done;

uint8_t *image;
int width;
VC_RECT_T image_rect;
DrawFunc draw_func;
int delay;
} render_shared;


void *render_thread_func(void *anon_render_shared)
{
int ret;
int next_resource = 0;
DISPMANX_UPDATE_HANDLE_T update;
render_shared *r = anon_render_shared;

while(!r->done) {
update = vc_dispmanx_update_start(10);
assert(update);
ret = vc_dispmanx_element_change_source(update, r->element, r->resource[next_resource]);
assert(ret == 0);
pthread_cond_wait(&r->cond, &r->lock);
usleep(r->delay);
ret = vc_dispmanx_update_submit(update, NULL, NULL );
assert(ret == 0);
next_resource ^= 1;
r->draw_func(r->image, next_resource);
ret = vc_dispmanx_resource_write_data(r->resource[next_resource], TYPE, PITCH(r->width), r->image, &r->image_rect);
assert(ret == 0);
}
}


void vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *anon_render_shared)
{
render_shared *r = anon_render_shared;
pthread_cond_signal(&r->cond);
}


void *render_start(int width, int height, InitFunc init_func, DrawFunc draw_func, int delay)
{
int ret;
VC_RECT_T src_rect;
VC_RECT_T dst_rect;

DISPMANX_UPDATE_HANDLE_T update;
uint32_t vc_image_ptr;
unsigned short palette[256] = { 0x0, 0xffff, 0xf000 };

// Build the render struct. Must malloc it so it is still valid
// after this function returns.
render_shared *r = (render_shared *)calloc(1, sizeof(render_shared));

pthread_cond_init(&r->cond, NULL);
pthread_mutex_init(&r->lock, NULL);

r->draw_func = draw_func;
if (delay > -1) {
r->delay = delay;
} else {
r->delay = 1000;
}
r->width = width;
r->image = calloc(1, PITCH(width) * height);
assert(r->image);
init_func(r->image);

bcm_host_init();
r->display = vc_dispmanx_display_open(0);

// set up some resources
vc_dispmanx_rect_set(&r->image_rect, 0, 0, width, height);
for (int n=0;n<2;n++) {
r->resource[n] = vc_dispmanx_resource_create(TYPE, width, height, &vc_image_ptr);
assert(r->resource[n]);
ret = vc_dispmanx_resource_set_palette(r->resource[n], palette, 0, sizeof palette);
assert(ret == 0);
ret = vc_dispmanx_resource_write_data(r->resource[n], TYPE, PITCH(width), r->image, &r->image_rect);
assert(ret == 0);
}
vc_dispmanx_rect_set(&r->image_rect, OFFSET+24, 0, 336, height); // from now on, only copy the parts that change

update = vc_dispmanx_update_start(10);
assert(update);

vc_dispmanx_rect_set(&src_rect, 0, 0, width << 16, height << 16);
vc_dispmanx_rect_set(&dst_rect, 0, 0, 720, height);
r->element = vc_dispmanx_element_add(update, r->display, 2000,
&dst_rect, r->resource[2], &src_rect,
DISPMANX_PROTECTION_NONE,
NULL, NULL, VC_IMAGE_ROT0);

ret = vc_dispmanx_update_submit_sync(update);
assert(ret == 0);

r->done = 0;

// Start the drawing thread.
pthread_create(&r->thread, NULL, render_thread_func, r);

// BUG: Clear any existing callbacks, even to other apps.
// https://github.com/raspberrypi/userland/issues/218
// TODO: Check if we still need this.
vc_dispmanx_vsync_callback(r->display, NULL, NULL);

// Set the callback function.
vc_dispmanx_vsync_callback(r->display, vsync_callback, r);

return r;
}


void render_stop(void *anon_render_shared) {
int ret;
DISPMANX_UPDATE_HANDLE_T update;

render_shared *r = anon_render_shared;

// Stop the thread first in case it is waiting on a signal.
r->done = 1;
pthread_join(r->thread, NULL);

// Stop the vsync callbacks.
vc_dispmanx_vsync_callback(r->display, NULL, NULL);

// Destroy element and resources.
update = vc_dispmanx_update_start( 10 );
assert( update );
ret = vc_dispmanx_element_remove(update, r->element);
assert(ret == 0);
ret = vc_dispmanx_update_submit_sync(update);
assert(ret == 0);
for (int n=0; n<2; n++) {
ret = vc_dispmanx_resource_delete(r->resource[n]);
assert(ret == 0);
}
ret = vc_dispmanx_display_close(r->display);
assert(ret == 0);

pthread_cond_destroy(&r->cond);
pthread_mutex_destroy(&r->lock);

free(r);
}
13 changes: 13 additions & 0 deletions render.h
@@ -0,0 +1,13 @@
#ifndef ALIGN_UP
#define ALIGN_UP(x,y) ((x + (y)-1) & ~((y)-1))
#endif

#define TYPE (VC_IMAGE_8BPP)
#define OFFSET (8)
#define PITCH(w) (ALIGN_UP(w, 32))

typedef void (*InitFunc)(uint8_t *image);
typedef void (*DrawFunc)(uint8_t *image, int next_resource);

void *render_start(int width, int height, InitFunc init_func, DrawFunc draw_func, int delay);
void render_stop(void *);

0 comments on commit 5b972dc

Please sign in to comment.