Revision Difference
Compute_Shaders#547065
<cat>Code.Shader</cat>
<title>Compute Shaders</title>
<note>
TODO - this isn't finished, it's not an exhaustive example.. there's lots more to add here.
</note>
You can use compute shaders to do stuff.
Here's how you can write a really simple compute texture to generate a texture on the GPU at runtime.
## C# side
You'll first need to create a `SceneCustomObject` so that you can call `Dispatch` within the render loop:
```
public class MySceneObject : SceneCustomObject
{
private MyTextureGenerator myTextureGenerator;
public MySceneObject( SceneWorld sceneWorld ) : base( sceneWorld )
{
myTextureGenerator = new();
}
public override void RenderSceneObject()
{
base.RenderSceneObject();
myTextureGenerator.Render();
}
}
```
You'll also probably want to wrap your compute stuff in a nice class so that everything's nice and tidy:
```
public MyTextureGenerator()
{
// Create a texture that we can use
texture = Texture.Create( 512, 512 )
.WithUAVBinding() // Needs to have this if we're using it in a compute shader
.WithFormat( ImageFormat.RGBA16161616F ) // Other formats are available :-)
.Finish();
computeShader = new ComputeShader( "my_compute_shader" ); // This should be the name of your shader
}
public void Render()
{
computeShader.Attributes.Set( "Texture", texture );
computeShader.Attributes.Set( "OutputTexture", texture );
computeShader.Dispatch( texture.Width, texture.Height, 1 );
}
```
You also want to actually use that scene object in an entity... here's how you can do that:
```
public class MyModel : ModelEntity
{
private MySceneObject mySceneObject;
public override void Spawn()
{
base.Spawn();
Transmit = TransmitType.Always;
}
public override void ClientSpawn()
{
mySceneObject = new( Map.Scene )
{
Transform = this.Transform,
Position = this.Position,
Bounds = this.CollisionBounds + this.Position
};
}
protected override void OnDestroy()
{
base.OnDestroy();
mySceneObject?.Delete();
}
}
```
Spawn your entity somewhere, make sure it's visible.
## Shader
Compute shaders are similar to normal VS/FS shaders, but they're a bit shorter and you can forego a lot of the includes.
Here's a really simple one that'll generate a solid pink texture:
```
//=========================================================================================================================
// Optional
//=========================================================================================================================
HEADER
{
CompileTargets = ( IS_SM_50 && ( PC || VULKAN ) );
DevShader = true;
Description = "My Cool Compute Shader";
}
//=========================================================================================================================
// Optional
//=========================================================================================================================
FEATURES
{
}
//=========================================================================================================================
MODES
{
Default();
}
//=========================================================================================================================
COMMON
{
#include "common/shared.hlsl"
}
//=========================================================================================================================
CS
{
// Output texture
RWTexture2D<float4> g_tOutput< Attribute( "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 );
}
}
```
## Tips
If you're writing a compute shader you probably want to see its contents. You can do that really easily using `Render.Draw2D.Quad`.
```
public void RenderHud()
private static void DrawTexture( Texture texture, Rect rect )
{
var draw = Render.Draw2D;
⤶
draw.Texture = texture;
draw.Color = Color.White;
⤶
draw.Quad( new Vector2( 0, 0 ),
new Vector2( 512, 0 ),
new Vector2( 512, 512 ),⤶
new Vector2( 0, 512 ) );⤶
Render.Draw2D.Texture = texture;
Render.Draw2D.Quad( rect.BottomLeft,
rect.BottomRight,
rect.TopRight,
rect.TopLeft );
}
```
⤶
This'll draw your generated texture in the top left of the screen.