/// <inheritdoc /> public override D3D12GraphicsBuffer CreateBuffer(GraphicsBufferKind kind, GraphicsResourceCpuAccess cpuAccess, ulong size, ulong alignment = 0, GraphicsMemoryAllocationFlags allocationFlags = GraphicsMemoryAllocationFlags.None) { var index = GetBlockCollectionIndex(cpuAccess, 0); var resourceDesc = D3D12_RESOURCE_DESC.Buffer(size, D3D12_RESOURCE_FLAG_NONE, alignment); var resourceAllocationInfo = Device.D3D12Device->GetResourceAllocationInfo(visibleMask: 0, numResourceDescs: 1, &resourceDesc); ref readonly var blockCollection = ref _blockCollections[index];
private Pointer <ID3D12Resource> CreateD3D12Resource() { _state.ThrowIfDisposedOrDisposing(); ID3D12Resource *d3d12Resource; var bufferDesc = D3D12_RESOURCE_DESC.Buffer(width: Size); var iid = IID_ID3D12Resource; var d3d12GraphicsHeap = D3D12GraphicsHeap; var d3d12Heap = d3d12GraphicsHeap.D3D12Heap; var d3d12GraphicsDevice = d3d12GraphicsHeap.D3D12GraphicsDevice; var d3d12Device = d3d12GraphicsDevice.D3D12Device; ThrowExternalExceptionIfFailed(nameof(ID3D12Device.CreatePlacedResource), d3d12Device->CreatePlacedResource( d3d12Heap, Offset, &bufferDesc, D3D12ResourceState, pOptimizedClearValue: null, &iid, (void **)&d3d12Resource )); return(d3d12Resource); }
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); }
private Pointer <ID3D12Resource> CreateD3D12Resource() { _state.ThrowIfDisposedOrDisposing(); ID3D12Resource *d3d12Resource; var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD); var bufferDesc = D3D12_RESOURCE_DESC.Buffer(width: Size); var iid = IID_ID3D12Resource; ThrowExternalExceptionIfFailed(nameof(ID3D12Device.CreateCommittedResource), D3D12GraphicsDevice.D3D12Device->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, &iid, (void **)&d3d12Resource )); return(d3d12Resource); }
// 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(); } } }
protected override void CreateAssets() { using ComPtr <ID3D12Resource> textureUploadHeap = null; _texture = CreateTexture(); base.CreateAssets(); ID3D12Resource *CreateTexture() { // 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, }; ID3D12Resource *texture; var iid = __uuidof <ID3D12Resource>(); var heapProperties = new D3D12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT); ThrowIfFailed(D3DDevice->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &textureDesc, D3D12_RESOURCE_STATE_COPY_DEST, pOptimizedClearValue: null, iid, (void **)&texture )); 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(D3DDevice->CreateCommittedResource( &heapProperties, D3D12_HEAP_FLAG_NONE, &bufferDesc, D3D12_RESOURCE_STATE_GENERIC_READ, pOptimizedClearValue: null, iid, (void **)textureUploadHeap.GetAddressOf() )); // Copy data to the intermediate upload heap and then schedule a copy // from the upload heap to the Texture2D. var textureData = GenerateTextureData(); var rowPitch = TextureWidth * TexturePixelSize; var slicePitch = rowPitch * TextureHeight; D3D12_SUBRESOURCE_DATA textureSubresourceData; fixed(byte *pTextureData = &textureData[0]) { textureSubresourceData = new D3D12_SUBRESOURCE_DATA { pData = (void *)pTextureData, RowPitch = (nint)rowPitch, SlicePitch = (nint)slicePitch, }; } _ = UpdateSubresources(GraphicsCommandList, texture, textureUploadHeap, 0, 0, 1, &textureSubresourceData); var barrier = D3D12_RESOURCE_BARRIER.InitTransition(texture, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); GraphicsCommandList->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; D3DDevice->CreateShaderResourceView(texture, &srvDesc, _srvHeap->GetCPUDescriptorHandleForHeapStart()); return(texture); } byte[] GenerateTextureData() { const uint RowPitch = TextureWidth * TexturePixelSize; const uint CellPitch = RowPitch >> 3; // The width of a cell in the checkboard texture. const uint CellHeight = TextureWidth >> 3; // The height of a cell in the checkerboard texture. const uint TextureSize = RowPitch * TextureHeight; var data = new byte[TextureSize]; fixed(byte *pData = &data[0]) { for (uint n = 0; n < TextureSize; n += TexturePixelSize) { var x = n % RowPitch; var y = n / RowPitch; var i = x / CellPitch; var j = y / CellHeight; if (i % 2 == j % 2) { pData[n] = 0x00; // R pData[n + 1] = 0x00; // G pData[n + 2] = 0x00; // B pData[n + 3] = 0xff; // A } else { pData[n] = 0xff; // R pData[n + 1] = 0xff; // G pData[n + 2] = 0xff; // B pData[n + 3] = 0xff; // A } } } return(data); } }
// 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(); } } }
// 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(); } } }
/// <summary> /// Creates a new <see cref="GpuResourceFormat"/> representing a buffer /// </summary> /// <param name="size">The size, in bytes, of the buffer</param> /// <returns>A new <see cref="GpuResourceFormat"/> representing a buffer</returns> public static GpuResourceFormat Buffer(ulong size) { return(new GpuResourceFormat(D3D12_RESOURCE_DESC.Buffer(size))); }