Skip to content

Instantly share code, notes, and snippets.

@bwenzel2
Created July 28, 2016 12:21
Show Gist options
  • Save bwenzel2/ef89cf68145556fa6dd289df0fe24329 to your computer and use it in GitHub Desktop.
Save bwenzel2/ef89cf68145556fa6dd289df0fe24329 to your computer and use it in GitHub Desktop.
using System.Linq;
using System.Windows.Controls;
using SharpDX.DXGI;
using SharpDX;
using SharpDX.WPF;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.D3DCompiler;
using System.Runtime.InteropServices;
namespace CurrentShaderWPFTest
{
// This struct represents the constant buffer data we send to the GPU, and contains a *timeSTEP*
// Need to explicitly set layout kind, buffer size (which must be a multiple of 16), and field offset,
// see: http://timjones.tw/blog/archive/2011/03/08/marshalling-c-structures-into-directd-cbuffers-using
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct TimeStruct
{
[FieldOffset(0)]
public float time;
}
// Represents a DirectX scene
class Scene
{
#region fields
// The interface to the WPF interop image that the rendered scene is output to
private SharpDX.WPF.DXImageSource d3dimgSource;
// Stores the width and height of the image that d3dimgSource connects to
private int width;
private int height;
// The DirectX device manages all non-drawing functionality
private SharpDX.Direct3D11.Device defaultDevice;
// The DirectX context manages all drawing functionality
private SharpDX.Direct3D11.DeviceContext d3dDeviceContext;
// RenderTargetView is an interface to the render target's subresources
private SharpDX.Direct3D11.RenderTargetView renderTargetView;
// The viewport is a rectangle that defines the area of the frame buffer that you're rendering to
private SharpDX.Viewport viewport;
// Vertex data
private Vector4[] vertices;
// A CPU-side buffer that DirectX duplicates and manages on the GPU side for us
private SharpDX.Direct3D11.Buffer vertexBuffer;
// Shaders
private SharpDX.Direct3D11.VertexShader vertexShader;
private SharpDX.Direct3D11.GeometryShader geometryShader;
private SharpDX.Direct3D11.PixelShader pixelShader;
// "A shader signature is a list of the parameters that are either input to or output from a shader function." --MSDN
private ShaderSignature inputSignature;
// The input layout describes how the GPU should interpret the vertex data that's fed to it
private SharpDX.Direct3D11.InputLayout inputLayout;
// The constant buffer that will feed the current time into the vertex shader
public SharpDX.Direct3D11.Buffer timeBuffer;
public SharpDX.Direct3D11.Buffer streamOutputBuffer;
// The texture the scene will be rendered to
private SharpDX.Direct3D11.Texture2D renderTexture;
#endregion
// paramter "img" is the image in the WPF application that the DirectX scene will be rendered to
public Scene(System.Windows.Controls.Image img)
{
this.d3dimgSource = (DXImageSource)img.Source;
this.width = (int)img.Width;
this.height = (int)img.Height;
InitializeSharpDX();
}
~Scene()
{
ReleaseResources();
}
// Should be called when the image is resized (could be as part of window resize?)
public void SetDimensionsFromImage(Image img)
{
this.width = (int)img.Width;
this.height = (int)img.Height;
}
public void Draw()
{
//d3dDeviceContext.StreamOutput.SetTarget(streamOutputBuffer, 0);
// Clear the render target, effectively erasing any old content
d3dDeviceContext.ClearRenderTargetView(renderTargetView, new SharpDX.Color(0, 255, 0, 255));
// Feed the new timestep into the shader via the constant buffer
UpdateTimeBuffer(0.5f);
// Draw the vertices
d3dDeviceContext.Draw(vertices.Count(), 0);
// Send all queued commands to the GPU
// Without calling this function, the vertices will only be drawn to screen intermittently (not sure why? TODO: find out)
d3dDeviceContext.Flush();
// Set the rendered texture as the back buffer of the WPF interop image source
d3dimgSource.SetBackBuffer(renderTexture);
// Assign the contents of the stream-output buffer to the vertex buffer for the next frame's input
//vertexBuffer = streamOutputBuffer;
}
private void InitializeSharpDX()
{
//==== DEVICE AND CONTEXT INITIALIZATION ====//
defaultDevice = new SharpDX.Direct3D11.Device(DriverType.Hardware);
d3dDeviceContext = defaultDevice.ImmediateContext;
//==== VIEWPORT INITIALIZATION ====//
viewport = new Viewport(0, 0, width, height);
// Bind the viewport to the rasterizer stage of the DirectX pipeline (necessary in order to display anything)
d3dDeviceContext.Rasterizer.SetViewport(viewport);
//==== RENDER TEXTURE INITIALIZATION ====//
Texture2DDescription desc = new Texture2DDescription()
{
Width = width,
Height = height,
MipLevels = 1,
ArraySize = 1,
Format = Format.B8G8R8A8_UNorm,
SampleDescription = new SampleDescription(1, 0),
Usage = ResourceUsage.Default,
BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.Shared // Has to be shared for D3DImage interop
};
renderTexture = new Texture2D(defaultDevice, desc);
//==== RENDER TARGET INITIALIZATION ====//
renderTargetView = new SharpDX.Direct3D11.RenderTargetView(defaultDevice, renderTexture);
// Bind the render target view to the output merger stage of the DirectX pipeline
d3dDeviceContext.OutputMerger.SetRenderTargets(renderTargetView);
//==== VERTEX DATA AND VERTEX BUFFER INITIALIZATION ====//
//4x4 grid of particles
vertices = new Vector4[] { new Vector4(-0.6f, 0.6f, 0.0f, 1.0f),
new Vector4(-0.2f, 0.6f, 0.0f, 1.0f),
new Vector4(0.2f, 0.6f, 0.0f, 1.0f),
new Vector4(0.6f, 0.6f, 0.0f, 1.0f),
new Vector4(-0.6f, 0.2f, 0.0f, 1.0f),
new Vector4(-0.2f, 0.2f, 0.0f, 1.0f),
new Vector4(0.2f, 0.2f, 0.0f, 1.0f),
new Vector4(0.6f, 0.2f, 0.0f, 1.0f),
new Vector4(-0.6f, -0.2f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.2f, 0.0f, 1.0f),
new Vector4(0.2f, -0.2f, 0.0f, 1.0f),
new Vector4(0.6f, -0.2f, 0.0f, 1.0f),
new Vector4(-0.6f, -0.6f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.6f, 0.0f, 1.0f),
new Vector4(0.2f, -0.6f, 0.0f, 1.0f),
new Vector4(0.6f, -0.6f, 0.0f, 1.0f) };
// Create stream-output buffer
BufferDescription streamOutputDesc = new BufferDescription()
{
BindFlags = BindFlags.StreamOutput | BindFlags.VertexBuffer,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
SizeInBytes = Utilities.SizeOf<Vector4>() * vertices.Length,
StructureByteStride = 0,
Usage = ResourceUsage.Default
};
streamOutputBuffer = new SharpDX.Direct3D11.Buffer(defaultDevice, streamOutputDesc);
// Create vertex buffer and initialize it with the vertex data
vertexBuffer = SharpDX.Direct3D11.Buffer.Create<Vector4>(defaultDevice, BindFlags.VertexBuffer, vertices);
CompileShaders();
SetShaderInputs();
AttachShaders();
InitializeTimeBuffer();
}
private void CompileShaders()
{
// Compile vertex shader
using (var vertexShaderByteCode = ShaderBytecode.CompileFromFile("shaders.hlsl", "VS", "vs_4_0", ShaderFlags.Debug))
{
inputSignature = ShaderSignature.GetInputSignature(vertexShaderByteCode);
vertexShader = new SharpDX.Direct3D11.VertexShader(defaultDevice, vertexShaderByteCode);
}
// Compile geometry shader
using (var geometryShaderByteCode = ShaderBytecode.CompileFromFile("shaders.hlsl", "GS", "gs_4_0", ShaderFlags.Debug))
{
SharpDX.Direct3D11.StreamOutputElement[] outputElements = new SharpDX.Direct3D11.StreamOutputElement[]
{
new SharpDX.Direct3D11.StreamOutputElement(0, "SV_POSITION", 0, 0, 1, 0)
};
geometryShader = new SharpDX.Direct3D11.GeometryShader(defaultDevice, geometryShaderByteCode, outputElements, null, 0);
}
// Compile pixel shader
using (var pixelShaderByteCode = ShaderBytecode.CompileFromFile("shaders.hlsl", "PS", "ps_4_0", ShaderFlags.Debug))
{
pixelShader = new SharpDX.Direct3D11.PixelShader(defaultDevice, pixelShaderByteCode);
}
}
private void SetShaderInputs()
{
//describe the elements for each vertex (we use this description to create the input layout)
SharpDX.Direct3D11.InputElement[] inputElements = new SharpDX.Direct3D11.InputElement[]
{
new SharpDX.Direct3D11.InputElement("POSITION", 0, Format.R32G32B32A32_Float, 0, 0)
};
inputLayout = new SharpDX.Direct3D11.InputLayout(defaultDevice, inputSignature, inputElements);
d3dDeviceContext.InputAssembler.InputLayout = inputLayout;
d3dDeviceContext.InputAssembler.PrimitiveTopology = PrimitiveTopology.PointList;
d3dDeviceContext.InputAssembler.SetVertexBuffers(0, new SharpDX.Direct3D11.VertexBufferBinding(vertexBuffer, Utilities.SizeOf<Vector4>(), 0));
// Set stream-output destination buffer
d3dDeviceContext.StreamOutput.SetTarget(streamOutputBuffer, 0);
}
// Bind shaders to their respective pipeline stages
private void AttachShaders()
{
d3dDeviceContext.VertexShader.Set(vertexShader);
d3dDeviceContext.GeometryShader.Set(geometryShader);
d3dDeviceContext.PixelShader.Set(pixelShader);
}
private void InitializeTimeBuffer()
{
// Initialize the buffer
var dataSize = Utilities.SizeOf<TimeStruct>();
timeBuffer = new SharpDX.Direct3D11.Buffer(defaultDevice, dataSize, ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 0);
// Bind the buffer to the vertex shader at slot 0 (which is register b0 in the shader)
int registerSlot = 0;
d3dDeviceContext.VertexShader.SetConstantBuffer(registerSlot, timeBuffer);
}
private void UpdateTimeBuffer(float timestep)
{
TimeStruct newTime = new TimeStruct()
{
time = timestep
};
// Update constant buffer
DataStream stream;
d3dDeviceContext.MapSubresource(timeBuffer, Utilities.SizeOf<TimeStruct>(), MapMode.WriteDiscard, SharpDX.Direct3D11.MapFlags.None, out stream);
stream.Write(newTime.time);
d3dDeviceContext.UnmapSubresource(timeBuffer, 0);
stream.Dispose();
}
private void ReleaseResources()
{
vertexBuffer.Dispose();
streamOutputBuffer.Dispose();
timeBuffer.Dispose();
inputSignature.Dispose();
inputLayout.Dispose();
pixelShader.Dispose();
vertexShader.Dispose();
renderTargetView.Dispose();
renderTexture.Dispose();
defaultDevice.Dispose();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment