private void FinishBakeJob()
        {
            var pixels = _currentBakeJob.Finish();

            int pixelCount = _currentBakeJob.Resolution * _currentBakeJob.Resolution;
            var arraySize  = 4 * 3 * pixelCount;

            arraySize += 4 * 4;


            var builder = new FlatBufferBuilder(arraySize);

            WorkloadComplete.StartResultsVector(builder, pixelCount);
            foreach (var color in pixels)
            {
                Vec3.CreateVec3(builder, color.r, color.g, color.b);
            }
            var resultsVectorOffset = builder.EndVector();

            WorkloadComplete.StartWorkloadComplete(builder);
            WorkloadComplete.AddResults(builder, resultsVectorOffset);
            WorkloadComplete.AddWorkloadID(builder, _currentBakeJob.JobID);
            var messageDataOffset = WorkloadComplete.EndWorkloadComplete(builder);

            Message.StartMessage(builder);
            Message.AddData(builder, messageDataOffset.Value);
            Message.AddDataType(builder, MessageDatum.WorkloadComplete);
            var messageOffset = Message.EndMessage(builder);

            builder.Finish(messageOffset.Value);
            var byteArray = builder.SizedByteArray();

            _currentBakeJob = null;
            _outgoingMessages.Enqueue(byteArray);
        }
        private void ProcessNewMessages()
        {
            while (_incommingMessages.TryDequeue(out Message message))
            {
                switch (message.DataType)
                {
                case MessageDatum.WorkloadRequest:
                    ProcessWorkloadRequest(message);
                    break;

                case MessageDatum.CancelWorkload:
                    ProcessCancelWorkload(message);
                    break;

                case MessageDatum.ShutdownMessage:
                    if (_currentBakeJob != null)
                    {
                        _currentBakeJob.CancelJob();
                    }

                    this._cancleQueue.Clear();
                    this._currentBakeJob = null;

                    while (_incommingMessages.Count > 0)
                    {
                        _incommingMessages.TryDequeue(out Message messageT);
                    }

                    _jobIDs.Clear();
                    _jobs.Clear();
                    _isShuttingDown = true;

                    break;
                }
            }
        }
        private BakeJob FilloutBakeJob(WorkloadRequest workloadRequest)
        {
            var bakeJob = new BakeJob();

            bakeJob.Processor = this;

            bakeJob.BounceCount   = workloadRequest.BounceCount;
            bakeJob.JobID         = workloadRequest.WorkloadID;
            bakeJob.ComputeShader = ComputeShader;

            var vec3 = workloadRequest.LightSourceForwardDir.Value;

            bakeJob.LightSourceForward = new Vector4(vec3.X, vec3.Y, vec3.Z, 0.0f);

            vec3 = workloadRequest.LightSourceUpwardDir.Value;
            bakeJob.LightSourceUpward = new Vector4(vec3.X, vec3.Y, vec3.Z, 0.0f);

            var vector3 = Vector3.Cross(bakeJob.LightSourceUpward, bakeJob.LightSourceForward);

            bakeJob.LightSourceRightward = new Vector4(vector3.x, vector3.y, vector3.z, 0.0f);

            vec3 = workloadRequest.LightSourcePosition.Value;
            bakeJob.LightSourcePosition = new Vector4(vec3.X, vec3.Y, vec3.Z, 1.0f);

            bakeJob.LightSourceTheta = workloadRequest.LightSourceThetaRads;

            bakeJob.MaxRange = workloadRequest.MaxRange;
            bakeJob.MinRange = workloadRequest.MinRange;

            bakeJob.Resolution       = workloadRequest.Resolution;
            bakeJob.SampleCount      = workloadRequest.SampleCount;
            bakeJob.ShadowFocusPlane = workloadRequest.ShadowFocusPlane;

            bakeJob.Indices = workloadRequest.GetIndicesArray();

            var arraySize = workloadRequest.VerticesLength;

            bakeJob.Vertices = new Vector3[arraySize];
            for (int i = 0; i < arraySize; i++)
            {
                vec3 = workloadRequest.Vertices(i).Value;
                bakeJob.Vertices[i] = new Vector3(vec3.X, vec3.Y, vec3.Z);
            }

            arraySize          = workloadRequest.ObjectDataLength;
            bakeJob.ObjectData = new ObjectMeshDatum[arraySize];
            for (int i = 0; i < arraySize; i++)
            {
                var ipc = workloadRequest.ObjectData(i).Value;

                var objectDatum = new ObjectMeshDatum();

                objectDatum.IndicesCount   = ipc.IndicesCount;
                objectDatum.IndicesOffset  = ipc.IndicesOffset;
                objectDatum.VerticesOffset = ipc.VerticesOffset;

                objectDatum.BoundingBox = new AABB_Bounds();

                var ipcBounds = ipc.Bounds;
                vec3 = ipcBounds.Center;
                objectDatum.BoundingBox.Center = new Vector3(vec3.X, vec3.Y, vec3.Z);
                vec3 = ipcBounds.Extent;
                objectDatum.BoundingBox.Extent = new Vector3(vec3.X, vec3.Y, vec3.Z);

                var ipcLTWM = ipc.LocalToWorldMatrix;
                objectDatum.LocalToWorldMatrix = new UnityEngine.Matrix4x4();

                objectDatum.LocalToWorldMatrix.SetRow(0, new Vector4(ipcLTWM.M00, ipcLTWM.M01, ipcLTWM.M02, ipcLTWM.M03));
                objectDatum.LocalToWorldMatrix.SetRow(1, new Vector4(ipcLTWM.M10, ipcLTWM.M11, ipcLTWM.M12, ipcLTWM.M13));
                objectDatum.LocalToWorldMatrix.SetRow(2, new Vector4(ipcLTWM.M20, ipcLTWM.M21, ipcLTWM.M22, ipcLTWM.M23));
                objectDatum.LocalToWorldMatrix.SetRow(3, new Vector4(ipcLTWM.M30, ipcLTWM.M31, ipcLTWM.M32, ipcLTWM.M33));

                bakeJob.ObjectData[i] = objectDatum;
            }

            return(bakeJob);
        }
        private void Update()
        {
            if (_isShuttingDown)
            {
                if (_udpBackgoundMessenger.IsBusy)
                {
                    return;
                }
                else
                {
                    Application.Quit(0);
                }
            }

            // Process any new messages that came in since the last update
            ProcessNewMessages();

            // Remove any jobs that have been canceled from the job queue.
            RemoveCanceledJobsFromQueue();

            // If there's a job currently running and it has been canceled, then cancel it
            if (_currentBakeJob != null)
            {
                if (_cancleQueue.Contains(_currentBakeJob.JobID))
                {
                    _currentBakeJob.CancelJob();
                    _currentBakeJob = null;
                }
            }

            ///
            /// Note: The if/else-if statement is there to stop me from starting a new job and running it's first update
            ///		  within the same frame. Other than that, there is no reason for using an if/else-if. I could use two
            ///		  if states and run a job's first update in the same frame as that job's start() call.
            ///
            // If there are no jobs running, and we have jobs queued up, then start the next job in the queue
            if (_currentBakeJob == null && _jobs.Count > 0)
            {
                _currentBakeJob = _jobs.Dequeue();
                _currentBakeJob.StartJob();
            }
            // If there's a job running
            else if (_currentBakeJob != null)
            {
                // If the job is complete
                if (_currentBakeJob.JobComplete)
                {
                    FinishBakeJob();
                }
                else
                {
                    _currentBakeJob.Update();
                }
            }

            // One thing I learned when dealing with systems with lots of memory is that you should run the garbage
            // collector from time to time when you are generating lots of items on the heap. The reason for this
            // is because there's a chance that the GC won't runt until you fill up on lots of un-recovered memory;
            // in a system with lots of memory, reclaiming a large heap can cause the process to pause long enough
            // for it to crash. I will repeat, reclaiming a large enough heap can cause the process to pause long
            // enough to cause it to crash. I think it has something to do with some Graphics APIs throwing up an
            // error when they drop below a certain FPS. It might also be a Windows thing, I know for a fact that
            // it doesn't like it when a windows' FPS drops well below 1. I learned this the hard way when I was
            // doing image capturing for some NN training on some server-hardware. The image capture was done on
            // server grade hardware; I've don't now which hardware the training was done on because I wasn't
            // working that part of the project. Anyways, each frame created a new byte array that would get saved
            // to disk, and those arrays just built-up inside the heap. A few hours in, the memory use had grown
            // well over 100 GB and the GC was triggered. That GC clean-up took so long that process crashed. So, I
            // throw in a CG call every couple of frames and ran the process for an 8 hour batch at a higher
            // resoultion than needed, to really make sure the problem was fixed. Now you know why I'm running a
            // call to the GC every 'X' frames on a process that's expected to create quite a few byte arrays that
            // aren't all needed at once. And, no, I wasn't able to put those byte arrays inside of a memory pool,
            // the item that generated them isn't part of the code that I wrote, and it didn't have a method for
            // incoding to a given array.
            // -FCT
            _updatesSinceLastGC++;
            if (_updatesSinceLastGC % 10 == 0)
            {
                System.GC.Collect();
                _updatesSinceLastGC = 0;
            }
        }