<> <> DIRECTORY BasicTime USING [PulsesToSeconds, GetClockPulses], Atom USING [PutPropOnList, GetPropFromList, GetPName], RuntimeError USING [UNCAUGHT], CedarProcess USING [Fork, ForkableProc, Join, Process, Status], Process USING [Pause, SecondsToTicks], ComputeServerClient USING [StartService, RemoteSuccess], IO USING [STREAM, PutRope], Rope USING [ROPE, Cat, Equal], Convert USING [RopeFromReal, RopeFromCard], Real USING [FixC, RoundC, Float], Vector2 USING [ VEC ], Imager USING [ Rectangle ], ImagerColor USING [ RGB ], Pixels USING [ PixelBuffer, Extent, Copy ], Vector3d USING [ Pair, Triple, Quad ], Matrix3d USING [ Matrix ], ThreeDScenes USING [ Box, ClipState, Context, Create, Error, FillInBackGround, FillViewPort, GetShading, SetWindow, SetViewPort, ShadingProcs, ShapeInstance, ShapeSequence, SixSides, Vertex, VtxToRealSeqProc, XfmToDisplay, XfmToEyeSpace ], ScanConvert USING [ GetColorProc ], TextureMaps USING [ TextureMap ], SolidTextures USING [ ProcToRope ], AISAnimation USING [ GetAIS ], ThreeDMisc USING [ StartLog, PrependWorkingDirectory, FlushLog]; DistributedDisplayImpl: CEDAR PROGRAM IMPORTS Pixels, ThreeDScenes, Real, IO, AISAnimation, RuntimeError, CedarProcess, Process, ComputeServerClient, Atom, Rope, Convert, BasicTime, ThreeDMisc, SolidTextures EXPORTS ThreeDMisc ~ BEGIN <> Context: TYPE ~ ThreeDScenes.Context; Pair: TYPE ~ Vector3d.Pair; -- RECORD [ x, y: REAL]; Triple: TYPE ~ Vector3d.Triple; RGB: TYPE ~ ImagerColor.RGB; Rectangle: TYPE ~ Pixels.Extent; NatSequence: TYPE ~ RECORD [ SEQUENCE length: NAT OF NAT ]; IntSequence: TYPE ~ RECORD [ SEQUENCE length: NAT OF INT ]; tooManyTries: NAT _ 16; -- number of retries before process gives up pauseTime: NAT _ 10; -- time to wait before retry when no processors available <> ElapsedTime: PROC[startTime: REAL] RETURNS[Rope.ROPE] ~ { RETURN[ Rope.Cat[ Convert.RopeFromCard[ Real.FixC[BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] - startTime] ], " secs. " ] ]; }; CurrentTime: PROC[] RETURNS[REAL] ~ { RETURN[ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[]] ]; }; <> RemoteMakeFrame: PUBLIC PROC[context: REF ThreeDScenes.Context, numForksHint: NAT _ 0] ~ { <> bufferContext: REF ThreeDScenes.Context; log: IO.STREAM _ NARROW[ Atom.GetPropFromList[context.props, $Log] ]; shape: REF ThreeDScenes.ShapeSequence _ context.shapes; numShapes: NAT _ shape.length - context.lights.length; shapeOrder: REF NatSequence _ NEW[NatSequence[numShapes]]; startTime: REAL _ CurrentTime[]; IF log = NIL THEN log _ ThreeDMisc.StartLog[context]; -- open log file if not yet done <> FOR i: NAT IN [0.. shape.length) DO IF Atom.GetPropFromList[shape[i].props, $Hidden] = NIL OR shape[i].type = $Light THEN IF shape[i].vtcesInValid THEN { shape[i].clipState _ ThreeDScenes.XfmToEyeSpace[context, shape[i]]; IF shape[i].clipState # out THEN ThreeDScenes.XfmToDisplay[context, shape[i] ]; }; ENDLOOP; <> numShapes _ SortAndForkProcs[context, shapeOrder, startTime, log, numForksHint]; <> ThreeDScenes.FillViewPort[context, [0.0, 0.0, 0.0] ]; -- clear all pixel bits ThreeDScenes.FillInBackGround[context]; -- load background bufferContext _ ThreeDScenes.Create[ -- get buffer width: Real.FixC[context.viewPort.w], height: Real.FixC[context.viewPort.h], renderMode: $FullClr, alpha: TRUE ]; <<>> <> FOR i: NAT DECREASING IN [0..numShapes) DO procList: LIST OF CedarProcess.Process _ NARROW[ Atom.GetPropFromList[ context.shapes[shapeOrder[i]].props, $RemoteProc ] ]; baseName: Rope.ROPE _ context.shapes[shapeOrder[i]].name; baseScreenExtent: ThreeDScenes.Box _ context.shapes[shapeOrder[i]].screenExtent; j: NAT _ 0; FOR procs: LIST OF CedarProcess.Process _ procList, procs.rest UNTIL procs = NIL DO shapeName: Rope.ROPE _ Rope.Cat[baseName, Convert.RopeFromCard[j]]; results: REF; msg: Rope.ROPE; status: CedarProcess.Status; [status, results] _ CedarProcess.Join[procs.first]; -- await termination msg _ NARROW[results, Rope.ROPE]; SELECT status FROM done => { doneTime: REAL _ CurrentTime[]; context.shapes[shapeOrder[i]].name _ shapeName; DisplayRemoteResult[ context, bufferContext, context.shapes[shapeOrder[i]], log ]; msg _ Rope.Cat[msg, shapeName, " matted in ", ElapsedTime[doneTime] ]; log.PutRope[Rope.Cat[msg, " done at ", ElapsedTime[startTime], "\n" ] ]; }; ENDCASE => SIGNAL ThreeDScenes.Error[[$Condition, "Forked process failed"]]; j _ j + 1; ENDLOOP; context.shapes[shapeOrder[i]].name _ baseName; context.shapes[shapeOrder[i]].screenExtent _ baseScreenExtent ENDLOOP; log.PutRope[Rope.Cat[" All done at ", ElapsedTime[startTime], "\n\n" ] ]; ThreeDMisc.FlushLog[context]; }; SortAndForkProcs: PROC[context: REF ThreeDScenes.Context, shapeOrder: REF NatSequence, startTime: REAL, log: IO.STREAM, numForksHint: NAT] RETURNS [NAT] ~ { <> costFactor: REF IntSequence _ NEW[IntSequence[context.shapes.length]]; totalCost, averageCost: INT _ 0; shape: REF ThreeDScenes.ShapeSequence _ context.shapes; shapesFound: NAT _ 0; numShapes: NAT _ context.shapes.length - context.lights.length; FOR i: NAT IN [0..context.shapes.length) DO IF shape[i] # NIL AND Atom.GetPropFromList[shape[i].props, $Hidden] = NIL THEN IF shape[i].clipState # out AND shape[i].surface # NIL THEN { <> costFactor[i] _ INT[(shape[i].screenExtent.right - shape[i].screenExtent.left)] * (shape[i].screenExtent.top - shape[i].screenExtent.bottom); IF ThreeDScenes.GetShading[shape[i], $Transmittance] # NIL AND shape[i].insideVisible THEN costFactor[i] _ costFactor[i] * 2; IF ThreeDScenes.GetShading[ shape[i], $TextureMap ] # NIL THEN costFactor[i] _ costFactor[i] * 2; IF ThreeDScenes.GetShading[ shape[i], $ShadingProcs ] # NIL THEN costFactor[i] _ costFactor[i] * 2; -- may be much higher <> FOR j: NAT DECREASING IN [0..shapesFound) DO IF shape[i].centroid.ez < shape[shapeOrder[j]].centroid.ez THEN { shapeOrder[j+1] _ shapeOrder[j]; shapeOrder[j] _ i; } ELSE { shapeOrder[j+1] _ i; EXIT; }; ENDLOOP; IF shapesFound = 0 THEN shapeOrder[0] _ i; shapesFound _ shapesFound + 1; }; ENDLOOP; log.PutRope[Rope.Cat["\nObject transform and sort: ", ElapsedTime[startTime], "\n" ]]; <> FOR i: NAT IN [0..shapesFound) DO totalCost _ totalCost + costFactor[shapeOrder[i]]; ENDLOOP; averageCost _ totalCost / shapesFound; IF shapesFound < numForksHint -- allows division of single-object scenes THEN averageCost _ averageCost / (numForksHint / shapesFound); -- note DIV fn <> FOR i: NAT DECREASING IN [0..shapesFound) DO -- do back to front wholeWindow: Imager.Rectangle _ context.window; wholeViewPort: Imager.Rectangle _ context.viewPort; baseName: Rope.ROPE _ context.shapes[shapeOrder[i]].name; screenExtent: ThreeDScenes.Box _ context.shapes[shapeOrder[i]].screenExtent; procList: LIST OF CedarProcess.Process _ NIL; imageCount: NAT _ Real.RoundC[ Real.Float[ costFactor[shapeOrder[i]] ] / averageCost ]; IF imageCount < 1 THEN imageCount _ 1; FOR j: NAT IN [0..imageCount) DO cmd: Rope.ROPE; <> viewPortSize: REAL _ MAX[wholeViewPort.w, wholeViewPort.h]; vuPtWindow: Imager.Rectangle _ [ x: MAX[ 0.0, wholeViewPort.h - wholeViewPort.w ] / viewPortSize, y: MAX[ 0.0, wholeViewPort.w - wholeViewPort.h ] / viewPortSize, w: 1.0, h: 1.0 ]; left: REAL _ screenExtent.left + (Real.Float[j] / imageCount) * (screenExtent.right - screenExtent.left); right: REAL _ screenExtent.left + (Real.Float[j+1] / imageCount) * (screenExtent.right - screenExtent.left); top: REAL _ screenExtent.top; bottom: REAL _ screenExtent.bottom; left _ left - 1; -- add one pixel border on left right _ right + 1; -- add one pixel border on right ThreeDScenes.SetWindow[ context, [ x: (2.0 * left / viewPortSize) - 1.0 + vuPtWindow.x, -- window range is -1.0  +1.0 y: (2.0 * bottom / viewPortSize) - 1.0 + vuPtWindow.y, w: 2.0 * (right - left) / viewPortSize, h: 2.0 * (top - bottom) / viewPortSize ] ]; ThreeDScenes.SetViewPort[ context, [left, bottom, right - left, top - bottom]]; context.shapes[shapeOrder[i]].name _ Rope.Cat[ baseName, Convert.RopeFromCard[j] ]; <> cmd _ RopeFromShapeInstance[ context, context.shapes[shapeOrder[i]] ]; <> procList _ CONS[ CedarProcess.Fork[StartRemote, cmd ], procList]; -- fork process ENDLOOP; context.shapes[shapeOrder[i]].props _ Atom.PutPropOnList[ -- store proc refs with shape context.shapes[shapeOrder[i]].props, $RemoteProc, procList -- leave list of proc refs here ]; context.shapes[shapeOrder[i]].name _ baseName; ThreeDScenes.SetWindow[ context, wholeWindow]; ThreeDScenes.SetViewPort[ context, wholeViewPort]; ENDLOOP; RETURN[shapesFound]; }; DisplayRemoteResult: PROC[context, bufferContext: REF ThreeDScenes.Context, shape: REF ThreeDScenes.ShapeInstance, log: IO.STREAM] ~ { width, height: NAT; xOffset: NAT _ shape.screenExtent.left -1; yOffset: NAT _ shape.screenExtent.bottom; [width, height] _ AISAnimation.GetAIS[ -- read image into buffer context: bufferContext, fileRoot: ThreeDMisc.PrependWorkingDirectory[context, Rope.Cat[shape.name, ".ais"] ], xOffset: xOffset, yOffset: yOffset, center: FALSE ! RuntimeError.UNCAUGHT => { log.PutRope[Rope.Cat[" Failed to load - ", shape.name, "\n"] ]; GO TO GiveUp; } ]; shape.screenExtent.left _ shape.screenExtent.left + width -2; -- for next slice (minus border) Pixels.Copy[ -- write over previous objects, etc. destination: context.display, source: bufferContext.display, destArea: [xOffset+1, yOffset, width-2, height], srcArea: [xOffset+1, yOffset, width-2, height], op: $WriteOver ]; EXITS GiveUp => NULL }; StartRemote: CedarProcess.ForkableProc ~ { <> retries: NAT _ 0; startTime: REAL _ CurrentTime[]; found: BOOLEAN; success: ComputeServerClient.RemoteSuccess _ false; returnMsg, successMsg, procMsg, server: Rope.ROPE _ NIL; WHILE success # true AND success # aborted AND retries < tooManyTries DO serversTried: LIST OF Rope.ROPE _ NIL; WaitOnRetry: PROC [server: Rope.ROPE] ~ { FOR servers: LIST OF Rope.ROPE _ serversTried, servers.rest UNTIL servers = NIL DO IF Rope.Equal[ servers.first, server, FALSE ] THEN { -- has server rejected us before? Process.Pause[ Process.SecondsToTicks[pauseTime] ]; -- wait decent interval serversTried _ NIL; RETURN[]; }; ENDLOOP; serversTried _ CONS[ server, serversTried ]; }; [ found: found, success: success, remoteMsg: procMsg, serverInstance: server ] _ ComputeServerClient.StartService[ service: "RenderOneShape", cmdLine: NARROW[ data, Rope.ROPE ], in: NIL, out: NIL ]; SELECT success FROM true => {}; false => { IF ~found THEN successMsg _ "compute server command not found" ELSE successMsg _ "compute server success was false (Computer Server bug?)"; }; aborted => successMsg _ "compute server command aborted"; communicationFailure => successMsg _ "communicationFailure"; cantImportController => successMsg _ "cantImportController"; cantImportServer => successMsg _ "cantImportServer"; serverTooBusy => { successMsg _ "serverTooBusy"; WaitOnRetry[server]; } ENDCASE => successMsg _ "unknown error code"; successMsg _ Rope.Cat["Server: ", server, " - ", successMsg]; IF procMsg # NIL THEN successMsg _ Rope.Cat[successMsg, "\nProcess Message: ", procMsg, "\n"]; returnMsg _ Rope.Cat[returnMsg, successMsg]; retries _ retries + 1; IF retries = tooManyTries AND success # true THEN successMsg _ Rope.Cat[successMsg, "\t\tMaximum number of tries"]; ENDLOOP; returnMsg _ Rope.Cat[returnMsg, " took ", ElapsedTime[startTime] ]; RETURN[returnMsg]; }; RopeFromShapeInstance: PROC[ context: REF ThreeDScenes.Context, shape: REF ThreeDScenes.ShapeInstance ] RETURNS[ Rope.ROPE ] ~ { Vec3toRope: PROC[ r1, r2, r3: REAL] RETURNS[Rope.ROPE] ~ { rope: Rope.ROPE; rope _ Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2] ]; rope _ Rope.Cat[ rope, " ", Convert.RopeFromReal[r3], " " ]; RETURN[ rope ]; }; Vec4toRope: PROC[ r1, r2, r3, r4: REAL] RETURNS[Rope.ROPE] ~ { rope: Rope.ROPE; rope _ Rope.Cat[ " ", Convert.RopeFromReal[r1], " ", Convert.RopeFromReal[r2], " " ]; rope _ Rope.Cat[ rope, Convert.RopeFromReal[r3], " ", Convert.RopeFromReal[r4], " " ]; RETURN[ rope ]; }; ref: REF ANY _ NIL; shadingType: ATOM _ NIL; color: REF RGB _ NIL; shininess, transmittance: REF REAL _ NIL; texture: REF TextureMaps.TextureMap _ NIL; getVtxProc: ThreeDScenes.VtxToRealSeqProc _ NIL; getColorProc: ScanConvert.GetColorProc _ NIL; xfm: Matrix3d.Matrix _ shape.position; clip: ARRAY ThreeDScenes.SixSides OF Vector3d.Quad _ context.clippingPlanes; cmd: Rope.ROPE _ " "; <> cmd _ Rope.Cat[cmd, " Shape: ", shape.fileName, " ", Atom.GetPName[shape.type]]; IF shape.insideVisible THEN cmd _ Rope.Cat[cmd, " TRUE "] ELSE cmd _ Rope.Cat[cmd, " FALSE "]; <> color _ NARROW[ThreeDScenes.GetShading[ shape, $Color ], REF RGB]; shadingType _ NARROW[ThreeDScenes.GetShading[ shape, $Type ], ATOM]; IF shadingType = $Smooth AND color # NIL THEN cmd _ Rope.Cat[cmd, " SmoothColor: ", Vec3toRope[color.R, color.G, color.B] ]; IF shadingType = $Faceted AND color # NIL THEN cmd _ Rope.Cat[cmd, " FacetedColor: ", Vec3toRope[color.R, color.G, color.B] ]; <> transmittance _ NARROW[ThreeDScenes.GetShading[ shape, $Transmittance ], REF REAL ]; IF transmittance # NIL THEN cmd _ Rope.Cat[cmd, " Transmittance: ", Convert.RopeFromReal[transmittance^] ]; <> shininess _ NARROW[ThreeDScenes.GetShading[ shape, $Shininess ], REF REAL ]; IF shininess # NIL THEN cmd _ Rope.Cat[cmd, " Shininess: ", Convert.RopeFromReal[shininess^] ]; <> texture _ NARROW[ ThreeDScenes.GetShading[ shape, $TextureMap ], REF TextureMaps.TextureMap ]; IF texture # NIL THEN { fileName: Rope.ROPE _ NARROW[ Atom.GetPropFromList[texture.props, $FileName] ]; coordType: ATOM _ NARROW[ Atom.GetPropFromList[texture.props, $CoordType] ]; coords: REF _ Atom.GetPropFromList[texture.props, $Coords]; IF coordType = NIL THEN coordType _ $NoCoords; cmd _ Rope.Cat[cmd, " MapTexture: ", fileName, " ", Atom.GetPName[texture.type] ]; cmd _ Rope.Cat[cmd, " ", Atom.GetPName[coordType], " " ]; SELECT coordType FROM $FromVtxNos, $FromNormals => { argList: LIST OF REAL _ NARROW[coords]; WHILE argList # NIL DO cmd _ Rope.Cat[cmd, Convert.RopeFromReal[argList.first], " " ]; argList _ argList.rest; ENDLOOP; }; $File => cmd _ Rope.Cat[cmd, " ", NARROW[coords, Rope.ROPE] ]; ENDCASE => SIGNAL ThreeDScenes.Error[[$MisMatch, "Bad texture coordinate type"]]; }; <> ref _ ThreeDScenes.GetShading[ shape, $ShadingProcs]; IF ref # NIL THEN { procName: Rope.ROPE; [getVtxProc, getColorProc] _ NARROW[ ref, REF ThreeDScenes.ShadingProcs ]^; IF getColorProc # NIL THEN { procName _ SolidTextures.ProcToRope[getColorProc]; cmd _ Rope.Cat[cmd, " SolidTexture: ", procName, " " ]; }; }; <> FOR i: NAT IN [0..context.lights.length) DO color _ NARROW[ThreeDScenes.GetShading[ context.lights[i], $Color ], REF RGB]; cmd _ Rope.Cat[cmd, " Light: ", context.lights[i].name, Vec3toRope[ context.lights[i].location.x, context.lights[i].location.y, context.lights[i].location.z ], Vec3toRope[color.R, color.G, color.B] ]; ENDLOOP; <> cmd _ Rope.Cat[cmd, " ShapeXfm: ", Vec4toRope[xfm[0][0], xfm[0][1], xfm[0][2], xfm[0][3]], Vec4toRope[xfm[1][0], xfm[1][1], xfm[1][2], xfm[1][3]] ]; cmd _ Rope.Cat[cmd, Vec4toRope[xfm[2][0], xfm[2][1], xfm[2][2], xfm[2][3]], Vec4toRope[xfm[3][0], xfm[3][1], xfm[3][2], xfm[3][3]] ]; <> cmd _ Rope.Cat[cmd, " View: ", Vec3toRope[context.eyePoint.x, context.eyePoint.y, context.eyePoint.z], Vec3toRope[context.ptOfInterest.x, context.ptOfInterest.y, context.ptOfInterest.z], Vec3toRope[context.upDirection.x, context.upDirection.y, context.upDirection.z] ]; cmd _ Rope.Cat[cmd, Vec4toRope[ context.rollAngle, context.fieldOfView, context.hitherLimit, context.yonLimit ] ]; <> cmd _ Rope.Cat[cmd, " Window: ", Vec4toRope[context.window.x, context.window.y, context.window.w, context.window.h] ]; <> cmd _ Rope.Cat[cmd, " ViewPort: ", -- ViewPort Vec4toRope[context.viewPort.x, context.viewPort.y, context.viewPort.w, context.viewPort.h] ]; <> cmd _ Rope.Cat[cmd, " ResultFile: ", ThreeDMisc.PrependWorkingDirectory[context, Rope.Cat[shape.name, ".ais"] ] ]; RETURN[cmd]; }; END.