public void ComputeShader3dTexture() { // Just a dumb compute shader that fills a 3D texture with the same value from a uniform multiplied by the depth. string shaderText = @" #version 450 layout(set = 0, binding = 0, rgba32f) uniform image3D TextureToFill; layout(set = 0, binding = 1) uniform FillValueBuffer { float FillValue; float Padding1; float Padding2; float Padding3; }; layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; void main() { ivec3 textureCoordinate = ivec3(gl_GlobalInvocationID.xyz); float dataToStore = FillValue * (textureCoordinate.z + 1); imageStore(TextureToFill, textureCoordinate, vec4(dataToStore)); } "; const float FillValue = 42.42f; const uint OutputTextureSize = 32; using Shader computeShader = RF.CreateFromSpirv(new ShaderDescription( ShaderStages.Compute, Encoding.ASCII.GetBytes(shaderText), "main")); using ResourceLayout computeLayout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("TextureToFill", ResourceKind.TextureReadWrite, ShaderStages.Compute), new ResourceLayoutElementDescription("FillValueBuffer", ResourceKind.UniformBuffer, ShaderStages.Compute))); using Pipeline computePipeline = RF.CreateComputePipeline(new ComputePipelineDescription( computeShader, computeLayout, 16, 16, 1)); using DeviceBuffer fillValueBuffer = RF.CreateBuffer(new BufferDescription((uint)Marshal.SizeOf <FillValueStruct>(), BufferUsage.UniformBuffer)); // Create our output texture. using Texture computeTargetTexture = RF.CreateTexture(TextureDescription.Texture3D( OutputTextureSize, OutputTextureSize, OutputTextureSize, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Sampled | TextureUsage.Storage)); using TextureView computeTargetTextureView = RF.CreateTextureView(computeTargetTexture); using ResourceSet computeResourceSet = RF.CreateResourceSet(new ResourceSetDescription( computeLayout, computeTargetTextureView, fillValueBuffer)); using CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.UpdateBuffer(fillValueBuffer, 0, new FillValueStruct(FillValue)); // Use the compute shader to fill the texture. cl.SetPipeline(computePipeline); cl.SetComputeResourceSet(0, computeResourceSet); const uint GroupDivisorXY = 16; cl.Dispatch(OutputTextureSize / GroupDivisorXY, OutputTextureSize / GroupDivisorXY, OutputTextureSize); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); // Read back from our texture and make sure it has been properly filled. for (uint depth = 0; depth < computeTargetTexture.Depth; depth++) { RgbaFloat expectedFillValue = new RgbaFloat(new System.Numerics.Vector4(FillValue * (depth + 1))); int notFilledCount = CountTexelsNotFilledAtDepth(GD, computeTargetTexture, expectedFillValue, depth); if (notFilledCount == 0) { // Expected behavior: Console.WriteLine($"All texels were properly set at depth {depth}"); } else { // Unexpected behavior: uint totalTexels = computeTargetTexture.Width * computeTargetTexture.Height; throw new Exception($"{notFilledCount} of {totalTexels} texels were not properly set at depth {depth}"); } } }