/// <summary> /// Executes a given test for a specified shader. /// </summary> /// <typeparam name="T">The type of shader to test.</typeparam> /// <param name="device">The device to use.</param> /// <param name="factory">A producer to create an instance of the shader to run.</param> /// <param name="delta">The comparison delta.</param> private static void RunAndCompareShader <T>(Device device, Func <ReadWriteTexture2D <Rgba32, Float4>, T> factory, float delta) where T : struct, IComputeShader { _ = device.Get(); string expectedPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) !, "Assets", $"{typeof(T).Name}.png"); IImageInfo imageInfo = Image.Identify(expectedPath); using Image <ImageSharpRgba32> image = new(imageInfo.Width, imageInfo.Height); using (ReadWriteTexture2D <Rgba32, Float4> texture = device.Get().AllocateReadWriteTexture2D <Rgba32, Float4>(imageInfo.Width, imageInfo.Height)) { device.Get().For(texture.Width, texture.Height, factory(texture)); _ = image.TryGetSinglePixelSpan(out Span <ImageSharpRgba32> span); texture.CopyTo(MemoryMarshal.Cast <ImageSharpRgba32, Rgba32>(span)); } string actualPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) !, "Shaders", $"{typeof(T).Name}.png"); _ = Directory.CreateDirectory(Path.GetDirectoryName(actualPath) !); image.SaveAsPng(actualPath, new PngEncoder() { CompressionLevel = PngCompressionLevel.BestCompression, ColorType = PngColorType.Rgb }); ImagingTests.TolerantImageComparer.AssertEqual(expectedPath, actualPath, delta); }
/// <summary> /// Retrieves a wrapping <see cref="IReadOnlyNormalizedTexture2D{TPixel}"/> instance for the input resource. /// </summary> /// <typeparam name="T">The type of items to store in the texture.</typeparam> /// <typeparam name="TPixel">The type of pixels used on the GPU side.</typeparam> /// <param name="texture">The input <see cref="ReadWriteTexture2D{T, TPixel}"/> instance to create a wrapper for.</param> /// <returns>An <see cref="IReadOnlyNormalizedTexture2D{TPixel}"/> instance wrapping the current resource.</returns> /// <remarks> /// <para>The returned instance can be used in a shader to enable texture sampling.</para> /// <para> /// This is an advanced API that can only be used after the current instance has been transitioned to be in a readonly state. To do so, /// use <see cref="ComputeContextExtensions.Transition{T, TPixel}(in ComputeContext, ReadWriteTexture2D{T, TPixel}, ResourceState)"/>, /// and specify <see cref="ResourceState.ReadOnly"/>. After that, this method can be used to get a readonly wrapper for /// the current texture to use in a shader. This instance should not be cached or reused, but just passed directly to a shader /// being dispatched through that same <see cref="ComputeContext"/>, as it will not work if the texture changes state later on. /// Before the end of that list of operations, the texture also needs to be transitioned back to writeable state, using the same /// API as before but specifying <see cref="ResourceState.ReadWrite"/>. Failing to do so results in undefined behavior. /// </para> /// </remarks> /// <exception cref="ObjectDisposedException">Thrown if the current instance or its associated device are disposed.</exception> /// <exception cref="InvalidOperationException">Thrown if the current instance is not in a readonly state.</exception> public static IReadOnlyNormalizedTexture2D <TPixel> AsReadOnly <T, TPixel>(this ReadWriteTexture2D <T, TPixel> texture) where T : unmanaged, IPixel <T, TPixel> where TPixel : unmanaged { Guard.IsNotNull(texture); return(texture.AsReadOnly()); }
public static ulong ValidateAndGetGpuDescriptorHandle <T>(ReadWriteTexture2D <T> texture, GraphicsDevice device) where T : unmanaged { texture.ThrowIfDisposed(); texture.ThrowIfDeviceMismatch(device); return(Unsafe.As <D3D12_GPU_DESCRIPTOR_HANDLE, ulong>(ref Unsafe.AsRef(in texture.D3D12GpuDescriptorHandle))); }
static void RunComputeShader <T>(ReadWriteTexture2D <Rgba32, float4> texture) where T : struct, IComputeShader { ShaderInfo info = ReflectionServices.GetShaderInfo <T>(); Assert.IsFalse(info.RequiresDoublePrecisionSupport); texture.GraphicsDevice.For(texture.Width, texture.Height, (T)Activator.CreateInstance(typeof(T), texture, 0f) !); }
public void Dispatch_NormalizedTexture2D(Device device, Type t, Type tPixel) { if (t == typeof(Bgra32) && tPixel == typeof(Float4)) { using ReadOnlyTexture2D <Bgra32, Float4> source = device.Get().AllocateReadOnlyTexture2D <Bgra32, Float4>(128, 128); using ReadWriteTexture2D <Bgra32, Float4> destination = device.Get().AllocateReadWriteTexture2D <Bgra32, Float4>(128, 128); device.Get().For(128, 128, new Shader_Unorm_Bgra32_Float4(source, destination)); } if (t == typeof(R16) && tPixel == typeof(float)) { using ReadOnlyTexture2D <R16, float> source = device.Get().AllocateReadOnlyTexture2D <R16, float>(128, 128); using ReadWriteTexture2D <R16, float> destination = device.Get().AllocateReadWriteTexture2D <R16, float>(128, 128); device.Get().For(128, 128, new Shader_Unorm_R16_float(source, destination)); } if (t == typeof(R8) && tPixel == typeof(float)) { using ReadOnlyTexture2D <R8, float> source = device.Get().AllocateReadOnlyTexture2D <R8, float>(128, 128); using ReadWriteTexture2D <R8, float> destination = device.Get().AllocateReadWriteTexture2D <R8, float>(128, 128); device.Get().For(128, 128, new Shader_Unorm_R8_float(source, destination)); } if (t == typeof(Rg16) && tPixel == typeof(Float2)) { using ReadOnlyTexture2D <Rg16, Float2> source = device.Get().AllocateReadOnlyTexture2D <Rg16, Float2>(128, 128); using ReadWriteTexture2D <Rg16, Float2> destination = device.Get().AllocateReadWriteTexture2D <Rg16, Float2>(128, 128); device.Get().For(128, 128, new Shader_Unorm_Rg16_Float2(source, destination)); } if (t == typeof(Rg32) && tPixel == typeof(Float2)) { using ReadOnlyTexture2D <Rg32, Float2> source = device.Get().AllocateReadOnlyTexture2D <Rg32, Float2>(128, 128); using ReadWriteTexture2D <Rg32, Float2> destination = device.Get().AllocateReadWriteTexture2D <Rg32, Float2>(128, 128); device.Get().For(128, 128, new Shader_Unorm_Rg32_Float2(source, destination)); } if (t == typeof(Rgba32) && tPixel == typeof(Float4)) { using ReadOnlyTexture2D <Rgba32, Float4> source = device.Get().AllocateReadOnlyTexture2D <Rgba32, Float4>(128, 128); using ReadWriteTexture2D <Rgba32, Float4> destination = device.Get().AllocateReadWriteTexture2D <Rgba32, Float4>(128, 128); device.Get().For(128, 128, new Shader_Unorm_Rgba32_Float4(source, destination)); } if (t == typeof(Rgba64) && tPixel == typeof(Float4)) { using ReadOnlyTexture2D <Rgba64, Float4> source = device.Get().AllocateReadOnlyTexture2D <Rgba64, Float4>(128, 128); using ReadWriteTexture2D <Rgba64, Float4> destination = device.Get().AllocateReadWriteTexture2D <Rgba64, Float4>(128, 128); device.Get().For(128, 128, new Shader_Unorm_Rgba64_Float4(source, destination)); } }
/// <summary> /// Executes a given test for a specified shader. /// </summary> /// <param name="device">The device to use.</param> /// <param name="shaderType">The type of shader being executed.</param> /// <param name="delta">The comparison delta.</param> private static void RunAndCompareShader(Device device, Type shaderType, float delta) { _ = device.Get(); string expectedPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) !, "Assets", $"{shaderType.Name}.png"); IImageInfo imageInfo = Image.Identify(expectedPath); using Image <ImageSharpRgba32> image = new(imageInfo.Width, imageInfo.Height); using (ReadWriteTexture2D <Rgba32, float4> texture = device.Get().AllocateReadWriteTexture2D <Rgba32, float4>(imageInfo.Width, imageInfo.Height)) { if (typeof(IComputeShader).IsAssignableFrom(shaderType)) {
/// <summary> /// Applies the actual resize logic that was scheduled from <see cref="OnResize"/>. /// </summary> private unsafe void ApplyResize() { this.d3D12CommandQueue.Get()->Signal(this.d3D12Fence.Get(), this.nextD3D12FenceValue); // Wait for the fence again to ensure there are no pending operations this.d3D12Fence.Get()->SetEventOnCompletion(this.nextD3D12FenceValue, default); this.nextD3D12FenceValue++; // Dispose the old buffers before resizing the buffer this.d3D12Resource0.Dispose(); this.d3D12Resource1.Dispose(); // Resize the swap chain buffers this.dxgiSwapChain1.Get()->ResizeBuffers(0, 0, 0, DXGI_FORMAT.DXGI_FORMAT_UNKNOWN, 0); // Get the index of the initial back buffer using (ComPtr <IDXGISwapChain3> dxgiSwapChain3 = default) { _ = this.dxgiSwapChain1.CopyTo(dxgiSwapChain3.GetAddressOf()); this.currentBufferIndex = dxgiSwapChain3.Get()->GetCurrentBackBufferIndex(); } // Retrieve the back buffers for the swap chain fixed(ID3D12Resource **d3D12Resource0 = this.d3D12Resource0) fixed(ID3D12Resource **d3D12Resource1 = this.d3D12Resource1) { _ = dxgiSwapChain1.Get()->GetBuffer(0, Windows.__uuidof <ID3D12Resource>(), (void **)d3D12Resource0); _ = dxgiSwapChain1.Get()->GetBuffer(1, Windows.__uuidof <ID3D12Resource>(), (void **)d3D12Resource1); } this.texture?.Dispose(); D3D12_RESOURCE_DESC d3D12Resource0Description = this.d3D12Resource0.Get()->GetDesc(); // Create the 2D texture to use to generate frames to display this.texture = GraphicsDevice.Default.AllocateReadWriteTexture2D <Rgba32, float4>( (int)d3D12Resource0Description.Width, (int)d3D12Resource0Description.Height); }
public void Verify_DispatchAsPixelShader(Device device) { using ReadWriteTexture2D <Rgba32, float4> texture = device.Get().AllocateReadWriteTexture2D <Rgba32, float4>(256, 256); device.Get().ForEach <DispatchPixelShader, float4>(texture); Rgba32[,] data = texture.ToArray(); for (int y = 0; y < texture.Height; y++) { for (int x = 0; x < texture.Width; x++) { Rgba32 pixel = data[y, x]; Assert.AreEqual((float)pixel.R / 255, (float)x / texture.Width, 0.1f); Assert.AreEqual((float)pixel.G / 255, (float)y / texture.Height, 0.1f); Assert.AreEqual(pixel.B, 255); Assert.AreEqual(pixel.A, 255); } } }
public unsafe void PixelShader_EarlyReturn(Device device) { using ReadWriteTexture2D <Rgba32, float4> texture = device.Get().AllocateReadWriteTexture2D <Rgba32, float4>(128, 128); device.Get().ForEach <EarlyReturnShader, float4>(texture); Rgba32[,] result = texture.ToArray(); for (int i = 0; i < texture.Height; i++) { for (int j = 0; j < texture.Width; j++) { if (j % 2 == 0) { Assert.AreEqual(result[i, j], new Rgba32(255, 0, 0, 0)); } else { Assert.AreEqual(result[i, j], new Rgba32(0, 255, 0, 0)); } } } }
static ReadOnly InitializeWrapper(ReadWriteTexture2D <T> texture) { return(texture.readOnlyWrapper = new(texture)); }
/// <summary> /// Retrieves a wrapping <see cref="IReadOnlyTexture2D{T}"/> instance for the input resource. /// </summary> /// <param name="texture">The input <see cref="ReadWriteTexture2D{T}"/> instance to create a wrapper for.</param> /// <returns>An <see cref="IReadOnlyTexture2D{T}"/> instance wrapping the current resource.</returns> /// <remarks> /// <para>The returned instance can be used in a shader to enable texture sampling.</para> /// <para> /// This is an advanced API that can only be used after the current instance has been transitioned to be in a readonly state. To do so, /// use <see cref="ComputeContextExtensions.Transition(in ComputeContext, ReadWriteTexture2D{Float3}, ResourceState)"/>, /// and specify <see cref="ResourceState.ReadOnly"/>. After that, this method can be used to get a readonly wrapper for /// the current texture to use in a shader. This instance should not be cached or reused, but just passed directly to a shader /// being dispatched through that same <see cref="ComputeContext"/>, as it will not work if the texture changes state later on. /// Before the end of that list of operations, the texture also needs to be transitioned back to writeable state, using the same /// API as before but specifying <see cref="ResourceState.ReadWrite"/>. Failing to do so results in undefined behavior. /// </para> /// </remarks> /// <exception cref="ObjectDisposedException">Thrown if the current instance or its associated device are disposed.</exception> /// <exception cref="InvalidOperationException">Thrown if the current instance is not in a readonly state.</exception> public static IReadOnlyTexture2D <Float3> AsReadOnly(this ReadWriteTexture2D <Float3> texture) { Guard.IsNotNull(texture); return(texture.AsReadOnly()); }