protected Texture GetReadback(Texture texture) { if ((texture.Usage & TextureUsage.Staging) != 0) { return(texture); } else { uint layers = texture.ArrayLayers; if ((texture.Usage & TextureUsage.Cubemap) != 0) { layers *= 6; } TextureDescription desc = new TextureDescription( texture.Width, texture.Height, texture.Depth, texture.MipLevels, layers, texture.Format, TextureUsage.Staging, texture.Type); Texture readback = RF.CreateTexture(ref desc); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture(texture, readback); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); return(readback); } }
public Texture LoadTexture(Stream stream, bool staging) { using (stream) { Texture stagingTex = LoadStaging(stream); if (staging) { return(stagingTex); } Texture sampledTex = _rf.CreateTexture(TextureDescription.Texture2D( stagingTex.Width, stagingTex.Height, mipLevels: 1, arrayLayers: 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled )); using CommandList cl = _gd.ResourceFactory.CreateCommandList(); cl.Begin(); cl.CopyTexture(source: stagingTex, destination: sampledTex); cl.End(); _gd.SubmitCommands(cl); // TODO: report the GL backend's quirk upstream if (_gd.BackendType == GraphicsBackend.OpenGL) { _gd.DisposeWhenIdle(stagingTex); } else { stagingTex.Dispose(); } return(sampledTex); } }
public void NoDepthTarget_ClearAllColors_Succeeds() { Texture colorTarget = RF.CreateTexture( TextureDescription.Texture2D(1024, 1024, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.RenderTarget)); Framebuffer fb = RF.CreateFramebuffer(new FramebufferDescription(null, colorTarget)); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetFramebuffer(fb); cl.ClearColorTarget(0, RgbaFloat.Red); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); Texture staging = RF.CreateTexture( TextureDescription.Texture2D(1024, 1024, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Staging)); cl.Begin(); cl.CopyTexture( colorTarget, 0, 0, 0, 0, 0, staging, 0, 0, 0, 0, 0, 1024, 1024, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <RgbaFloat> view = GD.Map <RgbaFloat>(staging, MapMode.Read); for (int i = 0; i < view.Count; i++) { Assert.Equal(RgbaFloat.Red, view[i]); } GD.Unmap(staging); }
public unsafe void Update_ThenCopySingleMip_Succeeds_R16UNorm() { TextureDescription desc = TextureDescription.Texture2D( 1024, 1024, 3, 1, PixelFormat.R16_UNorm, TextureUsage.Staging); Texture src = RF.CreateTexture(desc); Texture dst = RF.CreateTexture(desc); ushort[] data = Enumerable.Range(0, 256 * 256).Select(i => (ushort)i).ToArray(); fixed(ushort *dataPtr = data) { GD.UpdateTexture(src, (IntPtr)dataPtr, 256 * 256 * sizeof(ushort), 0, 0, 0, 256, 256, 1, 2, 0); } CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture(src, dst, 2, 0); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResource map = GD.Map(dst, MapMode.Read, 2); ushort * mappedFloatPtr = (ushort *)map.Data; for (int y = 0; y < 256; y++) { for (int x = 0; x < 256; x++) { uint mapIndex = (uint)(y * (map.RowPitch / sizeof(ushort)) + x); ushort value = (ushort)(y * 256 + x); Assert.Equal(value, mappedFloatPtr[mapIndex]); } } }
public unsafe Texture CreateDeviceTexture(GraphicsDevice gd, ResourceFactory rf, TextureUsage usage) { using (Texture staging = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format, TextureUsage.Staging, Type))) { staging.Name = Name + "_Staging"; fixed(uint *texDataPtr = &TextureData[0]) { uint subresourceSize = Width * Height * 4; gd.UpdateTexture( staging, (IntPtr)texDataPtr, subresourceSize, 0, 0, 0, Width, Height, 1, 0, 0); } Texture texture = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format, usage, Type)); texture.Name = Name; using (CommandList cl = rf.CreateCommandList()) { cl.Begin(); cl.CopyTexture(staging, texture); cl.End(); gd.SubmitCommands(cl); } IsDirty = false; return(texture); } }
public void Render(CommandList cl, ISurfaceCopyStageModel stage, GpuSurface source) { if (cl == null || stage == null || source == null) { _frameworkMessenger.Report("Warning: you are feeding the Surface Copy Stage Renderer null inputs, aborting"); return; } //Ensure the staging texture is of the correct size var stagingTexture = _gpuSurfaceManager.RetrieveSurface(stage.StagingTextureId); var doStagingTexturePropertiesMatch = source.Texture.Width == stagingTexture.Texture.Width && source.Texture.Height == stagingTexture.Texture.Height; var pixelFormatOfSource = source.Texture.Format; if (pixelFormatOfSource != stagingTexture.Texture.Format) { doStagingTexturePropertiesMatch = false; } if (!doStagingTexturePropertiesMatch) { stage.SetPixelFormatAndCreateStagingTextureAndDataArray(source.Texture.Width, source.Texture.Height, TexturePixelFormatConverter.ConvertVeldridToYak(pixelFormatOfSource)); stagingTexture = _gpuSurfaceManager.RetrieveSurface(stage.StagingTextureId); } cl.CopyTexture(source.Texture, stagingTexture.Texture); }
public static unsafe Texture CreateSimpleTexture <T>(GraphicsDevice gd, TextureUsage usage, IReadOnlyTexture <T> texture) where T : unmanaged { if (gd == null) { throw new ArgumentNullException(nameof(gd)); } if (texture == null) { throw new ArgumentNullException(nameof(texture)); } var pixelFormat = GetFormat(typeof(T)); bool mip = (usage & TextureUsage.GenerateMipmaps) != 0; uint mipLevels = mip ? MipLevelCount(texture.Width, texture.Height) : 1; using Texture staging = gd.ResourceFactory.CreateTexture(new TextureDescription( (uint)texture.Width, (uint)texture.Height, 1, mipLevels, 1, pixelFormat, TextureUsage.Staging, TextureType.Texture2D)); staging.Name = "T_" + texture.Name + "_Staging"; var buffer = texture.PixelData; fixed(T *texDataPtr = &buffer[0]) { gd.UpdateTexture( staging, (IntPtr)texDataPtr, (uint)(buffer.Length * Unsafe.SizeOf <T>()), 0, 0, 0, (uint)texture.Width, (uint)texture.Height, 1, 0, 0); } Texture veldridTexture = gd.ResourceFactory.CreateTexture(new TextureDescription( (uint)texture.Width, (uint)texture.Height, 1, mipLevels, 1, pixelFormat, usage, TextureType.Texture2D)); veldridTexture.Name = "T_" + texture.Name; using CommandList cl = gd.ResourceFactory.CreateCommandList(); cl.Begin(); cl.CopyTexture(staging, veldridTexture); if (mip) { cl.GenerateMipmaps(veldridTexture); } cl.End(); gd.SubmitCommands(cl); return(veldridTexture); }
/// <summary> /// Returns the number of texels in the texture that DO NOT match the fill value. /// </summary> private static int CountTexelsNotFilledAtDepth <TexelType>(GraphicsDevice device, Texture texture, TexelType fillValue, uint depth) where TexelType : unmanaged { ResourceFactory factory = device.ResourceFactory; // We need to create a staging texture and copy into it. TextureDescription description = new TextureDescription(texture.Width, texture.Height, depth: 1, texture.MipLevels, texture.ArrayLayers, texture.Format, TextureUsage.Staging, texture.Type, texture.SampleCount); Texture staging = factory.CreateTexture(ref description); using CommandList cl = factory.CreateCommandList(); cl.Begin(); cl.CopyTexture(texture, srcX: 0, srcY: 0, srcZ: depth, srcMipLevel: 0, srcBaseArrayLayer: 0, staging, dstX: 0, dstY: 0, dstZ: 0, dstMipLevel: 0, dstBaseArrayLayer: 0, staging.Width, staging.Height, depth: 1, layerCount: 1); cl.End(); device.SubmitCommands(cl); device.WaitForIdle(); try { MappedResourceView <TexelType> mapped = device.Map <TexelType>(staging, MapMode.Read); int notFilledCount = 0; for (int y = 0; y < staging.Height; y++) { for (int x = 0; x < staging.Width; x++) { TexelType actual = mapped[x, y]; if (!fillValue.Equals(actual)) { notFilledCount++; } } } return(notFilledCount); } finally { device.Unmap(staging); } }
private unsafe Texture CreateTextureViaStaging(GraphicsDevice gd, ResourceFactory factory) { Texture staging = factory.CreateTexture( TextureDescription.Texture2D(Width, Height, MipLevels, 1, Format, TextureUsage.Staging)); Texture ret = factory.CreateTexture( TextureDescription.Texture2D(Width, Height, MipLevels, 1, Format, TextureUsage.Sampled)); CommandList cl = gd.ResourceFactory.CreateCommandList(); cl.Begin(); for (uint level = 0; level < MipLevels; level++) { Image <Rgba32> image = Images[level]; if (!image.TryGetSinglePixelSpan(out Span <Rgba32> pixelSpan)) { throw new VeldridException("Unable to get image pixelspan."); } fixed(void *pin = &MemoryMarshal.GetReference(pixelSpan)) { MappedResource map = gd.Map(staging, MapMode.Write, level); uint rowWidth = (uint)(image.Width * 4); if (rowWidth == map.RowPitch) { Unsafe.CopyBlock(map.Data.ToPointer(), pin, (uint)(image.Width * image.Height * 4)); } else { for (uint y = 0; y < image.Height; y++) { byte *dstStart = (byte *)map.Data.ToPointer() + y * map.RowPitch; byte *srcStart = (byte *)pin + y * rowWidth; Unsafe.CopyBlock(dstStart, srcStart, rowWidth); } } gd.Unmap(staging, level); cl.CopyTexture( staging, 0, 0, 0, level, 0, ret, 0, 0, 0, level, 0, (uint)image.Width, (uint)image.Height, 1, 1); } } cl.End(); gd.SubmitCommands(cl); staging.Dispose(); cl.Dispose(); return(ret); }
public void NonZeroMipLevel_ClearColor_Succeeds() { Texture testTex = RF.CreateTexture( TextureDescription.Texture2D(1024, 1024, 11, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.RenderTarget)); Framebuffer[] framebuffers = new Framebuffer[11]; for (uint level = 0; level < 11; level++) { framebuffers[level] = RF.CreateFramebuffer( new FramebufferDescription(null, new[] { new FramebufferAttachmentDescription(testTex, 0, level) })); } CommandList cl = RF.CreateCommandList(); cl.Begin(); for (uint level = 0; level < 11; level++) { cl.SetFramebuffer(framebuffers[level]); cl.ClearColorTarget(0, new RgbaFloat(level, level, level, 1)); } cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); Texture readback = RF.CreateTexture( TextureDescription.Texture2D(1024, 1024, 11, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Staging)); cl.Begin(); cl.CopyTexture(testTex, readback); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); uint mipWidth = 1024; uint mipHeight = 1024; for (uint level = 0; level < 11; level++) { MappedResourceView <RgbaFloat> readView = GD.Map <RgbaFloat>(readback, MapMode.Read, level); for (uint y = 0; y < mipHeight; y++) { for (uint x = 0; x < mipWidth; x++) { Assert.Equal(new RgbaFloat(level, level, level, 1), readView[x, y]); } } GD.Unmap(readback, level); mipWidth = Math.Max(1, mipWidth / 2); mipHeight = Math.Max(1, mipHeight / 2); } }
public unsafe void Copy_Compressed_Texture(PixelFormat format, uint blockSizeInBytes, uint srcX, uint srcY, uint copyWidth, uint copyHeight) { if (!GD.GetPixelFormatSupport(format, TextureType.Texture2D, TextureUsage.Sampled)) { return; } Texture copySrc = RF.CreateTexture(TextureDescription.Texture2D( 64, 64, 1, 1, format, TextureUsage.Staging)); Texture copyDst = RF.CreateTexture(TextureDescription.Texture2D( copyWidth, copyHeight, 1, 1, format, TextureUsage.Staging)); const int numPixelsInBlockSide = 4; const int numPixelsInBlock = 16; uint totalDataSize = copyWidth * copyHeight / numPixelsInBlock * blockSizeInBytes; byte[] data = new byte[totalDataSize]; for (int i = 0; i < data.Length; i++) { data[i] = (byte)i; } fixed(byte *dataPtr = data) { GD.UpdateTexture(copySrc, (IntPtr)dataPtr, totalDataSize, srcX, srcY, 0, copyWidth, copyHeight, 1, 0, 0); } CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture( copySrc, srcX, srcY, 0, 0, 0, copyDst, 0, 0, 0, 0, 0, copyWidth, copyHeight, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); uint numBytesPerRow = copyWidth / numPixelsInBlockSide * blockSizeInBytes; MappedResourceView <byte> view = GD.Map <byte>(copyDst, MapMode.Read); for (uint i = 0; i < data.Length; i++) { uint viewRow = i / numBytesPerRow; uint viewIndex = (view.MappedResource.RowPitch * viewRow) + (i % numBytesPerRow); Assert.Equal(data[i], view[viewIndex]); } GD.Unmap(copyDst); }
public unsafe Texture CreateDeviceTexture(GraphicsDevice gd, ResourceFactory rf, TextureUsage usage) { if (gd == null) { throw new ArgumentNullException(nameof(gd)); } if (rf == null) { throw new ArgumentNullException(nameof(rf)); } using Texture staging = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format.ToVeldrid(), TextureUsage.Staging, Type)); staging.Name = "T_" + Name + "_Staging"; ulong offset = 0; fixed(uint *texDataPtr = &_pixelData[0]) { for (uint level = 0; level < MipLevels; level++) { uint mipWidth = GetDimension(Width, level); uint mipHeight = GetDimension(Height, level); uint mipDepth = GetDimension(Depth, level); uint subresourceSize = mipWidth * mipHeight * mipDepth * FormatSize; for (uint layer = 0; layer < ArrayLayers; layer++) { gd.UpdateTexture( staging, (IntPtr)(texDataPtr + offset), subresourceSize, 0, 0, 0, mipWidth, mipHeight, mipDepth, level, layer); offset += subresourceSize; } } } Texture texture = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format.ToVeldrid(), usage, Type)); texture.Name = "T_" + Name; using (CommandList cl = rf.CreateCommandList()) { cl.Begin(); cl.CopyTexture(staging, texture); cl.End(); gd.SubmitCommands(cl); } IsDirty = false; return(texture); }
private protected override void UpdateTextureCore( Texture texture, IntPtr source, uint sizeInBytes, uint x, uint y, uint z, uint width, uint height, uint depth, uint mipLevel, uint arrayLayer) { MTLTexture mtlTex = Util.AssertSubtype <Texture, MTLTexture>(texture); if (mtlTex.StagingBuffer.IsNull) { Texture stagingTex = ResourceFactory.CreateTexture(new TextureDescription( width, height, depth, 1, 1, texture.Format, TextureUsage.Staging, texture.Type)); UpdateTexture(stagingTex, source, sizeInBytes, 0, 0, 0, width, height, depth, 0, 0); CommandList cl = ResourceFactory.CreateCommandList(); cl.Begin(); cl.CopyTexture( stagingTex, 0, 0, 0, 0, 0, texture, x, y, z, mipLevel, arrayLayer, width, height, depth, 1); cl.End(); SubmitCommands(cl); cl.Dispose(); stagingTex.Dispose(); } else { mtlTex.GetSubresourceLayout(mipLevel, arrayLayer, out uint dstRowPitch, out uint dstDepthPitch); ulong dstOffset = Util.ComputeSubresourceOffset(mtlTex, mipLevel, arrayLayer); uint srcRowPitch = FormatHelpers.GetRowPitch(width, texture.Format); uint srcDepthPitch = FormatHelpers.GetDepthPitch(srcRowPitch, height, texture.Format); Util.CopyTextureRegion( source.ToPointer(), 0, 0, 0, srcRowPitch, srcDepthPitch, (byte *)mtlTex.StagingBuffer.contents() + dstOffset, x, y, z, dstRowPitch, dstDepthPitch, width, height, depth, texture.Format); } }
private unsafe Texture CreateTextureViaStaging(GraphicsDevice gd, ResourceFactory factory) { Texture staging = factory.CreateTexture( new TextureDescription(Width, Height, 1, MipLevels, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); Texture ret = factory.CreateTexture( new TextureDescription(Width, Height, 1, MipLevels, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled)); CommandList cl = gd.ResourceFactory.CreateCommandList(); cl.Begin(); for (uint level = 0; level < MipLevels; level++) { Image <Rgba32> image = Images[level]; fixed(void *pin = &image.DangerousGetPinnableReferenceToPixelBuffer()) { MappedResource map = gd.Map(staging, MapMode.Write, level); uint rowWidth = (uint)(image.Width * 4); if (rowWidth == map.RowPitch) { Unsafe.CopyBlock(map.Data.ToPointer(), pin, (uint)(image.Width * image.Height * 4)); } else { for (uint y = 0; y < image.Height; y++) { byte *dstStart = (byte *)map.Data.ToPointer() + y * map.RowPitch; byte *srcStart = (byte *)pin + y * rowWidth; Unsafe.CopyBlock(dstStart, srcStart, rowWidth); } } gd.Unmap(staging, level); cl.CopyTexture( staging, 0, 0, 0, level, 0, ret, 0, 0, 0, level, 0, (uint)image.Width, (uint)image.Height, 1, 1); } } cl.End(); gd.ExecuteCommands(cl); cl.Dispose(); staging.Dispose(); return(ret); }
public unsafe Texture CreateTexture(GraphicsDevice gd, ResourceFactory rf, TextureUsage usage, ImageBuffer image) { uint MipLevels = 1; uint Width = (uint)image.Width; uint Height = (uint)image.Height; uint Depth = 1; // (uint)image.BitDepth; uint ArrayLayers = 1; var Format = PixelFormat.B8_G8_R8_A8_UNorm; Texture texture = rf.CreateTexture(new TextureDescription( Width, Height, Depth, MipLevels, ArrayLayers, Format, usage, TextureType.Texture2D)); Texture staging = rf.CreateTexture(new TextureDescription( Width, Height, Depth, MipLevels, ArrayLayers, Format, TextureUsage.Staging, TextureType.Texture2D)); ulong offset = 0; fixed(byte *texDataPtr = &image.GetBuffer()[0]) { for (uint level = 0; level < MipLevels; level++) { uint mipWidth = GetDimension(Width, level); uint mipHeight = GetDimension(Height, level); uint mipDepth = GetDimension(Depth, level); uint subresourceSize = mipWidth * mipHeight * mipDepth * GetFormatSize(Format); for (uint layer = 0; layer < ArrayLayers; layer++) { gd.UpdateTexture( staging, (IntPtr)(texDataPtr + offset), subresourceSize, 0, 0, 0, mipWidth, mipHeight, mipDepth, level, layer); offset += subresourceSize; } } } CommandList cl = rf.CreateCommandList(); cl.Begin(); cl.CopyTexture(staging, texture); cl.End(); gd.SubmitCommands(cl); return(texture); }
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 void Copy_WithOffsets_2D(TextureUsage srcUsage, TextureUsage dstUsage) { Texture src = RF.CreateTexture(TextureDescription.Texture2D( 100, 100, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, srcUsage)); Texture dst = RF.CreateTexture(TextureDescription.Texture2D( 100, 100, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, dstUsage)); RgbaByte[] srcData = new RgbaByte[src.Height * src.Width]; for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { srcData[y * src.Width + x] = new RgbaByte((byte)x, (byte)y, 0, 1); } } GD.UpdateTexture(src, srcData, 0, 0, 0, src.Width, src.Height, 1, 0, 0); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture( src, 50, 50, 0, 0, 0, dst, 10, 10, 0, 0, 0, 50, 50, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); Texture readback = GetReadback(dst); MappedResourceView <RgbaByte> readView = GD.Map <RgbaByte>(readback, MapMode.Read); for (int y = 10; y < 60; y++) { for (int x = 10; x < 60; x++) { Assert.Equal(new RgbaByte((byte)(x + 40), (byte)(y + 40), 0, 1), readView[x, y]); } } GD.Unmap(readback); }
public void Copy_WitOffsets_2D() { Texture src = RF.CreateTexture(TextureDescription.Texture2D( 100, 100, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); Texture dst = RF.CreateTexture(TextureDescription.Texture2D( 100, 100, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); MappedResourceView <RgbaByte> writeView = GD.Map <RgbaByte>(src, MapMode.Write); for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { writeView[x, y] = new RgbaByte((byte)x, (byte)y, 0, 1); } } GD.Unmap(src); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture( src, 50, 50, 0, 0, 0, dst, 10, 10, 0, 0, 0, 50, 50, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <RgbaByte> readView = GD.Map <RgbaByte>(dst, MapMode.Read); for (int y = 10; y < 60; y++) { for (int x = 10; x < 60; x++) { Assert.Equal(new RgbaByte((byte)(x + 40), (byte)(y + 40), 0, 1), readView[x, y]); } } GD.Unmap(dst); }
public void Copy_DifferentMip_1DTo2D() { if (!GD.Features.Texture1D) { return; } Texture tex1D = RF.CreateTexture( TextureDescription.Texture1D(200, 2, 1, PixelFormat.R16_UNorm, TextureUsage.Staging)); Texture tex2D = RF.CreateTexture( TextureDescription.Texture2D(100, 10, 1, 1, PixelFormat.R16_UNorm, TextureUsage.Staging)); MappedResourceView <ushort> writeView = GD.Map <ushort>(tex1D, MapMode.Write, 1); Assert.Equal(tex2D.Width, (uint)writeView.Count); for (int i = 0; i < writeView.Count; i++) { writeView[i] = (ushort)(i * 2); } GD.Unmap(tex1D, 1); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture( tex1D, 0, 0, 0, 1, 0, tex2D, 0, 5, 0, 0, 0, tex2D.Width, 1, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.DisposeWhenIdle(cl); GD.WaitForIdle(); MappedResourceView <ushort> readView = GD.Map <ushort>(tex2D, MapMode.Read); for (int i = 0; i < tex2D.Width; i++) { Assert.Equal((ushort)(i * 2), readView[i, 5]); } GD.Unmap(tex2D); }
/// <inheritdoc /> public unsafe int RegisterTexture(Span <Color> pixels, int width, int height) { var factory = GraphicsDevice.ResourceFactory; Texture staging = factory.CreateTexture( TextureDescription.Texture2D((uint)width, (uint)height, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Staging)); Texture ret = factory.CreateTexture( TextureDescription.Texture2D((uint)width, (uint)height, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.Sampled)); CommandList cl = factory.CreateCommandList(); cl.Begin(); MappedResource map = GraphicsDevice.Map(staging, MapMode.Write, 0); if (width == map.RowPitch / 4) { var dst = new Span <Color>(map.Data.ToPointer(), width * height); pixels.CopyTo(dst); } else { for (var y = 0; y < height; y++) { var dst = new Span <Color>(IntPtr.Add(map.Data, y * (int)map.RowPitch).ToPointer(), width); var src = pixels.Slice(y * width, width); src.CopyTo(dst); } } GraphicsDevice.Unmap(staging); cl.CopyTexture(staging, ret); cl.End(); GraphicsDevice.SubmitCommands(cl); GraphicsDevice.DisposeWhenIdle(staging); GraphicsDevice.DisposeWhenIdle(cl); return(Register(ret)); }
public unsafe Texture CreateDeviceTexture(GraphicsDevice gd, ResourceFactory rf, TextureUsage usage) { if (gd == null) { throw new ArgumentNullException(nameof(gd)); } if (rf == null) { throw new ArgumentNullException(nameof(rf)); } using Texture staging = rf.CreateTexture(new TextureDescription( Width, Height, Depth, MipLevels, ArrayLayers, Format.ToVeldrid(), TextureUsage.Staging, Type)); staging.Name = Name + "_Staging"; fixed(uint *texDataPtr = &TextureData[0]) { uint subresourceSize = Width * Height * 4; gd.UpdateTexture( staging, (IntPtr)texDataPtr, subresourceSize, 0, 0, 0, Width, Height, 1, 0, 0); } Texture texture = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format.ToVeldrid(), usage, Type)); texture.Name = Name; using (CommandList cl = rf.CreateCommandList()) // TODO: Update texture without a dedicated command list to improve perf. { cl.Begin(); cl.CopyTexture(staging, texture); cl.End(); gd.SubmitCommands(cl); } IsDirty = false; return(texture); }
public unsafe Texture CreateDeviceTexture(GraphicsDevice gd, ResourceFactory rf, TextureUsage usage) { Texture texture = rf.CreateTexture(new TextureDescription( Width, Height, Depth, MipLevels, ArrayLayers, Format, usage, Type)); Texture staging = rf.CreateTexture(new TextureDescription( Width, Height, Depth, MipLevels, ArrayLayers, Format, TextureUsage.Staging, Type)); ulong offset = 0; fixed(byte *texDataPtr = &TextureData[0]) { for (uint level = 0; level < MipLevels; level++) { uint mipWidth = GetDimension(Width, level); uint mipHeight = GetDimension(Height, level); uint mipDepth = GetDimension(Depth, level); uint subresourceSize = mipWidth * mipHeight * mipDepth * GetFormatSize(Format); for (uint layer = 0; layer < ArrayLayers; layer++) { gd.UpdateTexture( staging, (IntPtr)(texDataPtr + offset), subresourceSize, 0, 0, 0, mipWidth, mipHeight, mipDepth, level, layer); offset += subresourceSize; } } } CommandList cl = rf.CreateCommandList(); cl.Begin(); cl.CopyTexture(staging, texture); cl.End(); gd.SubmitCommands(cl); return(texture); }
public unsafe void Copy_BC3_Unorm() { Texture copySrc = RF.CreateTexture(TextureDescription.Texture2D( 64, 64, 1, 1, PixelFormat.BC3_UNorm, TextureUsage.Staging)); Texture copyDst = RF.CreateTexture(TextureDescription.Texture2D( 64, 64, 1, 1, PixelFormat.BC3_UNorm, TextureUsage.Staging)); uint totalDataSize = copySrc.Width * copySrc.Height; byte[] data = new byte[totalDataSize]; for (int i = 0; i < data.Length; i++) { data[i] = (byte)i; } fixed(byte *dataPtr = data) { GD.UpdateTexture(copySrc, (IntPtr)dataPtr, totalDataSize, 0, 0, 0, copySrc.Width, copySrc.Height, 1, 0, 0); } CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.CopyTexture( copySrc, 0, 0, 0, 0, 0, copyDst, 0, 0, 0, 0, 0, copySrc.Width, copySrc.Height, 1, 1); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <byte> view = GD.Map <byte>(copyDst, MapMode.Read); for (int i = 0; i < data.Length; i++) { Assert.Equal(view[i], data[i]); } }
public static Icon Load(RenderContext renderContext, IconPathPattern pathPattern) { ContentManager content = renderContext.Content; ResourceFactory rf = renderContext.ResourceFactory; Texture? texture = null; CommandList cl = renderContext.RentCommandList(); cl.Begin(); uint layer = 0; foreach (string path in pathPattern.EnumeratePaths()) { Texture staging = content.LoadTexture(path, staging: true); texture ??= rf.CreateTexture(TextureDescription.Texture2D( staging.Width, staging.Height, mipLevels: 1, arrayLayers: pathPattern.IconCount, staging.Format, TextureUsage.Sampled )); cl.CopyTexture( staging, srcX: 0, srcY: 0, srcZ: 0, srcMipLevel: 0, srcBaseArrayLayer: 0, texture, dstX: 0, dstY: 0, dstZ: 0, dstMipLevel: 0, layer++, texture.Width, texture.Height, depth: 1, layerCount: 1 ); } cl.End(); renderContext.GraphicsDevice.SubmitCommands(cl); renderContext.ReturnCommandList(cl); Debug.Assert(texture is not null); return(new Icon(texture)); }
public unsafe void Copy_Compressed_Array(bool separateLayerCopies) { PixelFormat format = PixelFormat.BC3_UNorm; if (!GD.GetPixelFormatSupport(format, TextureType.Texture2D, TextureUsage.Sampled)) { return; } TextureDescription texDesc = TextureDescription.Texture2D( 16, 16, 1, 4, format, TextureUsage.Sampled); Texture copySrc = RF.CreateTexture(texDesc); texDesc.Usage = TextureUsage.Staging; Texture copyDst = RF.CreateTexture(texDesc); for (uint layer = 0; layer < copySrc.ArrayLayers; layer++) { int byteCount = 16 * 16; byte[] data = Enumerable.Range(0, byteCount).Select(i => (byte)(i + layer)).ToArray(); GD.UpdateTexture( copySrc, data, 0, 0, 0, 16, 16, 1, 0, layer); } CommandList copyCL = RF.CreateCommandList(); copyCL.Begin(); if (separateLayerCopies) { for (uint layer = 0; layer < copySrc.ArrayLayers; layer++) { copyCL.CopyTexture(copySrc, 0, 0, 0, 0, layer, copyDst, 0, 0, 0, 0, layer, 16, 16, 1, 1); } } else { copyCL.CopyTexture(copySrc, 0, 0, 0, 0, 0, copyDst, 0, 0, 0, 0, 0, 16, 16, 1, copySrc.ArrayLayers); } copyCL.End(); Fence fence = RF.CreateFence(false); GD.SubmitCommands(copyCL, fence); GD.WaitForFence(fence); for (uint layer = 0; layer < copyDst.ArrayLayers; layer++) { MappedResource map = GD.Map(copyDst, MapMode.Read, layer); byte * basePtr = (byte *)map.Data; int index = 0; uint rowSize = 64; uint numRows = 4; for (uint row = 0; row < numRows; row++) { byte *rowBase = basePtr + (row * map.RowPitch); for (uint x = 0; x < rowSize; x++) { Assert.Equal((byte)(index + layer), rowBase[x]); index += 1; } } GD.Unmap(copyDst, layer); } }
public unsafe void Points_WithTexture_UpdateUnrelated(bool useTextureView) { // This is a regression test for the case where a user modifies an unrelated texture // at a time after a ResourceSet containing a texture has been bound. The OpenGL // backend was caching texture state improperly, resulting in wrong textures being sampled. Texture target = RF.CreateTexture(TextureDescription.Texture2D( 50, 50, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.RenderTarget)); Texture staging = RF.CreateTexture(TextureDescription.Texture2D( 50, 50, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Staging)); Framebuffer framebuffer = RF.CreateFramebuffer(new FramebufferDescription(null, target)); DeviceBuffer orthoBuffer = RF.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer)); Matrix4x4 orthoMatrix = Matrix4x4.CreateOrthographicOffCenter( 0, framebuffer.Width, framebuffer.Height, 0, -1, 1); GD.UpdateBuffer(orthoBuffer, 0, ref orthoMatrix); Texture sampledTexture = RF.CreateTexture( TextureDescription.Texture2D(1, 1, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Sampled)); RgbaFloat white = RgbaFloat.White; GD.UpdateTexture(sampledTexture, (IntPtr)(&white), (uint)Unsafe.SizeOf <RgbaFloat>(), 0, 0, 0, 1, 1, 1, 0, 0); Texture shouldntBeSampledTexture = RF.CreateTexture( TextureDescription.Texture2D(1, 1, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Sampled)); ShaderSetDescription shaderSet = new ShaderSetDescription( new VertexLayoutDescription[] { new VertexLayoutDescription( new VertexElementDescription("Position", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2)) }, TestShaders.LoadVertexFragment(RF, "TexturedPoints")); ResourceLayout layout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("Ortho", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("Tex", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("Smp", ResourceKind.Sampler, ShaderStages.Fragment))); ResourceSet set; if (useTextureView) { TextureView view = RF.CreateTextureView(sampledTexture); set = RF.CreateResourceSet(new ResourceSetDescription(layout, orthoBuffer, view, GD.PointSampler)); } else { set = RF.CreateResourceSet(new ResourceSetDescription(layout, orthoBuffer, sampledTexture, GD.PointSampler)); } GraphicsPipelineDescription gpd = new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.Disabled, RasterizerStateDescription.Default, PrimitiveTopology.PointList, shaderSet, layout, framebuffer.OutputDescription); Pipeline pipeline = RF.CreateGraphicsPipeline(ref gpd); Vector2[] vertices = new Vector2[] { new Vector2(0.5f, 0.5f), new Vector2(15.5f, 15.5f), new Vector2(25.5f, 26.5f), new Vector2(3.5f, 25.5f), }; DeviceBuffer vb = RF.CreateBuffer( new BufferDescription((uint)(Unsafe.SizeOf <Vector2>() * vertices.Length), BufferUsage.VertexBuffer)); GD.UpdateBuffer(vb, 0, vertices); CommandList cl = RF.CreateCommandList(); for (int i = 0; i < 2; i++) { cl.Begin(); cl.SetFramebuffer(framebuffer); cl.ClearColorTarget(0, RgbaFloat.Black); cl.SetPipeline(pipeline); cl.SetVertexBuffer(0, vb); cl.SetGraphicsResourceSet(0, set); // Modify an unrelated texture. // This must have no observable effect on the next draw call. RgbaFloat pink = RgbaFloat.Pink; GD.UpdateTexture(shouldntBeSampledTexture, (IntPtr)(&pink), (uint)Unsafe.SizeOf <RgbaFloat>(), 0, 0, 0, 1, 1, 1, 0, 0); cl.Draw((uint)vertices.Length); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); } cl.Begin(); cl.CopyTexture(target, staging); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <RgbaFloat> readView = GD.Map <RgbaFloat>(staging, MapMode.Read); foreach (Vector2 vertex in vertices) { uint x = (uint)vertex.X; uint y = (uint)vertex.Y; if (!GD.IsUvOriginTopLeft || GD.IsClipSpaceYInverted) { y = framebuffer.Height - y - 1; } Assert.Equal(white, readView[x, y], RgbaFloatFuzzyComparer.Instance); } GD.Unmap(staging); }
public Texture CreateDeviceTexture(GraphicsDevice gd, ResourceFactory rf, TextureUsage usage) { using var _ = PerfTracker.FrameEvent("6.1.2.1 Rebuild MultiTextures"); if (IsMetadataDirty) { RebuildLayers(); } var palette = PaletteManager.Palette.GetCompletePalette(); using var staging = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format, TextureUsage.Staging, Type)); staging.Name = "T_" + Name + "_Staging"; Span <uint> toBuffer = stackalloc uint[(int)(Width * Height)]; foreach (var lsi in LogicalSubImages) { //if (!rebuildAll && !lsi.IsPaletteAnimated) // TODO: Requires caching a single Texture and then modifying it // continue; for (int i = 0; i < lsi.Frames; i++) { toBuffer.Fill(lsi.IsAlphaTested ? 0 : 0xff000000); Rebuild(lsi, i, toBuffer, palette); uint destinationLayer = (uint)LayerLookup[new LayerKey(lsi.Id, i)]; unsafe { fixed(uint *toBufferPtr = toBuffer) { gd.UpdateTexture( staging, (IntPtr)toBufferPtr, Width * Height * sizeof(uint), 0, 0, 0, Width, Height, 1, 0, destinationLayer); } } } } /* TODO: Mipmap * for (uint level = 1; level < MipLevels; level++) * { * } //*/ var texture = rf.CreateTexture(new TextureDescription(Width, Height, Depth, MipLevels, ArrayLayers, Format, usage, Type)); texture.Name = "T_" + Name; using (CommandList cl = rf.CreateCommandList()) { cl.Begin(); cl.CopyTexture(staging, texture); cl.End(); gd.SubmitCommands(cl); } IsDirty = false; return(texture); }
public void Points_WithFloat16Color() { Texture target = RF.CreateTexture(TextureDescription.Texture2D( 50, 50, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.RenderTarget)); Texture staging = RF.CreateTexture(TextureDescription.Texture2D( 50, 50, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Staging)); Framebuffer framebuffer = RF.CreateFramebuffer(new FramebufferDescription(null, target)); DeviceBuffer infoBuffer = RF.CreateBuffer(new BufferDescription(16, BufferUsage.UniformBuffer)); DeviceBuffer orthoBuffer = RF.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer)); Matrix4x4 orthoMatrix = Matrix4x4.CreateOrthographicOffCenter( 0, framebuffer.Width, framebuffer.Height, 0, -1, 1); GD.UpdateBuffer(orthoBuffer, 0, ref orthoMatrix); ShaderSetDescription shaderSet = new ShaderSetDescription( new VertexLayoutDescription[] { new VertexLayoutDescription( new VertexElementDescription("Position", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2), new VertexElementDescription("Color_Half", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Half4)) }, TestShaders.LoadVertexFragment(RF, "F16VertexAttribs")); ResourceLayout layout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("InfoBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("OrthoBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex))); ResourceSet set = RF.CreateResourceSet(new ResourceSetDescription(layout, infoBuffer, orthoBuffer)); GraphicsPipelineDescription gpd = new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.Disabled, RasterizerStateDescription.Default, PrimitiveTopology.PointList, shaderSet, layout, framebuffer.OutputDescription); Pipeline pipeline = RF.CreateGraphicsPipeline(ref gpd); uint colorNormalizationFactor = 2500; const ushort f16_375 = 0x5DDC; // 375.0 const ushort f16_500 = 0x5FD0; // 500.0 const ushort f16_625 = 0x60E2; // 625.0 const ushort f16_875 = 0x62D6; // 875.0 const ushort f16_1250 = 0x64E2; // 1250.0 const ushort f16_1875 = 0x6753; // 1875.0 VertexCPU_UShort[] vertices = new VertexCPU_UShort[] { new VertexCPU_UShort { Position = new Vector2(0.5f, 0.5f), R = f16_625, G = f16_1250, B = f16_1875, }, new VertexCPU_UShort { Position = new Vector2(10.5f, 12.5f), R = f16_625, G = f16_1250, B = f16_1875, }, new VertexCPU_UShort { Position = new Vector2(25.5f, 35.5f), R = f16_1875, G = f16_1250, B = f16_625, }, new VertexCPU_UShort { Position = new Vector2(49.5f, 49.5f), R = f16_375, G = f16_500, B = f16_875, }, }; RgbaFloat[] expectedColors = new[] { new RgbaFloat( 625.0f / colorNormalizationFactor, 1250.0f / colorNormalizationFactor, 1875.0f / colorNormalizationFactor, 1), new RgbaFloat( 625.0f / colorNormalizationFactor, 1250.0f / colorNormalizationFactor, 1875.0f / colorNormalizationFactor, 1), new RgbaFloat( 1875.0f / colorNormalizationFactor, 1250.0f / colorNormalizationFactor, 625.0f / colorNormalizationFactor, 1), new RgbaFloat( 375.0f / colorNormalizationFactor, 500.0f / colorNormalizationFactor, 875.0f / colorNormalizationFactor, 1), }; DeviceBuffer vb = RF.CreateBuffer( new BufferDescription((uint)(Unsafe.SizeOf <UIntVertexAttribsVertex>() * vertices.Length), BufferUsage.VertexBuffer)); GD.UpdateBuffer(vb, 0, vertices); GD.UpdateBuffer(infoBuffer, 0, new UIntVertexAttribsInfo { ColorNormalizationFactor = colorNormalizationFactor }); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetFramebuffer(framebuffer); cl.SetFullViewports(); cl.SetFullScissorRects(); cl.ClearColorTarget(0, RgbaFloat.Black); cl.SetPipeline(pipeline); cl.SetVertexBuffer(0, vb); cl.SetGraphicsResourceSet(0, set); cl.Draw((uint)vertices.Length); cl.CopyTexture(target, staging); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <RgbaFloat> readView = GD.Map <RgbaFloat>(staging, MapMode.Read); for (int i = 0; i < vertices.Length; i++) { VertexCPU_UShort vertex = vertices[i]; uint x = (uint)vertex.Position.X; uint y = (uint)vertex.Position.Y; if (!GD.IsUvOriginTopLeft || GD.IsClipSpaceYInverted) { y = framebuffer.Height - y - 1; } RgbaFloat expectedColor = expectedColors[i]; Assert.Equal(expectedColor, readView[x, y], RgbaFloatFuzzyComparer.Instance); } GD.Unmap(staging); }
public static unsafe Texture LoadTexture( GraphicsDevice gd, ResourceFactory factory, Stream assetStream, PixelFormat format) { KtxFile ktxTex2D = Load(assetStream, false); uint width = ktxTex2D.Header.PixelWidth; uint height = ktxTex2D.Header.PixelHeight; if (height == 0) { height = width; } uint arrayLayers = Math.Max(1, ktxTex2D.Header.NumberOfArrayElements); uint mipLevels = Math.Max(1, ktxTex2D.Header.NumberOfMipmapLevels); Texture ret = factory.CreateTexture(TextureDescription.Texture2D( width, height, mipLevels, arrayLayers, format, TextureUsage.Sampled)); Texture stagingTex = factory.CreateTexture(TextureDescription.Texture2D( width, height, mipLevels, arrayLayers, format, TextureUsage.Staging)); // Copy texture data into staging buffer for (uint level = 0; level < mipLevels; level++) { KtxMipmapLevel mipmap = ktxTex2D.Mipmaps[level]; for (uint layer = 0; layer < arrayLayers; layer++) { KtxArrayElement ktxLayer = mipmap.ArrayElements[layer]; Debug.Assert(ktxLayer.Faces.Length == 1); byte[] pixelData = ktxLayer.Faces[0].Data; fixed(byte *pixelDataPtr = &pixelData[0]) { gd.UpdateTexture(stagingTex, (IntPtr)pixelDataPtr, (uint)pixelData.Length, 0, 0, 0, mipmap.Width, mipmap.Height, 1, level, layer); } } } CommandList copyCL = factory.CreateCommandList(); copyCL.Begin(); for (uint level = 0; level < mipLevels; level++) { KtxMipmapLevel mipLevel = ktxTex2D.Mipmaps[level]; for (uint layer = 0; layer < arrayLayers; layer++) { copyCL.CopyTexture( stagingTex, 0, 0, 0, level, layer, ret, 0, 0, 0, level, layer, mipLevel.Width, mipLevel.Height, mipLevel.Depth, 1); } } copyCL.End(); gd.SubmitCommands(copyCL); gd.DisposeWhenIdle(copyCL); gd.DisposeWhenIdle(stagingTex); return(ret); }
public void Points_WithUShortColor() { Texture target = RF.CreateTexture(TextureDescription.Texture2D( 50, 50, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.RenderTarget)); Texture staging = RF.CreateTexture(TextureDescription.Texture2D( 50, 50, 1, 1, PixelFormat.R32_G32_B32_A32_Float, TextureUsage.Staging)); Framebuffer framebuffer = RF.CreateFramebuffer(new FramebufferDescription(null, target)); DeviceBuffer infoBuffer = RF.CreateBuffer(new BufferDescription(16, BufferUsage.UniformBuffer)); DeviceBuffer orthoBuffer = RF.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer)); Matrix4x4 orthoMatrix = Matrix4x4.CreateOrthographicOffCenter( 0, framebuffer.Width, framebuffer.Height, 0, -1, 1); GD.UpdateBuffer(orthoBuffer, 0, ref orthoMatrix); ShaderSetDescription shaderSet = new ShaderSetDescription( new VertexLayoutDescription[] { new VertexLayoutDescription( new VertexElementDescription("Position", VertexElementSemantic.Position, VertexElementFormat.Float2), new VertexElementDescription("Color_UInt", VertexElementSemantic.Color, VertexElementFormat.UShort4)) }, new Shader[] { TestShaders.Load(RF, "U16VertexAttribs", ShaderStages.Vertex, "VS"), TestShaders.Load(RF, "U16VertexAttribs", ShaderStages.Fragment, "FS") }); ResourceLayout layout = RF.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("InfoBuffer", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("Ortho", ResourceKind.UniformBuffer, ShaderStages.Vertex))); ResourceSet set = RF.CreateResourceSet(new ResourceSetDescription(layout, infoBuffer, orthoBuffer)); GraphicsPipelineDescription gpd = new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.Disabled, RasterizerStateDescription.Default, PrimitiveTopology.PointList, shaderSet, layout, framebuffer.OutputDescription); Pipeline pipeline = RF.CreateGraphicsPipeline(ref gpd); uint colorNormalizationFactor = 2500; VertexCPU_UShort[] vertices = new VertexCPU_UShort[] { new VertexCPU_UShort { Position = new Vector2(0.5f, 0.5f), R = (ushort)(0.25f * colorNormalizationFactor), G = (ushort)(0.5f * colorNormalizationFactor), B = (ushort)(0.75f * colorNormalizationFactor), }, new VertexCPU_UShort { Position = new Vector2(10.5f, 12.5f), R = (ushort)(0.25f * colorNormalizationFactor), G = (ushort)(0.5f * colorNormalizationFactor), B = (ushort)(0.75f * colorNormalizationFactor), }, new VertexCPU_UShort { Position = new Vector2(25.5f, 35.5f), R = (ushort)(0.75f * colorNormalizationFactor), G = (ushort)(0.5f * colorNormalizationFactor), B = (ushort)(0.25f * colorNormalizationFactor), }, new VertexCPU_UShort { Position = new Vector2(49.5f, 49.5f), R = (ushort)(0.15f * colorNormalizationFactor), G = (ushort)(0.2f * colorNormalizationFactor), B = (ushort)(0.35f * colorNormalizationFactor), }, }; DeviceBuffer vb = RF.CreateBuffer( new BufferDescription((uint)(Unsafe.SizeOf <UIntVertexAttribs.Vertex>() * vertices.Length), BufferUsage.VertexBuffer)); GD.UpdateBuffer(vb, 0, vertices); GD.UpdateBuffer(infoBuffer, 0, new UIntVertexAttribs.Info { ColorNormalizationFactor = colorNormalizationFactor }); CommandList cl = RF.CreateCommandList(); cl.Begin(); cl.SetFramebuffer(framebuffer); cl.SetFullViewports(); cl.SetFullScissorRects(); cl.ClearColorTarget(0, RgbaFloat.Black); cl.SetPipeline(pipeline); cl.SetVertexBuffer(0, vb); cl.SetGraphicsResourceSet(0, set); cl.Draw((uint)vertices.Length); cl.SetFramebuffer(GD.SwapchainFramebuffer); cl.ClearColorTarget(0, RgbaFloat.Red); cl.CopyTexture(target, staging); cl.End(); GD.SubmitCommands(cl); GD.WaitForIdle(); MappedResourceView <RgbaFloat> readView = GD.Map <RgbaFloat>(staging, MapMode.Read); foreach (VertexCPU_UShort vertex in vertices) { uint x = (uint)vertex.Position.X; uint y = (uint)vertex.Position.Y; if (GD.BackendType == GraphicsBackend.OpenGL) { y = framebuffer.Height - y - 1; } RgbaFloat expectedColor = new RgbaFloat( vertex.R / (float)colorNormalizationFactor, vertex.G / (float)colorNormalizationFactor, vertex.B / (float)colorNormalizationFactor, 1); Assert.Equal(expectedColor, readView[x, y], RgbaFloatFuzzyComparer.Instance); } GD.Unmap(staging); }