Skip to content

Instantly share code, notes, and snippets.

@oguna
Created March 7, 2018 17:37
Show Gist options
  • Select an option

  • Save oguna/624969e732a868ec17f05694012c1b63 to your computer and use it in GitHub Desktop.

Select an option

Save oguna/624969e732a868ec17f05694012c1b63 to your computer and use it in GitHub Desktop.
Sum operation on DirectCompute ( SharpDX )
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Direct3D;
using Buffer = SharpDX.Direct3D11.Buffer;
namespace VectorSum
{
class Program
{
static void Main(string[] args)
{
// データの大きさ
const int DATASIZE = 10000;
// DirectXデバイスの作成
var device = new Device(DriverType.Hardware, DeviceCreationFlags.Debug);
// シェーダのコンパイル
var bytecode = SharpDX.D3DCompiler.ShaderBytecode.CompileFromFile("vectorsum.hlsl", "CS", "cs_4_0");
// コンピュートシェーダの作成
var cs = new ComputeShader(device, bytecode);
// バイトコードの解放
bytecode.Dispose();
// 初期データの作成
var inputData = new float[DATASIZE];
var random = new Random(0);
for (int i = 0; i < DATASIZE; i++)
{
inputData[i] = (float)random.NextDouble() * 10;
}
// シェーダの入出力に用いるバッファの作成
var inputDesc = new BufferDescription()
{
SizeInBytes = DATASIZE * 4,
Usage = ResourceUsage.Default,
BindFlags = BindFlags.ShaderResource | BindFlags.UnorderedAccess,
OptionFlags = ResourceOptionFlags.BufferStructured,
StructureByteStride = 4,
};
var buffers = new Buffer[2];
buffers[0] = Buffer.Create(device, inputData, inputDesc);
buffers[1] = Buffer.Create(device, inputData, inputDesc);
// シェーダーに入力する定数バッファの作成
var inputCBDesc = new BufferDescription()
{
SizeInBytes = 16,
Usage = ResourceUsage.Default,
BindFlags = BindFlags.ConstantBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
StructureByteStride = 0
};
var constantBuffer = new Buffer(device, inputCBDesc);
{
device.ImmediateContext.UpdateSubresource(new int[] { DATASIZE, 0, 0, 0 }, constantBuffer);
}
// CPUにコピーするためのバッファの作成
var readBuckBufferDesc = new BufferDescription()
{
SizeInBytes = 4,
Usage = ResourceUsage.Staging,
BindFlags = BindFlags.None,
OptionFlags = ResourceOptionFlags.None,
StructureByteStride = 4,
CpuAccessFlags = CpuAccessFlags.Read
};
var readBackBuffer = new Buffer(device, readBuckBufferDesc);
// シェーダに入力するSRVの作成
var srvDesc = new ShaderResourceViewDescription()
{
Format = SharpDX.DXGI.Format.Unknown,
Dimension = ShaderResourceViewDimension.Buffer,
Buffer = new ShaderResourceViewDescription.BufferResource()
{
ElementWidth = DATASIZE,
}
};
var srvs = new ShaderResourceView[2];
srvs[0] = new ShaderResourceView(device, buffers[0], srvDesc);
srvs[1] = new ShaderResourceView(device, buffers[1], srvDesc);
// シェーダからの出力を指定するUAVの作成
var uavDesc = new UnorderedAccessViewDescription()
{
Format = SharpDX.DXGI.Format.Unknown,
Dimension = UnorderedAccessViewDimension.Buffer,
Buffer = new UnorderedAccessViewDescription.BufferResource()
{
ElementCount = DATASIZE,
}
};
var uavs = new UnorderedAccessView[2];
uavs[0] = new UnorderedAccessView(device, buffers[0], uavDesc);
uavs[1] = new UnorderedAccessView(device, buffers[1], uavDesc);
// GPU処理の実行
var context = device.ImmediateContext;
context.ComputeShader.Set(cs);
context.ComputeShader.SetConstantBuffer(0, constantBuffer);
int dataCount = DATASIZE;
bool flag = false;
do
{
// SRVとUAVを入れ替える
flag = !flag;
context.ComputeShader.SetShaderResource(0, null);
context.ComputeShader.SetUnorderedAccessView(0, uavs[flag ? 1 : 0]);
context.ComputeShader.SetShaderResource(0, srvs[flag ? 0 : 1]);
// 定数バッファを更新
device.ImmediateContext.UpdateSubresource(new int[] { dataCount, 0, 0, 0 }, constantBuffer);
// 実行
int threadGroup = (dataCount + 127) / 128;
context.Dispatch(threadGroup, 1, 1);
dataCount = threadGroup;
} while (dataCount > 1);
// GPUメモリから結果をコピー
context.CopySubresourceRegion(buffers[flag ? 1 : 0], 0, new ResourceRegion(0, 0, 0, 4, 1, 1), readBackBuffer, 0);
DataStream ds;
var dataBox = context.MapSubresource(readBackBuffer, MapMode.Read, MapFlags.None, out ds);
float result = ds.Read<float>();
context.UnmapSubresource(readBackBuffer, 0);
// DirectXのリソースを全て解放
context.ClearState();
Utilities.Dispose(ref srvs[0]);
Utilities.Dispose(ref srvs[1]);
Utilities.Dispose(ref uavs[0]);
Utilities.Dispose(ref uavs[1]);
Utilities.Dispose(ref buffers[0]);
Utilities.Dispose(ref buffers[1]);
Utilities.Dispose(ref constantBuffer);
Utilities.Dispose(ref readBackBuffer);
Utilities.Dispose(ref cs);
Utilities.Dispose(ref device);
// 結果の比較
float sum = 0;
for (int i = 0; i < DATASIZE; i++)
{
sum += inputData[i];
}
Console.WriteLine("CPU=" + sum);
Console.WriteLine("GPU=" + result);
}
}
}
cbuffer CB : register(b0)
{
unsigned int g_iCount;
unsigned int dummy0;
unsigned int dummy1;
unsigned int dummy2;
}
StructuredBuffer<float> Input : register(t0);
RWStructuredBuffer<float> Result : register(u0);
groupshared float shared_data[128];
[numthreads(128, 1, 1)]
void CS(uint3 Gid : SV_GroupID,
uint3 DTid : SV_DispatchThreadID,
uint3 GTid : SV_GroupThreadID,
uint GI : SV_GroupIndex)
{
if (DTid.x < g_iCount) {
shared_data[GI] = Input[DTid.x];
}
else {
shared_data[GI] = 0;
}
GroupMemoryBarrierWithGroupSync();
if (GI < 64) {
shared_data[GI] += shared_data[GI + 64];
}
GroupMemoryBarrierWithGroupSync();
if (GI < 32) {
shared_data[GI] += shared_data[GI + 32];
}
GroupMemoryBarrierWithGroupSync();
if (GI < 16) {
shared_data[GI] += shared_data[GI + 16];
}
GroupMemoryBarrierWithGroupSync();
if (GI < 8) {
shared_data[GI] += shared_data[GI + 8];
}
GroupMemoryBarrierWithGroupSync();
if (GI < 4) {
shared_data[GI] += shared_data[GI + 4];
}
GroupMemoryBarrierWithGroupSync();
if (GI < 2) {
shared_data[GI] += shared_data[GI + 2];
}
GroupMemoryBarrierWithGroupSync();
if (GI < 1) {
shared_data[GI] += shared_data[GI + 1];
}
if (GI == 0) {
Result[Gid.x] = shared_data[0];
}
}
@ninekorn
Copy link

ninekorn commented Jan 1, 2023

Hi,

i was wondering which type of license will you be using for your scripts? I don't use your vectorsum logic yet in my project except the logic for swapping the SRV's and UAV's in order to send the data directly to a vertex shader without using deviceContext.CopyResource. I learned how to use compute shaders in low-level barebone c# with SharpDX because of your example, and learned by myself how to send the data to a vertex shader. There was nowhere else to learn from compute shaders with SharpDX. Somehow i don't understand why i need to do a swap of the UAVs and SRVs for anything to work even in my example. If you have anytime to answer, it would be great.

thank you so much for sharing this.

@oguna
Copy link
Author

oguna commented Jan 3, 2023

First of all, the license for this program is in the public domain. Please feel free to use it.

I cannot remember the details of the program, because it was written several years ago.
I am sorry that I cannot help you.

@ninekorn
Copy link

ninekorn commented Jan 4, 2023

Thank you for your time answering my question. The reason i was asking, was because of Github's licensing rules as explained below:


  1. License Grant to Other Users

Any User-Generated Content you post publicly, including issues, comments, and contributions to other Users' repositories, may be viewed by others. By setting your repositories to be viewed publicly, you agree to allow others to view and "fork" your repositories (this means that others may make their own copies of Content from your repositories in repositories they control).

If you set your pages and repositories to be viewed publicly, you grant each User of GitHub a nonexclusive, worldwide license to use, display, and perform Your Content through the GitHub Service and to reproduce Your Content solely on GitHub as permitted through GitHub's functionality (for example, through forking). You may grant further rights if you adopt a license. If you are uploading Content you did not create or own, you are responsible for ensuring that the Content you upload is licensed under terms that grant these permissions to other GitHub Users.

They say that i can only reproduce my content on github through forking (or through other githubs services) when there is no licenses available. Whether you choose to put a license on your program or not in the future, i thank you for sharing it because there was nothing anywhere on the web where it was explained how to create and communicate to a compute shader in SharpDX.

Thank you again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment