public void FillBuffer_WithOffsets(uint srcSetMultiple, uint srcBindingMultiple, uint dstSetMultiple, uint dstBindingMultiple, bool combinedLayout) { if (!GD.Features.ComputeShader) { return; } Debug.Assert((GD.StructuredBufferMinOffsetAlignment % sizeof(uint)) == 0); uint valueCount = 512; uint dataSize = valueCount * sizeof(uint); uint totalSrcAlignment = GD.StructuredBufferMinOffsetAlignment * (srcSetMultiple + srcBindingMultiple); uint totalDstAlignment = GD.StructuredBufferMinOffsetAlignment * (dstSetMultiple + dstBindingMultiple); DeviceBuffer copySrc = RF.CreateBuffer( new BufferDescription(totalSrcAlignment + dataSize, BufferUsage.StructuredBufferReadOnly, sizeof(uint))); DeviceBuffer copyDst = RF.CreateBuffer( new BufferDescription(totalDstAlignment + dataSize, BufferUsage.StructuredBufferReadWrite, sizeof(uint))); ResourceLayout[] layouts; ResourceSet[] sets; DeviceBufferRange srcRange = new DeviceBufferRange(copySrc, srcSetMultiple * GD.StructuredBufferMinOffsetAlignment, dataSize); DeviceBufferRange dstRange = new DeviceBufferRange(copyDst, dstSetMultiple * GD.StructuredBufferMinOffsetAlignment, dataSize); if (combinedLayout) { layouts = new[] { RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription( "CopySrc", ResourceKind.StructuredBufferReadOnly, ShaderStages.Compute, ResourceLayoutElementOptions.DynamicBinding), new ResourceLayoutElementDescription( "CopyDst", ResourceKind.StructuredBufferReadWrite, ShaderStages.Compute, ResourceLayoutElementOptions.DynamicBinding))) }; sets = new[] { RF.CreateResourceSet(new ResourceSetDescription(layouts[0], srcRange, dstRange)) }; } else { layouts = new[] { RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription( "CopySrc", ResourceKind.StructuredBufferReadOnly, ShaderStages.Compute, ResourceLayoutElementOptions.DynamicBinding))), RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription( "CopyDst", ResourceKind.StructuredBufferReadWrite, ShaderStages.Compute, ResourceLayoutElementOptions.DynamicBinding))) }; sets = new[] { RF.CreateResourceSet(new ResourceSetDescription(layouts[0], srcRange)), RF.CreateResourceSet(new ResourceSetDescription(layouts[1], dstRange)), }; } Pipeline pipeline = RF.CreateComputePipeline(new ComputePipelineDescription( TestShaders.LoadCompute(RF, combinedLayout ? "FillBuffer" : "FillBuffer_SeparateLayout"), layouts, 1, 1, 1)); uint[] srcData = Enumerable.Range(0, (int)copySrc.SizeInBytes / sizeof(uint)).Select(i => (uint)i).ToArray(); GD.UpdateBuffer(copySrc, 0, srcData); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetPipeline(pipeline); if (combinedLayout) { uint[] offsets = new[] { srcBindingMultiple *GD.StructuredBufferMinOffsetAlignment, dstBindingMultiple *GD.StructuredBufferMinOffsetAlignment }; cl.SetComputeResourceSet(0, sets[0], offsets); } else { uint offset = srcBindingMultiple * GD.StructuredBufferMinOffsetAlignment; cl.SetComputeResourceSet(0, sets[0], 1, ref offset); offset = dstBindingMultiple * GD.StructuredBufferMinOffsetAlignment; cl.SetComputeResourceSet(1, sets[1], 1, ref offset); } cl.Dispatch(512, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); DeviceBuffer readback = GetReadback(copyDst); MappedResourceView <uint> readView = GD.Map <uint>(readback, MapMode.Read); for (uint i = 0; i < valueCount; i++) { uint srcIndex = totalSrcAlignment / sizeof(uint) + i; uint expected = srcData[(int)srcIndex]; uint dstIndex = totalDstAlignment / sizeof(uint) + i; uint actual = readView[dstIndex]; Assert.Equal(expected, actual); } GD.Unmap(readback); }
public void BasicCompute() { if (!GD.Features.ComputeShader) { return; } ResourceLayout layout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("Params", ResourceKind.UniformBuffer, ShaderStages.Compute), new ResourceLayoutElementDescription("Source", ResourceKind.StructuredBufferReadWrite, ShaderStages.Compute), new ResourceLayoutElementDescription("Destination", ResourceKind.StructuredBufferReadWrite, ShaderStages.Compute))); uint width = 1024; uint height = 1024; DeviceBuffer paramsBuffer = RF.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <BasicComputeTestParams>(), BufferUsage.UniformBuffer)); DeviceBuffer sourceBuffer = RF.CreateBuffer(new BufferDescription(width * height * 4, BufferUsage.StructuredBufferReadWrite, 4)); DeviceBuffer destinationBuffer = RF.CreateBuffer(new BufferDescription(width * height * 4, BufferUsage.StructuredBufferReadWrite, 4)); GD.UpdateBuffer(paramsBuffer, 0, new BasicComputeTestParams { Width = width, Height = height }); float[] sourceData = new float[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = y * (int)width + x; sourceData[index] = index; } } GD.UpdateBuffer(sourceBuffer, 0, sourceData); ResourceSet rs = RF.CreateResourceSet(new ResourceSetDescription(layout, paramsBuffer, sourceBuffer, destinationBuffer)); Pipeline pipeline = RF.CreateComputePipeline(new ComputePipelineDescription( TestShaders.LoadCompute(RF, "BasicComputeTest"), layout, 16, 16, 1)); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetPipeline(pipeline); cl.SetComputeResourceSet(0, rs); cl.Dispatch(width / 16, width / 16, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); DeviceBuffer sourceReadback = GetReadback(sourceBuffer); DeviceBuffer destinationReadback = GetReadback(destinationBuffer); MappedResourceView <float> sourceReadView = GD.Map <float>(sourceReadback, MapMode.Read); MappedResourceView <float> destinationReadView = GD.Map <float>(destinationReadback, MapMode.Read); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = y * (int)width + x; Assert.Equal(2 * sourceData[index], sourceReadView[index]); Assert.Equal(sourceData[index], destinationReadView[index]); } } GD.Unmap(sourceReadback); GD.Unmap(destinationReadback); }
public void ComputeGeneratedVertices() { if (!GD.Features.ComputeShader) { return; } uint width = 512; uint height = 512; Texture output = RF.CreateTexture( TextureDescription.Texture2D(width, height, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.RenderTarget)); Framebuffer framebuffer = RF.CreateFramebuffer(new FramebufferDescription(null, output)); uint vertexSize = (uint)Unsafe.SizeOf <ColoredVertex>(); DeviceBuffer buffer = RF.CreateBuffer(new BufferDescription( vertexSize * 4, BufferUsage.StructuredBufferReadWrite, vertexSize, true)); ResourceLayout computeLayout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("OutputVertices", ResourceKind.StructuredBufferReadWrite, ShaderStages.Compute))); ResourceSet computeSet = RF.CreateResourceSet(new ResourceSetDescription(computeLayout, buffer)); Pipeline computePipeline = RF.CreateComputePipeline(new ComputePipelineDescription( TestShaders.LoadCompute(RF, "ComputeColoredQuadGenerator"), computeLayout, 1, 1, 1)); ResourceLayout graphicsLayout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("InputVertices", ResourceKind.StructuredBufferReadOnly, ShaderStages.Vertex))); ResourceSet graphicsSet = RF.CreateResourceSet(new ResourceSetDescription(graphicsLayout, buffer)); Pipeline graphicsPipeline = RF.CreateGraphicsPipeline(new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.Disabled, RasterizerStateDescription.Default, PrimitiveTopology.TriangleStrip, new ShaderSetDescription( Array.Empty <VertexLayoutDescription>(), TestShaders.LoadVertexFragment(RF, "ColoredQuadRenderer")), graphicsLayout, framebuffer.OutputDescription)); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetPipeline(computePipeline); cl.SetComputeResourceSet(0, computeSet); cl.Dispatch(1, 1, 1); cl.SetFramebuffer(framebuffer); cl.ClearColorTarget(0, new RgbaFloat()); cl.SetPipeline(graphicsPipeline); cl.SetGraphicsResourceSet(0, graphicsSet); cl.Draw(4); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); Texture readback = GetReadback(output); MappedResourceView <RgbaFloat> readView = GD.Map <RgbaFloat>(readback, MapMode.Read); for (uint y = 0; y < height; y++) { for (uint x = 0; x < width; x++) { Assert.Equal(RgbaFloat.Red, readView[x, y]); } } GD.Unmap(readback); }
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}"); } } }
public void ComputeGeneratedTexture() { if (!GD.Features.ComputeShader) { return; } uint width = 4; uint height = 1; TextureDescription texDesc = TextureDescription.Texture2D( width, height, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Sampled | TextureUsage.Storage); Texture computeOutput = RF.CreateTexture(texDesc); texDesc.Usage = TextureUsage.RenderTarget; Texture finalOutput = RF.CreateTexture(texDesc); Framebuffer framebuffer = RF.CreateFramebuffer(new FramebufferDescription(null, finalOutput)); ResourceLayout computeLayout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("ComputeOutput", ResourceKind.TextureReadWrite, ShaderStages.Compute))); ResourceSet computeSet = RF.CreateResourceSet(new ResourceSetDescription(computeLayout, computeOutput)); Pipeline computePipeline = RF.CreateComputePipeline(new ComputePipelineDescription( TestShaders.LoadCompute(RF, "ComputeTextureGenerator"), computeLayout, 4, 1, 1)); ResourceLayout graphicsLayout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("Input", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("InputSampler", ResourceKind.Sampler, ShaderStages.Fragment))); ResourceSet graphicsSet = RF.CreateResourceSet(new ResourceSetDescription(graphicsLayout, computeOutput, GD.PointSampler)); Pipeline graphicsPipeline = RF.CreateGraphicsPipeline(new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.Disabled, RasterizerStateDescription.CullNone, PrimitiveTopology.TriangleStrip, new ShaderSetDescription( Array.Empty <VertexLayoutDescription>(), TestShaders.LoadVertexFragment(RF, "FullScreenBlit")), graphicsLayout, framebuffer.OutputDescription)); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetPipeline(computePipeline); cl.SetComputeResourceSet(0, computeSet); cl.Dispatch(1, 1, 1); cl.SetFramebuffer(framebuffer); cl.ClearColorTarget(0, new RgbaFloat()); cl.SetPipeline(graphicsPipeline); cl.SetGraphicsResourceSet(0, graphicsSet); cl.Draw(4); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); Texture readback = GetReadback(finalOutput); MappedResourceView <RgbaFloat> readView = GD.Map <RgbaFloat>(readback, MapMode.Read); Assert.Equal(RgbaFloat.Red, readView[0, 0]); Assert.Equal(RgbaFloat.Green, readView[1, 0]); Assert.Equal(RgbaFloat.Blue, readView[2, 0]); Assert.Equal(RgbaFloat.White, readView[3, 0]); GD.Unmap(readback); }