#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include "cm_setup.h"
#include "cm_error_handler.h"
#define IMAGE_NAME "fractal.ppm"
#define TMP_IMAGE_NAME "tmp.ppm"
#define IMAGE_DIR "/tmp/em-frac_ramdisk/"
#define IMAGE_PATH (IMAGE_DIR IMAGE_NAME)
#define TMP_IMAGE_PATH (IMAGE_DIR TMP_IMAGE_NAME)
#define REMOUNT_RAMDISK_FMT \
"(umount %s; mount -t tmpfs -o size=%dM tmpfs %s) 2> /dev/null"
#define WIDTH 640
#define HEIGHT 480
#define ZOOM_POINT_X -1.401155
#define ZOOM_POINT_Y 0.00002
#define START_FROM_FRAME 0
#define STOP_AFTER_FRAMES 0
#define MAX_ITERATIONS 1000
#define IMAGE_VIEW_RATE 0.05
#define IMAGE_BLOCK_SIZE 80
#define EVENTS_PER_IMAGE ((ROUND_UP(WIDTH, IMAGE_BLOCK_SIZE) \
/ IMAGE_BLOCK_SIZE) * HEIGHT)
#define ALLOC_EVENTS ((WIDTH / IMAGE_BLOCK_SIZE + 1) * HEIGHT)
#define SEND_MULTI_MAX 32
typedef struct {
char col[WIDTH * 3];
} h_line_t;
typedef struct {
h_line_t line[HEIGHT];
} buffer_t;
typedef struct {
uint32_t frame;
} pixel_handler_event_t;
typedef struct {
uint16_t y;
uint16_t x_start;
uint16_t x_len;
uint16_t pix_iter_array[IMAGE_BLOCK_SIZE];
uint32_t frame;
uint32_t data_evtbl_idx;
} imager_data_event_t;
typedef struct {
uint64_t seq;
} notif_event_t;
typedef union {
pixel_handler_event_t pixel_handler_event;
imager_data_event_t imager_data_event;
notif_event_t notif_event;
} fractal_event_t;
typedef struct {
char name[16];
em_eo_t eo;
} my_eo_context_t;
typedef struct {
char name[16];
em_eo_t eo;
struct timespec start_time;
struct timespec next_print_time;
uint32_t prev_print_frame;
uint32_t frame;
int buf_idx;
} imager_eo_context_t;
COMPILE_TIME_ASSERT((offsetof(imager_eo_context_t, buf[1]) -
offsetof(imager_eo_context_t, buf[0])) %
IMAGER_EO_CONTEXT_T__SIZE_ERROR);
typedef struct {
#define DATA_QUEUE 0
#define NOTIF_QUEUE 1
uint8_t id;
} imager_queue_context_t;
typedef struct {
em_pool_t pool;
struct {
em_queue_t queue_pixel;
em_queue_t queue_worker;
em_queue_t queue_data_imager;
em_queue_t queue_notif_imager;
em_event_group_t egrp_imager;
} fractal_shm_t;
pixel_handler_start(my_eo_context_t *eo_ctx, em_eo_t eo,
pixel_handler_stop(my_eo_context_t *eo_ctx, em_eo_t eo);
static void
pixel_handler_receive_event(my_eo_context_t *eo_ctx, em_event_t event,
void *q_ctx);
worker_start(my_eo_context_t *eo_ctx, em_eo_t eo,
const em_eo_conf_t *conf);
worker_stop(my_eo_context_t *eo_ctx, em_eo_t eo);
static void
worker_receive_event(my_eo_context_t *eo_ctx, em_event_t event,
imager_start(imager_eo_context_t *eo_ctx, em_eo_t eo,
imager_stop(imager_eo_context_t *eo_ctx, em_eo_t eo);
static void
imager_receive_event(imager_eo_context_t *eo_ctx, em_event_t event,
imager_queue_context_t *q_ctx);
static void
snprintf_check(int cmd_size, int ret);
static void
run_system_cmds(void);
static void
create_data_events(void);
static void
free_data_events(void);
int main(int argc, char *argv[])
{
return cm_setup(argc, argv);
}
void test_init(const appl_conf_t *appl_conf)
{
(void)appl_conf;
if (core == 0) {
fractal_shm = env_shared_reserve("FractalShMem",
sizeof(fractal_shm_t));
} else {
fractal_shm = env_shared_lookup("FractalShMem");
}
if (fractal_shm == NULL)
"Fractal init failed on EM-core: %u",
else if (core == 0)
memset(fractal_shm, 0, sizeof(fractal_shm_t));
}
void test_start(const appl_conf_t *appl_conf)
{
em_eo_t eo_pixel_handler, eo_worker, eo_imager;
if (appl_conf->num_pools >= 1)
fractal_shm->pool = appl_conf->pools[0];
else
APPL_PRINT("\n"
"***********************************************************\n"
"EM APPLICATION: '%s' initializing:\n"
" %s: %s() - EM-core:%d\n"
" Application running on %u EM-cores (procs:%u, threads:%u)\n"
"***********************************************************\n"
"\n",
appl_conf->name, NO_PATH(__FILE__), __func__,
em_core_id(),
appl_conf->core_count, appl_conf->num_procs, appl_conf->num_threads,
fractal_shm->pool);
"Undefined application event pool!");
eo_pixel_handler =
&fractal_shm->eo_context_pixel_handler);
"pixel handler creation failed!");
&fractal_shm->eo_context_worker);
test_fatal_if(eo_worker ==
EM_EO_UNDEF,
"Worker creation failed!");
&fractal_shm->eo_context_imager);
test_fatal_if(eo_imager ==
EM_EO_UNDEF,
"Imager creation failed!");
fractal_shm->eo_context_pixel_handler.eo = eo_pixel_handler;
fractal_shm->eo_context_worker.eo = eo_worker;
fractal_shm->eo_context_imager.eo = eo_imager;
"Event group creation failed!");
run_system_cmds();
create_data_events();
"EO pixel handler start:%" PRI_STAT " %" PRI_STAT "",
ret, start_ret);
"EO worker start:%" PRI_STAT " %" PRI_STAT "",
ret, start_ret);
"EO imager start:%" PRI_STAT " %" PRI_STAT "",
ret, start_ret);
}
void test_stop(const appl_conf_t *appl_conf)
{
const em_eo_t eo_pixel_handler =
fractal_shm->eo_context_pixel_handler.eo;
const em_eo_t eo_worker = fractal_shm->eo_context_worker.eo;
const em_eo_t eo_imager = fractal_shm->eo_context_imager.eo;
em_event_group_t egrp;
int num_notifs;
(void)appl_conf;
APPL_PRINT("%s() on EM-core %d\n", __func__, core);
APPL_EXIT_FAILURE("EO pixel handler stop failed!");
APPL_EXIT_FAILURE("EO pixel handler delete failed!");
APPL_EXIT_FAILURE("EO worker stop failed!");
APPL_EXIT_FAILURE("EO worker delete failed!");
APPL_EXIT_FAILURE("EO imager stop failed!");
APPL_EXIT_FAILURE("EO imager delete failed!");
egrp = fractal_shm->egrp_imager;
if (stat ==
EM_OK && num_notifs == 1)
}
test_fatal_if(stat !=
EM_OK,
"egrp:%" PRI_EGRP " delete:%" PRI_STAT
"",
egrp, stat);
free_data_events();
}
void test_term(const appl_conf_t *appl_conf)
{
(void)appl_conf;
APPL_PRINT("%s() on EM-core %d\n", __func__, core);
if (core == 0) {
env_shared_free(fractal_shm);
}
}
static void
snprintf_check(int cmd_size, int ret)
{
if (ret < 0 || ret >= cmd_size)
APPL_EXIT_FAILURE("snprintf(): ret=%d, errno(%i)=%s",
ret, errno, strerror(errno));
}
static void
run_system_cmds(void)
{
int ret;
char cmd[256];
int ramdisk_size = (WIDTH * HEIGHT * 6) / 1000000 + 1;
struct sigaction sa, old_sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = SIG_DFL;
if (sigaction(SIGCHLD, &sa, &old_sa) == -1)
APPL_EXIT_FAILURE("sigaction(): errno(%i)=%s", errno,
strerror(errno));
ret = snprintf(cmd, sizeof(cmd), "mkdir -p %s", IMAGE_DIR);
snprintf_check(sizeof(cmd), ret);
ret = system(cmd);
if (ret != 0)
APPL_EXIT_FAILURE("Failed to create output dir: %s\n", cmd);
ret = snprintf(cmd, sizeof(cmd), REMOUNT_RAMDISK_FMT,
IMAGE_DIR, ramdisk_size, IMAGE_DIR);
snprintf_check(sizeof(cmd), ret);
ret = system(cmd);
if (ret != 0)
APPL_PRINT("Ramdisk mount failed.\n"
"Run as root/sudo for better performance.\n");
ret = system("feh -v > /dev/null");
if (ret != 0) {
APPL_PRINT("NOTE: Install 'feh' or open %s\n", IMAGE_PATH);
} else {
ret = snprintf(cmd, sizeof(cmd),
"echo \"P6\n1 1\n255\n\" > %s; feh %s -R %f &",
IMAGE_PATH, IMAGE_PATH, IMAGE_VIEW_RATE);
snprintf_check(sizeof(cmd), ret);
ret = system(cmd);
if (ret != 0)
APPL_PRINT("NOTE: 'feh' unable to display %s\n",
IMAGE_PATH);
}
if (sigaction(SIGCHLD, &old_sa, NULL) == -1)
APPL_EXIT_FAILURE("sigaction(): errno(%i)=%s", errno,
strerror(errno));
}
static void
create_data_events(void)
{
imager_data_event_t *image_data_event;
em_event_t event;
int y, x_start, x_len;
int index;
index = 0;
for (y = 0; y < HEIGHT; y++) {
x_start = 0;
while (x_start < WIDTH) {
if (x_start + IMAGE_BLOCK_SIZE > WIDTH - 1)
x_len = WIDTH - x_start;
else
x_len = IMAGE_BLOCK_SIZE;
event =
em_alloc(
sizeof(fractal_event_t),
fractal_shm->pool);
"Event alloc failed!");
image_data_event->y = y;
image_data_event->x_len = x_len;
image_data_event->x_start = x_start;
image_data_event->frame = 0;
image_data_event->data_evtbl_idx = index;
test_fatal_if(index >= ALLOC_EVENTS,
"Event-tbl too small");
fractal_shm->data_event_tbl[index++] = event;
image_data_event = NULL;
x_start += IMAGE_BLOCK_SIZE;
}
}
}
static void
free_data_events(void)
{
em_event_t event;
int y, x_start;
int index = 0;
for (y = 0; y < HEIGHT; y++) {
for (x_start = 0; x_start < WIDTH;
x_start += IMAGE_BLOCK_SIZE) {
test_fatal_if(index >= ALLOC_EVENTS,
"Event-tbl too small");
event = fractal_shm->data_event_tbl[index++];
}
}
}
pixel_handler_start(my_eo_context_t *eo_ctx, em_eo_t eo,
{
em_queue_t queue;
const char *queue_name;
(void)conf;
queue_name = "queue Pixel handler";
NULL);
queue_name);
fractal_shm->queue_pixel = queue;
test_fatal_if(status !=
EM_OK,
"EO add queue:%" PRI_STAT
"\n"
status, eo, queue);
eo_ctx->name, eo, queue);
}
static void
pixel_handler_receive_event(my_eo_context_t *eo_ctx, em_event_t event,
void *q_ctx)
{
const uint32_t frame = pixel->frame;
int num_sent = 0;
int i, j;
(void)eo_ctx;
(void)type;
(void)queue;
(void)q_ctx;
if (unlikely(appl_shm->exit_flag))
return;
for (i = 0; i < EVENTS_PER_IMAGE; i++) {
imager_data_event_t *data;
data->frame = frame;
}
const int send_rounds = EVENTS_PER_IMAGE / SEND_MULTI_MAX;
const int left_over = EVENTS_PER_IMAGE % SEND_MULTI_MAX;
for (i = 0, j = 0; i < send_rounds; i++, j += SEND_MULTI_MAX) {
SEND_MULTI_MAX,
fractal_shm->queue_worker);
}
if (left_over)
left_over,
fractal_shm->queue_worker);
if (unlikely(num_sent != EVENTS_PER_IMAGE)) {
for (i = num_sent; i < EVENTS_PER_IMAGE; i++)
em_free(fractal_shm->data_event_tbl[i]);
test_fatal_if(!appl_shm->exit_flag,
"em_send_multi(): num_sent:%d Q:%" PRI_QUEUE "",
num_sent, fractal_shm->queue_worker);
}
}
pixel_handler_stop(my_eo_context_t *eo_ctx, em_eo_t eo)
{
APPL_PRINT(
"pixel handler stop (%s, eo id %" PRI_EO ")\n",
eo_ctx->name, eo);
test_fatal_if(ret !=
EM_OK,
"EO remove queue all:%" PRI_STAT
" EO:%" PRI_EO "",
ret, eo);
}
worker_stop(my_eo_context_t *eo_ctx, em_eo_t eo)
{
APPL_PRINT(
"worker stop (%s, eo id %" PRI_EO ")\n",
eo_ctx->name, eo);
test_fatal_if(ret !=
EM_OK,
"EO remove queue all:%" PRI_STAT
" EO:%" PRI_EO "",
ret, eo);
}
imager_stop(imager_eo_context_t *eo_ctx, em_eo_t eo)
{
APPL_PRINT(
"imager stop (%s, eo id %" PRI_EO ")\n",
eo_ctx->name, eo);
test_fatal_if(ret !=
EM_OK,
"EO remove queue all:%" PRI_STAT
" EO:%" PRI_EO "",
ret, eo);
}
static void
worker_receive_event(my_eo_context_t *eo_ctx, em_event_t event,
{
em_event_group_t event_group;
double frame = block->frame;
(void)eo_ctx;
(void)type;
(void)queue;
(void)q_ctx;
if (unlikely(appl_shm->exit_flag)) {
fractal_shm->data_event_tbl[block->data_evtbl_idx] = event;
return;
}
double pr, pi;
double new_re, new_im, old_re, old_im;
double zoom = pow(frame, 2) / 1000;
double move_x = ZOOM_POINT_X;
double move_y = -ZOOM_POINT_Y;
int x;
for (x = block->x_start; x < (block->x_len + block->x_start); x++) {
pr = 1.5 * (x - WIDTH / 2) / (0.5 * zoom * WIDTH) + move_x;
pi = (block->y - HEIGHT / 2) / (0.5 * zoom * HEIGHT) + move_y;
new_re = 0, new_im = 0, old_re = 0, old_im = 0;
int i;
for (i = 0; i < MAX_ITERATIONS; i++) {
old_re = new_re;
old_im = new_im;
new_re = old_re * old_re - old_im * old_im + pr;
new_im = 2 * old_re * old_im + pi;
if (new_re * new_re + new_im * new_im > 4)
break;
}
block->pix_iter_array[x - block->x_start] = i;
}
event_group = fractal_shm->egrp_imager;
event_group);
if (unlikely(status !=
EM_OK)) {
test_fatal_if(!appl_shm->exit_flag,
"em_send_group():%" PRI_STAT
" Q:%" PRI_QUEUE "",
status, fractal_shm->queue_data_imager);
}
}
worker_start(my_eo_context_t *eo_ctx, em_eo_t eo,
const em_eo_conf_t *conf)
{
em_queue_t queue;
const char *queue_name;
(void)conf;
queue_name = "queue Worker";
NULL);
queue_name);
fractal_shm->queue_worker = queue;
test_fatal_if(status !=
EM_OK,
"EO add queue:%" PRI_STAT
"\n"
status, eo, queue);
eo_ctx->name, eo, queue);
}
static void
imager_receive_event(imager_eo_context_t *eo_ctx, em_event_t event,
imager_queue_context_t *q_ctx)
{
(void)type;
(void)queue;
switch (q_ctx->id) {
case DATA_QUEUE: {
fractal_shm->data_event_tbl[block->data_evtbl_idx] = event;
if (unlikely(appl_shm->exit_flag))
return;
const unsigned int y = block->y;
const unsigned int x_start = block->x_start;
const unsigned int x_len = block->x_len;
buffer_t *const buf = &eo_ctx->buf[eo_ctx->buf_idx];
unsigned int i;
for (i = 0; i < x_len; i++) {
const unsigned int x = (x_start + i) * 3;
const uint16_t pix_iter = block->pix_iter_array[i];
buf->line[y].col[x] = pix_iter % 256;
buf->line[y].col[x + 1] =
(eo_ctx->frame * pix_iter) % 256;
buf->line[y].col[x + 2] =
255 * (pix_iter < MAX_ITERATIONS);
}
}
break;
case NOTIF_QUEUE: {
if (unlikely(appl_shm->exit_flag)) {
return;
}
em_event_group_t event_group;
const int buf_idx = eo_ctx->buf_idx;
notif_tbl[0].
event = event;
notif_tbl[0].
queue = fractal_shm->queue_notif_imager;
event_group = fractal_shm->egrp_imager;
1, notif_tbl);
test_fatal_if(status !=
EM_OK,
"em_event_group_apply():%" PRI_STAT "",
status);
eo_ctx->frame++;
em_event_t new_pixel_event =
em_alloc(
sizeof(fractal_event_t),
fractal_shm->pool);
"Event allocation failed!");
pixel_handler_event_t *const new_pixel =
new_pixel->frame = eo_ctx->frame;
eo_ctx->buf_idx = (eo_ctx->buf_idx + 1) % 2;
status =
em_send(new_pixel_event, fractal_shm->queue_pixel);
if (unlikely(status !=
EM_OK)) {
test_fatal_if(!appl_shm->exit_flag,
status, fractal_shm->queue_pixel);
}
FILE *fp = fopen(TMP_IMAGE_PATH, "wb+");
test_fatal_if(fp == NULL, "Can't open output file %s",
TMP_IMAGE_PATH);
fprintf(fp, "P6\n%d %d\n255\n", WIDTH, HEIGHT);
fwrite(&eo_ctx->buf[buf_idx].line[0].col[0],
sizeof(char), WIDTH * HEIGHT * 3, fp);
fclose(fp);
test_fatal_if(rename(TMP_IMAGE_PATH, IMAGE_PATH) != 0,
"Failed to rename file %s to %s.",
TMP_IMAGE_PATH, IMAGE_PATH);
struct timespec cur_time;
clock_gettime(CLOCK_MONOTONIC, &cur_time);
if (eo_ctx->next_print_time.tv_sec <= cur_time.tv_sec) {
int fps = eo_ctx->frame - eo_ctx->prev_print_frame;
fprintf(stdout,
"Frames per second: %02d | frames %d - %d\n",
fps, eo_ctx->prev_print_frame,
eo_ctx->frame - 1);
eo_ctx->prev_print_frame = eo_ctx->frame;
eo_ctx->next_print_time.tv_sec = cur_time.tv_sec + 1;
}
if (START_FROM_FRAME + STOP_AFTER_FRAMES == eo_ctx->frame) {
double ms = 0;
ms += (cur_time.tv_sec - eo_ctx->start_time.tv_sec) *
1000;
ms += (cur_time.tv_nsec - eo_ctx->start_time.tv_nsec) /
1000000;
APPL_PRINT("%d frames in %.3f seconds.\n",
STOP_AFTER_FRAMES, ms / 1000);
exit(EXIT_SUCCESS);
}
}
break;
default:
"Unknown queue id(%u)!", q_ctx->id);
break;
}
}
imager_start(imager_eo_context_t *eo_ctx, em_eo_t eo,
const em_eo_conf_t *conf)
{
em_queue_t queue;
imager_queue_context_t *q_ctx;
const char *queue_name;
em_event_group_t event_group;
em_event_t event;
(void)conf;
queue_name = "data queue imager";
NULL);
queue_name);
test_fatal_if(status !=
EM_OK,
"EO add queue:%" PRI_STAT
"\n"
fractal_shm->queue_data_imager = queue;
q_ctx = &fractal_shm->imager_data_queue_ctx;
q_ctx->id = DATA_QUEUE;
test_fatal_if(status !=
EM_OK,
"EO set queue context:%" PRI_STAT "\n"
queue_name = "notif queue imager";
NULL);
"%s creation failed!", queue_name);
q_ctx = &fractal_shm->imager_notif_queue_ctx;
q_ctx->id = NOTIF_QUEUE;
test_fatal_if(status !=
EM_OK,
"Set queue context:%" PRI_STAT "\n"
status, eo, queue);
test_fatal_if(status !=
EM_OK,
"EO add queue:%" PRI_STAT "\n"
fractal_shm->queue_notif_imager = queue;
eo_ctx->name, eo, queue);
fractal_shm->pool);
"Event allocation failed!");
notif_tbl[0].
event = event;
notif_tbl[0].
queue = fractal_shm->queue_notif_imager;
event_group = fractal_shm->egrp_imager;
notif_tbl);
test_fatal_if(status !=
EM_OK,
"em_event_group_apply():%" PRI_STAT "", status);
clock_gettime(CLOCK_MONOTONIC, &eo_ctx->start_time);
eo_ctx->frame = START_FROM_FRAME;
eo_ctx->next_print_time = eo_ctx->start_time;
eo_ctx->next_print_time.tv_sec += 1;
eo_ctx->prev_print_frame = eo_ctx->frame;
pixel_handler_event_t *pixel;
fractal_shm->pool);
"Event allocation failed!");
pixel->frame = eo_ctx->frame;
status =
em_send(event, fractal_shm->queue_pixel);
test_fatal_if(status !=
EM_OK,
"em_send():%" PRI_STAT
"\n"
status, eo, fractal_shm->queue_pixel);
}