From 43d0f376241ad450ead94144698f312a29530469 Mon Sep 17 00:00:00 2001 From: John Kelly Date: Tue, 7 Mar 2023 16:50:07 +0000 Subject: [PATCH] Initial work on PAL --- .../ComputeSharp.SwapChain.WinUI.csproj | 12 +- .../ComputeSharp.D2D1.csproj | 2 +- .../ComputeSharp.Dynamic.csproj | 2 +- src/ComputeSharp.Pix/ComputeSharp.Pix.csproj | 2 +- .../ComputeSharp.WinUI.csproj | 4 +- src/ComputeSharp/ComputeSharp.csproj | 2 +- ...t.cs => D3D12GraphicsDevice.DeviceLost.cs} | 4 +- ...cute.cs => D3D12GraphicsDevice.Execute.cs} | 6 +- .../Graphics/D3D12GraphicsDevice.cs | 459 ++++++++++++++++++ src/ComputeSharp/Graphics/GraphicsDevice.cs | 418 ++-------------- .../Graphics/Helpers/DeviceHelper.Factory.cs | 2 +- .../Shaders/Loading/PipelineDataLoader{T}.cs | 20 +- .../Shaders/Models/D3D12PipelineData.cs | 48 ++ .../Shaders/Models/PipelineData.cs | 42 +- 14 files changed, 563 insertions(+), 460 deletions(-) rename src/ComputeSharp/Graphics/{GraphicsDevice.DeviceLost.cs => D3D12GraphicsDevice.DeviceLost.cs} (99%) rename src/ComputeSharp/Graphics/{GraphicsDevice.Execute.cs => D3D12GraphicsDevice.Execute.cs} (98%) create mode 100644 src/ComputeSharp/Graphics/D3D12GraphicsDevice.cs create mode 100644 src/ComputeSharp/Shaders/Models/D3D12PipelineData.cs diff --git a/samples/ComputeSharp.SwapChain.WinUI/ComputeSharp.SwapChain.WinUI.csproj b/samples/ComputeSharp.SwapChain.WinUI/ComputeSharp.SwapChain.WinUI.csproj index 4b8cb8cfa..eab831f1e 100644 --- a/samples/ComputeSharp.SwapChain.WinUI/ComputeSharp.SwapChain.WinUI.csproj +++ b/samples/ComputeSharp.SwapChain.WinUI/ComputeSharp.SwapChain.WinUI.csproj @@ -1,8 +1,8 @@ WinExe - net6.0-windows10.0.19041.0 - 10.0.17763.0 + net6.0 + ComputeSharp.SwapChain.WinUI app.manifest x64;arm64 @@ -36,7 +36,7 @@ - diff --git a/src/ComputeSharp.D2D1/ComputeSharp.D2D1.csproj b/src/ComputeSharp.D2D1/ComputeSharp.D2D1.csproj index 1a8cfbdae..fb1dde763 100644 --- a/src/ComputeSharp.D2D1/ComputeSharp.D2D1.csproj +++ b/src/ComputeSharp.D2D1/ComputeSharp.D2D1.csproj @@ -3,7 +3,7 @@ netstandard2.0;net6.0 bin\Release ComputeSharp.D2D1.xml - windows6.1 + diff --git a/src/ComputeSharp.Dynamic/ComputeSharp.Dynamic.csproj b/src/ComputeSharp.Dynamic/ComputeSharp.Dynamic.csproj index ca24b4513..3dc36f1c0 100644 --- a/src/ComputeSharp.Dynamic/ComputeSharp.Dynamic.csproj +++ b/src/ComputeSharp.Dynamic/ComputeSharp.Dynamic.csproj @@ -4,7 +4,7 @@ bin\Release ComputeSharp.Dynamic.xml AnyCPU;x64;ARM64 - windows6.2 + diff --git a/src/ComputeSharp.Pix/ComputeSharp.Pix.csproj b/src/ComputeSharp.Pix/ComputeSharp.Pix.csproj index 682355fda..0f1aa4702 100644 --- a/src/ComputeSharp.Pix/ComputeSharp.Pix.csproj +++ b/src/ComputeSharp.Pix/ComputeSharp.Pix.csproj @@ -4,7 +4,7 @@ bin\Release ComputeSharp.Pix.xml AnyCPU;x64;ARM64 - windows6.2 + diff --git a/src/ComputeSharp.WinUI/ComputeSharp.WinUI.csproj b/src/ComputeSharp.WinUI/ComputeSharp.WinUI.csproj index f627377b5..f0853be3c 100644 --- a/src/ComputeSharp.WinUI/ComputeSharp.WinUI.csproj +++ b/src/ComputeSharp.WinUI/ComputeSharp.WinUI.csproj @@ -1,7 +1,7 @@ - net6.0-windows10.0.19041.0 - 10.0.17763.0 + net6.0 + ComputeSharp.WinUI x64;ARM64 win10-x64;win10-arm64 diff --git a/src/ComputeSharp/ComputeSharp.csproj b/src/ComputeSharp/ComputeSharp.csproj index f10eabdd0..f04e8c9c2 100644 --- a/src/ComputeSharp/ComputeSharp.csproj +++ b/src/ComputeSharp/ComputeSharp.csproj @@ -3,7 +3,7 @@ netstandard2.0;net6.0 bin\Release ComputeSharp.xml - windows6.2 + diff --git a/src/ComputeSharp/Graphics/GraphicsDevice.DeviceLost.cs b/src/ComputeSharp/Graphics/D3D12GraphicsDevice.DeviceLost.cs similarity index 99% rename from src/ComputeSharp/Graphics/GraphicsDevice.DeviceLost.cs rename to src/ComputeSharp/Graphics/D3D12GraphicsDevice.DeviceLost.cs index 15f9f4706..99703947b 100644 --- a/src/ComputeSharp/Graphics/GraphicsDevice.DeviceLost.cs +++ b/src/ComputeSharp/Graphics/D3D12GraphicsDevice.DeviceLost.cs @@ -10,7 +10,7 @@ namespace ComputeSharp; /// -unsafe partial class GraphicsDevice +unsafe partial class D3D12GraphicsDevice { #if !NET6_0_OR_GREATER /// @@ -24,7 +24,7 @@ unsafe partial class GraphicsDevice /// /// Thrown if the current device has been lost. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ThrowIfDeviceLost() + internal override void ThrowIfDeviceLost() { // This method is called as a check before performing any operations. In order to minimize overhead, // the GetDeviceRemovedReason() method is not called here, and the local field is just used instead. diff --git a/src/ComputeSharp/Graphics/GraphicsDevice.Execute.cs b/src/ComputeSharp/Graphics/D3D12GraphicsDevice.Execute.cs similarity index 98% rename from src/ComputeSharp/Graphics/GraphicsDevice.Execute.cs rename to src/ComputeSharp/Graphics/D3D12GraphicsDevice.Execute.cs index 897a7ce38..69e025be1 100644 --- a/src/ComputeSharp/Graphics/GraphicsDevice.Execute.cs +++ b/src/ComputeSharp/Graphics/D3D12GraphicsDevice.Execute.cs @@ -16,7 +16,7 @@ namespace ComputeSharp; /// -unsafe partial class GraphicsDevice +unsafe partial class D3D12GraphicsDevice { #if !NET6_0_OR_GREATER /// @@ -175,7 +175,7 @@ private struct CallbackContext /// This method is only supported for compute operations. private static WaitForFenceValueTaskSource WaitForFenceAsync( ulong d3D12FenceValue, - GraphicsDevice device, + D3D12GraphicsDevice device, ID3D12GraphicsCommandList* d3D12GraphicsCommandList, ID3D12CommandAllocator* d3D12CommandAllocator) { @@ -232,7 +232,7 @@ private static void WaitForSingleObjectCallbackForWaitForFenceAsync(void* pConte CallbackContext* callbackContext = (CallbackContext*)pContext; WaitForFenceValueTaskSource waitForFenceValueTaskSource = Unsafe.As(callbackContext->WaitForFenceValueTaskSourceHandle.Target)!; - GraphicsDevice device = Unsafe.As(callbackContext->GraphicsDeviceHandle.Target)!; + D3D12GraphicsDevice device = Unsafe.As(callbackContext->GraphicsDeviceHandle.Target)!; ID3D12GraphicsCommandList* d3D12GraphicsCommandList = callbackContext->D3D12GraphicsCommandList; ID3D12CommandAllocator* d3D12CommandAllocator = callbackContext->D3D12CommandAllocator; HANDLE eventHandle = callbackContext->EventHandle; diff --git a/src/ComputeSharp/Graphics/D3D12GraphicsDevice.cs b/src/ComputeSharp/Graphics/D3D12GraphicsDevice.cs new file mode 100644 index 000000000..d5d2c42d6 --- /dev/null +++ b/src/ComputeSharp/Graphics/D3D12GraphicsDevice.cs @@ -0,0 +1,459 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using ComputeSharp.Graphics.Commands.Interop; +#if NET6_0_OR_GREATER +using ComputeSharp.Core.Extensions; +#endif +using ComputeSharp.Shaders.Dispatching; +using ComputeSharp.Shaders.Extensions; +using ComputeSharp.Graphics.Extensions; +using ComputeSharp.Graphics.Helpers; +using ComputeSharp.Interop; +using ComputeSharp.Shaders.Models; +using TerraFX.Interop.DirectX; +using TerraFX.Interop.Windows; +using static TerraFX.Interop.DirectX.D3D12_COMMAND_LIST_TYPE; +using static TerraFX.Interop.DirectX.D3D12_FEATURE; +using static TerraFX.Interop.DirectX.D3D12_FORMAT_SUPPORT1; +using static TerraFX.Interop.DirectX.DXGI_ADAPTER_FLAG; + +#pragma warning disable CS0618 + +namespace ComputeSharp; + +/// +/// A that represents an instance that can be used to run compute shaders. +/// +[DebuggerDisplay("{ToString(),raw}")] +public sealed unsafe partial class D3D12GraphicsDevice : GraphicsDevice, IReferenceTrackedObject +{ + /// + /// The underlying wrapped by the current instance. + /// + private ComPtr d3D12Device; + + /// + /// The instance to use for compute operations. + /// + private ComPtr d3D12ComputeCommandQueue; + + /// + /// The instance to use for copy operations. + /// + private ComPtr d3D12CopyCommandQueue; + + /// + /// The instance used for compute operations. + /// + private ComPtr d3D12ComputeFence; + + /// + /// The instance used for copy operations. + /// + private ComPtr d3D12CopyFence; + + /// + /// The instance to use when allocating new buffers. + /// + private ID3D12DescriptorHandleAllocator shaderResourceViewDescriptorAllocator; + + /// + /// The instance for compute operations. + /// + private readonly ID3D12CommandListPool computeCommandListPool; + + /// + /// Gets the instance for copy operations. + /// + private readonly ID3D12CommandListPool copyCommandListPool; + + /// + /// The next fence value for compute operations using . + /// + private ulong nextD3D12ComputeFenceValue; + + /// + /// The next fence value for copy operations using . + /// + private ulong nextD3D12CopyFenceValue; + +#if NET6_0_OR_GREATER + /// + /// The in use associated to the current device. + /// + private ComPtr allocator; + + /// + /// The instance in use, if is . + /// + private ComPtr pool; +#endif + + /// + /// A weak to the current instance (used to support the device lost callback). + /// + private GCHandle deviceHandle; + + /// + /// The event for the device removed callback. + /// + private readonly HANDLE deviceRemovedEvent; + + /// + /// The wait handle for the device removed callback. + /// + private readonly HANDLE deviceRemovedWaitHandle; + + /// + /// The reason the device was removed, if any. + /// + private HRESULT deviceRemovedReason; + + /// + /// The list of cached objects for the current device. + /// + /// + /// A new entry is added to this list every time a shader is dispatched using this device. These cached items + /// are stored via a , meaning they will be collected automatically + /// when the device is collected. But, due to the fact that could happen at any time, a list is required to guarantee + /// the additional references added by the native objects in the pipeline model can be released immediately. + /// + private readonly List cachedPipelineData; + + /// + /// Raised whenever the device is lost. + /// + /// + /// + /// "Device lost" refers to a situation where the GPU graphics device becomes unusable for further operations. This can occur + /// due to GPU hardware malfunction, driver bugs, driver software updates, or switching the app from one GPU to another. + /// + /// + /// A lost device can no longer be used, and any attempt to do so will throw an exception. To recover from + /// this situation, the app must create a new device and then recreate all its graphics resources. + /// + /// + /// This event will only be raised at most once for a given instance. Additionally, + /// the event is raised asynchronously with respect to the device being lost, and on a thread pool thread. + /// + /// + public event EventHandler? DeviceLost; + + /// + /// Creates a new instance for the input . + /// + /// The to use for the new instance. + /// The that was created from. + /// The available info for the new instance. + internal D3D12GraphicsDevice(ID3D12Device* d3D12Device, IDXGIAdapter* dxgiAdapter, DXGI_ADAPTER_DESC1* dxgiDescription1) + { + this.referenceTracker = new ReferenceTracker(this); + + this.d3D12Device = new ComPtr(d3D12Device); + + this.d3D12ComputeCommandQueue = d3D12Device->CreateCommandQueue(D3D12_COMMAND_LIST_TYPE_COMPUTE); + this.d3D12CopyCommandQueue = d3D12Device->CreateCommandQueue(D3D12_COMMAND_LIST_TYPE_COPY); + this.d3D12ComputeFence = d3D12Device->CreateFence(); + this.d3D12CopyFence = d3D12Device->CreateFence(); + + this.shaderResourceViewDescriptorAllocator = new ID3D12DescriptorHandleAllocator(d3D12Device); + this.computeCommandListPool = new ID3D12CommandListPool(D3D12_COMMAND_LIST_TYPE_COMPUTE); + this.copyCommandListPool = new ID3D12CommandListPool(D3D12_COMMAND_LIST_TYPE_COPY); + + Luid = Luid.FromLUID(dxgiDescription1->AdapterLuid); + Name = new string((char*)dxgiDescription1->Description); + DedicatedMemorySize = dxgiDescription1->DedicatedVideoMemory; + SharedMemorySize = dxgiDescription1->SharedSystemMemory; + IsHardwareAccelerated = (dxgiDescription1->Flags & (uint)DXGI_ADAPTER_FLAG_SOFTWARE) == 0; + + D3D12_FEATURE_DATA_D3D12_OPTIONS1 d3D12Options1Data = d3D12Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1); + + ComputeUnits = d3D12Options1Data.TotalLaneCount; + WavefrontSize = d3D12Options1Data.WaveLaneCountMin; + + D3D12_FEATURE_DATA_ARCHITECTURE1 d3D12Architecture1Data = d3D12Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1); + + IsCacheCoherentUMA = d3D12Architecture1Data.CacheCoherentUMA != 0; + +#if NET6_0_OR_GREATER + this.allocator = d3D12Device->CreateAllocator(dxgiAdapter); + + if (IsCacheCoherentUMA) + { + this.pool = this.allocator.Get()->CreatePoolForCacheCoherentUMA(); + } +#endif + + this.deviceRemovedReason = S.S_OK; + this.cachedPipelineData = new List(); + + RegisterDeviceLostCallback(this, out this.deviceHandle, out this.deviceRemovedEvent, out this.deviceRemovedWaitHandle); + } + + internal override unsafe void CreatePipelineData(ICachedShader shaderData, out PipelineData pipelineData) + { + using ComPtr d3D12RootSignature = default; + + ShaderDispatchMetadataLoader metadataLoader = new(this.D3D12Device); + + Unsafe.SkipInit(out T shader); + + shader.LoadDispatchMetadata(ref metadataLoader, out *(IntPtr*)&d3D12RootSignature); + + using ComPtr d3D12PipelineState = D3D12Device->CreateComputePipelineState(d3D12RootSignature.Get(), shaderData.D3D12ShaderBytecode); + + pipelineData = new D3D12PipelineData(d3D12RootSignature.Get(), d3D12PipelineState.Get()); + + // Register the newly created pipeline data to enable early disposal + RegisterPipelineData(pipelineData); + } + + /// + /// Gets the underlying wrapped by the current instance. + /// + internal ID3D12Device* D3D12Device => this.d3D12Device; + +#if NET6_0_OR_GREATER + /// + /// Gets the underlying wrapped by the current instance. + /// + internal D3D12MA_Allocator* Allocator => this.allocator; + + /// + /// Gets the underlying wrapped by the current instance, if any. + /// + internal D3D12MA_Pool* Pool => this.pool; +#endif + /// + /// Checks whether the current device supports double precision floating point operations in shaders. + /// + /// Whether the current device supports double precision floating point operations in shaders. + public override bool IsDoublePrecisionSupportAvailable() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + D3D12_FEATURE_DATA_D3D12_OPTIONS d3D12OptionsData = this.d3D12Device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS); + + return d3D12OptionsData.DoublePrecisionFloatShaderOps; + } + + /// + /// Checks whether the current device supports the creation of + /// resources for a specified type . + /// + /// The type of values to check support for. + /// Whether instances can be created by the current device. + public override bool IsReadOnlyTexture1DSupportedForType() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + return this.d3D12Device.Get()->IsDxgiFormatSupported(DXGIFormatHelper.GetForType(), D3D12_FORMAT_SUPPORT1_TEXTURE1D); + } + + /// + /// Checks whether the current device supports the creation of + /// resources for a specified type . + /// + /// The type of values to check support for. + /// Whether instances can be created by the current device. + public override bool IsReadWriteTexture1DSupportedForType() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + return this.d3D12Device.Get()->IsDxgiFormatSupported( + DXGIFormatHelper.GetForType(), + D3D12_FORMAT_SUPPORT1_TEXTURE1D | D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW); + } + + /// + /// Checks whether the current device supports the creation of + /// resources for a specified type . + /// + /// The type of values to check support for. + /// Whether instances can be created by the current device. + public override bool IsReadOnlyTexture2DSupportedForType() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + return this.d3D12Device.Get()->IsDxgiFormatSupported(DXGIFormatHelper.GetForType(), D3D12_FORMAT_SUPPORT1_TEXTURE2D); + } + + /// + /// Checks whether the current device supports the creation of + /// resources for a specified type . + /// + /// The type of values to check support for. + /// Whether instances can be created by the current device. + public override bool IsReadWriteTexture2DSupportedForType() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + return this.d3D12Device.Get()->IsDxgiFormatSupported( + DXGIFormatHelper.GetForType(), + D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW); + } + + /// + /// Checks whether the current device supports the creation of + /// resources for a specified type . + /// + /// The type of values to check support for. + /// Whether instances can be created by the current device. + public override bool IsReadOnlyTexture3DSupportedForType() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + return this.d3D12Device.Get()->IsDxgiFormatSupported(DXGIFormatHelper.GetForType(), D3D12_FORMAT_SUPPORT1_TEXTURE3D); + } + + /// + /// Checks whether the current device supports the creation of + /// resources for a specified type . + /// + /// The type of values to check support for. + /// Whether instances can be created by the current device. + public override bool IsReadWriteTexture3DSupportedForType() + { + using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + + ThrowIfDeviceLost(); + + return this.d3D12Device.Get()->IsDxgiFormatSupported( + DXGIFormatHelper.GetForType(), + D3D12_FORMAT_SUPPORT1_TEXTURE3D | D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW); + } + + /// + /// Registers a object for the current device to enable early disposal. + /// + /// The instance just loaded to run a shader. + internal void RegisterPipelineData(PipelineData pipelineData) + { + // This is only used when first initializing a pipeline data for a given shader. + // Adding an item is very quick, so we can just use a lock and not a concurrent + // list. That would've added more overhead given the low contention in this case. + lock (this.cachedPipelineData) + { + this.cachedPipelineData.Add(pipelineData); + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void RentShaderResourceViewDescriptorHandles(out ID3D12ResourceDescriptorHandles d3D12ResourceDescriptorHandles) + { + this.shaderResourceViewDescriptorAllocator.Rent(out d3D12ResourceDescriptorHandles); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void ReturnShaderResourceViewDescriptorHandles(in ID3D12ResourceDescriptorHandles d3D12ResourceDescriptorHandles) + { + this.shaderResourceViewDescriptorAllocator.Return(in d3D12ResourceDescriptorHandles); + } + + /// + /// The type of command allocator to rent. + /// The resulting value. + /// The resulting value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void GetCommandListAndAllocator( + D3D12_COMMAND_LIST_TYPE d3D12CommandListType, + out ID3D12GraphicsCommandList* d3D12CommandList, + out ID3D12CommandAllocator* d3D12CommandAllocator) + { + switch (d3D12CommandListType) + { + case D3D12_COMMAND_LIST_TYPE_COMPUTE: + this.computeCommandListPool.Rent(this.d3D12Device.Get(), null, out d3D12CommandList, out d3D12CommandAllocator); + break; + case D3D12_COMMAND_LIST_TYPE_COPY: + this.copyCommandListPool.Rent(this.d3D12Device.Get(), null, out d3D12CommandList, out d3D12CommandAllocator); + break; + default: + default(ArgumentException).Throw(nameof(d3D12CommandListType)); + d3D12CommandList = null; + d3D12CommandAllocator = null; + break; + } + } + + /// + /// The instance to use for the new command list. + /// The resulting value. + /// The resulting value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void GetCommandListAndAllocator( + ID3D12PipelineState* d3D12PipelineState, + out ID3D12GraphicsCommandList* d3D12CommandList, + out ID3D12CommandAllocator* d3D12CommandAllocator) + { + this.computeCommandListPool.Rent(this.d3D12Device.Get(), d3D12PipelineState, out d3D12CommandList, out d3D12CommandAllocator); + } + + /// + /// Sets the descriptor heap for a given instance. + /// + /// The input instance to use. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void SetDescriptorHeapForCommandList(ID3D12GraphicsCommandList* d3D12GraphicsCommandList) + { + ID3D12DescriptorHeap* d3D12DescriptorHeap = this.shaderResourceViewDescriptorAllocator.D3D12DescriptorHeap; + + d3D12GraphicsCommandList->SetDescriptorHeaps(1, &d3D12DescriptorHeap); + } + + /// + void IReferenceTrackedObject.DangerousOnDispose() + { + DeviceHelper.NotifyDisposedDevice(this); + + this.d3D12Device.Dispose(); + this.d3D12ComputeCommandQueue.Dispose(); + this.d3D12CopyCommandQueue.Dispose(); + this.d3D12ComputeFence.Dispose(); + this.d3D12CopyFence.Dispose(); + this.computeCommandListPool.Dispose(); + this.copyCommandListPool.Dispose(); + this.shaderResourceViewDescriptorAllocator.Dispose(); + + // On .NET 6, D3D12MA is used. In this case, the pool and allocator must be kept alive + // until all associated resources are returned and destroyed. Because of this, when the + // device is disposed (since there might be outstanding resources that are still alive), + // the pool and allocator are only released, but not disposed. This allows reosurces to + // also release them when disposed (since each resource keeps a reference back to the + // parent device). When the last one is disposed, the pool and allocator will be deleted. +#if NET6_0_OR_GREATER + this.pool.Release(); + this.allocator.Release(); +#endif + + UnregisterDeviceLostCallback(this); + + // Explicitly release the cached pipeline objects as well. + // Locking is not needed here, as this method is only invoked + // when the device has been disposed and no other usage is active + // for the device (ie. no lease exists), meaning that no other + // thread could potentially add a new pipeline data concurrently. + foreach (PipelineData pipelineData in this.cachedPipelineData) + { + pipelineData.Dispose(); + } + } + +} diff --git a/src/ComputeSharp/Graphics/GraphicsDevice.cs b/src/ComputeSharp/Graphics/GraphicsDevice.cs index 08d5cbf16..83d3698f1 100644 --- a/src/ComputeSharp/Graphics/GraphicsDevice.cs +++ b/src/ComputeSharp/Graphics/GraphicsDevice.cs @@ -1,22 +1,14 @@ using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using ComputeSharp.Graphics.Commands.Interop; #if NET6_0_OR_GREATER using ComputeSharp.Core.Extensions; #endif -using ComputeSharp.Graphics.Extensions; -using ComputeSharp.Graphics.Helpers; +using ComputeSharp.__Internals; using ComputeSharp.Interop; -using ComputeSharp.Shaders.Models; using TerraFX.Interop.DirectX; -using TerraFX.Interop.Windows; -using static TerraFX.Interop.DirectX.D3D12_COMMAND_LIST_TYPE; -using static TerraFX.Interop.DirectX.D3D12_FEATURE; -using static TerraFX.Interop.DirectX.D3D12_FORMAT_SUPPORT1; -using static TerraFX.Interop.DirectX.DXGI_ADAPTER_FLAG; +using ComputeSharp.Shaders.Models; + +#pragma warning disable CS0618 namespace ComputeSharp; @@ -24,248 +16,69 @@ namespace ComputeSharp; /// A that represents an instance that can be used to run compute shaders. /// [DebuggerDisplay("{ToString(),raw}")] -public sealed unsafe partial class GraphicsDevice : IReferenceTrackedObject +public abstract unsafe partial class GraphicsDevice : IReferenceTrackedObject { - /// - /// The value for the current instance. - /// - private ReferenceTracker referenceTracker; - - /// - /// The underlying wrapped by the current instance. - /// - private ComPtr d3D12Device; - - /// - /// The instance to use for compute operations. - /// - private ComPtr d3D12ComputeCommandQueue; - - /// - /// The instance to use for copy operations. - /// - private ComPtr d3D12CopyCommandQueue; - - /// - /// The instance used for compute operations. - /// - private ComPtr d3D12ComputeFence; - - /// - /// The instance used for copy operations. - /// - private ComPtr d3D12CopyFence; - - /// - /// The instance to use when allocating new buffers. - /// - private ID3D12DescriptorHandleAllocator shaderResourceViewDescriptorAllocator; - - /// - /// The instance for compute operations. - /// - private readonly ID3D12CommandListPool computeCommandListPool; - - /// - /// Gets the instance for copy operations. - /// - private readonly ID3D12CommandListPool copyCommandListPool; - - /// - /// The next fence value for compute operations using . - /// - private ulong nextD3D12ComputeFenceValue; - - /// - /// The next fence value for copy operations using . - /// - private ulong nextD3D12CopyFenceValue; - -#if NET6_0_OR_GREATER - /// - /// The in use associated to the current device. - /// - private ComPtr allocator; - - /// - /// The instance in use, if is . - /// - private ComPtr pool; -#endif - - /// - /// A weak to the current instance (used to support the device lost callback). - /// - private GCHandle deviceHandle; - - /// - /// The event for the device removed callback. - /// - private readonly HANDLE deviceRemovedEvent; - - /// - /// The wait handle for the device removed callback. - /// - private readonly HANDLE deviceRemovedWaitHandle; - - /// - /// The reason the device was removed, if any. - /// - private HRESULT deviceRemovedReason; - - /// - /// The list of cached objects for the current device. - /// - /// - /// A new entry is added to this list every time a shader is dispatched using this device. These cached items - /// are stored via a , meaning they will be collected automatically - /// when the device is collected. But, due to the fact that could happen at any time, a list is required to guarantee - /// the additional references added by the native objects in the pipeline model can be released immediately. - /// - private readonly List cachedPipelineData; + internal abstract unsafe void CreatePipelineData(ICachedShader shaderData, out PipelineData pipelineData) + where T : struct, IShader; /// - /// Raised whenever the device is lost. + /// Throws an if the current device has been lost. /// - /// - /// - /// "Device lost" refers to a situation where the GPU graphics device becomes unusable for further operations. This can occur - /// due to GPU hardware malfunction, driver bugs, driver software updates, or switching the app from one GPU to another. - /// - /// - /// A lost device can no longer be used, and any attempt to do so will throw an exception. To recover from - /// this situation, the app must create a new device and then recreate all its graphics resources. - /// - /// - /// This event will only be raised at most once for a given instance. Additionally, - /// the event is raised asynchronously with respect to the device being lost, and on a thread pool thread. - /// - /// - public event EventHandler? DeviceLost; + /// Thrown if the current device has been lost. + internal abstract void ThrowIfDeviceLost(); /// - /// Creates a new instance for the input . + /// The value for the current instance. /// - /// The to use for the new instance. - /// The that was created from. - /// The available info for the new instance. - internal GraphicsDevice(ID3D12Device* d3D12Device, IDXGIAdapter* dxgiAdapter, DXGI_ADAPTER_DESC1* dxgiDescription1) - { - this.referenceTracker = new ReferenceTracker(this); - - this.d3D12Device = new ComPtr(d3D12Device); - - this.d3D12ComputeCommandQueue = d3D12Device->CreateCommandQueue(D3D12_COMMAND_LIST_TYPE_COMPUTE); - this.d3D12CopyCommandQueue = d3D12Device->CreateCommandQueue(D3D12_COMMAND_LIST_TYPE_COPY); - this.d3D12ComputeFence = d3D12Device->CreateFence(); - this.d3D12CopyFence = d3D12Device->CreateFence(); - - this.shaderResourceViewDescriptorAllocator = new ID3D12DescriptorHandleAllocator(d3D12Device); - this.computeCommandListPool = new ID3D12CommandListPool(D3D12_COMMAND_LIST_TYPE_COMPUTE); - this.copyCommandListPool = new ID3D12CommandListPool(D3D12_COMMAND_LIST_TYPE_COPY); - - Luid = Luid.FromLUID(dxgiDescription1->AdapterLuid); - Name = new string((char*)dxgiDescription1->Description); - DedicatedMemorySize = dxgiDescription1->DedicatedVideoMemory; - SharedMemorySize = dxgiDescription1->SharedSystemMemory; - IsHardwareAccelerated = (dxgiDescription1->Flags & (uint)DXGI_ADAPTER_FLAG_SOFTWARE) == 0; - - D3D12_FEATURE_DATA_D3D12_OPTIONS1 d3D12Options1Data = d3D12Device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS1); - - ComputeUnits = d3D12Options1Data.TotalLaneCount; - WavefrontSize = d3D12Options1Data.WaveLaneCountMin; - - D3D12_FEATURE_DATA_ARCHITECTURE1 d3D12Architecture1Data = d3D12Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1); - - IsCacheCoherentUMA = d3D12Architecture1Data.CacheCoherentUMA != 0; - -#if NET6_0_OR_GREATER - this.allocator = d3D12Device->CreateAllocator(dxgiAdapter); - - if (IsCacheCoherentUMA) - { - this.pool = this.allocator.Get()->CreatePoolForCacheCoherentUMA(); - } -#endif - - this.deviceRemovedReason = S.S_OK; - this.cachedPipelineData = new List(); - - RegisterDeviceLostCallback(this, out this.deviceHandle, out this.deviceRemovedEvent, out this.deviceRemovedWaitHandle); - } + private protected ReferenceTracker referenceTracker; /// /// Gets the locally unique identifier for the current device. /// - public Luid Luid { get; } + public Luid Luid { get; protected set; } /// /// Gets the name of the current instance. /// - public string Name { get; } + public string Name { get; protected set; } /// /// Gets the size of the dedicated memory for the current device. /// - public nuint DedicatedMemorySize { get; } + public nuint DedicatedMemorySize { get; protected set; } /// /// Gets the size of the shared system memory for the current device. /// - public nuint SharedMemorySize { get; } + public nuint SharedMemorySize { get; protected set; } /// /// Gets whether or not the current device is hardware accelerated. /// This value is for software fallback devices. /// - public bool IsHardwareAccelerated { get; } + public bool IsHardwareAccelerated { get; protected set; } /// /// Gets the number of total lanes on the current device (eg. CUDA cores on an nVidia GPU). /// - public uint ComputeUnits { get; } + public uint ComputeUnits { get; protected set; } /// /// Gets the number of lanes in a SIMD wave on the current device (also known as "wavefront size" or "warp width"). /// - public uint WavefrontSize { get; } - - /// - /// Gets the underlying wrapped by the current instance. - /// - internal ID3D12Device* D3D12Device => this.d3D12Device; - -#if NET6_0_OR_GREATER - /// - /// Gets the underlying wrapped by the current instance. - /// - internal D3D12MA_Allocator* Allocator => this.allocator; - - /// - /// Gets the underlying wrapped by the current instance, if any. - /// - internal D3D12MA_Pool* Pool => this.pool; -#endif + public uint WavefrontSize { get; protected set; } /// /// Gets whether or not the current device has a cache coherent UMA architecture. /// internal bool IsCacheCoherentUMA { get; } + /// /// Checks whether the current device supports double precision floating point operations in shaders. /// /// Whether the current device supports double precision floating point operations in shaders. - public bool IsDoublePrecisionSupportAvailable() - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); - - ThrowIfDeviceLost(); - - D3D12_FEATURE_DATA_D3D12_OPTIONS d3D12OptionsData = this.d3D12Device.Get()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS); - - return d3D12OptionsData.DoublePrecisionFloatShaderOps; - } + public abstract bool IsDoublePrecisionSupportAvailable(); /// /// Checks whether the current device supports the creation of @@ -273,15 +86,8 @@ public bool IsDoublePrecisionSupportAvailable() /// /// The type of values to check support for. /// Whether instances can be created by the current device. - public bool IsReadOnlyTexture1DSupportedForType() - where T : unmanaged - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); - - ThrowIfDeviceLost(); - - return this.d3D12Device.Get()->IsDxgiFormatSupported(DXGIFormatHelper.GetForType(), D3D12_FORMAT_SUPPORT1_TEXTURE1D); - } + public abstract bool IsReadOnlyTexture1DSupportedForType() + where T : unmanaged; /// /// Checks whether the current device supports the creation of @@ -289,17 +95,8 @@ public bool IsReadOnlyTexture1DSupportedForType() /// /// The type of values to check support for. /// Whether instances can be created by the current device. - public bool IsReadWriteTexture1DSupportedForType() - where T : unmanaged - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); - - ThrowIfDeviceLost(); - - return this.d3D12Device.Get()->IsDxgiFormatSupported( - DXGIFormatHelper.GetForType(), - D3D12_FORMAT_SUPPORT1_TEXTURE1D | D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW); - } + public abstract bool IsReadWriteTexture1DSupportedForType() + where T : unmanaged; /// /// Checks whether the current device supports the creation of @@ -307,15 +104,8 @@ public bool IsReadWriteTexture1DSupportedForType() /// /// The type of values to check support for. /// Whether instances can be created by the current device. - public bool IsReadOnlyTexture2DSupportedForType() - where T : unmanaged - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); - - ThrowIfDeviceLost(); - - return this.d3D12Device.Get()->IsDxgiFormatSupported(DXGIFormatHelper.GetForType(), D3D12_FORMAT_SUPPORT1_TEXTURE2D); - } + public abstract bool IsReadOnlyTexture2DSupportedForType() + where T : unmanaged; /// /// Checks whether the current device supports the creation of @@ -323,17 +113,8 @@ public bool IsReadOnlyTexture2DSupportedForType() /// /// The type of values to check support for. /// Whether instances can be created by the current device. - public bool IsReadWriteTexture2DSupportedForType() - where T : unmanaged - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); - - ThrowIfDeviceLost(); - - return this.d3D12Device.Get()->IsDxgiFormatSupported( - DXGIFormatHelper.GetForType(), - D3D12_FORMAT_SUPPORT1_TEXTURE2D | D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW); - } + public abstract bool IsReadWriteTexture2DSupportedForType() + where T : unmanaged; /// /// Checks whether the current device supports the creation of @@ -341,15 +122,8 @@ public bool IsReadWriteTexture2DSupportedForType() /// /// The type of values to check support for. /// Whether instances can be created by the current device. - public bool IsReadOnlyTexture3DSupportedForType() - where T : unmanaged - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); - - ThrowIfDeviceLost(); - - return this.d3D12Device.Get()->IsDxgiFormatSupported(DXGIFormatHelper.GetForType(), D3D12_FORMAT_SUPPORT1_TEXTURE3D); - } + public abstract bool IsReadOnlyTexture3DSupportedForType() + where T : unmanaged; /// /// Checks whether the current device supports the creation of @@ -357,16 +131,12 @@ public bool IsReadOnlyTexture3DSupportedForType() /// /// The type of values to check support for. /// Whether instances can be created by the current device. - public bool IsReadWriteTexture3DSupportedForType() - where T : unmanaged - { - using ReferenceTracker.Lease _0 = GetReferenceTracker().GetLease(); + public abstract bool IsReadWriteTexture3DSupportedForType() + where T : unmanaged; - ThrowIfDeviceLost(); + void IReferenceTrackedObject.DangerousOnDispose() + { - return this.d3D12Device.Get()->IsDxgiFormatSupported( - DXGIFormatHelper.GetForType(), - D3D12_FORMAT_SUPPORT1_TEXTURE3D | D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW); } /// @@ -391,124 +161,6 @@ internal void UnregisterAllocatedResource() #endif } - /// - /// Registers a object for the current device to enable early disposal. - /// - /// The instance just loaded to run a shader. - internal void RegisterPipelineData(PipelineData pipelineData) - { - // This is only used when first initializing a pipeline data for a given shader. - // Adding an item is very quick, so we can just use a lock and not a concurrent - // list. That would've added more overhead given the low contention in this case. - lock (this.cachedPipelineData) - { - this.cachedPipelineData.Add(pipelineData); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void RentShaderResourceViewDescriptorHandles(out ID3D12ResourceDescriptorHandles d3D12ResourceDescriptorHandles) - { - this.shaderResourceViewDescriptorAllocator.Rent(out d3D12ResourceDescriptorHandles); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void ReturnShaderResourceViewDescriptorHandles(in ID3D12ResourceDescriptorHandles d3D12ResourceDescriptorHandles) - { - this.shaderResourceViewDescriptorAllocator.Return(in d3D12ResourceDescriptorHandles); - } - - /// - /// The type of command allocator to rent. - /// The resulting value. - /// The resulting value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void GetCommandListAndAllocator( - D3D12_COMMAND_LIST_TYPE d3D12CommandListType, - out ID3D12GraphicsCommandList* d3D12CommandList, - out ID3D12CommandAllocator* d3D12CommandAllocator) - { - switch (d3D12CommandListType) - { - case D3D12_COMMAND_LIST_TYPE_COMPUTE: - this.computeCommandListPool.Rent(this.d3D12Device.Get(), null, out d3D12CommandList, out d3D12CommandAllocator); - break; - case D3D12_COMMAND_LIST_TYPE_COPY: - this.copyCommandListPool.Rent(this.d3D12Device.Get(), null, out d3D12CommandList, out d3D12CommandAllocator); - break; - default: - default(ArgumentException).Throw(nameof(d3D12CommandListType)); - d3D12CommandList = null; - d3D12CommandAllocator = null; - break; - } - } - - /// - /// The instance to use for the new command list. - /// The resulting value. - /// The resulting value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void GetCommandListAndAllocator( - ID3D12PipelineState* d3D12PipelineState, - out ID3D12GraphicsCommandList* d3D12CommandList, - out ID3D12CommandAllocator* d3D12CommandAllocator) - { - this.computeCommandListPool.Rent(this.d3D12Device.Get(), d3D12PipelineState, out d3D12CommandList, out d3D12CommandAllocator); - } - - /// - /// Sets the descriptor heap for a given instance. - /// - /// The input instance to use. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void SetDescriptorHeapForCommandList(ID3D12GraphicsCommandList* d3D12GraphicsCommandList) - { - ID3D12DescriptorHeap* d3D12DescriptorHeap = this.shaderResourceViewDescriptorAllocator.D3D12DescriptorHeap; - - d3D12GraphicsCommandList->SetDescriptorHeaps(1, &d3D12DescriptorHeap); - } - - /// - void IReferenceTrackedObject.DangerousOnDispose() - { - DeviceHelper.NotifyDisposedDevice(this); - - this.d3D12Device.Dispose(); - this.d3D12ComputeCommandQueue.Dispose(); - this.d3D12CopyCommandQueue.Dispose(); - this.d3D12ComputeFence.Dispose(); - this.d3D12CopyFence.Dispose(); - this.computeCommandListPool.Dispose(); - this.copyCommandListPool.Dispose(); - this.shaderResourceViewDescriptorAllocator.Dispose(); - - // On .NET 6, D3D12MA is used. In this case, the pool and allocator must be kept alive - // until all associated resources are returned and destroyed. Because of this, when the - // device is disposed (since there might be outstanding resources that are still alive), - // the pool and allocator are only released, but not disposed. This allows reosurces to - // also release them when disposed (since each resource keeps a reference back to the - // parent device). When the last one is disposed, the pool and allocator will be deleted. -#if NET6_0_OR_GREATER - this.pool.Release(); - this.allocator.Release(); -#endif - - UnregisterDeviceLostCallback(this); - - // Explicitly release the cached pipeline objects as well. - // Locking is not needed here, as this method is only invoked - // when the device has been disposed and no other usage is active - // for the device (ie. no lease exists), meaning that no other - // thread could potentially add a new pipeline data concurrently. - foreach (PipelineData pipelineData in this.cachedPipelineData) - { - pipelineData.Dispose(); - } - } - /// public override string ToString() { diff --git a/src/ComputeSharp/Graphics/Helpers/DeviceHelper.Factory.cs b/src/ComputeSharp/Graphics/Helpers/DeviceHelper.Factory.cs index d964ffab9..0f8a898d8 100644 --- a/src/ComputeSharp/Graphics/Helpers/DeviceHelper.Factory.cs +++ b/src/ComputeSharp/Graphics/Helpers/DeviceHelper.Factory.cs @@ -72,7 +72,7 @@ private static unsafe GraphicsDevice GetOrCreateDevice(ID3D12Device* d3D12Device if (!DevicesCache.TryGetValue(luid, out GraphicsDevice? device)) { - device = new GraphicsDevice(d3D12Device, dxgiAdapter, dxgiDescription1); + device = new D3D12GraphicsDevice(d3D12Device, dxgiAdapter, dxgiDescription1); DevicesCache.Add(luid, device); diff --git a/src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs b/src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs index fc4a80fef..2cf59470f 100644 --- a/src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs +++ b/src/ComputeSharp/Shaders/Loading/PipelineDataLoader{T}.cs @@ -1,11 +1,7 @@ -using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using ComputeSharp.Shaders.Dispatching; -using ComputeSharp.Shaders.Extensions; using ComputeSharp.__Internals; -using TerraFX.Interop.DirectX; -using TerraFX.Interop.Windows; using ComputeSharp.Shaders.Models; #pragma warning disable CS0618 @@ -85,21 +81,7 @@ private static unsafe void LoadShader(int threadsX, int threadsY, int threadsZ, [MethodImpl(MethodImplOptions.NoInlining)] private static unsafe void CreatePipelineData(GraphicsDevice device, ICachedShader shaderData, out PipelineData pipelineData) { - using ComPtr d3D12RootSignature = default; - - ShaderDispatchMetadataLoader metadataLoader = new(device.D3D12Device); - - Unsafe.SkipInit(out T shader); - - shader.LoadDispatchMetadata(ref metadataLoader, out *(IntPtr*)&d3D12RootSignature); - - using ComPtr d3D12PipelineState = device.D3D12Device->CreateComputePipelineState(d3D12RootSignature.Get(), shaderData.D3D12ShaderBytecode); - - pipelineData = new PipelineData(d3D12RootSignature.Get(), d3D12PipelineState.Get()); - + device.CreatePipelineData(out pipelineData); shaderData.CachedPipelines.Add(device, pipelineData); - - // Register the newly created pipeline data to enable early disposal - device.RegisterPipelineData(pipelineData); } } \ No newline at end of file diff --git a/src/ComputeSharp/Shaders/Models/D3D12PipelineData.cs b/src/ComputeSharp/Shaders/Models/D3D12PipelineData.cs new file mode 100644 index 000000000..9604a7a23 --- /dev/null +++ b/src/ComputeSharp/Shaders/Models/D3D12PipelineData.cs @@ -0,0 +1,48 @@ +using TerraFX.Interop.DirectX; +using TerraFX.Interop.Windows; + +namespace ComputeSharp.Shaders.Models; + +/// +/// A representing a custom pipeline state for a compute operation. +/// +internal sealed unsafe class D3D12PipelineData : PipelineData +{ + /// + /// The instance for the current object. + /// + private ComPtr d3D12RootSignature; + + /// + /// The instance for the current object. + /// + private ComPtr d3D12PipelineState; + + /// + /// Creates a new instance with the specified parameters. + /// + /// The value for the current shader. + /// The compiled pipeline state to reuse for the current shader. + public D3D12PipelineData(ID3D12RootSignature* d3D12RootSignature, ID3D12PipelineState* d3D12PipelineState) + { + this.d3D12RootSignature = new ComPtr(d3D12RootSignature); + this.d3D12PipelineState = new ComPtr(d3D12PipelineState); + } + + /// + /// Gets the instance for the current object. + /// + public ID3D12RootSignature* D3D12RootSignature => this.d3D12RootSignature; + + /// + /// Gets the instance for the current object. + /// + public ID3D12PipelineState* D3D12PipelineState => this.d3D12PipelineState; + + /// + protected override void DangerousOnDispose() + { + this.d3D12RootSignature.Dispose(); + this.d3D12PipelineState.Dispose(); + } +} diff --git a/src/ComputeSharp/Shaders/Models/PipelineData.cs b/src/ComputeSharp/Shaders/Models/PipelineData.cs index f48ac9a54..5ed1b683f 100644 --- a/src/ComputeSharp/Shaders/Models/PipelineData.cs +++ b/src/ComputeSharp/Shaders/Models/PipelineData.cs @@ -1,49 +1,11 @@ using ComputeSharp.Interop; -using TerraFX.Interop.DirectX; -using TerraFX.Interop.Windows; namespace ComputeSharp.Shaders.Models; /// /// A representing a custom pipeline state for a compute operation. /// -internal sealed unsafe class PipelineData : ReferenceTrackedObject +internal abstract unsafe class PipelineData : ReferenceTrackedObject { - /// - /// The instance for the current object. - /// - private ComPtr d3D12RootSignature; - - /// - /// The instance for the current object. - /// - private ComPtr d3D12PipelineState; - - /// - /// Creates a new instance with the specified parameters. - /// - /// The value for the current shader. - /// The compiled pipeline state to reuse for the current shader. - public PipelineData(ID3D12RootSignature* d3D12RootSignature, ID3D12PipelineState* d3D12PipelineState) - { - this.d3D12RootSignature = new ComPtr(d3D12RootSignature); - this.d3D12PipelineState = new ComPtr(d3D12PipelineState); - } - - /// - /// Gets the instance for the current object. - /// - public ID3D12RootSignature* D3D12RootSignature => this.d3D12RootSignature; - - /// - /// Gets the instance for the current object. - /// - public ID3D12PipelineState* D3D12PipelineState => this.d3D12PipelineState; - - /// - protected override void DangerousOnDispose() - { - this.d3D12RootSignature.Dispose(); - this.d3D12PipelineState.Dispose(); - } + protected override abstract void DangerousOnDispose(); } \ No newline at end of file