S&box Wiki

Revision Difference

Compute_Shaders#551338

<cat>Material.ShaderBasic</cat> <title>Compute Shaders</title> # What can you do with compute shaders? You can use compute shaders to do lots of things, but their most common use is to generate textures at blazing fast speeds; here's an example that shows you how you can do that. ## Using compute shaders from C# Here's an example of creating a compute shader and populating a texture with it. ```csharp // Create a compute shader from a .shader file var computeShader = new ComputeShader( "my_compute_shader" ); // Create a texture for the compute shader to use var texture = Texture.Create( 512, 512 ) .WithUAVBinding() // Needs to have this if we're using it in a compute shader .WithFormat( ImageFormat.RGBA16161616F ) // Use whatever you need .Finish(); // Attach texture to OutputTexture attribute in shader computeShader.Attributes.Set( "OutputTexture", texture ); // Dispatch computeShader.Dispatch( texture.Width, texture.Height, 1 ); ``` ## HLSL Shader Compute shaders are HLSL like normal shaders, except everything goes in a CS block and runs a method named MainCs. Here's a really simple one that'll generate a solid pink texture: ``` MODES { Default(); } COMMON { #include "common/shared.hlsl" } CS { // Output texture RWTexture2D<float4> g_tOutput< Attribute( "OutputTexture" ); >; [numthreads(8, 8, 1)] void MainCs( uint uGroupIndex : SV_GroupIndex, uint3 vThreadId : SV_DispatchThreadID ) { g_tOutput[vThreadId.xy] = float4( 1, 0, 1, 1 ); } } ``` ## Compute Buffers You can read and write arbitrary structured data using ComputeBuffers too. ### Setting data ```csharp var computeShader = new ComputeShader( "my_compute_shader" ); struct MyData { public float Value; } // Allocate the GPU buffer using ( var buffer = new ComputeBuffer<MyData>( 2 ) ) { // Upload data to the GPU buffer var data = new MyData[] { new MyData { Value = 1.0f }, new MyData { Value = 2.0f } }; buffer.SetData( data ); // Pass the buffer to a compute shader computeShader.Attributes.Set( "myData", buffer ); // Dispatch the shader computeShader.Dispatch(); } ``` ### Getting data ```csharp var computeShader = new ComputeShader( "my_compute_shader" ); // Allocate the GPU buffer to receive data using ( var buffer = new ComputeBuffer<float>( 2 ) ) { // Pass the buffer to a compute shader computeShader.Attributes.Set( "myData", buffer ); // Dispatch the shader computeShader.Dispatch(); // Get the data the compute shader has generated from the GPU var data = new float[2]; buffer.GetData( data ); } ``` ### Append buffers Append buffers have a hidden counter that can be accessed with ComputeBuffer.CopyStructureCount, this means the CPU can know how many elements have been pushed to a buffer by the GPU. ```csharp ComputeShader ??= new ComputeShader( "marchingcubes_cs" ); var countBuffer = new ComputeBuffer<int>( 1, ComputeBufferType.ByteAddress ); var trianglesBuffer = new ComputeBuffer<Triangle>( 512, ComputeBufferType.Append ); ComputeShader.Attributes.Set( "Triangles", trianglesBuffer ); ComputeShader.Dispatch( 8, 8, 8 );⤶ ⤶ // This sets the initial UAV counter for an AppendStructuredBuffer⤶ ComputeShader.Attributes.Set( "Triangles", 0 );⤶ ⤶ ComputeShader.Dispatch( 8, 8, 8 );⤶ // Copy how many triangles have been pushed to the buffer into our count buffer trianglesBuffer.CopyStructureCount( countBuffer, 0 ); // Grab the count to the CPU from the GPU buffer var count = new int[1] { 0 }; countBuffer.GetData( count, 0, 1 ); // Grab that number of triangles from the append buffer var tris = new Triangle[count[0]]; trianglesBuffer.GetData( tris, 0, count[0] ); ```