/* Copyright 2017-2018, The AROS Development Team. All rights reserved. $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "work.h" #include "support.h" CONST_STRPTR version = "$VER: Buddhabrot 1.0 (03.03.2017) (C) 2017 The AROS Development Team"; struct Window * createMainWindow(int req_size) { struct Screen *pubScreen; struct Window *displayWin = NULL; int width, height; pubScreen = LockPubScreen(0); if (pubScreen) { width = ((pubScreen->Width * 3) / 4); height = ((pubScreen->Height * 3) / 4); if (req_size == 0) { if (width < height) req_size = width; else req_size = height; } } if (req_size == 0) req_size = 200; if ((displayWin = OpenWindowTags(0, WA_PubScreen, (Tag)pubScreen, WA_Left, 0, WA_Top, (pubScreen) ? pubScreen->BarHeight : 10, WA_InnerWidth, req_size, WA_InnerHeight, req_size, WA_Title, (Tag) "Buddhabrot", WA_SmartRefresh, TRUE, WA_CloseGadget, TRUE, WA_DepthGadget, TRUE, WA_DragBar, TRUE, WA_SizeGadget, FALSE, WA_SizeBBottom, FALSE, WA_SizeBRight, FALSE, WA_IDCMP, IDCMP_CLOSEWINDOW | IDCMP_INTUITICKS, TAG_DONE)) != NULL) { if (pubScreen) UnlockPubScreen(0, pubScreen); } return displayWin; } #define ARG_TEMPLATE "X0/K,Y0/K,SCALE/K,SIZE/K/N,SUBDIVIDE/K/N,THREADS/K/N,MAXITER/K/N,OVERSAMPLE/K/N,BUDDHA/S" enum { ARG_X0, ARG_Y0, ARG_SCALE, ARG_SIZE, ARG_SUBDIVIDE, ARG_MAXCPU, ARG_MAXITER, ARG_OVERSAMPLE, ARG_BUDDHA, ARG_COUNT }; struct TimerBase *TimerBase = NULL; struct RedrawMessage { struct RenderInfo * rinfo; struct Window * window; ULONG width; ULONG height; struct Library * pbase; ULONG * workBuffer; }; void redrawTaskMain(struct RedrawMessage *rm) { struct Library *P96Base = rm->pbase; ULONG sigset; p96WritePixelArray(rm->rinfo, 0, 0, rm->window->RPort, rm->window->BorderLeft, rm->window->BorderTop, rm->width, rm->height); while((sigset = Wait(SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D)) != 0) { if (sigset & SIGBREAKF_CTRL_D) { ULONG *rgba = rm->rinfo->Memory; for (ULONG i = 0; i < rm->width * rm->height; i++) { const double gamma_r = 1.2; const double gamma_g = 0.95; const double gamma_b = 1.0; const double pre_r = 96.0; const double pre_g = 128.0; const double pre_b = 256.0; double rgb = rm->workBuffer[i] / 256.0; ULONG r=256.0*pow(rgb, gamma_r) * pre_r; ULONG g=256.0*pow(rgb, gamma_g) * pre_g; ULONG b=256.0*pow(rgb, gamma_b) * pre_b; b = (b + 255) / 512; r = (r + 255) / 512; g = (g + 255) / 512; if (b > 255) b = 255; if (r > 255) r = 255; if (g > 255) g = 255; ULONG c = 0xff | (r << 8) | (g << 16) | (b << 24); rgba[i] = c; } p96WritePixelArray(rm->rinfo, 0, 0, rm->window->RPort, rm->window->BorderLeft, rm->window->BorderTop, rm->width, rm->height); } if (sigset & SIGBREAKF_CTRL_C) break; } } APTR memPool; int main() { struct SignalSemaphore lock; struct Task *masterTask = NULL; struct Task *redrawTask = NULL; struct MsgPort *masterPort = NULL; struct MsgPort *mainPort = NULL; struct MyMessage *cmd; struct RDArgs *rda; struct Library *P96Base; struct Window *displayWin; ULONG args[ARG_COUNT] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; ULONG coreCount = 1; ULONG maxWork = 0; ULONG oversample = 0; ULONG subdivide = 0; ULONG req_size = 0; ULONG type = TYPE_NORMAL; double x0 = 0.0; double y0 = 0.0; double size = 4.0; InitSemaphore(&lock); memPool = CreatePool(MEMF_ANY | MEMF_CLEAR, 8192, 4096); struct MsgPort *timerPort = CreateMsgPort(); struct timerequest *tr = CreateIORequest(timerPort, sizeof(struct timerequest)); struct timeval start_time; struct timeval last_redraw; struct timeval now; P96Base = OpenLibrary("Picasso96API.library", 0); if (P96Base == NULL) { Printf("Failed to open Picasso96API.library\n"); return -1; } if (timerPort) { FreeSignal(timerPort->mp_SigBit); timerPort->mp_SigBit = -1; timerPort->mp_Flags = PA_IGNORE; } if (tr) { if (!OpenDevice("timer.device", UNIT_VBLANK, (struct IORequest *)tr, 0)) { TimerBase = (struct TimerBase *)tr->tr_node.io_Device; } } else return 0; rda = ReadArgs(ARG_TEMPLATE, args, NULL); if (rda != NULL) { LONG *ptr = (LONG *)args[ARG_MAXCPU]; if (ptr) { coreCount = *ptr; if (coreCount == 0) coreCount = 1; } ptr = (LONG *)args[ARG_SUBDIVIDE]; if (ptr) { subdivide = *ptr; if (subdivide == 0) subdivide = 1; } ptr = (LONG *)args[ARG_SIZE]; if (ptr) req_size = *ptr; ptr = (LONG *)args[ARG_MAXITER]; if (ptr) { maxWork = *ptr; if (maxWork < 2) maxWork = 2; else if (maxWork > 10000000) maxWork = 10000000; } ptr = (LONG *)args[ARG_OVERSAMPLE]; if (ptr) { oversample = *ptr; if (oversample < 1) oversample = 1; else if (oversample > 10) oversample = 10; } if (args[ARG_BUDDHA]) type = TYPE_BUDDHA; if (args[ARG_X0]) x0 = atof((char *)args[ARG_X0]); if (args[ARG_Y0]) y0 = atof((char *)args[ARG_Y0]); if (args[ARG_SCALE]) size = atof((char *)args[ARG_SCALE]); if (size == 0.0) size = 4.0; } if (type == TYPE_BUDDHA) { if (maxWork == 0) maxWork = 4000; if (oversample == 0) oversample = 4; } else { if (maxWork == 0) maxWork = 1000; if (oversample == 0) oversample = 1; } mainPort = CreateMsgPort(); SetTaskPri(FindTask(NULL), 5); masterTask = NewCreateTask( TASKTAG_NAME, (Tag)"Buddha Master", TASKTAG_PRI, 2, TASKTAG_PC, (Tag)SMPTestMaster, TASKTAG_ARG1, (Tag)mainPort, TASKTAG_STACKSIZE, 65536, TAG_DONE); Printf("Waiting for master to wake up\n"); struct Message *msg = WaitPort(mainPort); masterPort = msg->mn_ReplyPort; Printf("Master alive\n"); displayWin = createMainWindow(req_size); if (displayWin) { int width, height; BOOL windowClosing = FALSE; BOOL renderingDone = FALSE; ULONG signals; ULONG *workBuffer; ULONG *rgba; width = (displayWin->Width - displayWin->BorderLeft - displayWin->BorderRight); height = (displayWin->Height - displayWin->BorderTop - displayWin->BorderBottom); if (subdivide == 0) subdivide = width * width / 5000; if (subdivide == 0) subdivide = 1; workBuffer = AllocPooled(memPool, width * height * sizeof(ULONG)); rgba = AllocPooled(memPool, width * height * sizeof(ULONG)); if (workBuffer && rgba) { struct RedrawMessage rm; struct RenderInfo ri; ri.Memory = rgba; ri.BytesPerRow = width * sizeof(ULONG); ri.RGBFormat = RGBFB_B8G8R8A8; rm.rinfo = &ri; rm.window = displayWin; rm.width = width; rm.height = height; rm.pbase = P96Base; rm.workBuffer = workBuffer; redrawTask = NewCreateTask( TASKTAG_NAME, (Tag)"Buddha redraw", TASKTAG_PC, (Tag)redrawTaskMain, TASKTAG_PRI, 0, TASKTAG_ARG1, (Tag)&rm, TASKTAG_STACKSIZE, 65536, TAG_DONE); Printf("Sending startup message to master\n"); cmd = AllocPooled(memPool, sizeof(struct MyMessage)); cmd->mm_Type = MSG_STARTUP; cmd->mm_Message.mn_ReplyPort = mainPort; cmd->mm_Message.mn_Length = sizeof(struct MyMessage); cmd->mm_Body.Startup.width = width; cmd->mm_Body.Startup.height = height; cmd->mm_Body.Startup.threadCount = coreCount; cmd->mm_Body.Startup.subdivide = subdivide; cmd->mm_Body.Startup.maxIterations = maxWork; cmd->mm_Body.Startup.oversample = oversample; cmd->mm_Body.Startup.type = type; cmd->mm_Body.Startup.x0 = x0; cmd->mm_Body.Startup.y0 = y0; cmd->mm_Body.Startup.size = size; cmd->mm_Body.Startup.workBuffer = workBuffer; cmd->mm_Body.Startup.writeLock = &lock; cmd->mm_Body.Startup.redrawTask = redrawTask; PutMsg(masterPort, &cmd->mm_Message); WaitPort(mainPort); GetMsg(mainPort); GetSysTime(&start_time); last_redraw = start_time; while (!(windowClosing && renderingDone) && ((signals = Wait((1 << displayWin->UserPort->mp_SigBit) | (1 << mainPort->mp_SigBit))) != 0)) { if (signals & (1 << mainPort->mp_SigBit)) { while ((cmd = (struct MyMessage *)GetMsg(mainPort))) { /* Reply message, discard and continue */ if (cmd->mm_Message.mn_Node.ln_Type == NT_REPLYMSG) { FreePooled(memPool, cmd, cmd->mm_Message.mn_Length); continue; } if (cmd->mm_Type == MSG_STATS) { char tmpbuf[128]; GetSysTime(&now); SubTime(&now, &start_time); if (renderingDone) { _sprintf(tmpbuf, "Buddhabrot finished; %d:%02d:%02d", now.tv_secs / 3600, (now.tv_secs / 60) % 60, now.tv_secs % 60); } else { _sprintf(tmpbuf, "Buddhabrot; %d threads; Packages: %d in, %d out; %d:%02d:%02d", cmd->mm_Body.Stats.tasksWork, cmd->mm_Body.Stats.tasksIn, cmd->mm_Body.Stats.tasksOut, now.tv_secs / 3600, (now.tv_secs / 60) % 60, now.tv_secs % 60); } SetWindowTitles(displayWin, tmpbuf, NULL); ReplyMsg(&cmd->mm_Message); } else if (cmd->mm_Type == MSG_DONE) { char tmpbuf[128]; FreePooled(memPool, msg, cmd->mm_Message.mn_Length); GetSysTime(&now); SubTime(&now, &start_time); _sprintf(tmpbuf, "Buddhabrot finished; %d:%02d:%02d", now.tv_secs / 3600, (now.tv_secs / 60) % 60, now.tv_secs % 60); SetWindowTitles(displayWin, tmpbuf, NULL); Printf("Rendering time: %ld:%02ld:%02ld\n", now.tv_secs / 3600, (now.tv_secs / 60) % 60, now.tv_secs % 60); Signal(redrawTask, SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D); redrawTask = NULL; renderingDone = TRUE; masterPort = NULL; } } } if (signals & (1 << displayWin->UserPort->mp_SigBit)) { struct IntuiMessage *msg; while ((msg = (struct IntuiMessage *)GetMsg(displayWin->UserPort))) { switch(msg->Class) { case IDCMP_CLOSEWINDOW: if (windowClosing == FALSE && masterPort != NULL) { struct MyMessage *m = AllocPooled(memPool, sizeof(struct MyMessage)); m->mm_Type = MSG_DIE; m->mm_Message.mn_Length = sizeof(struct MyMessage); m->mm_Message.mn_ReplyPort = mainPort; PutMsg(masterPort, &m->mm_Message); } windowClosing = TRUE; break; case IDCMP_INTUITICKS: GetSysTime(&now); SubTime(&now, &last_redraw); if (now.tv_secs >= 5) { GetSysTime(&last_redraw); if (redrawTask) Signal(redrawTask, SIGBREAKF_CTRL_D); } break; } ReplyMsg((struct Message *)msg); } } } } CloseWindow(displayWin); if (workBuffer) FreePooled(memPool, workBuffer, sizeof(ULONG) * width * height); if (rgba) FreePooled(memPool, rgba, sizeof(ULONG) * width * height); } DeleteMsgPort(mainPort); CloseLibrary(P96Base); DeletePool(memPool); return 0; }