Compute Sharp - это пакет NuGet, который утверждает, что может принимать код C#, преобразовывать его в код HLSL , и запустите его на графическом процессоре. Однако это, похоже, не совсем то, что я хочу, поскольку он принимает не код HLSL, а код C#. В любом случае решил проверить это, так как это довольно незначительный недостаток, однако при попытке установить последнюю версию на NuGet (1.3.1), который также является последней стабильной версией, выдает ошибку «Не удалось установить пакет [... ]. Вы пытаетесь установить этот пакет в проект, предназначенный для [.NetFramework версии 4.7.2], но пакет не содержит ссылок на сборки или файлов содержимого, совместимых с этой структурой. [...] ". Поэтому я решил сначала проверить другие методы.
SharpDX кажется пакетом NuGet, который позволяет вам работать с кодом HLSL в вашем C#. NET проект. Используя SharpDX.Direct3D11, вы даже можете создавать вычислительные шейдеры. Кажется, именно то, что я хочу, поэтому я больше изучал этот, чем другие. Я только что столкнулся с одной большой проблемой с этим: SharpDX, похоже, очень мало в виде руководств или объяснений о том, как его использовать. Используя то, что Google Translate называет японским примером вычислительного шейдера, использующего SharpDX в качестве чертежа, я смог заставить кое-что работать. Проблема с тем, что мне удалось сделать, заключается в том, что 1) есть части, которые я вообще не понимаю, что они делают, просто без них ничего не работает, и 2) внесение того, что кажется вполне приемлемыми изменениями, например, наличие второй буфер может сломать его причудливыми способами. Плюс, казалось бы, необходимость ссылаться на структурированные буферы через их позицию в скомпилированном шейдере (а не их предварительно скомпилированную позицию), кажется действительно плохой и, по моему небольшому опыту, не удобна для работы. Можно предположить, что большинство проблем, которые у меня есть, легко решаются, если кто-то знает, как правильно использовать пакет, однако тогда это просто возвращает меня к тому, что я действительно не могу найти множество руководств, объяснений или примеров того, как использовать пакет. Если кому-то интересно, вот мой, вероятно, плохой пример использования SharpDX для генерации 4 чисел 0,1,2,3 и умножения их на 4 в шейдере:
C#
using System;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.D3DCompiler;
//Based on https://gist.github.com/oguna/624969e732a868ec17f05694012c1b63
namespace C_Sharp_Shader_test
{
class Program
{
static void Main(string[] args)
{
int groupSize = 2; //Needs to match what is written in the shader
int totalSize = 4; //Needs to be a multiple of groupSize, or else the shader will try to either change part of the array past its length, or not change the last parts of the array
int elementByteSize = 4; //The size of a single element of the input-data in bytes (An int is made of 4 bytes)
//Create device
Device device = new Device(DriverType.Hardware, DeviceCreationFlags.SingleThreaded);
//Create compute shader
CompilationResult bytecode = ShaderBytecode.CompileFromFile("Shader.hlsl", "CSMain", "cs_5_0"); //(Gotta have the shader-file Shader.hlsl be copied to the output directory for this to work)
ComputeShader cs = new ComputeShader(device, bytecode);
bytecode.Dispose();
//Create input data (0,1,2,3)
int[] inputData = new int[totalSize];
for (int i = 0; i < inputData.Length; i++)
{
inputData[i] = i;
}
for (int i = 0; i < inputData.Length; i++)
{
Console.WriteLine(inputData[i]);
}
Console.WriteLine("");
//Create input buffer that has the input data
BufferDescription inputDesc = new BufferDescription()
{
SizeInBytes = elementByteSize * totalSize, //Size of the buffer in bytes
Usage = ResourceUsage.Default, //Lets the buffer be both written and read by the GPU
BindFlags = BindFlags.ShaderResource | BindFlags.UnorderedAccess,
OptionFlags = ResourceOptionFlags.BufferStructured,
StructureByteStride = elementByteSize, //The size of each element in bytes
CpuAccessFlags = CpuAccessFlags.Read //Lets the CPU read this buffer
};
SharpDX.Direct3D11.Buffer buffer = SharpDX.Direct3D11.Buffer.Create(device, inputData, inputDesc);
//Create resource view (Seems to just be needed for the buffer)
ShaderResourceViewDescription srvDesc = new ShaderResourceViewDescription()
{
Format = SharpDX.DXGI.Format.Unknown,
Dimension = ShaderResourceViewDimension.Buffer,
Buffer = new ShaderResourceViewDescription.BufferResource()
{
ElementWidth = elementByteSize
}
};
ShaderResourceView srvs = new ShaderResourceView(device, buffer, srvDesc);
//Create access view (Seems to just be needed for the buffer)
UnorderedAccessViewDescription uavDesc = new UnorderedAccessViewDescription()
{
Format = SharpDX.DXGI.Format.Unknown,
Dimension = UnorderedAccessViewDimension.Buffer,
Buffer = new UnorderedAccessViewDescription.BufferResource()
{
ElementCount = totalSize
}
};
UnorderedAccessView uavs = new UnorderedAccessView(device, buffer, uavDesc);
//Set up shader
DeviceContext context = device.ImmediateContext;
context.ComputeShader.Set(cs);
//Set up shader's buffer
context.ComputeShader.SetConstantBuffer(0, buffer);
context.ComputeShader.SetShaderResource(0, srvs);
context.ComputeShader.SetUnorderedAccessView(0, uavs);
//Execute shader
int threadGroupCount = (totalSize + groupSize - 1) / groupSize; // +groupSize-1 to round up
context.Dispatch(threadGroupCount, 1, 1);
//Set an array "outputData" equal to the buffer's values
DataStream ds;
context.MapSubresource(buffer, MapMode.Read, MapFlags.None, out ds);
int[] outputData = ds.ReadRange<int>(4);
//Dispose stuff
context.ClearState();
Utilities.Dispose(ref srvs);
Utilities.Dispose(ref uavs);
Utilities.Dispose(ref buffer);
Utilities.Dispose(ref cs);
Utilities.Dispose(ref device);
//Print values
for (int i = 0; i < outputData.Length; i++)
{
Console.WriteLine(outputData[i]);
}
//Wait so it doesn't close the console immediately.
Console.ReadKey();
}
}
}
HLSL
RWStructuredBuffer<int> Result;
[numthreads(2, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
Result[id.x] = Result[id.x] * 4;
}
Вывод:
0 1 2 3 0 4 8 12