OpenCL(Open Computing Language)是一种异构计算框架,可以在不同厂商的不同设备上执行并行计算任务,内核代码与c语言高度相似,网上的相关资料有很多,不做赘述,直接进入正题。
-
在BoltBait的codelab插件当中,新建一个插件,保存之后点构建dll。
2. 可以看到下方有个“生成VS解决方案”,点它。

-
好,现在我们已经生成完整的.net解决方案,我用的paint.net是5.0的,依赖于.net 7.0,但是写这篇文章的时候paint.net版本迭代到5.1了,5.1版本依赖的…net 7.0架构在Codelab最新版本6.12中并不支持。也许BoltBait后续会更新,再次之前还是先使用5.0.x版本。
现在打开nuget包管理器,在里面搜索OpenCL.Net

右键解决方案窗口中的项目


安装 -
在代码中添加
// 其他依赖的包...
using OpenCL.Net;
using OpenCL.Net.Extensions;
// ...
namespace MyScriptEffect {
// ...
public static class Opencl
{
private const string CLCode = @"
__kernel void mark_red(
const int width,
const int height,
const int channel, // RGBA
__global int* image
) {
int x = get_global_id(0);
int y = get_global_id(1);
for (int c=0;c<channel;c++) {
int idx = x + y * width + c * width * height;
if (c == 0 || c == 3) {
image[idx] = 0xFF;
} else {
image[idx] = 0x00;
}
}
}
";
private static OpenCL.Net.Platform m_platform;
private static OpenCL.Net.Device[] m_devices;
private static OpenCL.Net.Context m_context;
private static OpenCL.Net.CommandQueue m_commandQueue;
private static OpenCL.Net.Program m_program;
private static bool m_hasInit = false;
static Opencl()
{
AppDomain.CurrentDomain.ProcessExit += (s, e) => Cleanup();
Init();
}
public static void Init()
{
if (m_hasInit) return;
ErrorCode err;
uint platformCount;
err = Cl.GetPlatformIDs(0, null, out platformCount);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create platform error: {0}", err.ToString()));
}
Platform[] platforms = new Platform[platformCount];
err = Cl.GetPlatformIDs(platformCount, platforms, out platformCount);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create platform error: {0}", err.ToString()));
}
m_platform = platforms[0];
uint deviceCount;
err = Cl.GetDeviceIDs(m_platform, DeviceType.Gpu, 0, null, out deviceCount);
if (err == ErrorCode.DeviceNotFound)
{
err = Cl.GetDeviceIDs(m_platform, DeviceType.Cpu, 0, null, out deviceCount);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create cpu device error: {0}",err.ToString()));
}
m_devices = new Device[deviceCount];
err = Cl.GetDeviceIDs(m_platform, DeviceType.Cpu, deviceCount, m_devices, out deviceCount);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create cpu device error: {0}", err.ToString()));
}
} else if (err != ErrorCode.Success) {
throw new PaintDotNet.InternalErrorException(string.Format("Create gpu device error: {0}", err.ToString()));
} else
{
m_devices = new Device[deviceCount];
err = Cl.GetDeviceIDs(m_platform, DeviceType.Gpu, deviceCount, m_devices, out deviceCount);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create gpu device error: {0}", err.ToString()));
}
}
m_context = Cl.CreateContext(null, 1, new[] { m_devices[0] }, null, IntPtr.Zero, out err);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create context error: {0}", err.ToString()));
}
m_commandQueue = Cl.CreateCommandQueue(m_context, m_devices[0], 0, out err);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create command queue error: {0}", err.ToString()));
}
m_program = Cl.CreateProgramWithSource(m_context, 1, new[] { CLCode }, null, out err);
Cl.BuildProgram(m_program, 1, new[] { m_devices[0] }, null, null, IntPtr.Zero);
if (err != ErrorCode.Success)
{
InfoBuffer log = Cl.GetProgramBuildInfo(m_program, m_devices[0], ProgramBuildInfo.Log, out err);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Build program error, fail to get build log: {0}", err.ToString()));
}
throw new PaintDotNet.InternalErrorException(string.Format("Build program error, log:\n{1}", log.ToString()));
}
m_hasInit = true;
} // Opencl::Init
public static void Cleanup()
{
if (!m_hasInit) return;
m_program.Dispose();
m_commandQueue.Dispose();
m_context.Dispose();
} // Opencl::Cleanup
public static unsafe IntPtr ToIntPtr(this int[] obj)
{
IntPtr PtrA = IntPtr.Zero;
fixed (int* Ap = obj) return new IntPtr(Ap);
}
public static void Execute(int width, int height, int channel, int[] buffer)
{
ErrorCode err;
OpenCL.Net.Kernel kernel = Cl.CreateKernel(m_program, "mark_red", out err);
if (err != ErrorCode.Success)
{
throw new PaintDotNet.InternalErrorException(string.Format("Create kernel error: {0}", err.ToString()));
}
err = Cl.SetKernelArg(kernel, 0, width);
err = Cl.SetKernelArg(kernel, 1, height);
err = Cl.SetKernelArg(kernel, 2, channel);
IMem clBuffer = Cl.CreateBuffer(m_context, MemFlags.ReadWrite | MemFlags.CopyHostPtr, sizeof(int) * buffer.Length, buffer.ToIntPtr(), out err);
err = Cl.SetKernelArg(kernel, 3, clBuffer);
nint[] globalWorkSize = new nint[] { (nint)width, (nint)height };
err = Cl.EnqueueNDRangeKernel(m_commandQueue, kernel, 2, null, globalWorkSize, null, 0, null, out _);
//Cl.EnqueueBarrier(m_commandQueue);
err = Cl.EnqueueReadBuffer(m_commandQueue, clBuffer, Bool.True, 0, sizeof(int) * buffer.Length, buffer.ToIntPtr(), 0, null, out _);
m_commandQueue.Finish();
clBuffer.Dispose();
kernel.Dispose();
}
} // class Opencl
// ...
}
这个内核的功能很简单,就是把区域全部涂红,其实完全不需要用opencl实现,这是为了教程展示才这么做的。
4. 下面的MyScriptEffectPlugin.OnRender方法当中把ROI区域传入内核执行
// ...
protected override void OnRender(IBitmapEffectOutput output)
{
using IEffectInputBitmap<ColorBgra32> sourceBitmap = Environment.GetSourceBitmapBgra32();
using IBitmapLock<ColorBgra32> sourceLock = sourceBitmap.Lock(new RectInt32(0, 0, sourceBitmap.Size));
RegionPtr<ColorBgra32> sourceRegion = sourceLock.AsRegionPtr();
RectInt32 outputBounds = output.Bounds;
using IBitmapLock<ColorBgra32> outputLock = output.LockBgra32();
RegionPtr<ColorBgra32> outputSubRegion = outputLock.AsRegionPtr();
var outputRegion = outputSubRegion.OffsetView(-outputBounds.Location);
//uint seed = RandomNumber.InitializeSeed(RandomNumberRenderSeed, outputBounds.Location);
// Delete any of these lines you don't need
ColorBgra32 primaryColor = Environment.PrimaryColor;
ColorBgra32 secondaryColor = Environment.SecondaryColor;
int canvasCenterX = Environment.Document.Size.Width / 2;
int canvasCenterY = Environment.Document.Size.Height / 2;
var selection = Environment.Selection.RenderBounds;
int selectionCenterX = (selection.Right - selection.Left) / 2 + selection.Left;
int selectionCenterY = (selection.Bottom - selection.Top) / 2 + selection.Top;
// Loop through the output canvas tile
int[] buffer = new int[outputBounds.Width * outputBounds.Height * 4];
for (int y = 0; y < outputBounds.Height; ++y)
{
if (IsCancelRequested) return;
for (int x = 0; x < outputBounds.Width; ++x)
{
int _x = outputBounds.Left + x;
int _y = outputBounds.Top + y;
buffer[x + y * outputBounds.Width + 0 * outputBounds.Width * outputBounds.Height] = sourceRegion[_x, _y].R; // R
buffer[x + y * outputBounds.Width + 1 * outputBounds.Width * outputBounds.Height] = sourceRegion[_x, _y].G; // G
buffer[x + y * outputBounds.Width + 2 * outputBounds.Width * outputBounds.Height] = sourceRegion[_x, _y].B; // B
buffer[x + y * outputBounds.Width + 3 * outputBounds.Width * outputBounds.Height] = sourceRegion[_x, _y].A; // A
}
}
Opencl.Execute(outputBounds.Width, outputBounds.Height, 4, buffer);
// save pixel
for (int y = 0; y < outputBounds.Height; ++y)
{
if (IsCancelRequested) return;
for (int x = 0; x < outputBounds.Width; ++x)
{
int _x = outputBounds.Left + x;
int _y = outputBounds.Top + y;
outputRegion[_x, _y].R = (byte)(buffer[x + y * outputBounds.Width + 0 * outputBounds.Width * outputBounds.Height]); // R
outputRegion[_x, _y].G = (byte)(buffer[x + y * outputBounds.Width + 1 * outputBounds.Width * outputBounds.Height]); // G
outputRegion[_x, _y].B = (byte)(buffer[x + y * outputBounds.Width + 2 * outputBounds.Width * outputBounds.Height]); // B
outputRegion[_x, _y].A = (byte)(buffer[x + y * outputBounds.Width + 3 * outputBounds.Width * outputBounds.Height]); // A
}
}
// save pixel end
} // OnRender
// ...
- 生成


- 找到paint.net安装目录下的
Effects文件夹,下面有个CodeLab.dll,这是之前安装的开发插件,把刚刚编译生成的dll和nuget包目录下的OpenCL.Net.dll一起拷过来。

- 启动paint.net看看效果


4653

被折叠的 条评论
为什么被折叠?



