protected override void CreateBuffers() { base.CreateBuffers(); _constantBuffer = CreateConstantBuffer(out _constantBufferDataBegin); ID3D12Resource *CreateConstantBuffer(out byte *constantBufferDataBegin) { ID3D12Resource *constantBuffer; var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(1024 * 64); ThrowIfFailed(D3DDevice->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, __uuidof <ID3D12Resource>(), (void **)&constantBuffer)); var cbvDesc = new D3D12_CONSTANT_BUFFER_VIEW_DESC { BufferLocation = constantBuffer->GetGPUVirtualAddress(), SizeInBytes = (uint)((sizeof(SceneConstantBuffer) + 255) & ~255) // CB size is required to be 256-byte aligned. }; D3DDevice->CreateConstantBufferView(&cbvDesc, _cbvHeap->GetCPUDescriptorHandleForHeapStart()); // Map and initialize the constant buffer. We don't unmap this until the // app closes. Keeping things mapped for the lifetime of the resource is okay. fixed(byte **pConstantBufferDataBegin = &constantBufferDataBegin) { var readRange = new D3D12_RANGE(); // We do not intend to read from this resource on the CPU. ThrowIfFailed(constantBuffer->Map(Subresource: 0, &readRange, (void **)pConstantBufferDataBegin)); Unsafe.CopyBlock(ref constantBufferDataBegin[0], ref Unsafe.As <SceneConstantBuffer, byte>(ref _constantBufferData), (uint)sizeof(SceneConstantBuffer)); } return(constantBuffer); } }
protected virtual ID3D12Resource *CreateVertexBuffer(out D3D12_VERTEX_BUFFER_VIEW vertexBufferView) { // Define the geometry for a triangle. const int TriangleVerticesCount = 3; var triangleVertices = stackalloc Vertex[TriangleVerticesCount] { new Vertex { Position = new Vector3(0.0f, 0.25f * AspectRatio, 0.0f), Color = new Vector4(1.0f, 0.0f, 0.0f, 1.0f) }, new Vertex { Position = new Vector3(0.25f, -0.25f * AspectRatio, 0.0f), Color = new Vector4(0.0f, 1.0f, 0.0f, 1.0f) }, new Vertex { Position = new Vector3(-0.25f, -0.25f * AspectRatio, 0.0f), Color = new Vector4(0.0f, 0.0f, 1.0f, 1.0f) }, }; var vertexBufferSize = (uint)sizeof(Vertex) * TriangleVerticesCount; // Note: using upload heaps to transfer static data like vert buffers is not // recommended. Every time the GPU needs it, the upload heap will be marshalled // over. Please read up on Default Heap usage. An upload heap is used here for // code simplicity and because there are very few verts to actually transfer. ID3D12Resource *vertexBuffer; var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(vertexBufferSize); var iid = IID_ID3D12Resource; ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), D3DDevice->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)&vertexBuffer )); // Copy the triangle data to the vertex buffer. var readRange = new D3D12_RANGE(); byte *pVertexDataBegin; ThrowIfFailed(nameof(ID3D12Resource.Map), vertexBuffer->Map(Subresource: 0, &readRange, (void **)&pVertexDataBegin)); Unsafe.CopyBlock(pVertexDataBegin, triangleVertices, vertexBufferSize); vertexBuffer->Unmap(0, null); // Initialize the vertex buffer view. vertexBufferView.BufferLocation = vertexBuffer->GetGPUVirtualAddress(); vertexBufferView.StrideInBytes = (uint)sizeof(Vertex); vertexBufferView.SizeInBytes = vertexBufferSize; return(vertexBuffer); }
/// <inheritdoc /> public override void Unmap(nuint writtenRangeOffset, nuint writtenRangeLength) { var writtenRange = new D3D12_RANGE { Begin = writtenRangeOffset, End = writtenRangeOffset + writtenRangeLength, }; D3D12Resource->Unmap(Subresource: 0, &writtenRange); }
/// <inheritdoc /> /// <exception cref="ExternalException">The call to <see cref="ID3D12Resource.Map(uint, D3D12_RANGE*, void**)" /> failed.</exception> public override T *Map <T>(nuint readRangeOffset, nuint readRangeLength) { var readRange = new D3D12_RANGE { Begin = readRangeOffset, End = readRangeOffset + readRangeLength, }; void *pDestination; ThrowExternalExceptionIfFailed(nameof(ID3D12Resource.Map), D3D12Resource->Map(Subresource: 0, &readRange, &pDestination)); return((T *)pDestination); }
public virtual void Unmap( uint Subresource, ref D3D12_RANGE pWrittenRange ) { var fp = GetFunctionPointer(9); if (m_UnmapFunc == null) { m_UnmapFunc = (UnmapFunc)Marshal.GetDelegateForFunctionPointer(fp, typeof(UnmapFunc)); } m_UnmapFunc(m_ptr, Subresource, ref pWrittenRange); }
public virtual int Map( uint Subresource, ref D3D12_RANGE pReadRange, out IntPtr ppData ) { var fp = GetFunctionPointer(8); if (m_MapFunc == null) { m_MapFunc = (MapFunc)Marshal.GetDelegateForFunctionPointer(fp, typeof(MapFunc)); } return(m_MapFunc(m_ptr, Subresource, ref pReadRange, out ppData)); }
/// <inheritdoc /> /// <exception cref="ExternalException">The call to <see cref="ID3D12Resource.Map(uint, D3D12_RANGE*, void**)" /> failed.</exception> public override void Write(ReadOnlySpan <byte> bytes) { var d3d12Resource = D3D12Resource; var bytesWritten = bytes.Length; var readRange = new D3D12_RANGE(); var writtenRange = new D3D12_RANGE(UIntPtr.Zero, (UIntPtr)bytesWritten); void *pDestination; ThrowExternalExceptionIfFailed(nameof(ID3D12Resource.Map), d3d12Resource->Map(Subresource: 0, &readRange, &pDestination)); var destination = new Span <byte>(pDestination, bytesWritten); bytes.CopyTo(destination); d3d12Resource->Unmap(Subresource: 0, &writtenRange); }
protected override void DestroyBuffers() { DestroyConstantBuffer(); base.DestroyBuffers(); void DestroyConstantBuffer() { var constantBuffer = _constantBuffer; if (constantBuffer != null) { _constantBuffer = null; var writtenRange = new D3D12_RANGE(); constantBuffer->Unmap(Subresource: 0, &writtenRange); _ = constantBuffer->Release(); } } }
// Load the sample assets. private void LoadAssets() { Guid iid; ID3DBlob *signature = null; ID3DBlob *error = null; ID3DBlob *vertexShader = null; ID3DBlob *pixelShader = null; try { // Create an empty root signature. { var rootSignatureDesc = new D3D12_ROOT_SIGNATURE_DESC { Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT }; ThrowIfFailed(nameof(D3D12SerializeRootSignature), D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); fixed(ID3D12RootSignature **rootSignature = &_rootSignature) { iid = IID_ID3D12RootSignature; ThrowIfFailed(nameof(ID3D12Device.CreateRootSignature), _device->CreateRootSignature(nodeMask: 0, signature->GetBufferPointer(), signature->GetBufferSize(), &iid, (void **)rootSignature)); } } // Create the pipeline state, which includes compiling and loading shaders. { var compileFlags = 0u; #if DEBUG // Enable better shader debugging with the graphics debugging tools. compileFlags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #endif fixed(char *fileName = GetAssetFullPath(@"D3D12\Assets\Shaders\HelloTriangle.hlsl")) { var entryPoint = 0x00006E69614D5356; // VSMain var target = 0x0000305F355F7376; // vs_5_0 ThrowIfFailed(nameof(D3DCompileFromFile), D3DCompileFromFile((ushort *)fileName, pDefines: null, pInclude: null, (sbyte *)&entryPoint, (sbyte *)&target, compileFlags, Flags2: 0, &vertexShader, ppErrorMsgs: null)); entryPoint = 0x00006E69614D5350; // PSMain target = 0x0000305F355F7370; // ps_5_0 ThrowIfFailed(nameof(D3DCompileFromFile), D3DCompileFromFile((ushort *)fileName, pDefines: null, pInclude: null, (sbyte *)&entryPoint, (sbyte *)&target, compileFlags, Flags2: 0, &pixelShader, ppErrorMsgs: null)); } // Define the vertex input layout. const int InputElementDescsCount = 2; var semanticName0 = stackalloc ulong[2] { 0x4E4F495449534F50, // POSITION 0x0000000000000000, }; var semanticName1 = stackalloc ulong[1] { 0x000000524F4C4F43, // COLOR }; var inputElementDescs = stackalloc D3D12_INPUT_ELEMENT_DESC[InputElementDescsCount] { new D3D12_INPUT_ELEMENT_DESC { SemanticName = (sbyte *)semanticName0, Format = DXGI_FORMAT_R32G32B32_FLOAT, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, }, new D3D12_INPUT_ELEMENT_DESC { SemanticName = (sbyte *)semanticName1, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, AlignedByteOffset = 12, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, }, }; // Describe and create the graphics pipeline state object (PSO). var psoDesc = new D3D12_GRAPHICS_PIPELINE_STATE_DESC { InputLayout = new D3D12_INPUT_LAYOUT_DESC { pInputElementDescs = inputElementDescs, NumElements = InputElementDescsCount, }, pRootSignature = _rootSignature, VS = new D3D12_SHADER_BYTECODE(vertexShader), PS = new D3D12_SHADER_BYTECODE(pixelShader), RasterizerState = D3D12_RASTERIZER_DESC.DEFAULT, BlendState = D3D12_BLEND_DESC.DEFAULT, DepthStencilState = D3D12_DEPTH_STENCIL_DESC.DEFAULT, SampleMask = uint.MaxValue, PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, NumRenderTargets = 1, SampleDesc = new DXGI_SAMPLE_DESC(count: 1, quality: 0), }; psoDesc.DepthStencilState.DepthEnable = FALSE; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; fixed(ID3D12PipelineState **pipelineState = &_pipelineState) { iid = IID_ID3D12PipelineState; ThrowIfFailed(nameof(ID3D12Device.CreateGraphicsPipelineState), _device->CreateGraphicsPipelineState(&psoDesc, &iid, (void **)pipelineState)); } } // Create the command list. fixed(ID3D12GraphicsCommandList **commandList = &_commandList) { iid = IID_ID3D12GraphicsCommandList; ThrowIfFailed(nameof(ID3D12Device.CreateCommandList), _device->CreateCommandList(nodeMask: 0, D3D12_COMMAND_LIST_TYPE_DIRECT, _commandAllocator, _pipelineState, &iid, (void **)commandList)); } // Command lists are created in the recording state, but there is nothing // to record yet. The main loop expects it to be closed, so close it now. ThrowIfFailed(nameof(ID3D12GraphicsCommandList.Close), _commandList->Close()); // Create the vertex buffer. { // Define the geometry for a triangle. const int TriangleVerticesCount = 3; var triangleVertices = stackalloc Vertex[TriangleVerticesCount] { new Vertex { Position = new Vector3(0.0f, 0.25f * AspectRatio, 0.0f), Color = new Vector4(1.0f, 0.0f, 0.0f, 1.0f) }, new Vertex { Position = new Vector3(0.25f, -0.25f * AspectRatio, 0.0f), Color = new Vector4(0.0f, 1.0f, 0.0f, 1.0f) }, new Vertex { Position = new Vector3(-0.25f, -0.25f * AspectRatio, 0.0f), Color = new Vector4(0.0f, 0.0f, 1.0f, 1.0f) }, }; var vertexBufferSize = (uint)sizeof(Vertex) * TriangleVerticesCount; // Note: using upload heaps to transfer static data like vert buffers is not // recommended. Every time the GPU needs it, the upload heap will be marshalled // over. Please read up on Default Heap usage. An upload heap is used here for // code simplicity and because there are very few verts to actually transfer. fixed(ID3D12Resource **vertexBuffer = &_vertexBuffer) { var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(vertexBufferSize); iid = IID_ID3D12Resource; ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), _device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)vertexBuffer )); } // Copy the triangle data to the vertex buffer. var readRange = new D3D12_RANGE(); byte *pVertexDataBegin; ThrowIfFailed(nameof(ID3D12Resource.Map), _vertexBuffer->Map(Subresource: 0, &readRange, (void **)&pVertexDataBegin)); Unsafe.CopyBlock(pVertexDataBegin, triangleVertices, vertexBufferSize); _vertexBuffer->Unmap(0, null); // Initialize the vertex buffer view. _vertexBufferView.BufferLocation = _vertexBuffer->GetGPUVirtualAddress(); _vertexBufferView.StrideInBytes = (uint)sizeof(Vertex); _vertexBufferView.SizeInBytes = vertexBufferSize; } // Create and record the bundle. { fixed(ID3D12GraphicsCommandList **ppBundle = &_bundle) { iid = IID_ID3D12GraphicsCommandList; ThrowIfFailed(nameof(ID3D12Device.CreateCommandList), _device->CreateCommandList(nodeMask: 0, D3D12_COMMAND_LIST_TYPE_BUNDLE, _bundleAllocator, _pipelineState, &iid, (void **)ppBundle)); } _bundle->SetGraphicsRootSignature(_rootSignature); _bundle->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); fixed(D3D12_VERTEX_BUFFER_VIEW *vertexBufferView = &_vertexBufferView) { _bundle->IASetVertexBuffers(StartSlot: 0, 1, vertexBufferView); } _bundle->DrawInstanced(3, 1, 0, 0); ThrowIfFailed(nameof(ID3D12GraphicsCommandList.Close), _bundle->Close()); } // Create synchronization objects and wait until assets have been uploaded to the GPU. { fixed(ID3D12Fence **fence = &_fence) { iid = IID_ID3D12Fence; ThrowIfFailed(nameof(ID3D12Device.CreateFence), _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, &iid, (void **)fence)); _fenceValue = 1; } // Create an event handle to use for frame synchronization. _fenceEvent = CreateEventW(lpEventAttributes: null, bManualReset: FALSE, bInitialState: FALSE, lpName: null); if (_fenceEvent == null) { var hr = Marshal.GetHRForLastWin32Error(); Marshal.ThrowExceptionForHR(hr); } // Wait for the command list to execute; we are reusing the same command // list in our main loop but for now, we just want to wait for setup to // complete before continuing. WaitForPreviousFrame(); } } finally { if (signature != null) { signature->Release(); } if (error != null) { error->Release(); } if (vertexShader != null) { vertexShader->Release(); } if (pixelShader != null) { pixelShader->Release(); } } }
// Load the sample assets. private void LoadAssets() { Guid iid; ID3DBlob *signature = null; ID3DBlob *error = null; ID3DBlob *vertexShader = null; ID3DBlob *pixelShader = null; try { // Create a root signature consisting of a descriptor table with a single CBV. { var featureData = new D3D12_FEATURE_DATA_ROOT_SIGNATURE { // This is the highest version the sample supports. If CheckFeatureSupport succeeds, the HighestVersion returned will not be greater than this. HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1 }; if (FAILED(_device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, (uint)sizeof(D3D12_FEATURE_DATA_ROOT_SIGNATURE)))) { featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; } const int RangesCount = 1; var ranges = stackalloc D3D12_DESCRIPTOR_RANGE1[RangesCount]; const int RootParametersCount = 1; var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[RootParametersCount]; ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); rootParameters[0].InitAsDescriptorTable(RangesCount, ranges, D3D12_SHADER_VISIBILITY_VERTEX); // Allow input layout and deny unnecessary access to certain pipeline stages. var rootSignatureFlags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS; var rootSignatureDesc = new D3D12_VERSIONED_ROOT_SIGNATURE_DESC(); rootSignatureDesc.Init_1_1(RootParametersCount, rootParameters, 0, null, rootSignatureFlags); ThrowIfFailed(nameof(D3D12SerializeRootSignature), D3D12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error)); fixed(ID3D12RootSignature **rootSignature = &_rootSignature) { iid = IID_ID3D12RootSignature; ThrowIfFailed(nameof(ID3D12Device.CreateRootSignature), _device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), &iid, (void **)rootSignature)); } } // Create the pipeline state, which includes compiling and loading shaders. { var compileFlags = 0u; #if DEBUG // Enable better shader debugging with the graphics debugging tools. compileFlags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #endif fixed(char *fileName = GetAssetFullPath(@"D3D12\Assets\Shaders\HelloConstBuffer.hlsl")) { var entryPoint = 0x00006E69614D5356; // VSMain var target = 0x0000305F355F7376; // vs_5_0 ThrowIfFailed(nameof(D3DCompileFromFile), D3DCompileFromFile((ushort *)fileName, pDefines: null, pInclude: null, (sbyte *)&entryPoint, (sbyte *)&target, compileFlags, Flags2: 0, &vertexShader, ppErrorMsgs: null)); entryPoint = 0x00006E69614D5350; // PSMain target = 0x0000305F355F7370; // ps_5_0 ThrowIfFailed(nameof(D3DCompileFromFile), D3DCompileFromFile((ushort *)fileName, pDefines: null, pInclude: null, (sbyte *)&entryPoint, (sbyte *)&target, compileFlags, Flags2: 0, &pixelShader, ppErrorMsgs: null)); } // Define the vertex input layout. const int InputElementDescsCount = 2; var semanticName0 = stackalloc ulong[2] { 0x4E4F495449534F50, // POSITION 0x0000000000000000, }; var semanticName1 = stackalloc ulong[1] { 0x000000524F4C4F43, // COLOR }; var inputElementDescs = stackalloc D3D12_INPUT_ELEMENT_DESC[InputElementDescsCount] { new D3D12_INPUT_ELEMENT_DESC { SemanticName = (sbyte *)semanticName0, Format = DXGI_FORMAT_R32G32B32_FLOAT, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, }, new D3D12_INPUT_ELEMENT_DESC { SemanticName = (sbyte *)semanticName1, Format = DXGI_FORMAT_R32G32B32A32_FLOAT, AlignedByteOffset = 12, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, }, }; // Describe and create the graphics pipeline state object (PSO). var psoDesc = new D3D12_GRAPHICS_PIPELINE_STATE_DESC { InputLayout = new D3D12_INPUT_LAYOUT_DESC { pInputElementDescs = inputElementDescs, NumElements = InputElementDescsCount, }, pRootSignature = _rootSignature, VS = new D3D12_SHADER_BYTECODE(vertexShader), PS = new D3D12_SHADER_BYTECODE(pixelShader), RasterizerState = D3D12_RASTERIZER_DESC.DEFAULT, BlendState = D3D12_BLEND_DESC.DEFAULT, DepthStencilState = D3D12_DEPTH_STENCIL_DESC.DEFAULT, SampleMask = uint.MaxValue, PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, NumRenderTargets = 1, SampleDesc = new DXGI_SAMPLE_DESC(count: 1, quality: 0), }; psoDesc.DepthStencilState.DepthEnable = FALSE; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; fixed(ID3D12PipelineState **pipelineState = &_pipelineState) { iid = IID_ID3D12PipelineState; ThrowIfFailed(nameof(ID3D12Device.CreateGraphicsPipelineState), _device->CreateGraphicsPipelineState(&psoDesc, &iid, (void **)pipelineState)); } } // Create the command list. fixed(ID3D12GraphicsCommandList **commandList = &_commandList) { iid = IID_ID3D12GraphicsCommandList; ThrowIfFailed(nameof(ID3D12Device.CreateCommandList), _device->CreateCommandList(nodeMask: 0, D3D12_COMMAND_LIST_TYPE_DIRECT, _commandAllocator, _pipelineState, &iid, (void **)commandList)); } // Command lists are created in the recording state, but there is nothing // to record yet. The main loop expects it to be closed, so close it now. ThrowIfFailed(nameof(ID3D12GraphicsCommandList.Close), _commandList->Close()); // Create the vertex buffer. { // Define the geometry for a triangle. const int TriangleVerticesCount = 3; var triangleVertices = stackalloc Vertex[TriangleVerticesCount] { new Vertex { Position = new Vector3(0.0f, 0.25f * AspectRatio, 0.0f), Color = new Vector4(1.0f, 0.0f, 0.0f, 1.0f) }, new Vertex { Position = new Vector3(0.25f, -0.25f * AspectRatio, 0.0f), Color = new Vector4(0.0f, 1.0f, 0.0f, 1.0f) }, new Vertex { Position = new Vector3(-0.25f, -0.25f * AspectRatio, 0.0f), Color = new Vector4(0.0f, 0.0f, 1.0f, 1.0f) }, }; var vertexBufferSize = (uint)sizeof(Vertex) * TriangleVerticesCount; // Note: using upload heaps to transfer static data like vert buffers is not // recommended. Every time the GPU needs it, the upload heap will be marshalled // over. Please read up on Default Heap usage. An upload heap is used here for // code simplicity and because there are very few verts to actually transfer. fixed(ID3D12Resource **vertexBuffer = &_vertexBuffer) { var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(vertexBufferSize); iid = IID_ID3D12Resource; ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), _device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)vertexBuffer )); } // Copy the triangle data to the vertex buffer. var readRange = new D3D12_RANGE(); byte *pVertexDataBegin; ThrowIfFailed(nameof(ID3D12Resource.Map), _vertexBuffer->Map(Subresource: 0, &readRange, (void **)&pVertexDataBegin)); Unsafe.CopyBlock(pVertexDataBegin, triangleVertices, vertexBufferSize); _vertexBuffer->Unmap(0, null); // Initialize the vertex buffer view. _vertexBufferView.BufferLocation = _vertexBuffer->GetGPUVirtualAddress(); _vertexBufferView.StrideInBytes = (uint)sizeof(Vertex); _vertexBufferView.SizeInBytes = vertexBufferSize; } // Create the constant buffer. { fixed(ID3D12Resource **pConstantBuffer = &_constantBuffer) { var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(1024 * 64); iid = IID_ID3D12Resource; ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), _device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)pConstantBuffer)); } // Describe and create a constant buffer view. var cbvDesc = new D3D12_CONSTANT_BUFFER_VIEW_DESC { BufferLocation = _constantBuffer->GetGPUVirtualAddress(), SizeInBytes = (uint)((sizeof(SceneConstantBuffer) + 255) & ~255) // CB size is required to be 256-byte aligned. }; _device->CreateConstantBufferView(&cbvDesc, _cbvHeap->GetCPUDescriptorHandleForHeapStart()); // Map and initialize the constant buffer. We don't unmap this until the // app closes. Keeping things mapped for the lifetime of the resource is okay. var readRange = new D3D12_RANGE(); // We do not intend to read from this resource on the CPU. fixed(byte **ppCbvDataBegin = &_pCbvDataBegin) { ThrowIfFailed(nameof(ID3D12Resource.Map), _constantBuffer->Map(Subresource: 0, &readRange, (void **)ppCbvDataBegin)); Unsafe.CopyBlock(ref _pCbvDataBegin[0], ref Unsafe.As <SceneConstantBuffer, byte>(ref _constantBufferData), (uint)sizeof(SceneConstantBuffer)); } // Create synchronization objects and wait until assets have been uploaded to the GPU. { fixed(ID3D12Fence **fence = &_fence) { iid = IID_ID3D12Fence; ThrowIfFailed(nameof(ID3D12Device.CreateFence), _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, &iid, (void **)fence)); _fenceValue = 1; } // Create an event handle to use for frame synchronization. _fenceEvent = CreateEventW(lpEventAttributes: null, bManualReset: FALSE, bInitialState: FALSE, lpName: null); if (_fenceEvent == null) { var hr = Marshal.GetHRForLastWin32Error(); Marshal.ThrowExceptionForHR(hr); } // Wait for the command list to execute; we are reusing the same command // list in our main loop but for now, we just want to wait for setup to // complete before continuing. WaitForPreviousFrame(); } } } finally { if (signature != null) { signature->Release(); } if (error != null) { error->Release(); } if (vertexShader != null) { vertexShader->Release(); } if (pixelShader != null) { pixelShader->Release(); } } }
private unsafe void CreateAssets() { ID3D12Device *d3dDevice = _deviceResources.D3DDevice; Guid iid; { iid = D3D12.IID_ID3D12GraphicsCommandList; ID3D12GraphicsCommandList *commandList; ThrowIfFailed(d3dDevice->CreateCommandList( 0, D3D12_COMMAND_LIST_TYPE.D3D12_COMMAND_LIST_TYPE_DIRECT, _deviceResources.CommandAllocator, _pipelineState.Ptr, &iid, (void **)&commandList)); _commandList = commandList; DX.NameD3D12Object(_commandList.Ptr, nameof(_commandList)); } // Cube vertices. Each vertex has a position and a color. const uint vertexPositionColorCount = 8; VertexPositionColor *cubeVertices = stackalloc VertexPositionColor[(int)vertexPositionColorCount] { new VertexPositionColor { pos = new Vector3(-0.5f, -0.5f, -0.5f), color = new Vector3(0.0f, 0.0f, 0.0f) }, new VertexPositionColor { pos = new Vector3(-0.5f, -0.5f, 0.5f), color = new Vector3(0.0f, 0.0f, 1.0f) }, new VertexPositionColor { pos = new Vector3(-0.5f, 0.5f, -0.5f), color = new Vector3(0.0f, 1.0f, 0.0f) }, new VertexPositionColor { pos = new Vector3(-0.5f, 0.5f, 0.5f), color = new Vector3(0.0f, 1.0f, 1.0f) }, new VertexPositionColor { pos = new Vector3(0.5f, -0.5f, -0.5f), color = new Vector3(1.0f, 0.0f, 0.0f) }, new VertexPositionColor { pos = new Vector3(0.5f, -0.5f, 0.5f), color = new Vector3(1.0f, 0.0f, 1.0f) }, new VertexPositionColor { pos = new Vector3(0.5f, 0.5f, -0.5f), color = new Vector3(1.0f, 1.0f, 0.0f) }, new VertexPositionColor { pos = new Vector3(0.5f, 0.5f, 0.5f), color = new Vector3(1.0f, 1.0f, 1.0f) } }; Debug.Assert(sizeof(VertexPositionColor) == Marshal.SizeOf <VertexPositionColor>()); uint vertexBufferSize = (uint)sizeof(VertexPositionColor) * vertexPositionColorCount; using ComPtr <ID3D12Resource> vertexBufferUpload = default; D3D12_HEAP_PROPERTIES defaultHeapProperties = CD3DX12_HEAP_PROPERTIES.Create(D3D12_HEAP_TYPE.D3D12_HEAP_TYPE_DEFAULT); D3D12_RESOURCE_DESC vertexBufferDesc = CD3DX12_RESOURCE_DESC.Buffer(vertexBufferSize); { iid = D3D12.IID_ID3D12Resource; ID3D12Resource *vertexBuffer; ThrowIfFailed(d3dDevice->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_NONE, &vertexBufferDesc, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST, null, &iid, (void **)&vertexBuffer)); _vertexBuffer = vertexBuffer; } iid = D3D12.IID_ID3D12Resource; D3D12_HEAP_PROPERTIES uploadHeapProperties = CD3DX12_HEAP_PROPERTIES.Create(D3D12_HEAP_TYPE.D3D12_HEAP_TYPE_UPLOAD); ThrowIfFailed(d3dDevice->CreateCommittedResource( &uploadHeapProperties, D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_NONE, &vertexBufferDesc, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_GENERIC_READ, null, &iid, (void **)vertexBufferUpload.GetAddressOf())); DX.NameD3D12Object(_vertexBuffer.Ptr, nameof(_vertexBuffer)); { D3D12_SUBRESOURCE_DATA vertexData; vertexData.pData = (byte *)cubeVertices; vertexData.RowPitch = (IntPtr)vertexBufferSize; vertexData.SlicePitch = vertexData.RowPitch; Functions.UpdateSubresources( _commandList.Ptr, _vertexBuffer.Ptr, vertexBufferUpload.Ptr, 0, 0, 1, &vertexData); D3D12_RESOURCE_BARRIER vertexBufferResourceBarrier = CD3DX12_RESOURCE_BARRIER.Transition(_vertexBuffer.Ptr, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); _commandList.Ptr->ResourceBarrier(1, &vertexBufferResourceBarrier); } const int cubeIndicesCount = 36; ushort * cubeIndices = stackalloc ushort[cubeIndicesCount] { 0, 2, 1, // -x 1, 2, 3, 4, 5, 6, // +x 5, 7, 6, 0, 1, 5, // -y 0, 5, 4, 2, 6, 7, // +y 2, 7, 3, 0, 4, 6, // -z 0, 6, 2, 1, 3, 7, // +z 1, 7, 5, }; const uint indexBufferSize = sizeof(ushort) * cubeIndicesCount; using var indexBufferUpload = new ComPtr <ID3D12Resource>(); D3D12_RESOURCE_DESC indexBufferDesc = CD3DX12_RESOURCE_DESC.Buffer(indexBufferSize); { iid = D3D12.IID_ID3D12Resource; ID3D12Resource *indexBuffer; ThrowIfFailed(d3dDevice->CreateCommittedResource( &defaultHeapProperties, D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_NONE, &indexBufferDesc, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST, null, &iid, (void **)&indexBuffer)); _indexBuffer = indexBuffer; } iid = D3D12.IID_ID3D12Resource; ThrowIfFailed(d3dDevice->CreateCommittedResource( &uploadHeapProperties, D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_NONE, &indexBufferDesc, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_GENERIC_READ, null, &iid, (void **)indexBufferUpload.GetAddressOf())); DX.NameD3D12Object(_indexBuffer.Ptr, nameof(_indexBuffer)); { D3D12_SUBRESOURCE_DATA indexData; indexData.pData = (byte *)cubeIndices; indexData.RowPitch = (IntPtr)indexBufferSize; indexData.SlicePitch = indexData.RowPitch; Functions.UpdateSubresources( _commandList.Ptr, _indexBuffer.Ptr, indexBufferUpload.Ptr, 0, 0, 1, &indexData); D3D12_RESOURCE_BARRIER indexBufferResourceBarrier = CD3DX12_RESOURCE_BARRIER.Transition(_indexBuffer.Ptr, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_INDEX_BUFFER); _commandList.Ptr->ResourceBarrier(1, &indexBufferResourceBarrier); } { D3D12_DESCRIPTOR_HEAP_DESC heapDesc; heapDesc.NumDescriptors = DeviceResources.FrameCount; heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE.D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAGS.D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; { ID3D12DescriptorHeap *cbvHeap; iid = D3D12.IID_ID3D12DescriptorHeap; ThrowIfFailed(d3dDevice->CreateDescriptorHeap(&heapDesc, &iid, (void **)&cbvHeap)); _cbvHeap = cbvHeap; DX.NameD3D12Object(_cbvHeap.Ptr, nameof(_cbvHeap)); } } D3D12_RESOURCE_DESC constantBufferDesc = CD3DX12_RESOURCE_DESC.Buffer( DeviceResources.FrameCount * AlignedConstantBufferSize); { iid = D3D12.IID_ID3D12Resource; ID3D12Resource *constantBuffer; ThrowIfFailed(d3dDevice->CreateCommittedResource( &uploadHeapProperties, D3D12_HEAP_FLAGS.D3D12_HEAP_FLAG_NONE, &constantBufferDesc, D3D12_RESOURCE_STATES.D3D12_RESOURCE_STATE_GENERIC_READ, null, &iid, (void **)&constantBuffer)); _constantBuffer = constantBuffer; DX.NameD3D12Object(_constantBuffer.Ptr, nameof(_constantBuffer)); } D3D12_GPU_VIRTUAL_ADDRESS cbvGpuAddress = _constantBuffer.Ptr->GetGPUVirtualAddress(); D3D12_CPU_DESCRIPTOR_HANDLE cbvCpuHandle = _cbvHeap.Ptr->GetCPUDescriptorHandleForHeapStart(); _cbvDescriptorSize = d3dDevice->GetDescriptorHandleIncrementSize( D3D12_DESCRIPTOR_HEAP_TYPE.D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); for (var i = 0; i < DeviceResources.FrameCount; i++) { D3D12_CONSTANT_BUFFER_VIEW_DESC desc; desc.BufferLocation = cbvGpuAddress; desc.SizeInBytes = AlignedConstantBufferSize; d3dDevice->CreateConstantBufferView(&desc, cbvCpuHandle); cbvGpuAddress += desc.SizeInBytes; cbvCpuHandle.Offset((int)_cbvDescriptorSize); } D3D12_RANGE readRange = CD3DX12_RANGE.Create((UIntPtr)0, (UIntPtr)0); fixed(byte **p = &_mappedConstantBuffer) { ThrowIfFailed(_constantBuffer.Ptr->Map(0, &readRange, (void **)p)); Unsafe.InitBlockUnaligned(_mappedConstantBuffer, 0, DeviceResources.FrameCount * AlignedConstantBufferSize); } ThrowIfFailed(_commandList.Ptr->Close()); const int ppCommandListCount = 1; ID3D12CommandList **ppCommandLists = stackalloc ID3D12CommandList *[ppCommandListCount] { (ID3D12CommandList *)_commandList.Ptr }; _deviceResources.CommandQueue->ExecuteCommandLists(ppCommandListCount, ppCommandLists); _vertexBufferView.BufferLocation = _vertexBuffer.Ptr->GetGPUVirtualAddress(); _vertexBufferView.StrideInBytes = (uint)sizeof(VertexPositionColor); _vertexBufferView.SizeInBytes = vertexBufferSize; _indexBufferView.BufferLocation = _indexBuffer.Ptr->GetGPUVirtualAddress(); _indexBufferView.SizeInBytes = indexBufferSize; _indexBufferView.Format = DXGI_FORMAT.DXGI_FORMAT_R16_UINT; _deviceResources.WaitForGpu(); } #endregion } }
// Load the sample assets. private void LoadAssets() { Guid iid; ID3DBlob *signature = null; ID3DBlob *error = null; ID3DBlob *vertexShader = null; ID3DBlob *pixelShader = null; try { // Create the root signature. { var featureData = new D3D12_FEATURE_DATA_ROOT_SIGNATURE { // This is the highest version the sample supports. If CheckFeatureSupport succeeds, the HighestVersion returned will not be greater than this. HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1 }; if (FAILED(_device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, (uint)sizeof(D3D12_FEATURE)))) { featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; } const int RangesCount = 1; var ranges = stackalloc D3D12_DESCRIPTOR_RANGE1[RangesCount]; ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); const int RootParametersCount = 1; var rootParameters = stackalloc D3D12_ROOT_PARAMETER1[RootParametersCount]; rootParameters[0].InitAsDescriptorTable(1, ranges, D3D12_SHADER_VISIBILITY_PIXEL); var sampler = new D3D12_STATIC_SAMPLER_DESC { Filter = D3D12_FILTER.D3D12_FILTER_MIN_MAG_MIP_POINT, AddressU = D3D12_TEXTURE_ADDRESS_MODE_BORDER, AddressV = D3D12_TEXTURE_ADDRESS_MODE_BORDER, AddressW = D3D12_TEXTURE_ADDRESS_MODE_BORDER, MipLODBias = 0, MaxAnisotropy = 0, ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER, BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK, MinLOD = 0.0f, MaxLOD = D3D12_FLOAT32_MAX, ShaderRegister = 0, RegisterSpace = 0, ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL, }; var rootSignatureDesc = new D3D12_VERSIONED_ROOT_SIGNATURE_DESC(); rootSignatureDesc.Init_1_1(RootParametersCount, rootParameters, 1, &sampler, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); ThrowIfFailed(nameof(D3D12SerializeVersionedRootSignature), D3D12SerializeVersionedRootSignature( &rootSignatureDesc, featureData.HighestVersion, &signature, &error)); fixed(ID3D12RootSignature **rootSignature = &_rootSignature) { iid = IID_ID3D12RootSignature; ThrowIfFailed(nameof(ID3D12Device.CreateRootSignature), _device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), &iid, (void **)rootSignature)); } } // Create the pipeline state, which includes compiling and loading shaders. { var compileFlags = 0u; #if DEBUG // Enable better shader debugging with the graphics debugging tools. compileFlags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #endif fixed(char *fileName = GetAssetFullPath(@"D3D12\Assets\Shaders\HelloTexture.hlsl")) { var entryPoint = 0x00006E69614D5356; // VSMain var target = 0x0000305F355F7376; // vs_5_0 ThrowIfFailed(nameof(D3DCompileFromFile), D3DCompileFromFile((ushort *)fileName, pDefines: null, pInclude: null, (sbyte *)&entryPoint, (sbyte *)&target, compileFlags, Flags2: 0, &vertexShader, ppErrorMsgs: null)); entryPoint = 0x00006E69614D5350; // PSMain target = 0x0000305F355F7370; // ps_5_0 ThrowIfFailed(nameof(D3DCompileFromFile), D3DCompileFromFile((ushort *)fileName, pDefines: null, pInclude: null, (sbyte *)&entryPoint, (sbyte *)&target, compileFlags, Flags2: 0, &pixelShader, ppErrorMsgs: null)); } // Define the vertex input layout. const int InputElementDescsCount = 2; var semanticName0 = stackalloc ulong[2] { 0x4E4F495449534F50, // POSITION 0x0000000000000000, }; var semanticName1 = stackalloc ulong[2] { 0x44524F4F43584554, // TEXCOORD 0x0000000000000000, }; var inputElementDescs = stackalloc D3D12_INPUT_ELEMENT_DESC[InputElementDescsCount] { new D3D12_INPUT_ELEMENT_DESC { SemanticName = (sbyte *)semanticName0, Format = DXGI_FORMAT_R32G32B32_FLOAT, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, }, new D3D12_INPUT_ELEMENT_DESC { SemanticName = (sbyte *)semanticName1, Format = DXGI_FORMAT_R32G32_FLOAT, AlignedByteOffset = 12, InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, }, }; // Describe and create the graphics pipeline state object (PSO). var psoDesc = new D3D12_GRAPHICS_PIPELINE_STATE_DESC { InputLayout = new D3D12_INPUT_LAYOUT_DESC { pInputElementDescs = inputElementDescs, NumElements = InputElementDescsCount, }, pRootSignature = _rootSignature, VS = new D3D12_SHADER_BYTECODE(vertexShader), PS = new D3D12_SHADER_BYTECODE(pixelShader), RasterizerState = D3D12_RASTERIZER_DESC.DEFAULT, BlendState = D3D12_BLEND_DESC.DEFAULT, DepthStencilState = new D3D12_DEPTH_STENCIL_DESC { DepthEnable = FALSE, StencilEnable = FALSE, }, SampleMask = uint.MaxValue, PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE, NumRenderTargets = 1, SampleDesc = new DXGI_SAMPLE_DESC(count: 1, quality: 0), }; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; fixed(ID3D12PipelineState **pipelineState = &_pipelineState) { iid = IID_ID3D12PipelineState; ThrowIfFailed(nameof(ID3D12Device.CreateGraphicsPipelineState), _device->CreateGraphicsPipelineState(&psoDesc, &iid, (void **)pipelineState)); } } // Create the command list. fixed(ID3D12GraphicsCommandList **commandList = &_commandList) { iid = IID_ID3D12GraphicsCommandList; ThrowIfFailed(nameof(ID3D12Device.CreateCommandList), _device->CreateCommandList(nodeMask: 0, D3D12_COMMAND_LIST_TYPE_DIRECT, _commandAllocator, _pipelineState, &iid, (void **)commandList)); } // Create the vertex buffer. { // Define the geometry for a triangle. const int TriangleVerticesCount = 3; var triangleVertices = stackalloc Vertex[TriangleVerticesCount] { new Vertex { Position = new Vector3(0.0f, 0.25f * AspectRatio, 0.0f), UV = new Vector2(0.5f, 0.0f) }, new Vertex { Position = new Vector3(0.25f, -0.25f * AspectRatio, 0.0f), UV = new Vector2(1.0f, 1.0f) }, new Vertex { Position = new Vector3(-0.25f, -0.25f * AspectRatio, 0.0f), UV = new Vector2(0.0f, 1.0f) }, }; var vertexBufferSize = (uint)sizeof(Vertex) * TriangleVerticesCount; // Note: using upload heaps to transfer static data like vert buffers is not // recommended. Every time the GPU needs it, the upload heap will be marshalled // over. Please read up on Default Heap usage. An upload heap is used here for // code simplicity and because there are very few verts to actually transfer. fixed(ID3D12Resource **vertexBuffer = &_vertexBuffer) { var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(vertexBufferSize); iid = IID_ID3D12Resource; ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), _device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)vertexBuffer )); } // Copy the triangle data to the vertex buffer. var readRange = new D3D12_RANGE(); byte *pVertexDataBegin; ThrowIfFailed(nameof(ID3D12Resource.Map), _vertexBuffer->Map(Subresource: 0, &readRange, (void **)&pVertexDataBegin)); Unsafe.CopyBlock(pVertexDataBegin, triangleVertices, vertexBufferSize); _vertexBuffer->Unmap(0, null); // Initialize the vertex buffer view. _vertexBufferView.BufferLocation = _vertexBuffer->GetGPUVirtualAddress(); _vertexBufferView.StrideInBytes = (uint)sizeof(Vertex); _vertexBufferView.SizeInBytes = vertexBufferSize; } // Note: textureUploadHeap needs to stay in scope until // the command list that references it has finished executing on the GPU. // We will flush the GPU at the end of this method to ensure the resource is not // prematurely destroyed. ID3D12Resource *textureUploadHeap; // Create the texture. { // Describe and create a Texture2D. var textureDesc = new D3D12_RESOURCE_DESC { MipLevels = 1, Format = DXGI_FORMAT_R8G8B8A8_UNORM, Width = TextureWidth, Height = TextureHeight, Flags = D3D12_RESOURCE_FLAG_NONE, DepthOrArraySize = 1, SampleDesc = new DXGI_SAMPLE_DESC { Count = 1, Quality = 0, }, Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D, }; fixed(ID3D12Resource **pTexture = &_texture) { var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); iid = IID_ID3D12Resource; ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), _device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_COPY_DEST, pOptimizedClearValue: null, &iid, (void **)pTexture )); var uploadBufferSize = GetRequiredIntermediateSize(_texture, 0, 1); // Create the GPU upload buffer. heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(uploadBufferSize); ThrowIfFailed(nameof(ID3D12Device.CreateCommittedResource), _device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)&textureUploadHeap )); } // Copy data to the intermediate upload heap and then schedule a copy // from the upload heap to the Texture2D. var texture = GenerateTextureData(); var rowPitch = TextureWidth * TexturePixelSize; var slicePitch = rowPitch * TextureHeight; D3D12_SUBRESOURCE_DATA textureData; fixed(byte *pTexture = &texture[0]) { textureData = new D3D12_SUBRESOURCE_DATA { pData = (void *)pTexture, RowPitch = (nint)rowPitch, SlicePitch = (nint)slicePitch, }; } UpdateSubresources(_commandList, _texture, textureUploadHeap, 0, 0, 1, &textureData); var barrier = D3D12_RESOURCE_BARRIER.InitTransition(_texture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); _commandList->ResourceBarrier(1, &barrier); // Describe and create a SRV for the texture. var srvDesc = new D3D12_SHADER_RESOURCE_VIEW_DESC { Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, Format = textureDesc.Format, ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D, }; srvDesc.Anonymous.Texture2D.MipLevels = 1; _device->CreateShaderResourceView(_texture, &srvDesc, _srvHeap->GetCPUDescriptorHandleForHeapStart()); } // Close the command list and execute it to begin the initial GPU setup. ThrowIfFailed(nameof(ID3D12GraphicsCommandList.Close), _commandList->Close()); const int CommandListsCount = 1; var ppCommandLists = stackalloc ID3D12CommandList *[CommandListsCount] { (ID3D12CommandList *)_commandList, }; _commandQueue->ExecuteCommandLists(CommandListsCount, ppCommandLists); // Create synchronization objects and wait until assets have been uploaded to the GPU. { fixed(ID3D12Fence **fence = &_fence) { iid = IID_ID3D12Fence; ThrowIfFailed(nameof(ID3D12Device.CreateFence), _device->CreateFence(0, D3D12_FENCE_FLAG_NONE, &iid, (void **)fence)); _fenceValue = 1; } // Create an event handle to use for frame synchronization. _fenceEvent = CreateEventW(lpEventAttributes: null, bManualReset: FALSE, bInitialState: FALSE, lpName: null); if (_fenceEvent == null) { var hr = Marshal.GetHRForLastWin32Error(); Marshal.ThrowExceptionForHR(hr); } // Wait for the command list to execute; we are reusing the same command // list in our main loop but for now, we just want to wait for setup to // complete before continuing. WaitForPreviousFrame(); } } finally { if (signature != null) { signature->Release(); } if (error != null) { error->Release(); } if (vertexShader != null) { vertexShader->Release(); } if (pixelShader != null) { pixelShader->Release(); } } }