From 2a878b74843597ffab12df4e2213a7d52af0bbf5 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Sun, 24 Jan 2016 16:37:43 +0200 Subject: [PATCH] forgot to commit x4 --- zrm3_adam_trd_x4.d | 947 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 947 insertions(+) create mode 100644 zrm3_adam_trd_x4.d diff --git a/zrm3_adam_trd_x4.d b/zrm3_adam_trd_x4.d new file mode 100644 index 0000000..3962d12 --- /dev/null +++ b/zrm3_adam_trd_x4.d @@ -0,0 +1,947 @@ +// ////////////////////////////////////////////////////////////////////////// // +import core.time : MonoTime, Duration; + +import core.atomic; +import core.sync.condition; +import core.sync.rwmutex; +import core.sync.mutex; +import core.thread; + +import std.concurrency; + + +version = show_frame_time; +//version = render_debug; + + +// ////////////////////////////////////////////////////////////////////////// // +__gshared Mutex mutexCondCanRender; +__gshared Condition waitCondCanRender; +shared bool renderComplete = true; +shared int renderDie = 0; // 1: waiting for death; 2: dead +shared uint frameDur; + +shared static this () { + mutexCondCanRender = new Mutex(); + waitCondCanRender = new Condition(mutexCondCanRender); +} + + +// ////////////////////////////////////////////////////////////////////////// // +static if (__traits(compiles, () { import arsd.simpledisplay; })) { + public import arsd.simpledisplay; +} else { + public import simpledisplay; +} + +__gshared SimpleWindow sdwindow; +__gshared Image texImage; + +shared static ~this () { + destroy(texImage); +} + +enum vlWidth = 160; +enum vlHeight = 120; +enum scale = 3; +enum scanlines = false; + +enum vlEffectiveWidth = vlWidth*scale; +enum vlEffectiveHeight = vlHeight*scale; + + +// ////////////////////////////////////////////////////////////////////////// // +ubyte clampToByte(T) (T n) pure nothrow @safe @nogc if (__traits(isIntegral, T)) { + //static assert(!__traits(isUnsigned, T), "clampToByte can't process unsigned types"); + static if (__VERSION__ > 2067) pragma(inline, true); + static if (T.sizeof == 2 || T.sizeof == 4) { + static if (__traits(isUnsigned, T)) { + return cast(ubyte)(n&0xff|(255-((-cast(int)(n < 256))>>24))); + } else { + n &= -cast(int)(n >= 0); + return cast(ubyte)(n|((255-cast(int)n)>>31)); + } + } else static if (T.sizeof == 1) { + static assert(__traits(isUnsigned, T), "clampToByte: signed byte? no, really?"); + return cast(ubyte)n; + } else { + static assert(false, "clampToByte: integer too big"); + } +} + + +// ////////////////////////////////////////////////////////////////////////// // +struct Vec3 { + import std.format : FormatSpec; + void toString (scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const @trusted { + import std.format; + sink("Vec3("); + sink.formatValue(x, fmt); + sink(","); + sink.formatValue(y, fmt); + sink(","); + sink.formatValue(z, fmt); + sink(")"); + } + +nothrow @safe @nogc: + float x = 0, y = 0, z = 0; + + @property float opIndex (size_t idx) const pure { + static if (__VERSION__ > 2067) pragma(inline, true); + if (idx > 2) assert(0, "invalid index in Vec3 opindex"); + return (idx == 0 ? x : (idx == 1 ? y : z)); + } + + @property float opIndexAssign (float v, size_t idx) { + static if (__VERSION__ > 2067) pragma(inline, true); + if (idx > 2) assert(0, "invalid index in Vec3 opindex"); + final switch (idx) { + case 0: x = v; break; + case 1: y = v; break; + case 2: z = v; break; + } + return v; + } + + ref Vec3 normalize () { + //pragma(inline, true); + import std.math : sqrt; + immutable invlen = 1.0f/sqrt(x*x+y*y+z*z); + x *= invlen; + y *= invlen; + z *= invlen; + return this; + } + + const pure { + static float length (float x, float y) { + //pragma(inline, true); + import std.math : sqrt; + return sqrt(x*x+y*y); + } + + static float length (float x, float y, float z) { + //pragma(inline, true); + import std.math : sqrt; + return sqrt(x*x+y*y+z*z); + } + + float length () { + //pragma(inline, true); + import std.math : sqrt; + return sqrt(x*x+y*y+z*z); + //return length(x, y, z); + } + + Vec3 normalized () { + //pragma(inline, true); + import std.math : sqrt; + immutable invlen = 1.0f/sqrt(x*x+y*y+z*z); + return Vec3(x*invlen, y*invlen, z*invlen); + } + + Vec3 abs () { + //pragma(inline, true); + import std.math : abs; + return Vec3(abs(x), abs(y), abs(z)); + } + + Vec3 minmax(string op) (in auto ref Vec3 b) if (op == "min" || op == "max") { + //pragma(inline, true); + mixin("import std.algorithm : "~op~";"); + return mixin("Vec3("~op~"(x, b.x), "~op~"(y, b.y), "~op~"(z, b.z))"); + } + + Vec3 minmax(string op) (float v) if (op == "min" || op == "max") { + //pragma(inline, true); + mixin("import std.algorithm : "~op~";"); + return mixin("Vec3("~op~"(x, v), "~op~"(y, v), "~op~"(z, v))"); + } + + /* + alias min = minmax!"min"; + alias max = minmax!"max"; + */ + + Vec3 opBinary(string op) (in auto ref Vec3 b) if (op == "+" || op == "-" || op == "*" || op == "/") { + static if (__VERSION__ > 2067) pragma(inline, true); + return mixin("Vec3(x"~op~"b.x, y"~op~"b.y, z"~op~"b.z)"); + } + + Vec3 opBinary(string op) (in float b) if (op == "+" || op == "-" || op == "*" || op == "/") { + static if (__VERSION__ > 2067) pragma(inline, true); + return mixin("Vec3(x"~op~"b, y"~op~"b, z"~op~"b)"); + } + + // cross product (normal to plane containing a and b) + Vec3 opBinary(string op : "%") (in auto ref Vec3 b) { + static if (__VERSION__ > 2067) pragma(inline, true); + return Vec3(y*b.z-z*b.y, z*b.x-x*b.z, x*b.y-y*b.x); + } + + float dot() (in auto ref Vec3 b) { + static if (__VERSION__ > 2067) pragma(inline, true); + return x*b.x+y*b.y+z*b.z; + } + + float anglev() (in auto ref Vec3 b) { + static if (__VERSION__ > 2067) pragma(inline, true); + import std.math : acos; + return acos(dot(b)/(length*b.length)); + } + + // things like `.xyy` + Vec3 opDispatch(string type) () + if (type.length == 3 && + (type[0] == 'x' || type[0] == 'y' || type[0] == 'z') && + (type[1] == 'x' || type[1] == 'y' || type[1] == 'z') && + (type[2] == 'x' || type[2] == 'y' || type[2] == 'z')) + { + static if (__VERSION__ > 2067) pragma(inline, true); + return mixin("Vec3("~type[0]~","~type[1]~","~type[2]~")"); + } + + /* + auto rotx() (float angle) { + //pragma(inline, true); + float sine = void, cosine = void; + asm pure nothrow @trusted @nogc { + fld angle; + fsincos; + fstp [cosine]; + fstp [sine]; + } + return Vec3(x, y*cosine-z*sine, y*sine+z*cosine); + } + + auto roty() (float angle) { + //pragma(inline, true); + float sine = void, cosine = void; + asm pure nothrow @trusted @nogc { + fld angle; + fsincos; + fstp [cosine]; + fstp [sine]; + } + return Vec3(x*cosine-z*sine, y, x*sine+z*cosine); + } + + auto rotz() (float angle) { + //pragma(inline, true); + float sine = void, cosine = void; + asm pure nothrow @trusted @nogc { + fld angle; + fsincos; + fstp [cosine]; + fstp [sine]; + } + return Vec3(x*cosine-y*sine, x*sine+y*cosine, z); + } + */ + } + + ref Vec3 opOpAssign(string op) (in auto ref Vec3 b) if (op == "+" || op == "-" || op == "*" || op == "/") { + static if (__VERSION__ > 2067) pragma(inline, true); + mixin("x"~op~"=b.x;"); + mixin("y"~op~"=b.y;"); + mixin("z"~op~"=b.z;"); + return this; + } + + //static float smoothstep (float x) pure { static if (__VERSION__ > 2067) pragma(inline, true); return x*x*(3-2*x); } +} + + +// ////////////////////////////////////////////////////////////////////////// // +// raymarch global vars +__gshared float worldtime = 0; // seconds +__gshared Vec3 vrp; // view reference point +__gshared Vec3 vuv; // view up vector +__gshared Vec3 prp; // camera position + + +immutable Vec3 e = Vec3(0.02f, 0.0f, 0.0f); +immutable Vec3 exyy = e.xyy; +immutable Vec3 eyxy = e.yxy; +immutable Vec3 eyyx = e.yyx; + + +float fract() (float x) { + //pragma(inline, true); + import std.math : floor; + return x-floor(x); +} + + +// is this correct? +float mod() (float x, float y) { + //pragma(inline, true); + import std.math : floor; + // x minus the product of y and floor(x/y) + return x-y*floor(x/y); +} + + +// ////////////////////////////////////////////////////////////////////////// // +// raymarch composition functions +// repetition +float objopRep(string objfunc) (in auto ref Vec3 pp, in auto ref Vec3 c) { + static if (__VERSION__ > 2067) pragma(inline, true); + // the following MUST be `p` + immutable auto p = Vec3(mod(p.x, c.x), mod(p.y, c.y), mod(p.z, c.z))-c*0.5f; + return mixin(objfunc); +} + +float objopScale(string objfunc) (in auto ref Vec3 pp, float s) { + static if (__VERSION__ > 2067) pragma(inline, true); + immutable auto p = pp/s; + return mixin(objfunc)*s; +} + +// rotation/translation +/* +Vec3 objopTx(string objfunc) (in auto ref Vec3 pp, in auto ref Mat4 mt) { + immutable auto p = m.invert*pp; + return mixin(objfunc); +} +*/ + +float objopUnion() (float obj0, float obj1) { + static if (__VERSION__ > 2067) pragma(inline, true); + return (obj0 < obj1 ? obj0 : obj1); // min +} + +float objopSub() (float a, float b) { + static if (__VERSION__ > 2067) pragma(inline, true); + return (a > -b ? a : -b); +} + +// intersection +float objopInter() (float a, float b) { + static if (__VERSION__ > 2067) pragma(inline, true); + return (obj0 > obj1 ? obj0 : obj1); // max +} + +// returns object number +int objopUnion() (ref float dest, float obj0, float obj1, int oid0, int oid1) { + static if (__VERSION__ > 2067) pragma(inline, true); + if (obj0 < obj1) { + dest = obj0; + return oid0; + } else { + dest = obj1; + return oid1; + } +} + +// returns object number +int objopSub() (ref float dest, float a, float b, int oid0, int oid1) { + static if (__VERSION__ > 2067) pragma(inline, true); + b = -b; + if (a > b) { + dest = a; + return oid0; + } else { + dest = b; + return oid1; + } +} + +// returns object number +int objopInter() (ref float dest, float obj0, float obj1, int oid0, int oid1) { + static if (__VERSION__ > 2067) pragma(inline, true); + if (obj0 > obj1) { + dest = obj0; + return oid0; + } else { + dest = obj1; + return oid1; + } +} + + +// ////////////////////////////////////////////////////////////////////////// // +// raymarch composition functions that doesn't preserve distance +// you will probably need to decrease your step size, if you are using a raymarcher to sample this +// the displacement example below is using sin(20*p.x)*sin(20*p.y)*sin(20*p.z) as displacement pattern +/* +float objopDisplace(string objfunc) (in auto ref Vec3 p) { + float d1 = mixin(objfunc); + float d2 = displacement(p); + return d1+d2; +} +*/ + + +// exponential smooth min (k = 32); +float sminExp (float a, float b, float k) { + import std.math : exp, log; + float res = exp(-k*a)+exp(-k*b); + return -log(res)/k; +} + +// power smooth min (k = 8); +float sminPow (float a, float b, float k) { + import std.math : pow; + a = pow(a, k); + b = pow(b, k); + return pow((a*b)/(a+b), 1.0f/k); +} + +// polynomial smooth min (k = 0.1); +/* +float sminPoly (float a, float b, float k) { + import std.math : clamp, exp, log; + float h = clamp(0.5f+0.5f*(b-a)/k, 0.0f, 1.0f); + return mix(b, a, h)-k*h*(1.0f-h); +} +*/ + +float objopBlend(alias sminfn, string objfunc0, string objfunc1) (in auto ref Vec3 p) { + float d1 = mixin(objfunc0); + float d2 = mixin(objfunc1); + return sminfn(d1, d2); +} + + +float objopTwist (string objfunc) (in auto ref Vec3 pp) { + import std.math : cos, sin; + immutable float c = cos(10.0f*pp.z+10.0f); + immutable float s = sin(10.0f*pp.z+10.0f); + //immutable auto m = Mat2(c, -s, s, c); + // Mat2 + /* + float[2][2] mm = [ + [c, -s], + [s, c] + ]; + */ + // multiple matrix2 by vector2 + //immutable auto p = Vec3(m*p.xz, p.y); + /* + float v2x = mm.ptr[0].ptr[0]*pp.x+mm.ptr[0].ptr[1]*pp.z; + float v2y = mm.ptr[1].ptr[0]*pp.x+mm.ptr[1].ptr[1]*pp.z; + */ + float v2x = c*pp.x-s*pp.y; + float v2y = s*pp.x+c*pp.y; + immutable auto p = Vec3(v2x, v2y, pp.z); + return mixin(objfunc); +} + + +// ////////////////////////////////////////////////////////////////////////// // +import std.algorithm : maxf = max, minf = min; + +// raymarch object functions +// as our functions are very simple, use strings and `mixin` to simulate inlining ;-) +enum floorHeight = 12.0f; +enum objfuncFloor = q{(p.y+floorHeight)}; + +// n must be normalized (invalid formula, broken by k8) +static immutable planeN = Vec3(0.0f, 0.0f, 0.0f); +enum planeD = 12.0f; +enum objfuncPlane = q{(p.dot(planeN)+planeD)}; + +enum torusOuterRadius = 2.4f; +enum torusRadius = 0.6f; +enum objfuncTorus = q{(Vec3.length(p.length-torusOuterRadius, p.z)-torusRadius)}; + +// sphere, signed +enum shpereRadius = 1.9f; +enum objfuncSphere = q{(p.length-shpereRadius)}; + +// round box, unsigned +static immutable roundboxSize = Vec3(2.0f, 0.7f, 2.0f); +enum roundboxRadius = 0.2f; +enum objfuncRoundbox = q{((p.abs-roundboxSize).minmax!"max"(0.0f).length-roundboxRadius)}; + +// box, signed (interestingly, this is more complex than round box ;-) +static immutable boxSize = Vec3(2.0f, 0.7f, 2.0f); +__gshared Vec3 boxTemp; +enum objfuncBox = q{(boxTemp = p.abs-boxSize, minf(maxf(boxTemp.x, maxf(boxTemp.y, boxTemp.z)), 0.0f)+boxTemp.minmax!"max"(0.0f).length)}; + +// box, unsigned +static immutable boxuSize = Vec3(2.0f, 0.7f, 2.0f); +enum objfuncBoxu = q{((p.abs-boxuSize).minmax!"max"(0.0f).length)}; + +// cylinder, signed +static immutable cylParams = Vec3(0.0f, 0.0f, 0.6f); // x, y, width +enum objfuncCyl = q{(Vec3.length(p.x-cylParams.x, p.z-cylParams.y)-cylParams.z)}; + + +// ////////////////////////////////////////////////////////////////////////// // +// object ids: +enum ObjId : int { + floor, + roundbox, + roundboxInside, + torus, + box, +} + +float mapWorld() (in auto ref Vec3 p, ref int obj) { + //static if (__VERSION__ > 2068) pragma(inline, true); // yep, 2.068 + // we need object number to distinguish floor from other objects + // we can paint objects too by getting their numbers ;-) + // subtraction always gives us roundbox + obj = ObjId.roundbox; + //obj = ObjId.floor; + float res = void; + obj = objopSub(res, mixin(objfuncRoundbox), mixin(objfuncSphere), ObjId.roundbox, ObjId.roundboxInside); + obj = objopUnion(res, mixin(objfuncTorus), res, ObjId.torus, obj); + obj = objopUnion(res, objopTwist!objfuncTorus(p), res, ObjId.torus, obj); + obj = objopUnion(res, mixin(objfuncFloor), res, ObjId.floor, obj); + return res; +} + + +// ////////////////////////////////////////////////////////////////////////// // +// floor color (checkerboard) +Vec3 floorColor() (in auto ref Vec3 p) @nogc { + static if (__VERSION__ > 2067) pragma(inline, true); + Vec3 res = void; + if (fract(p.x*0.2f) > 0.2f) { + if (fract(p.z*0.2f) > 0.2f) { + res.x = 0.0f; + res.y = 0.1f; + res.z = 0.2f; + } else { + res.x = res.y = res.z = 1.0f; + } + } else { + if (fract(p.z*0.2f) > 0.2f) { + res.x = res.y = res.z = 1.0f; + } else { + res.x = 0.3f; + res.y = 0.0f; + res.z = 0.0f; + } + } + return res; +} + + +// ////////////////////////////////////////////////////////////////////////// // +// raymarching +// L: light +void raymarch() (float x, float y, in auto ref Vec3 scp, in auto ref Vec3 L, ref Vec3 color) @nogc { + enum maxd = 100.0f; // max depth + Vec3 c = void, p = void, N = void; + float dx = 0.1f; + int obj = 0; + + float f = 1.0f; + foreach (immutable _; 0..256) { + import std.math : abs; + if (abs(dx) < 0.001 || f > maxd) break; + f += dx; + p = prp+scp*f; + dx = mapWorld(p, obj); + } + + // did we hit something? + if (f < maxd) { + // get primitive color + switch (obj) { + case ObjId.floor: + c = floorColor(p); + break; + case ObjId.roundbox: + c = Vec3(0.0f, 0.8f, 0.0f); + break; + case ObjId.roundboxInside: + c = Vec3(0.0f, 0.3f, 0.0f); + break; + case ObjId.torus: + c = Vec3(0.6f, 0.6f, 0.8f); + break; + default: + // RED + c.x = 1.0f; + c.y = c.z = 0; + { import core.stdc.stdio; printf("WTF?!\n"); } + break; + } + // do lighting + import std.math : pow; + N = Vec3(dx-mapWorld(p-exyy, obj), + dx-mapWorld(p-eyxy, obj), + dx-mapWorld(p-eyyx, obj)).normalize; + float b = N.dot((prp-p+L).normalize); + if (b < 0) { + color.x = color.y = color.z = 0.0f; // background color + color.z = 0.1f; + } else { + // simple phong lighting + color = (c*b+pow(b, 16.0f))*(1.0f-f*0.01f); + } + } else { + color.x = color.y = color.z = 0.0f; // background color + } +} + + +// ////////////////////////////////////////////////////////////////////////// // +// various image parameters +__gshared int nextLineAdjustment; +__gshared int offR; +__gshared int offB; +__gshared int offG; +__gshared int bpp; +__gshared ubyte* imgdata; +__gshared int fpxoffset; + +__gshared Vec3[4] scp; +__gshared Vec3 L; + +__gshared Vec3 grady1v; +__gshared Vec3 grady2v; + +enum TileSize = 32; // tile is 32x32 pixels + + +void prepareRenderer () @nogc { + import std.math : sin, cos; + + // vertex shader common part + auto vpn = (vrp-prp).normalize; + auto u = (vuv%vpn).normalize; + auto v = vpn%u; + auto vcv = prp+vpn; + + //vertex shader for each vertex + immutable float[2][4] vPos = [ + [-1, 1], //0--1 + [ 1, 1], //| | + [ 1, -1], //3--2 + [-1, -1], + ]; + + Vec3 scrCoord = void; // temp + immutable float cxy = cast(float)vlWidth/cast(float)vlHeight; + foreach (immutable i; 0..vPos.length) { + scrCoord.x = vcv.x+vPos.ptr[i].ptr[0]*u.x*cxy+vPos.ptr[i].ptr[1]*v.x; + scrCoord.y = vcv.y+vPos.ptr[i].ptr[0]*u.y*cxy+vPos.ptr[i].ptr[1]*v.y; + scrCoord.z = vcv.z+vPos.ptr[i].ptr[0]*u.z*cxy+vPos.ptr[i].ptr[1]*v.z; + scp.ptr[i] = (scrCoord-prp).normalize; + } + + float y_inc = 1.0f/vlHeight; + + grady1v = (scp.ptr[3]-scp.ptr[0])*y_inc; + grady2v = (scp.ptr[2]-scp.ptr[1])*y_inc; + + L = Vec3(sin(worldtime)*20.0f, 10.0f+sin(worldtime)*20.0f, cos(worldtime)*20.0f); +} + + +uint tilesPerTexture () @nogc { + return ((vlWidth+TileSize-1)/TileSize)*((vlHeight+TileSize-1)/TileSize); +} + + +void tileOfs (uint tile, out int x, out int y) @nogc { + immutable tpx = (vlWidth+TileSize-1)/TileSize; + x = (tile%tpx)*TileSize; + y = (tile/tpx)*TileSize; +} + + +// Adam don't care about @nogc, but image functions actually are +void renderToTexture (uint tile) @nogc { + float x = -0.5f, y = -0.5f, x_inc = 1.0f/vlWidth, y_inc = 1.0f/vlHeight; + + int tofsx, tofsy; + tileOfs(tile, tofsx, tofsy); + //{ import core.stdc.stdio; printf("tile=%u; x=%u; y=%u\n", tile, tofsx, tofsy); } + int ex = tofsx+TileSize; + int ey = tofsy+TileSize; + if (ex > vlWidth) ex = vlWidth; + if (ey > vlHeight) ey = vlHeight; + + x += x_inc*tofsx; + y += y_inc*tofsy; + + auto accy1v = scp.ptr[0]+grady1v*tofsy; + auto accy2v = scp.ptr[1]+grady2v*tofsy; + + Vec3 colorout = void; + Vec3 gradxv = void, accxv = void; + + auto offset = fpxoffset+tofsy*scale*nextLineAdjustment+tofsx*scale*bpp; + auto startOfLine = imgdata+offset; // get our pointer lined up on the first pixel + + float oldx = x; + foreach (int iy; tofsy..ey) { + auto vbptr = startOfLine; // we keep the start of line separately so moving to the next line is simple and portable + startOfLine += nextLineAdjustment*scale; + gradxv = (accy2v-accy1v)*x_inc; + accxv = accy1v+gradxv*tofsx; + foreach (int ix; tofsx..ex) { + raymarch(x, y, accxv, L, colorout); + ubyte r = void, g = void, b = void; + if (colorout.x < 0 || colorout.y < 0 || colorout.z < 0) { + r = g = b = 0; + } else { + r = clampToByte(cast(int)(colorout.x*255.0f)); + g = clampToByte(cast(int)(colorout.y*255.0f)); + b = clampToByte(cast(int)(colorout.z*255.0f)); + } + static if (scale > 1) { + foreach (immutable _; 0..scale) { + vbptr[offR] = r; + vbptr[offG] = g; + vbptr[offB] = b; + static if (!scanlines) { + auto vbptrv = vbptr; + foreach (immutable _1; 1..scale) { + vbptrv += nextLineAdjustment; + vbptrv[offR] = r; + vbptrv[offG] = g; + vbptrv[offB] = b; + } + } + vbptr += bpp; + } + } else { + vbptr[offR] = r; + vbptr[offG] = g; + vbptr[offB] = b; + vbptr += bpp; + } + x += x_inc; + accxv += gradxv; + } + accy1v += grady1v; + accy2v += grady2v; + y += y_inc; + //x = -0.5f; + x = oldx; + } +} + + +// ////////////////////////////////////////////////////////////////////////// // +__gshared MonoTime sttime; + +void animate () @nogc { + __gshared bool firstTime = true; + if (firstTime) { + sttime = MonoTime.currTime; + firstTime = false; + } + + import std.math : sin, cos; + + worldtime = cast(float)(MonoTime.currTime-sttime).total!"msecs"/1000.0f; + + Vec3 auto_vuv = void, auto_vrp = void, auto_prp = void; + + // view up vector + auto_vuv.x = 0; //sin(worldtime/*timemsecs*/); + auto_vuv.y = 1; + auto_vuv.z = 0; + + // view reference point + auto_vrp.x = 0; //sin(time*0.7f)*10.0f; + auto_vrp.y = 0; + auto_vrp.z = 0; //cos(time*0.9f)*10.0f; + + // camera position + auto_prp.x = 3.0f; //sin(time*0.7f)*20.0f+auto_vrp.x+20.0f; + auto_prp.y = 3.0f; //sin(time)*4.0f+4.0f+auto_vrp.y+3.0f; + auto_prp.z = 3.0f; //cos(time*0.6f)*20.0f+auto_vrp.z+14.0f; + + vuv = auto_vuv; + vrp = auto_vrp; + prp = auto_prp; +} + + +// ////////////////////////////////////////////////////////////////////////// // +void tileRenderFunc (Tid ownerTid, uint wkid) { + bool exit = false; + uint tnum; + while (!exit) { + version(render_debug) { import core.stdc.stdio; printf(" worker %u idle...\n", wkid); } + receive( + (uint tile) { + tnum = tile; + if (tile == uint.max) exit = true; + }, + ); + if (exit) break; + version(render_debug) { import core.stdc.stdio; printf(" worker %u got tile %u\n", wkid, tnum); } + renderToTexture(tnum); + version(render_debug) { import core.stdc.stdio; printf(" worker %u completed tile %u\n", wkid, tnum); } + ownerTid.send(wkid, 1); + } + ownerTid.send(wkid, 666); +} + + +// ////////////////////////////////////////////////////////////////////////// // +enum ThreadCount = 4; +__gshared Tid[ThreadCount] wkTids; +__gshared bool[ThreadCount] wkFree; +__gshared uint wkFreeCount = 0; + + +void spawnWorkers () { + foreach (uint idx; 0..ThreadCount) { + wkTids[idx] = spawn(&tileRenderFunc, thisTid, idx); + wkFree[idx] = true; + ++wkFreeCount; + } +} + + +void renderThreadFunc () { + static void waitForFreeWorker () { + receive( + (uint wkid, int id) { + wkFree[wkid] = true; + ++wkFreeCount; + version(render_debug) { import core.stdc.stdio; printf(" worker %u added to free pool (%u free workers now)\n", wkid, wkFreeCount); } + }, + (Variant v) { + { import core.stdc.stdio; printf(" FUUUUUUUUUUUUUUU\n"); } + } + ); + } + + spawnWorkers(); + for (;;) { + if (atomicLoad(renderDie) == 1) break; + synchronized(mutexCondCanRender) waitCondCanRender.wait(); + version(render_debug) { import core.stdc.stdio; printf("render job started (%u free workers, %u tiles)...\n", wkFreeCount, tilesPerTexture); } + auto cft = MonoTime.currTime; + animate(); + prepareRenderer(); + // pass tiles to threads + uint tile = 0, tileDone = 0, tcount = tilesPerTexture; + while (tileDone < tcount) { + // collect free threads + if (tile >= tcount || wkFreeCount == 0) { + // nothing to do, just wait + version(render_debug) { import core.stdc.stdio; printf(" %u of %u tiles done; %u workers still busy\n", tileDone, tcount, ThreadCount-wkFreeCount); } + waitForFreeWorker(); + ++tileDone; + } + if (tile < tcount) { + // dispatch next tile + foreach (uint idx; 0..ThreadCount) { + if (wkFree[idx]) { + version(render_debug) { import core.stdc.stdio; printf(" dispatching tile %u to worker %u\n", tile, idx); } + wkFree[idx] = false; + --wkFreeCount; + wkTids[idx].send(tile); + ++tile; + break; + } + } + } + } + version(render_debug) { import core.stdc.stdio; printf("render job complete\n"); } + atomicStore(frameDur, cast(uint)(MonoTime.currTime-cft).total!"msecs"); + atomicStore(renderComplete, true); + } + // kill all threads + foreach (uint idx; 0..ThreadCount) wkTids[idx].send(uint.max); + foreach (uint idx; 0..ThreadCount) { + receive( + (uint wkid, int id) {} + ); + } + atomicStore(renderDie, 2); +} + + +// ////////////////////////////////////////////////////////////////////////// // +__gshared string aftstr; // average frame time + +void blitScene () { + if (sdwindow.closed) return; + auto painter = sdwindow.draw(); + painter.drawImage(Point(0, 0), texImage); + + painter.outlineColor = Color.white; + painter.drawText(Point(10, 10), aftstr); +} + + +__gshared Thread renderThread; + +void main (string[] args) { + texImage = new Image(vlEffectiveWidth, vlEffectiveHeight); + + nextLineAdjustment = texImage.adjustmentForNextLine(); + offR = texImage.redByteOffset(); + offB = texImage.blueByteOffset(); + offG = texImage.greenByteOffset(); + bpp = texImage.bytesPerPixel(); + imgdata = texImage.getDataPointer(); + fpxoffset = texImage.offsetForTopLeftPixel(); + + { import core.stdc.stdio; printf("%u tiles per image\n", tilesPerTexture()); } + + animate(); + prepareRenderer(); + foreach (uint tile; 0..tilesPerTexture) renderToTexture(tile); + + sdwindow = new SimpleWindow(vlEffectiveWidth, vlEffectiveHeight, "RayMarching demo", OpenGlOptions.no, Resizablity.fixedSize); + blitScene(); + + enum MSecsPerFrame = 1000/60; /* 60 is FPS */ + + renderThread = new Thread(&renderThreadFunc); + renderThread.start(); + + uint[6] frameTimes = 0; + + sdwindow.eventLoop(MSecsPerFrame, + delegate () { + if (sdwindow.closed) return; + if (atomicLoad(renderComplete)) { + // do average frame time + uint aft = 0; + foreach (immutable idx; 1..frameTimes.length) { + aft += frameTimes.ptr[idx]; + frameTimes.ptr[idx-1] = frameTimes.ptr[idx]; + } + frameTimes[$-1] = atomicLoad(frameDur); + aft += frameTimes[$-1]; + aft /= cast(uint)frameTimes.length; + if (aft == 0) aft = 1000; + { + import std.string; + aftstr = "%s FPS".format(1000/aft); + } + //version(show_frame_time) { import core.stdc.stdio; printf("%u msecs per frame\n", frameDur); } + blitScene(); + atomicStore(renderComplete, false); + synchronized(mutexCondCanRender) waitCondCanRender.notify(); + } + }, + delegate (KeyEvent event) { + if (sdwindow.closed) return; + if (event.key == Key.Escape) { + flushGui(); + sdwindow.close(); + } + }, + delegate (MouseEvent event) { + }, + delegate (dchar ch) { + }, + ); + synchronized(mutexCondCanRender) waitCondCanRender.notify(); // just in case + atomicStore(renderDie, 1); // die + while (atomicLoad(renderDie) != 2) {} + if (!sdwindow.closed) { + flushGui(); + sdwindow.close(); + } + flushGui(); +} -- 2.11.4.GIT