public unsafe void MapWrite_ThenMapRead_3D() { Texture tex3D = RF.CreateTexture(TextureDescription.Texture3D( 10, 10, 10, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); MappedResourceView <RgbaByte> writeView = GD.Map <RgbaByte>(tex3D, MapMode.Write); for (int z = 0; z < tex3D.Depth; z++) { for (int y = 0; y < tex3D.Height; y++) { for (int x = 0; x < tex3D.Width; x++) { writeView[x, y, z] = new RgbaByte((byte)x, (byte)y, (byte)z, 1); } } } GD.Unmap(tex3D); MappedResourceView <RgbaByte> readView = GD.Map <RgbaByte>(tex3D, MapMode.Read, 0); for (int z = 0; z < tex3D.Depth; z++) { for (int y = 0; y < tex3D.Height; y++) { for (int x = 0; x < tex3D.Width; x++) { Assert.Equal(new RgbaByte((byte)x, (byte)y, (byte)z, 1), readView[x, y, z]); } } } GD.Unmap(tex3D); }
public unsafe void Map_NonZeroMip_3D() { Texture tex3D = RF.CreateTexture(TextureDescription.Texture3D( 40, 40, 40, 3, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); MappedResourceView <RgbaByte> writeView = GD.Map <RgbaByte>(tex3D, MapMode.Write, 2); for (int z = 0; z < 10; z++) { for (int y = 0; y < 10; y++) { for (int x = 0; x < 10; x++) { writeView[x, y, z] = new RgbaByte((byte)x, (byte)y, (byte)z, 1); } } } GD.Unmap(tex3D, 2); MappedResourceView <RgbaByte> readView = GD.Map <RgbaByte>(tex3D, MapMode.Read, 2); for (int z = 0; z < 10; z++) { for (int y = 0; y < 10; y++) { for (int x = 0; x < 10; x++) { Assert.Equal(new RgbaByte((byte)x, (byte)y, (byte)z, 1), readView[x, y, z]); } } } GD.Unmap(tex3D, 2); }
public unsafe void Update_NonStaging_3D() { Texture tex3D = RF.CreateTexture(TextureDescription.Texture3D( 16, 16, 16, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled)); RgbaByte[] data = new RgbaByte[16 * 16 * 16]; for (int z = 0; z < 16; z++) for (int y = 0; y < 16; y++) { for (int x = 0; x < 16; x++) { int index = (int)(z * tex3D.Width * tex3D.Height + y * tex3D.Height + x); data[index] = new RgbaByte((byte)x, (byte)y, (byte)z, 1); } } fixed(RgbaByte *dataPtr = data) { GD.UpdateTexture(tex3D, (IntPtr)dataPtr, (uint)(data.Length * Unsafe.SizeOf <RgbaByte>()), 0, 0, 0, tex3D.Width, tex3D.Height, tex3D.Depth, 0, 0); } Texture staging = RF.CreateTexture(TextureDescription.Texture3D( 16, 16, 16, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture(tex3D, staging); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <RgbaByte> view = GD.Map <RgbaByte>(staging, MapMode.Read); for (int z = 0; z < tex3D.Depth; z++) for (int y = 0; y < tex3D.Height; y++) { for (int x = 0; x < tex3D.Width; x++) { Assert.Equal(new RgbaByte((byte)x, (byte)y, (byte)z, 1), view[x, y, z]); } } }
public unsafe void Update_ThenMapRead_3D() { Texture tex3D = RF.CreateTexture(TextureDescription.Texture3D( 10, 10, 10, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); RgbaByte[] data = new RgbaByte[tex3D.Width * tex3D.Height * tex3D.Depth]; for (int z = 0; z < tex3D.Depth; z++) { for (int y = 0; y < tex3D.Height; y++) { for (int x = 0; x < tex3D.Width; x++) { int index = (int)(z * tex3D.Width * tex3D.Height + y * tex3D.Height + x); data[index] = new RgbaByte((byte)x, (byte)y, (byte)z, 1); } } fixed(RgbaByte *dataPtr = data) { GD.UpdateTexture(tex3D, (IntPtr)dataPtr, (uint)(data.Length * Unsafe.SizeOf <RgbaByte>()), 0, 0, 0, tex3D.Width, tex3D.Height, tex3D.Depth, 0, 0); } MappedResourceView <RgbaByte> view = GD.Map <RgbaByte>(tex3D, MapMode.Read, 0); for (int z = 0; z < tex3D.Depth; z++) { for (int y = 0; y < tex3D.Height; y++) { for (int x = 0; x < tex3D.Width; x++) { Assert.Equal(new RgbaByte((byte)x, (byte)y, (byte)z, 1), view[x, y, z]); } } } GD.Unmap(tex3D); }
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}"); } } }