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);
        }
    }
Exemplo n.º 2
0
        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);
        }
Exemplo n.º 3
0
        /// <inheritdoc />
        public override void Unmap(nuint writtenRangeOffset, nuint writtenRangeLength)
        {
            var writtenRange = new D3D12_RANGE {
                Begin = writtenRangeOffset,
                End   = writtenRangeOffset + writtenRangeLength,
            };

            D3D12Resource->Unmap(Subresource: 0, &writtenRange);
        }
Exemplo n.º 4
0
        /// <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);
        }
Exemplo n.º 5
0
        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);
        }
Exemplo n.º 6
0
        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));
        }
Exemplo n.º 7
0
        /// <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);
        }
Exemplo n.º 8
0
        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();
                }
            }
        }
Exemplo n.º 9
0
        // 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
    }
}
Exemplo n.º 12
0
        // 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();
                }
            }
        }