Created
July 28, 2016 12:21
-
-
Save bwenzel2/ef89cf68145556fa6dd289df0fe24329 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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