static float EncodingToSlope(int encoding) => 4f * encoding / 1000000f; // Haven't looked at the numerics closely to see whether these are exact inverses. Don't care enough yet. /// <summary> /// Generate the near horizon for the patch, overwriting what was there. /// The horizons will be in slope format, not angles /// </summary> /// <param name="target"></param> public void AddNearHorizon(TerrainPatch target) { Debug.Assert(Terrain != null); Debug.Assert(target != null); if (target.Horizons == null) { target.InitializeHorizons(); } using (var context = new Context()) { AcceleratorId aid = Accelerator.Accelerators.Where(id => id.AcceleratorType == AcceleratorType.Cuda).FirstOrDefault(); //AcceleratorId aid = Accelerator.Accelerators.Where(id => id.AcceleratorType == AcceleratorType.CPU).FirstOrDefault(); if (aid.AcceleratorType != AcceleratorType.Cuda) { Console.WriteLine(@"There is no CUDA accelerator present. Doing nothing."); return; } using (var accelerator = Accelerator.Create(context, aid)) { target.Matrices = null; // Be sure!! target.FillPoints(Terrain); target.FillMatricesRelativeToPoint(Terrain, target.Points[0][0]); // Matrices var cpu_matrices_size = target.Height * target.Width * 12; var basePoint = target.Points[0][0]; var cpu_matrices = MakeCPUMatrices(target); // Horizon (load from target) var cpu_horizon_size = target.Height * target.Width * Horizon.HorizonSamples; var cpu_horizon = new float[cpu_horizon_size]; // Initialize to float.MinValue. Maintain horizon as slope for (var i = 0; i < cpu_horizon_size; i++) { cpu_horizon[i] = float.MinValue; } // Caster array const int dem_size = TerrainPatch.DEM_size; const int patch_size = TerrainPatch.DefaultSize; var maxDistance = (float)TerrainPatch.MaximumLocalDistance; var border = 2 + (int)Math.Ceiling(maxDistance); // the 2 is margin for the bilinear interpolation var line_min = Math.Max(0, target.Line - border); var line_max = Math.Min(dem_size - 1, target.Line + patch_size + border); // 1 more than the highest index var line_size = line_max - line_min; var line_offset = target.Line - line_min; var sample_min = Math.Max(0, target.Sample - border); var sample_max = Math.Min(dem_size - 1, target.Sample + patch_size + border); // 1 more than the highest index var sample_size = sample_max - sample_min; var sample_offset = target.Sample - sample_min; var cpu_caster_points_size = line_size * sample_size * 3; var cpu_caster_points = new float[cpu_caster_points_size]; FillNearCasterArray(cpu_caster_points, target.Points[0][0], line_min, line_max, sample_min, sample_max); //DumpNearFieldTestCsv(cpu_caster_points, cpu_matrices); var test_floats_size = Horizon.HorizonSamples * TerrainPatch.NearHorizonOversample; var test_floats = new float[test_floats_size]; using (var gpu_matrices = accelerator.Allocate <float>(cpu_matrices_size)) using (var gpu_horizon = accelerator.Allocate <float>(cpu_horizon_size)) using (var gpu_caster_points = accelerator.Allocate <float>(cpu_caster_points_size)) using (var gpu_test_floats = accelerator.Allocate <float>(test_floats_size)) { gpu_matrices.CopyFrom(cpu_matrices, 0, 0, cpu_matrices_size); gpu_horizon.CopyFrom(cpu_horizon, 0, 0, cpu_horizon_size); gpu_caster_points.CopyFrom(cpu_caster_points, 0, 0, cpu_caster_points_size); gpu_test_floats.CopyFrom(test_floats, 0, 0, test_floats_size); var groupSize = accelerator.MaxNumThreadsPerGroup; Index launchDimension = Horizon.HorizonSamples * TerrainPatch.NearHorizonOversample; const float d_min = 1f; var d_max = (float)TerrainPatch.MaximumLocalDistance; const float d_step = TerrainPatch.LocalStep; var kernel1 = accelerator.LoadAutoGroupedStreamKernel <Index, int, int, int, int, int, int, float, float, float, ArrayView <float>, ArrayView <float>, ArrayView <float>, ArrayView <float> >(NearHorizonKernel1); Console.WriteLine(@"Launching near horizon kernels ... "); if (true) { for (var target_line = 0; target_line < TerrainPatch.DefaultSize; target_line++) { for (var target_sample = 0; target_sample < TerrainPatch.DefaultSize; target_sample++) { kernel1(launchDimension, target_line, target_sample, line_offset, sample_offset, line_size, sample_size, d_min, d_max, d_step, gpu_caster_points, gpu_matrices, gpu_horizon, gpu_test_floats); } } } else { kernel1(launchDimension, 0, 0, line_offset, sample_offset, line_size, sample_size, d_min, d_max, d_step, gpu_caster_points, gpu_matrices, gpu_horizon, gpu_test_floats); } gpu_horizon.CopyTo(cpu_horizon, 0, 0, cpu_horizon_size); gpu_test_floats.CopyTo(test_floats, 0, 0, test_floats_size); // Update the horizons for (var line = 0; line < TerrainPatch.DefaultSize; line++) { for (var sample = 0; sample < TerrainPatch.DefaultSize; sample++) { var offset = (line * TerrainPatch.DefaultSize + sample) * Horizon.HorizonSamples; var buffer = target.Horizons[line][sample].Buffer; for (var i = 0; i < Horizon.HorizonSamples; i++) { buffer[i] = cpu_horizon[i + offset]; } } } } } } }
/// <summary> /// Update the horizons of a patch based on a list of shadow casters. /// The horizons will be in slope, not angle, format /// </summary> /// <param name="target"></param> /// <param name="casters"></param> public void UpdateHorizons(TerrainPatch target, List <TerrainPatch> casters) { Debug.Assert(Terrain != null); if (casters.Count < 1) { return; } using (var context = new Context()) { AcceleratorId aid = Accelerator.Accelerators.Where(id => id.AcceleratorType == AcceleratorType.Cuda).FirstOrDefault(); if (aid.AcceleratorType != AcceleratorType.Cuda) { Console.WriteLine(@"There is no CUDA accelerator present. Doing nothing."); return; } using (var accelerator = Accelerator.Create(context, aid)) { target.FillPoints(Terrain); target.FillMatricesRelativeToPoint(Terrain, target.Points[0][0]); // Matrices var cpu_matrices_size = target.Height * target.Width * 12; var basePoint = target.Points[0][0]; var cpu_matrices = MakeCPUMatrices(target); // Horizon (load from target) var cpu_horizon_size = target.Height * target.Width * Horizon.HorizonSamples; var cpu_horizon = new int[cpu_horizon_size]; for (var line = 0; line < TerrainPatch.DefaultSize; line++) { for (var sample = 0; sample < TerrainPatch.DefaultSize; sample++) { var offset = (line * TerrainPatch.DefaultSize + sample) * Horizon.HorizonSamples; var buffer = target.Horizons[line][sample].Buffer; for (var i = 0; i < Horizon.HorizonSamples; i++) { cpu_horizon[i + offset] = SlopeToEncoding(buffer[i]); } } } // Caster points var cpu_caster_points_size = casters[0].Width * casters[0].Height * 3; var cpu_caster_points = new float[cpu_caster_points_size]; // test array var cpu_test_array = new float[20]; using (var gpu_matrices = accelerator.Allocate <float>(cpu_matrices_size)) using (var gpu_horizon = accelerator.Allocate <int>(cpu_horizon_size)) using (var gpu_caster_points = accelerator.Allocate <float>(cpu_caster_points_size)) using (var gpu_test_array = accelerator.Allocate <float>(cpu_test_array.Length)) { gpu_matrices.CopyFrom(cpu_matrices, 0, 0, cpu_matrices_size); gpu_horizon.CopyFrom(cpu_horizon, 0, 0, cpu_horizon_size); var groupSize = accelerator.MaxNumThreadsPerGroup; var launchDimension = new GroupedIndex2( new Index2(128, 128), // (data.Length + groupSize - 1) / groupSize, // Compute the number of groups (round up) new Index2(1, 128)); var kernel1 = accelerator.LoadSharedMemoryStreamKernel1 <GroupedIndex2, ArrayView <float>, ArrayView <float>, ArrayView <int>, ArrayView <float>, ArrayView <int> >(ShadowKernel1); //var stopwatch = new Stopwatch(); //stopwatch.Start(); foreach (var caster in casters) { caster.FillPoints(Terrain); CopyPointsToCpuArray(caster, basePoint, cpu_caster_points); gpu_caster_points.CopyFrom(cpu_caster_points, 0, 0, cpu_caster_points_size); kernel1(launchDimension, gpu_caster_points, gpu_matrices, gpu_horizon, gpu_test_array); accelerator.Synchronize(); } // Copy out data gpu_horizon.CopyTo(cpu_horizon, 0, 0, cpu_horizon_size); gpu_test_array.CopyTo(cpu_test_array, 0, 0, cpu_test_array.Length); //stopwatch.Stop(); //Console.WriteLine($"kernel time={stopwatch.Elapsed} cpu_horizon.Max()={cpu_horizon.Max()} cpu_horizon[0]={cpu_horizon[0]}"); // Update the horizons for (var line = 0; line < TerrainPatch.DefaultSize; line++) { for (var sample = 0; sample < TerrainPatch.DefaultSize; sample++) { var offset = (line * TerrainPatch.DefaultSize + sample) * Horizon.HorizonSamples; var buffer = target.Horizons[line][sample].Buffer; for (var i = 0; i < Horizon.HorizonSamples; i++) { buffer[i] = EncodingToSlope(cpu_horizon[i + offset]); } } } //Console.WriteLine($" max slope={cpu_horizon.Select(EncodingToSlope).Max()}"); } } } }