using Unity.Collections; using Unity.Burst; using Unity.Jobs; using UnityEngine.Rendering.HighDefinition; using Unity.Mathematics; using UnityEngine; using static Unity.Mathematics.math; namespace UnityEditor.Rendering.HighDefinition { class WaterAmplitudeEvaluator { // Defines the number of subdivisions const int k_NumIterations = 32; const int k_NumTimeSteps = 512; const WaterSimulationResolution resolutionEnum = WaterSimulationResolution.Low64; const int resolution = (int)resolutionEnum; const int numPixels = resolution * resolution; [BurstCompile] internal struct ReductionStep : IJobParallelFor { [ReadOnly] public NativeArray InputBuffer; // Output spectrum buffer [WriteOnly] [NativeDisableParallelForRestriction] public NativeArray OutputBuffer; public void Execute(int index) { float4 maxDisplacement = 0.0f; for (int i = 0; i < resolution; ++i) maxDisplacement = max(maxDisplacement, InputBuffer[i + index * resolution]); OutputBuffer[index] = maxDisplacement; } } static void EvaluateMaxAmplitude(NativeArray startBuffer, NativeArray intBuffer, NativeArray finBuffer) { ReductionStep reductionStep = new ReductionStep(); reductionStep.InputBuffer = startBuffer; reductionStep.OutputBuffer = intBuffer; JobHandle handle = reductionStep.Schedule(resolution, 1); handle.Complete(); reductionStep = new ReductionStep(); reductionStep.InputBuffer = intBuffer; reductionStep.OutputBuffer = finBuffer; handle = reductionStep.Schedule(1, 1); handle.Complete(); } #if UNITY_EDITOR // [MenuItem("Generation/Water Amplitude Table")] static public void GenerateAmplitudeTable() { // Number of pixels per band int waterSampleOffset = WaterSystem.EvaluateWaterNoiseSampleOffset(resolutionEnum); float waterSpectrumOffset = WaterSystem.EvaluateFrequencyOffset(resolutionEnum); // Allocate all the native buffers NativeArray h0BufferCPU = new NativeArray(numPixels, Allocator.Persistent); NativeArray htR0 = new NativeArray(numPixels, Allocator.Persistent); NativeArray htI0 = new NativeArray(numPixels, Allocator.Persistent); NativeArray htR1 = new NativeArray(numPixels, Allocator.Persistent); NativeArray htI1 = new NativeArray(numPixels, Allocator.Persistent); NativeArray pingPong = new NativeArray(numPixels * 4, Allocator.Persistent); NativeArray indices = new NativeArray(numPixels, Allocator.Persistent); NativeArray displacementBufferCPU = new NativeArray(numPixels, Allocator.Persistent); NativeArray intermediateBuffer = new NativeArray(resolution, Allocator.Persistent); NativeArray maxDisplacement = new NativeArray(1, Allocator.Persistent); // output table for the string //string outputTable = new string("PatchSize/WindSpeed,"); string outputTable = new string(""); // Evaluate the wind speed for this patch size float maxWindSpeed = WaterConsts.k_SwellMaximumWindSpeed * WaterConsts.k_KilometerPerHourToMeterPerSecond; // Iterate over the patch size for (int patchSizeIndex = 0; patchSizeIndex < k_NumIterations; ++patchSizeIndex) { // Evaluate the current patch size float patchRatio = patchSizeIndex / (float)(k_NumIterations - 1); float currentPatchSize = 25.0f + patchRatio * (5000.0f - 25.0f); for (int windSpeedIndex = 0; windSpeedIndex < k_NumIterations; ++windSpeedIndex) { // Evaluate the wind speed float windRatio = windSpeedIndex / (float)(k_NumIterations - 1); float windSpeed = windRatio * maxWindSpeed; // Prepare the first band WaterCPUSimulation.PhillipsSpectrumInitialization spectrumInit = new WaterCPUSimulation.PhillipsSpectrumInitialization(); spectrumInit.simulationResolution = resolution; spectrumInit.waterSampleOffset = waterSampleOffset; spectrumInit.sliceIndex = 0; spectrumInit.patchSize = currentPatchSize; spectrumInit.orientation = 0; spectrumInit.directionDampner = 1.0f; spectrumInit.windSpeed = windSpeed; spectrumInit.bufferOffset = 0; spectrumInit.H0Buffer = h0BufferCPU; // Schedule the job with one Execute per index in the results array and only 1 item per processing batch JobHandle handle = spectrumInit.Schedule((int)numPixels, 1); handle.Complete(); float maxAmplitude = 0.0f; for (int i = 0; i < k_NumTimeSteps; ++i) { // Evaluate the simulation time float simulationTime = (float)i * 10.0f; // Prepare the first band WaterCPUSimulation.EvaluateDispersion dispersion = new WaterCPUSimulation.EvaluateDispersion(); dispersion.simulationResolution = resolution; dispersion.patchSize = currentPatchSize; dispersion.bufferOffset = 0; dispersion.simulationTime = simulationTime; // Input buffers dispersion.H0Buffer = h0BufferCPU; // Output buffers dispersion.HtRealBuffer = htR0; dispersion.HtImaginaryBuffer = htI0; // Schedule the job with one Execute per index in the results array and only 1 item per processing batch JobHandle dispersionHandle = dispersion.Schedule((int)numPixels, 1); dispersionHandle.Complete(); // Prepare the first band WaterCPUSimulation.InverseFFT inverseFFT0 = new WaterCPUSimulation.InverseFFT(); inverseFFT0.simulationResolution = resolution; inverseFFT0.butterflyCount = WaterSystem.ButterFlyCount(resolutionEnum); inverseFFT0.bufferOffset = 0; inverseFFT0.columnPass = false; // Input buffers inverseFFT0.HtRealBufferInput = htR0; inverseFFT0.HtImaginaryBufferInput = htI0; // Temp buffers inverseFFT0.pingPongArray = pingPong; inverseFFT0.textureIndicesArray = indices; // Output buffers buffers inverseFFT0.HtRealBufferOutput = htR1; inverseFFT0.HtImaginaryBufferOutput = htI1; // Schedule the job with one Execute per index in the results array and only 1 item per processing batch JobHandle ifft0Handle = inverseFFT0.Schedule(resolution, 1, dispersionHandle); ifft0Handle.Complete(); // Second inverse FFT WaterCPUSimulation.InverseFFT inverseFFT1 = new WaterCPUSimulation.InverseFFT(); inverseFFT1.simulationResolution = resolution; inverseFFT1.butterflyCount = WaterSystem.ButterFlyCount(resolutionEnum); inverseFFT1.bufferOffset = 0; inverseFFT1.columnPass = true; // Input buffers inverseFFT1.HtRealBufferInput = htR1; inverseFFT1.HtImaginaryBufferInput = htI1; // Temp buffers inverseFFT1.pingPongArray = pingPong; inverseFFT1.textureIndicesArray = indices; // Output buffers buffers inverseFFT1.HtRealBufferOutput = displacementBufferCPU; inverseFFT1.HtImaginaryBufferOutput = htR0; // Schedule the job with one Execute per index in the results array and only 1 item per processing batch JobHandle ifft1Handle = inverseFFT1.Schedule(resolution, 1, ifft0Handle); ifft1Handle.Complete(); // Evaluate the max amplitude of the displacement EvaluateMaxAmplitude(displacementBufferCPU, intermediateBuffer, maxDisplacement); // Add this contribution maxAmplitude += maxDisplacement[0].x; } // Output the results outputTable += (maxAmplitude / (float)k_NumTimeSteps).ToString() + "f, "; } outputTable += "\n"; } // Release the buffers maxDisplacement.Dispose(); intermediateBuffer.Dispose(); displacementBufferCPU.Dispose(); indices.Dispose(); pingPong.Dispose(); htI1.Dispose(); htR1.Dispose(); htI0.Dispose(); htR0.Dispose(); h0BufferCPU.Dispose(); // Output our table Debug.Log(outputTable); } #endif } }