Skip to content

Instantly share code, notes, and snippets.

@OptimisticPeach
Created December 5, 2018 00:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OptimisticPeach/176be0672551c2a755dbd3696ad79448 to your computer and use it in GitHub Desktop.
Save OptimisticPeach/176be0672551c2a755dbd3696ad79448 to your computer and use it in GitHub Desktop.
Shader helper
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.IO;
using Buffer = SharpDX.Direct3D11.Buffer;
namespace VertexLerp
{
/// <summary>
/// Compute Shader Helper
/// Currently only supports Unordered Acces View binds
/// </summary>
public class ComputeShaderHelper
{
public struct ViewBuffer
{
public Buffer Buffer;
public UnorderedAccessView View;
public ViewBuffer(Buffer buffer, UnorderedAccessView view)
{
Buffer = buffer;
View = view;
}
}
Device Device { get; set; }
ComputeShader Shader { get; set; }
ViewBuffer[] Data;
int[] DataCount;
int currentDataset = 0;
static private MemoryStream MemStream;
static private StreamWriter MemWriter;
/// <summary>
/// Creates a stream from a string and returns it.
/// </summary>
/// <param name="s">The string to prelod into the stream</param>
/// <returns>A stream containing the string</returns>
public static Stream GenerateStreamFromString(string s)
{
MemStream = new MemoryStream();
MemWriter = new StreamWriter(MemStream);
MemWriter.Write(s);
MemWriter.Flush();
MemStream.Position = 0;
return MemStream;
}
/// <summary>
/// Compute Shader
/// </summary>
/// <param name="device">Graphics device</param>
/// <param name="filename">Path to uncompiled shaderfile.fx</param>
/// <param name="main">Main function in shader</param>
/// <param name="shaderversion">Compute shader version</param>
/// <param name="compile">Compile the shader, if false, uses previous compiled file</param>
public ComputeShaderHelper(Device device, string filename, string shaderversion = "cs_5_0", string main = "CSMain", bool compile = true)
{
Device = device;
// The maximum amount of unordered acces views that can be bound to the shader
// More info here: https://msdn.microsoft.com/en-us/library/windows/desktop/ff476331%28v=vs.85%29.aspx
if (Device.FeatureLevel == SharpDX.Direct3D.FeatureLevel.Level_11_0)
{
Data = new ViewBuffer[8];
DataCount = new int[8];
}
else
{
Data = new ViewBuffer[1];
DataCount = new int[1];
}
string existing = filename + ".hlsl";
if (File.Exists(existing) && !compile)
{
ShaderBytecode code = ShaderBytecode.FromFile(existing);
Shader = new ComputeShader(device, code);
}
else
{
CompilationResult code = ShaderBytecode.CompileFromFile(filename, main, shaderversion, ShaderFlags.None, EffectFlags.None);
Shader = new ComputeShader(device, code.Bytecode.Data);
//code.Save(GenerateStreamFromString(existing));
File.WriteAllBytes(existing, code.Bytecode.Data);
}
}
int GetNearestMultipleUp(int A, int Multiplier)
{
return A + Multiplier - (A % Multiplier);
}
/// <summary>
/// Initialize the buffer with correct size
/// </summary>
void InitializeBuffer(ref ViewBuffer buffer, int length, int elemSize)
{
buffer.Buffer = new Buffer(Device, elemSize * length, ResourceUsage.Default,
BindFlags.ShaderResource | BindFlags.UnorderedAccess, CpuAccessFlags.None,
ResourceOptionFlags.BufferStructured, elemSize);
buffer.View = new UnorderedAccessView(Device, buffer.Buffer);
}
/// <summary>
/// Execute the compute shader
/// </summary>
public void Execute(int nThreads)
{
Device.ImmediateContext.ComputeShader.Set(Shader);
for (int i = 0; i < currentDataset; i++)
{
Device.ImmediateContext.ComputeShader.SetUnorderedAccessView(i, Data[i].View);
}
Device.ImmediateContext.Dispatch(nThreads, 1, 1);
// Reset so the next data can be bound
currentDataset = 0;
}
static Buffer StagingBuffer;
DataStream dataStream;
ResourceRegion RR = new ResourceRegion(0, 0, 0, 1, 1, 1);
/// <summary>
/// Fills the buffer with data
/// <returns>Index of the array to retrieve the data</returns>
/// </summary>
public int SetData<T>(T[] data) where T : struct
{
if (currentDataset >= Data.Length)
throw new Exception("You can't bind more buffers to the shader!");
DataCount[currentDataset] = data.Length;
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
int totalSize = size * data.Length;
InitializeBuffer(ref Data[currentDataset], data.Length, size);
StagingBuffer = new Buffer(Device, totalSize, ResourceUsage.Staging, BindFlags.None, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
Device.ImmediateContext.MapSubresource(StagingBuffer, 0, MapMode.Write, MapFlags.None, out dataStream);
{
dataStream.WriteRange(data, 0, data.Length);
}
Device.ImmediateContext.UnmapSubresource(StagingBuffer, 0);
RR.Right = totalSize;
Device.ImmediateContext.CopySubresourceRegion(StagingBuffer, 0, RR, Data[currentDataset].Buffer, 0);
StagingBuffer.Dispose();
return currentDataset++;
}
/// <summary>
/// Fills the buffer with data
/// <returns>Index of the array to retrieve the data</returns>
/// </summary>
public void SetData<T>(T[] data, int index) where T : struct
{
if (index >= Data.Length)
throw new Exception("You can't bind more buffers to the shader!");
DataCount[index] = data.Length;
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
int totalSize = size * data.Length;
InitializeBuffer(ref Data[index], data.Length, size);
StagingBuffer = new Buffer(Device, totalSize, ResourceUsage.Staging, BindFlags.None, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
Device.ImmediateContext.MapSubresource(StagingBuffer, 0, MapMode.Write, MapFlags.None, out dataStream);
{
dataStream.WriteRange(data, 0, data.Length);
}
Device.ImmediateContext.UnmapSubresource(StagingBuffer, 0);
RR.Right = totalSize;
Device.ImmediateContext.CopySubresourceRegion(StagingBuffer, 0, RR, Data[index].Buffer, 0);
StagingBuffer.Dispose();
}
/// <summary>
/// Returns data from the buffer
/// </summary>
public T[] GetData<T>(int index) where T : struct
{
int count = DataCount[index];
int size = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
int totalSize = size * count;
var data = new T[count];
StagingBuffer = new Buffer(Device, totalSize, ResourceUsage.Staging, BindFlags.None, CpuAccessFlags.Read, ResourceOptionFlags.None, 0);
RR.Right = totalSize;
Device.ImmediateContext.CopySubresourceRegion(Data[index].Buffer, 0, RR, StagingBuffer, 0);
Device.ImmediateContext.MapSubresource(StagingBuffer, MapMode.Read, MapFlags.None, out dataStream);
dataStream.Position = 0;
dataStream.ReadRange(data, 0, count);
StagingBuffer.Dispose();
return data;
}
public void CopyToBuffer(int index, Resource Res)
{
Device.ImmediateContext.CopySubresourceRegion(Data[index].Buffer, 0, null, Res, 0);
}
}
}

Compute Shader Library Credit

I must credit Robbin Marcus for the use of their Compute Shader framework built around SharpDX. I have made changes mainly to optimize the library, but it is otherwise intact.

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