Refactor AppState and rendering pipeline components

Changed the `AppStateMachine` to implement `IDisposable` and `IAsyncDisposable` for better resource management.
Changed the `IAppState` interface to include asynchronous methods for state transitions.
Changed the `App` class to start the host asynchronously and added an `OnClosed` method for proper shutdown.
Changed the `EditorState` class to ensure the window closes correctly when exiting the state.
Changed the `LandingState` class to improve window activation and deactivation management.
Changed the `HostHelper` class to register `LandingWindow` and `EngineEditorWindow` as singletons for better performance.
Changed the `ScenePage` class to utilize a new interface for swap chain management.
Changed the `OpenProjectPage` and `CreateProjectPage` classes to enhance navigation handling.
Changed the `ConsoleViewModel` to improve log update handling with a new context structure.
Changed the `OpenProjectViewModel` to clear project lists when navigating away.
Changed the `EngineCore` class to start the graphics pipeline asynchronously.
Changed the `Logger` class to use a new context structure for log changes.
Added the `ICommandBuffer`, `IGraphicsDevice`, and `IRenderView` interfaces to enhance the rendering pipeline.
Changed the `DX12CommandBuffer`, `DX12GraphicsDevice`, and `DX12RenderView` classes for improved resource management and rendering efficiency.
Refactored the `Mesh` class to use a new `Vertex` structure for simplified vertex management.
Added the `TextureUtility` class for texture management utilities, including mip count calculation.
Changed the `launchSettings.json` to include a new profile for the graphics project with native debugging enabled.
Changed the `MeshBuilder` class to utilize the new `Vertex` structure for vertex creation.
This commit is contained in:
2025-06-29 11:38:29 +09:00
parent 4110c166cf
commit 8fd1222780
34 changed files with 1479 additions and 269 deletions

View File

@@ -0,0 +1,14 @@
using Ghost.Graphics.Contracts;
using Vortice.Direct3D12;
namespace Ghost.Graphics.DX12;
internal class DX12CommandBuffer : ICommandBuffer
{
private ID3D12GraphicsCommandList10 _commandList;
public DX12CommandBuffer(ID3D12GraphicsCommandList10 commandList)
{
_commandList = commandList;
}
}

View File

@@ -8,29 +8,29 @@ namespace Ghost.Graphics.DX12;
internal class DX12DebugLayer : IDebugLayer
{
#if DEBUG
private readonly ID3D12Debug6 _d3d12Debug;
private readonly IDXGIDebug1 _dxgiDebug;
#endif
private readonly IDXGIInfoQueue? _dxgiInfoQueue;
public DX12DebugLayer()
{
#if DEBUG
_d3d12Debug = D3D12.D3D12GetDebugInterface<ID3D12Debug6>();
_d3d12Debug.EnableDebugLayer();
_dxgiDebug = DXGI.DXGIGetDebugInterface1<IDXGIDebug1>();
_dxgiDebug.EnableLeakTrackingForThread();
#endif
_dxgiInfoQueue = DXGI.DXGIGetDebugInterface1<IDXGIInfoQueue>();
_dxgiInfoQueue.SetBreakOnSeverity(DXGI.DebugAll, InfoQueueMessageSeverity.Error, true);
_dxgiInfoQueue.SetBreakOnSeverity(DXGI.DebugAll, InfoQueueMessageSeverity.Corruption, true);
}
public void Dispose()
{
#if DEBUG
_dxgiDebug.ReportLiveObjects(DXGI.DebugAll, ReportLiveObjectFlags.Detail | ReportLiveObjectFlags.IgnoreInternal);
_d3d12Debug?.Dispose();
_dxgiDebug?.Dispose();
#endif
_d3d12Debug.Dispose();
_dxgiDebug.Dispose();
_dxgiInfoQueue?.Dispose();
}
}

View File

@@ -13,11 +13,14 @@ internal class DX12GraphicsDevice : IGraphicsDevice
private readonly ID3D12CommandQueue _commandQueue;
private readonly List<IRenderView> _renderViews = new();
private readonly Lock _lock = new();
#if DEBUG
private readonly IDebugLayer _debugLayer;
private readonly DX12DebugLayer _debugLayer;
#endif
private bool _disposed;
public ID3D12Device14 Device => _device;
public IDXGIFactory7 DXGIFactory => _dxgiFactory;
public ID3D12CommandQueue CommandQueue => _commandQueue;
@@ -61,6 +64,8 @@ internal class DX12GraphicsDevice : IGraphicsDevice
adapter.Dispose();
break;
}
adapter.Dispose();
}
if (d3d12Device == null)
@@ -83,35 +88,52 @@ internal class DX12GraphicsDevice : IGraphicsDevice
queue = _device.CreateCommandQueue(queueDesc);
}
public IRenderView CreateRenderView(in SwapChainSurface swapChainSurface)
public IRenderView CreateRenderView(in SwapChainPresenter swapChainSurface)
{
var renderView = new DX12RenderView(this, swapChainSurface);
_renderViews.Add(renderView);
lock (_lock)
{
_renderViews.Add(renderView);
}
return renderView;
}
public void OnRender()
{
foreach (var renderView in _renderViews)
lock (_lock)
{
renderView.Render();
foreach (var renderView in _renderViews)
{
renderView.ExecutePendingResize();
renderView.BeginRender();
renderView.Render();
renderView.EndRender();
}
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
foreach (var renderView in _renderViews)
{
renderView.Dispose();
}
_renderViews.Clear();
_commandQueue?.Dispose();
_device?.Dispose();
_dxgiFactory?.Dispose();
_commandQueue.Release();
_device.Release();
_dxgiFactory.Release();
#if DEBUG
_debugLayer.Dispose();
#endif
_disposed = true;
}
}

View File

@@ -1,7 +1,6 @@
using Ghost.Graphics.Contracts;
using Ghost.Graphics.Data;
using Ghost.Graphics.DX12.Utilities;
using System.Runtime.CompilerServices;
using Vortice.Direct3D12;
using Vortice.DXGI;
@@ -13,6 +12,7 @@ internal class DX12RenderView : IRenderView
private const int _DEPTH_STENCIL_VIEW_HEAP_SIZE = 256;
private readonly DX12GraphicsDevice _graphicsDevice;
private readonly SwapChainPresenter _swapChainPresenter;
private readonly IDXGISwapChain4 _swapChain;
private readonly ID3D12Resource[] _renderTargets;
@@ -20,7 +20,7 @@ internal class DX12RenderView : IRenderView
private uint _backBufferIndex;
private readonly ID3D12CommandAllocator[] _commandAllocators;
private readonly ID3D12GraphicsCommandList7 _commandList;
private readonly ID3D12GraphicsCommandList10 _commandList;
private readonly ID3D12Fence1 _fence;
private readonly AutoResetEvent _fenceEvent;
@@ -28,9 +28,19 @@ internal class DX12RenderView : IRenderView
private readonly D3D12DescriptorAllocator _rtvHeap;
public DX12RenderView(DX12GraphicsDevice pipelineContext, in SwapChainSurface swapChainSurface)
private readonly ICommandBuffer _commandBuffer;
private readonly Lock _lock = new();
private uint _pendingWidth;
private uint _pendingHeight;
private bool _resizeRequested;
private bool _disposed;
public DX12RenderView(DX12GraphicsDevice graphicsDevice, in SwapChainPresenter swapChainSurface)
{
_graphicsDevice = pipelineContext;
_graphicsDevice = graphicsDevice;
_swapChainPresenter = swapChainSurface;
_rtvHeap = new(_graphicsDevice.Device, DescriptorHeapType.RenderTargetView, _RENDER_TARGET_VIEW_HEAP_SIZE);
@@ -39,21 +49,23 @@ internal class DX12RenderView : IRenderView
_fenceValues = new ulong[GraphicsPipeline.FRAME_COUNT];
_renderTargetDescriptorIndexes = new uint[GraphicsPipeline.FRAME_COUNT];
InitializeSwapChain(swapChainSurface, out _swapChain);
InitializeSwapChain(out _swapChain);
InitializeCommandObjects(out _commandAllocators, out _commandList, out _fence);
CreateRenderTargets();
_commandBuffer = new DX12CommandBuffer(_commandList);
}
private void InitializeSwapChain(in SwapChainSurface swapChainSurface, out IDXGISwapChain4 swapChain)
private void InitializeSwapChain(out IDXGISwapChain4 swapChain)
{
var swapChainDesc = new SwapChainDescription1
{
Width = swapChainSurface.Width,
Height = swapChainSurface.Height,
Width = _swapChainPresenter.Width,
Height = _swapChainPresenter.Height,
Format = Format.B8G8R8A8_UNorm,
Stereo = false,
SampleDescription = new SampleDescription(1, 0),
BufferUsage = Usage.RenderTargetOutput,
BufferUsage = Usage.Backbuffer | Usage.RenderTargetOutput,
BufferCount = GraphicsPipeline.FRAME_COUNT,
Scaling = Scaling.Stretch,
SwapEffect = SwapEffect.FlipDiscard,
@@ -61,36 +73,37 @@ internal class DX12RenderView : IRenderView
Flags = SwapChainFlags.AllowTearing
};
// NOTE: Not going to need it for now, this is for standalone applications.
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
switch (_swapChainPresenter.Type)
{
Windowed = true,
};
switch (swapChainSurface.Type)
{
case SwapChainSurface.TargetType.Composition:
case SwapChainPresenter.TargetType.Composition:
var swapChain1 = _graphicsDevice.DXGIFactory.CreateSwapChainForComposition(_graphicsDevice.CommandQueue, swapChainDesc);
swapChain = swapChain1.QueryInterface<IDXGISwapChain4>();
swapChain1.Dispose();
_backBufferIndex = swapChain.CurrentBackBufferIndex;
swapChainSurface.SwapChainPanelNative!.SetSwapChain(swapChain);
_swapChainPresenter.SwapChainPanelNative!.SetSwapChain(swapChain);
break;
case SwapChainSurface.TargetType.Hwnd:
case SwapChainPresenter.TargetType.Hwnd:
var swapChainFullscreenDesc = new SwapChainFullscreenDescription
{
Windowed = true,
};
var swapChain2 = _graphicsDevice.DXGIFactory.CreateSwapChainForHwnd(
_graphicsDevice.CommandQueue,
swapChainSurface.Hwnd,
_swapChainPresenter.Hwnd,
swapChainDesc,
swapChainFullscreenDesc,
null);
swapChain = swapChain2.QueryInterface<IDXGISwapChain4>();
swapChain2.Dispose();
break;
default:
throw new ArgumentException("Unsupported swap chain surface type.");
}
}
private void InitializeCommandObjects(out ID3D12CommandAllocator[] commandAllocator, out ID3D12GraphicsCommandList7 commandList, out ID3D12Fence1 fence)
private void InitializeCommandObjects(out ID3D12CommandAllocator[] commandAllocator, out ID3D12GraphicsCommandList10 commandList, out ID3D12Fence1 fence)
{
commandAllocator = new ID3D12CommandAllocator[GraphicsPipeline.FRAME_COUNT];
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
@@ -98,10 +111,10 @@ internal class DX12RenderView : IRenderView
commandAllocator[i] = _graphicsDevice.Device.CreateCommandAllocator(CommandListType.Direct);
}
commandList = _graphicsDevice.Device.CreateCommandList<ID3D12GraphicsCommandList7>(CommandListType.Direct, commandAllocator[0], null!);
commandList = _graphicsDevice.Device.CreateCommandList<ID3D12GraphicsCommandList10>(CommandListType.Direct, commandAllocator[0], null!);
commandList.Close();
fence = _graphicsDevice.Device.CreateFence<ID3D12Fence1>(_fenceValues[_backBufferIndex], FenceFlags.None);
_commandList.Close();
_fenceValues[_backBufferIndex]++;
}
@@ -118,32 +131,80 @@ internal class DX12RenderView : IRenderView
}
}
public void Resize(uint width, uint height)
public void RequestResize(uint width, uint height)
{
lock (_lock)
{
if (_pendingWidth == width && _pendingHeight == height)
{
return;
}
_resizeRequested = true;
_pendingWidth = width;
_pendingHeight = height;
}
}
public void ExecutePendingResize()
{
if (!_resizeRequested)
{
return;
}
uint newWidth;
uint newHeight;
lock (_lock)
{
newWidth = _pendingWidth;
newHeight = _pendingHeight;
_resizeRequested = false;
}
WaitIdle();
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
{
_renderTargets[i].Dispose();
_rtvHeap.ReleaseDescriptor(_renderTargetDescriptorIndexes[i]);
if (_renderTargets[i] is not null)
{
_renderTargets[i].Dispose();
_rtvHeap.ReleaseDescriptor(_renderTargetDescriptorIndexes[i]);
}
_fenceValues[i] = _fenceValues[_backBufferIndex];
}
_swapChain.ResizeBuffers(GraphicsPipeline.FRAME_COUNT, width, height, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
_swapChain.ResizeBuffers(GraphicsPipeline.FRAME_COUNT, newWidth, newHeight, Format.B8G8R8A8_UNorm, SwapChainFlags.AllowTearing).CheckError();
CreateRenderTargets();
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
}
public void Render()
public ICommandBuffer BeginRender()
{
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
var commandAllocator = _commandAllocators[_backBufferIndex];
commandAllocator.Reset();
_commandList.Reset(commandAllocator, null);
_commandList.ResourceBarrierTransition(_renderTargets[_backBufferIndex], ResourceStates.Present, ResourceStates.RenderTarget);
return _commandBuffer;
}
public void Render()
{
}
public void EndRender()
{
_commandList.ResourceBarrierTransition(_renderTargets[_backBufferIndex], ResourceStates.RenderTarget, ResourceStates.Present);
_commandList.Close();
_graphicsDevice.CommandQueue.ExecuteCommandLists([_commandList]);
_graphicsDevice.CommandQueue.ExecuteCommandLists(new[] { _commandList });
_swapChain.Present(1, PresentFlags.None).CheckError();
@@ -159,7 +220,6 @@ internal class DX12RenderView : IRenderView
return;
}
_backBufferIndex = _swapChain.CurrentBackBufferIndex;
if (_fence.CompletedValue < _fenceValues[_backBufferIndex]
&& _fence.SetEventOnCompletion(_fenceValues[_backBufferIndex], _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
{
@@ -172,38 +232,36 @@ internal class DX12RenderView : IRenderView
public void WaitIdle()
{
var fenceValue = _fenceValues[_backBufferIndex];
if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Failure
|| _fence.SetEventOnCompletion(fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Failure)
if (_graphicsDevice.CommandQueue.Signal(_fence, fenceValue).Success
&& _fence.SetEventOnCompletion(fenceValue, _fenceEvent.SafeWaitHandle.DangerousGetHandle()).Success)
{
return;
}
_fenceEvent.WaitOne();
_fenceValues[_backBufferIndex]++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Flush()
{
for (var i = 0; i < GraphicsPipeline.FRAME_COUNT; i++)
{
WaitIdle();
_fenceEvent.WaitOne();
_fenceValues[_backBufferIndex]++;
}
}
public void Dispose()
{
Flush();
if (_disposed)
{
return;
}
WaitIdle();
_swapChainPresenter.SwapChainPanelNative?.SetSwapChain(null);
foreach (var commandAllocator in _commandAllocators)
{
commandAllocator.Dispose();
}
_commandAllocators.AsSpan().Clear();
foreach (var renderTarget in _renderTargets)
{
renderTarget.Dispose();
}
_renderTargets.AsSpan().Clear();
_swapChain.Dispose();
_commandList.Dispose();
@@ -215,5 +273,7 @@ internal class DX12RenderView : IRenderView
_backBufferIndex = 0;
_fenceValues.AsSpan().Clear();
_disposed = true;
}
}

View File

@@ -10,7 +10,7 @@ internal class D3D12DescriptorAllocator : IDisposable
private const DescriptorIndex _INVALID_DESCRIPTOR_INDEX = ~0u;
private readonly ID3D12Device _device;
private readonly Lock _mutex = new();
private readonly Lock _lock = new();
private ID3D12DescriptorHeap? _heap;
private ID3D12DescriptorHeap? _shaderVisibleHeap;
@@ -56,14 +56,15 @@ internal class D3D12DescriptorAllocator : IDisposable
ShaderVisible = type == DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView || type == DescriptorHeapType.Sampler;
Stride = device.GetDescriptorHandleIncrementSize(type);
Debug.Assert(AllocateResources(numDescriptors));
var success = AllocateResources(numDescriptors);
Debug.Assert(success);
}
public DescriptorIndex AllocateDescriptor() => AllocateDescriptors(1);
public DescriptorIndex AllocateDescriptors(uint count)
{
lock (_mutex)
lock (_lock)
{
DescriptorIndex foundIndex = 0;
uint freeCount = 0;
@@ -120,7 +121,7 @@ internal class D3D12DescriptorAllocator : IDisposable
return;
}
lock (_mutex)
lock (_lock)
{
for (var index = baseIndex; index < baseIndex + count; index++)
{

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,234 @@
using Ghost.Graphics.Utilities;
using System.Runtime.CompilerServices;
using Vortice.Direct3D12;
using Vortice.DXGI;
namespace Ghost.Graphics.DX12.Utilities;
internal unsafe class D3D12ResourceUtils
{
private const ResourceStates _INITIALCOPYTARGETSTATE = ResourceStates.Common;
private const ResourceStates _INITIALREADTARGETSTATE = ResourceStates.Common;
private const ResourceStates _INITIALUAVTARGETSTATE = ResourceStates.Common;
public static ID3D12Resource CreateStaticBuffer<T>(
ID3D12Device device,
D3D12ResourceUploadBatch resourceUpload,
T[] data, ResourceStates afterState,
ResourceFlags flags = ResourceFlags.None)
where T : unmanaged
{
Span<T> span = data;
return CreateStaticBuffer(device, resourceUpload, span, afterState, flags);
}
public static ID3D12Resource CreateStaticBuffer<T>(
ID3D12Device device,
D3D12ResourceUploadBatch resourceUpload,
Span<T> data,
ResourceStates afterState,
ResourceFlags flags = ResourceFlags.None)
where T : unmanaged
{
var sizeInBytes = (uint)(sizeof(T) * data.Length);
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
if (sizeInBytes > c_maxBytes)
{
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
}
var buffer = device.CreateCommittedResource(
HeapType.Default,
HeapFlags.None,
ResourceDescription.Buffer(sizeInBytes, flags),
_INITIALCOPYTARGETSTATE
);
fixed (T* dataPtr = data)
{
SubresourceData initData = new()
{
pData = dataPtr,
};
resourceUpload.Upload(buffer, 0, &initData, 1);
resourceUpload.Transition(buffer, ResourceStates.CopyDest, afterState);
return buffer;
}
}
public static ID3D12Resource CreateUploadBuffer<T>(
ID3D12Device device,
T[] data,
ResourceFlags flags = ResourceFlags.None)
where T : unmanaged
{
var sizeInBytes = (uint)(sizeof(T) * data.Length);
fixed (T* dataPtr = data)
{
return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
}
}
public static ID3D12Resource CreateUploadBuffer<T>(
ID3D12Device device,
Span<T> data,
ResourceFlags flags = ResourceFlags.None)
where T : unmanaged
{
var sizeInBytes = (uint)(sizeof(T) * data.Length);
fixed (T* dataPtr = data)
{
return CreateUploadBuffer(device, sizeInBytes, dataPtr, flags);
}
}
public static ID3D12Resource CreateUploadBuffer(
ID3D12Device device,
uint sizeInBytes,
void* data = default,
ResourceFlags flags = ResourceFlags.None)
{
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
if (sizeInBytes > c_maxBytes)
{
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
}
var buffer = device.CreateCommittedResource(
HeapType.Upload,
HeapFlags.None,
ResourceDescription.Buffer(sizeInBytes, flags),
ResourceStates.GenericRead
);
if (data is not null)
{
void* mappedPtr = default;
buffer.Map(0, null, &mappedPtr).CheckError();
Unsafe.CopyBlock(data, mappedPtr, sizeInBytes);
buffer.Unmap(0, null);
}
return buffer;
}
public static ID3D12Resource CreateReadbackBuffer(
ID3D12Device device,
uint sizeInBytes,
ResourceFlags flags = ResourceFlags.None)
{
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
if (sizeInBytes > c_maxBytes)
{
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
}
var buffer = device.CreateCommittedResource(
HeapType.Readback,
HeapFlags.None,
ResourceDescription.Buffer(sizeInBytes, flags),
_INITIALREADTARGETSTATE
);
return buffer;
}
public static ID3D12Resource CreateCPUDestinationBuffer(
ID3D12Device device,
uint sizeInBytes,
ResourceFlags flags = ResourceFlags.None)
{
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
if (sizeInBytes > c_maxBytes)
{
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {sizeInBytes})");
}
var buffer = device.CreateCommittedResource(
HeapType.Default,
HeapFlags.None,
ResourceDescription.Buffer(sizeInBytes, flags),
ResourceStates.CopyDest
);
return buffer;
}
public static ID3D12Resource CreateUAVBuffer(ID3D12Device device, uint bufferSize,
ResourceStates initialState = ResourceStates.Common,
ResourceFlags flags = ResourceFlags.None)
{
var c_maxBytes = D3D12.RequestResourceSizeInMegaBytesExpressionATerm * 1024u * 1024u;
if (bufferSize > c_maxBytes)
{
throw new InvalidOperationException($"ERROR: Resource size too large for DirectX 12 (size {bufferSize})");
}
var buffer = device.CreateCommittedResource(
HeapType.Default,
HeapFlags.None,
ResourceDescription.Buffer(bufferSize, ResourceFlags.AllowUnorderedAccess | flags),
_INITIALCOPYTARGETSTATE
);
return buffer;
}
public static ID3D12Resource CreateTexture2D<T>(
ID3D12Device device,
D3D12ResourceUploadBatch resourceUpload,
uint width, uint height, Format format,
Span<T> data,
bool generateMips = false,
ResourceStates afterState = ResourceStates.PixelShaderResource,
ResourceFlags flags = ResourceFlags.None)
where T : unmanaged
{
if ((width > D3D12.RequestTexture2DUOrVDimension) || (height > D3D12.RequestTexture2DUOrVDimension))
{
throw new InvalidOperationException($"ERROR: Resource dimensions too large for DirectX 12 (2D: size {width} by {height})");
}
ushort mipLevels = 1;
if (generateMips)
{
generateMips = resourceUpload.IsSupportedForGenerateMips(format);
if (generateMips)
{
mipLevels = (ushort)TextureUtility.CountMips(width, height);
}
}
var texture = device.CreateCommittedResource(
HeapType.Default,
HeapFlags.None,
ResourceDescription.Texture2D(format, width, height, 1, mipLevels, 1, 0, flags),
_INITIALCOPYTARGETSTATE
);
fixed (T* dataPtr = data)
{
FormatHelper.GetSurfaceInfo(format, width, height, out var rowPitch, out var slicePitch);
SubresourceData initData = new()
{
pData = dataPtr,
RowPitch = (nint)rowPitch,
SlicePitch = (nint)slicePitch
};
resourceUpload.Upload(texture, 0, &initData, 1);
resourceUpload.Transition(texture, ResourceStates.CopyDest, afterState);
if (generateMips)
{
resourceUpload.GenerateMips(texture);
}
return texture;
}
}
}