Skip to content

Commit daca4eb

Browse files
v2.0: OpenCL!!!! (#3)
* OpenCL kernel works * Spiral algorithm * Save results in file * work size option * Device selection with command line option * Implement readme option * Search area options start and stop use radius in blocks * v2.0 readme update
1 parent b821415 commit daca4eb

20 files changed

+1026
-309
lines changed

MCSlimeClusterFinder/Extensions.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using OpenCL.NetCore;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Text;
5+
6+
namespace MCSlimeClusterFinder
7+
{
8+
public static class Extensions
9+
{
10+
public static InfoBuffer GetInfo(this Device device, DeviceInfo param)
11+
=> Cl.GetDeviceInfo(device, param, out ErrorCode error);
12+
public static InfoBuffer GetInfo(this Platform platform, PlatformInfo param)
13+
=> Cl.GetPlatformInfo(platform, param, out ErrorCode error);
14+
public static string DeviceInfoLine(this Device device)
15+
=> $"{device.GetInfo(DeviceInfo.Name)} {device.GetInfo(DeviceInfo.Platform).CastTo<Platform>().GetInfo(PlatformInfo.Name)}";
16+
}
17+
}

MCSlimeClusterFinder/MCSlimeClusterFinder.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@
1515
<ItemGroup>
1616
<PackageReference Include="Mono.Options" Version="6.6.0.161" />
1717
<PackageReference Include="morelinq" Version="3.3.2" />
18+
<PackageReference Include="OpenCL.NetCore" Version="1.0.0" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<None Update="kernels.cl">
23+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
24+
</None>
25+
<None Update="README-copy.md">
26+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
27+
</None>
1828
</ItemGroup>
1929

2030
</Project>

MCSlimeClusterFinder/MainThread.cs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using Mono.Options;
2+
using MoreLinq;
3+
using OpenCL.NetCore;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Text;
7+
using System.Text.Json;
8+
using System.Text.Json.Serialization;
9+
using System.Threading;
10+
using System.Linq;
11+
using System.Threading.Tasks;
12+
13+
namespace MCSlimeClusterFinder
14+
{
15+
public class MainThread
16+
{
17+
private static Supervisor workSupervisor { get; set; }
18+
private static SettingsResults settingsResults { get; } = new SettingsResults();
19+
public static void Main(string[] args)
20+
{
21+
if (!parseArgs(args))
22+
{
23+
return;
24+
}
25+
workSupervisor = new Supervisor(settingsResults);
26+
workSupervisor.Start();
27+
waitForWorkEnd();
28+
System.IO.File.WriteAllText(settingsResults.Settings.OutputFile, JsonSerializer.Serialize(settingsResults, new JsonSerializerOptions() { WriteIndented = true }));
29+
}
30+
31+
private static bool parseArgs(string[] args)
32+
{
33+
var stng = settingsResults.Settings;
34+
try
35+
{
36+
bool seedInput = false;
37+
bool shouldShowHelp = false;
38+
bool printReadme = false;
39+
string inputFile = null;
40+
bool deviceInput = false;
41+
42+
var options = new OptionSet
43+
{
44+
{ "s|seed=", "world seed, type long", (long s) => {stng.WorldSeed = s; seedInput = true; } },
45+
{ "i|in=", "input file to continue saved work", i => inputFile = i },
46+
{ "o|out=", "file to save the results", o => stng.OutputFile = o },
47+
{ "h|help", "show this message and exit", h => shouldShowHelp = h != null },
48+
{ "start=", "the start \"radius\" of the search area in blocks/meters", (int s) => stng.Start = s },
49+
{ "stop=", "the end \"radius\" of the search area in blocks/meters", (int s) => stng.Stop = s },
50+
{ "w|work-size=", "length of the square chunk of work sent to the GPU at once less than 2^14", (short w) => stng.GpuWorkChunkDimension = w },
51+
{ "r|readme", "print the readme and exit. Includes a how-to", r => printReadme = r != null },
52+
{ "d|device=", "the index of the OpenCL device to use", (int d) => { stng.Device = OpenCLWrapper.GetDevices()[d]; deviceInput = true; } }
53+
};
54+
55+
options.Parse(args);
56+
57+
58+
if (shouldShowHelp)
59+
{
60+
Console.Write(optionsHeader);
61+
options.WriteOptionDescriptions(Console.Out);
62+
Console.WriteLine(optionsFooter);
63+
return false;
64+
}
65+
if (printReadme)
66+
{
67+
Console.WriteLine(getOptionsOutputString(System.IO.File.ReadAllText("README-copy.md")));
68+
return false;
69+
}
70+
if (!seedInput)
71+
{
72+
Console.WriteLine(getOptionsOutputString("A world seed must be specified with -s [world seed]"));
73+
return false;
74+
}
75+
if (!string.IsNullOrEmpty(inputFile))
76+
{
77+
throw new NotImplementedException();
78+
}
79+
if (!deviceInput)
80+
{
81+
try
82+
{
83+
List<Device> devices = OpenCLWrapper.GetDevices();
84+
string output = devices.Select((d, i) => $"[{i}]: {d.DeviceInfoLine()}").Aggregate((a, b) => $"{a}\n{b}");
85+
Console.Write("Devices:\n\n" + output + "\nSelect a device index: ");
86+
int index = int.Parse(Console.ReadLine());
87+
stng.Device = devices[index];
88+
} catch (Exception)
89+
{
90+
Console.WriteLine("Invalid device number selected");
91+
return false;
92+
}
93+
}
94+
95+
} catch (OptionException e)
96+
{
97+
Console.WriteLine(getOptionsOutputString(e.Message));
98+
return false;
99+
}
100+
return true;
101+
}
102+
103+
private static string getOptionsOutputString(string content) =>
104+
optionsHeader + content + optionsFooter;
105+
private const string optionsHeader = "\nUsage: MCSlimeClusterFinder -s WORLD_SEED [OPTIONS]\n\n";
106+
private const string optionsFooter = "\nTry `MCSlimeClusterFinder --help' for more information.\n";
107+
108+
private static void waitForWorkEnd()
109+
{
110+
while (!workSupervisor.Completed)
111+
{
112+
Thread.Sleep(100);
113+
//TODO progress meter
114+
}
115+
}
116+
}
117+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using OpenCL.NetCore;
5+
using System.Linq;
6+
using System.Runtime.InteropServices;
7+
using MoreLinq;
8+
9+
namespace MCSlimeClusterFinder
10+
{
11+
public class OpenCLWrapper
12+
{
13+
CommandQueue queue;
14+
Kernel kernel;
15+
IMem dataOut;
16+
Program program;
17+
Context context;
18+
public int[] candidates;
19+
int squareLength;
20+
int globalSize;
21+
22+
23+
private long worldSeed { get; }
24+
private Device device { get; }
25+
26+
public static List<Device> GetDevices()
27+
=> Cl.GetPlatformIDs(out ErrorCode error)
28+
.SelectMany(p => Cl.GetDeviceIDs(p, DeviceType.Gpu, out error))
29+
.ToList();
30+
31+
public OpenCLWrapper(int squareLength, Device dev, long seed)
32+
{
33+
this.squareLength = squareLength;
34+
globalSize = squareLength * squareLength;
35+
candidates = new int[globalSize];
36+
device = dev;
37+
worldSeed = seed;
38+
ready();
39+
}
40+
41+
~OpenCLWrapper()
42+
{
43+
Cl.ReleaseKernel(kernel);
44+
Cl.ReleaseMemObject(dataOut);
45+
Cl.ReleaseCommandQueue(queue);
46+
Cl.ReleaseProgram(program);
47+
Cl.ReleaseContext(context);
48+
}
49+
private void allGood(ErrorCode ec)
50+
{
51+
if (ec != ErrorCode.Success)
52+
throw new Exception($"OpenCL had an error: {ec}");
53+
}
54+
private void ready()
55+
{
56+
ErrorCode error;
57+
58+
context = Cl.CreateContext(null, 1, new[] { device }, null, IntPtr.Zero, out error);
59+
60+
string source = System.IO.File.ReadAllText("kernels.cl");
61+
program = Cl.CreateProgramWithSource(context, 1, new[] { source }, null, out error);
62+
63+
error = Cl.BuildProgram(program, 1, new[] { device }, string.Empty, null, IntPtr.Zero);
64+
InfoBuffer buildStatus = Cl.GetProgramBuildInfo(program, device, ProgramBuildInfo.Status, out error);
65+
if (buildStatus.CastTo<BuildStatus>() != BuildStatus.Success)
66+
throw new Exception($"OpenCL could not build the kernel successfully: {buildStatus.CastTo<BuildStatus>()}");
67+
allGood(error);
68+
69+
Kernel[] kernels = Cl.CreateKernelsInProgram(program, out error);
70+
kernel = kernels[0];
71+
allGood(error);
72+
73+
queue = Cl.CreateCommandQueue(context, device, CommandQueueProperties.None, out error);
74+
allGood(error);
75+
76+
dataOut = Cl.CreateBuffer(context, MemFlags.WriteOnly, (IntPtr)(globalSize * sizeof(int)), out error);
77+
allGood(error);
78+
79+
var intSizePtr = new IntPtr(Marshal.SizeOf(typeof(int)));
80+
error |= Cl.SetKernelArg(kernel, 2, new IntPtr(Marshal.SizeOf(typeof(IntPtr))), dataOut);
81+
error |= Cl.SetKernelArg(kernel, 3, intSizePtr, new IntPtr(worldSeed));
82+
error |= Cl.SetKernelArg(kernel, 4, intSizePtr, new IntPtr(globalSize));
83+
allGood(error);
84+
}
85+
86+
public void Work((long x, long z) startingPoint)
87+
{
88+
ErrorCode error;
89+
int local_size = 256;
90+
int global_size = (int)Math.Ceiling(globalSize / (float)local_size) * local_size;
91+
92+
var intSizePtr = new IntPtr(Marshal.SizeOf(typeof(int)));
93+
error = Cl.SetKernelArg(kernel, 0, intSizePtr, new IntPtr(startingPoint.x));
94+
error |= Cl.SetKernelArg(kernel, 1, intSizePtr, new IntPtr(startingPoint.z));
95+
allGood(error);
96+
97+
error = Cl.EnqueueNDRangeKernel(queue, kernel, 1, null, new IntPtr[] { new IntPtr(global_size) }, null, 0, null, out Event clevent);
98+
allGood(error);
99+
Cl.EnqueueReadBuffer(queue, dataOut, Bool.False, IntPtr.Zero, (IntPtr)(globalSize * sizeof(int)), candidates, 0, null, out clevent);
100+
Cl.Finish(queue);
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)