【學習DirectX12(6)】渲染——第三部分

Tutorial2::Tutorial2

Tutorial2的構造類需要幾個參數。

Tutorial2::Tutorial2( const std::wstring& name, int width, int height, bool vSync )
    : super(name, width, height, vSync)
    , m_ScissorRect(CD3DX12_RECT(0, 0, LONG_MAX, LONG_MAX))
    , m_Viewport(CD3DX12_VIEWPORT(0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height)))
    , m_FoV(45.0)
    , m_ContentLoaded(false)
{
}

m_ScissorRect用於標記出一個屏幕上矩形區域,用於渲染。之前版本的DX無需這樣做。而DX12需要使用{0, 0, LONG_MAX, LONG_MAX}來指定。縮放窗口後無需更新。m_Viewport指定了屏幕上可用於渲染的窗口。Viewport可以比屏幕略小,但是不應該大於綁定至output merger階段的Render Target。Viewport在Rasterizer階段指定,用於告訴光柵化器如何把頂點從normalized device coordinate space (NDC)轉變爲屏幕座標。m_FoV則是攝像機的field of view,默認45度,可用鼠標滾輪調節。

Tutorial2::UpdateBufferResource

UpdateBufferResouce方法用於創建ID3D12Resouce資源,保證其足夠大來存儲緩存詩句,並且創建intermediate緩存用於從CPU緩存數據中複製給GPU。在此,用於初始化頂點緩存和索引緩存。The commandList argument is required to transfer the buffer data to the destination resource.The pDestinationResource and the pIntermediateResource pointers are used to store the destination and intermediate resources that are created in this method.The numElementselementSize, and bufferData arguments refer to the CPU buffer data that is transferred to the GPU resource.

On line 75, the DirectX 12 device is queried from the Application class.I didn’t show the implementation details of the application class, but this class can be viewed on GitHub: https://git.io/vApW1The bufferSize variable is size of the buffer in bytes.The next step is to create a GPU resource in committed memory that is large enough to store the buffer.

The ID3D12Device::CreateCommittedResource method is used to create a resource and an implicit heap that is large enough to store the resource. The resource is also mapped to the implicit heap.The ID3D12Device::CreateCommittedResource has the following signature [9]:

// Create a committed resource for the GPU resource in a default heap.
ThrowIfFailed(device->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
    D3D12_HEAP_FLAG_NONE,
    &CD3DX12_RESOURCE_DESC::Buffer(bufferSize, flags),
    D3D12_RESOURCE_STATE_COPY_DEST,
    nullptr,
    IID_PPV_ARGS(pDestinationResource)));
    HRESULT CreateCommittedResource(
  [in]            const D3D12_HEAP_PROPERTIES *pHeapProperties,
                        D3D12_HEAP_FLAGS      HeapFlags,
  [in]            const D3D12_RESOURCE_DESC   *pDesc,
                        D3D12_RESOURCE_STATES InitialResourceState,
  [in, optional]  const D3D12_CLEAR_VALUE     *pOptimizedClearValue,
                        REFIID                riidResource,
  [out, optional]       void                  **ppvResource
);

And takes the following arguments:

On lines 82-88, the default resource is created but the buffer data is not yet uploaded to that resource. The next step is to create another resource that is used to transfer the CPU buffer data into GPU memory. To perform the memory transfer, an intermediate buffer resource is created using an upload heap.

然後默認資源已經創建好,但是還沒有更新到屏幕上。下一步是創建另一個資源,用於把CPU緩存數據傳遞到GPU內存中,這樣需要一個intermediate buffer resouce。

// Create an committed resource for the upload.
if (bufferData)
{
    ThrowIfFailed(device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
        D3D12_HEAP_FLAG_NONE,
        &CD3DX12_RESOURCE_DESC::Buffer(bufferSize),
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(pIntermediateResource)));

如果緩存數據不爲NULL,那麼將使用ID3D12Device::CreateCommittedResource 創建另一個committed resouce。這裏,heap類型設置爲D3D12_HEAP_TYPE_UPLOAD,並且資源狀態設置爲D3D12_RESOURCE_STATE_GENERIC_READ,並且只能是這種類型,不能改變。大概destination 和intermediate 資源創建好後,就可以從CPU向GPU傳遞數據了。

D3D12_SUBRESOURCE_DATA subresourceData = {};
        subresourceData.pData = bufferData;
        subresourceData.RowPitch = bufferSize;
        subresourceData.SlicePitch = subresourceData.RowPitch;
        UpdateSubresources(commandList.Get(),
            *pDestinationResource, *pIntermediateResource,
            0, 0, 1, &subresourceData);
    }
}

 

D3D12_SUBRESOURCE_DATA 結構用於描述真正要傳遞的數據。

typedef struct D3D12_SUBRESOURCE_DATA {
  const void *pData;
  LONG_PTR   RowPitch;
  LONG_PTR   SlicePitch;
} D3D12_SUBRESOURCE_DATA;

And has the following members:

  • void *pData: 指向用於存儲數據的內存塊的指針。
  • LONG_PTR RowPitch: The row pitch, or width, or physical size, in bytes, of the subresource data.
  • LONG_PTR SlicePitch: The depth pitch, or width, or physical size, in bytes, of the subresource data.
UINT64 inline UpdateSubresources(
  _In_ ID3D12GraphicsCommandList *pCmdList,
  _In_ ID3D12Resource            *pDestinationResource,
  _In_ ID3D12Resource            *pIntermediate,
       UINT64                    IntermediateOffset,
  _In_ UINT                      FirstSubresource,
  _In_ UINT                      NumSubresources,
  _In_ D3D12_SUBRESOURCE_DATA    *pSrcData

);

 

  • ID3D12GraphicsCommandList *pCmdList: A pointer to the ID3D12GraphicsCommandList interface for the command list.
  • ID3D12Resource *pDestinationResource: A pointer to the ID3D12Resource interface that represents the destination resource.
  • ID3D12Resource *pIntermediate: A pointer to the ID3D12Resource interface that represents the intermediate resource.
  • UINT64 IntermediateOffset: The offset, in bytes, to the intermediate resource.
  • UINT FirstSubresource: The index of the first subresource in the resource. The range of valid values is 0 to D3D12_REQ_SUBRESOURCES.
  • UINT NumSubresources: The number of subresources in the resource to be updated. The range of valid values is 0 to (D3D12_REQ_SUBRESOURCES – FirstSubresource).
  • D3D12_SUBRESOURCE_DATA *pSrcData: Pointer to an array (of length NumSubresources) of pointers to D3D12_SUBRESOURCE_DATA structures containing descriptions of the subresource data used for the update.

For buffer resources, the FirstSubresource is always 0 and the NumSubresources is always 1 (or 0, but then this function does nothing) because buffers only have a single subresource at index 0.

Tutorial2::LoadContent

The LoadContent function is used to load all of the content that is required by the demo. In summary, the LoadContent function will perform the following steps:

  1. Upload the Vertex buffer data for a cube mesh
    • Create a vertex buffer view
  2. Upload the index buffer data for a cube mesh
    • Create an index buffer view
  3. Create a descriptor heap for the depth-stencil view
  4. Load the vertex shader
  5. Load the pixel shader
  6. Create the input layout for the vertex shader
  7. Create a root signature
  8. Create a graphics pipeline state object (PSO)
  9. Create a depth buffer

The vertex buffer data and index buffer data require a ID3D12GraphicsCommandList to perform the copy operation. Creating and retrieving command lists was described in the Command Queue Class section above. The LoadContent function uses the (copy) CommandQueue class to retrieve a command list that can be used to upload the buffer data to the GPU resources.

頂點緩存和索引緩存需要一個ID3D12GraphicsCommandList來執行復制操作。此函數使用(複製)了一個CommandQueue類來接收指令集,用於把緩存數據上傳到GPU資源上。

bool Tutorial2::LoadContent()
{
    auto device = Application::Get().GetDevice();
    auto commandQueue = Application::Get().GetCommandQueue(D3D12_COMMAND_LIST_TYPE_COPY);
    auto commandList = commandQueue->GetCommandList();

 

UPLOAD VERTEX BUFFER

// Upload vertex buffer data.
ComPtr<ID3D12Resource> intermediateVertexBuffer;
UpdateBufferResource(commandList.Get(),
    &m_VertexBuffer, &intermediateVertexBuffer,
    _countof(g_Vertices), sizeof(VertexPosColor), g_Vertices);

The intermediateVertexBuffer is the temporary vertex buffer resource that is used to transfer the CPU vertex buffer data to the GPU. The m_VertexBuffer member variable is the destination resource for the vertex buffer data and is used for rendering the cube geometry.The g_Vertices global variable is declared on line 40 and contains the vertices in CPU memory for the cube geometry

D3D12_VERTEX_BUFFER_VIEW structure is used to tell the Input Assembler stage where the vertices are stored in GPU memory.

// Create the vertex buffer view.
m_VertexBufferView.BufferLocation = m_VertexBuffer->GetGPUVirtualAddress();
m_VertexBufferView.SizeInBytes = sizeof(g_Vertices);
m_VertexBufferView.StrideInBytes = sizeof(VertexPosColor);

The m_VertexBufferView member variable is the D3D12_VERTEX_BUFFER_VIEW structure that describes the vertex buffer. The D3D12_VERTEX_BUFFER_VIEW structure has the following syntax [12]:

typedef struct D3D12_VERTEX_BUFFER_VIEW {
  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
  UINT                      SizeInBytes;
  UINT                      StrideInBytes;
} D3D12_VERTEX_BUFFER_VIEW;

And has the following members:

  • D3D12_GPU_VIRTUAL_ADDRESS BufferLocation: Specifies a D3D12_GPU_VIRTUAL_ADDRESS that identifies the address of the buffer.
  • UINT SizeInBytes: Specifies the size in bytes of the buffer.
  • UINT StrideInBytes: Specifies the size in bytes of each vertex entry.

The index buffer data is uploaded next.

UPLOAD INDEX BUFFER

Similar to the vertex buffer data, the Tutorial2::UpdateBufferResource method is used to create the GPU resources and upload the index buffer data to the GPU.

// Upload index buffer data.
ComPtr<ID3D12Resource> intermediateIndexBuffer;
UpdateBufferResource(commandList.Get(),
    &m_IndexBuffer, &intermediateIndexBuffer,
    _countof(g_Indicies), sizeof(WORD), g_Indicies);

The intermediateIndexBuffer is the temporary index buffer resource that is used to transfer the CPU index buffer data to the GPU. The m_IndexBuffer member variable is the destination resource for the index buffer data and is used for rendering the cube geometry.The g_Indicies global variable is declared on line 51 and contains the indices for the cube geometry.Similar to the Vertex Buffer View, the Index Buffer View is used to describe the index buffer to the Input Assembler stage of the rendering pipeline.

// Create index buffer view.
m_IndexBufferView.BufferLocation = m_IndexBuffer->GetGPUVirtualAddress();
m_IndexBufferView.Format = DXGI_FORMAT_R16_UINT;
m_IndexBufferView.SizeInBytes = sizeof(g_Indicies);

The m_IndexBufferView member variable is the D3D12_INDEX_BUFFER_VIEW structure that is used to describe the index buffer.The D3D12_INDEX_BUFFER_VIEW structure has the following syntax [13]:

typedef struct D3D12_INDEX_BUFFER_VIEW {
  D3D12_GPU_VIRTUAL_ADDRESS BufferLocation;
  UINT                      SizeInBytes;
  DXGI_FORMAT               Format;
} D3D12_INDEX_BUFFER_VIEW;

And has the following members:

  • D3D12_GPU_VIRTUAL_ADDRESS BufferLocation: The GPU virtual address of the index buffer.
  • UINT SizeInBytes: The size in bytes of the index buffer.
  • DXGI_FORMAT Format: A DXGI_FORMAT-typed value for the index-buffer format. In this case, the DXGI_FORMAT_R16_UINT enumeration value is used to indicate that each value in the index buffer is a single component 16-bit unsigned integer.

DSV DESCRIPTOR HEAP

The depth buffer stores the depth of the current pixel in normalized device coordinate space (NDC)Similar to the Render Target Views (RTV) that were created for the swap-chain buffers in the previous lesson, the depth buffer requires a Depth-Stencil View (DSV). The DSV is stored in a descriptor heap. A descriptor heap is an array of descriptors (resource views). A descriptor heap is required even if only a single DSV is needed.

// Create the descriptor heap for the depth-stencil view.
D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc = {};
dsvHeapDesc.NumDescriptors = 1;
dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ThrowIfFailed(device->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(&m_DSVHeap)));

 

typedef struct D3D12_DESCRIPTOR_HEAP_DESC {
  D3D12_DESCRIPTOR_HEAP_TYPE  Type;
  UINT                        NumDescriptors;
  D3D12_DESCRIPTOR_HEAP_FLAGS Flags;
  UINT                        NodeMask;
} D3D12_DESCRIPTOR_HEAP_DESC;

And has the following members:

  • D3D12_DESCRIPTOR_HEAP_TYPE Type: A D3D12_DESCRIPTOR_HEAP_TYPE-typed value that specifies the types of descriptors in the heap.
  • UINT NumDescriptors: The number of descriptors in the heap. In this case, only one descriptor is needed.
  • D3D12_DESCRIPTOR_HEAP_FLAGS Flags: A combination of D3D12_DESCRIPTOR_HEAP_FLAGS-typed values that are combined by using a bitwise OR operation. The resulting value specifies options for the heap. The following options are available for descriptor heaps:
    • D3D12_DESCRIPTOR_HEAP_FLAG_NONE: Indicates default usage of a heap.
    • D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE: The flag D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE can optionally be set on a descriptor heap to indicate it can be be bound on a command list for reference by shaders. Descriptor heaps created without this flag allow applications the option to stage descriptors in CPU memory before copying them to a shader visible descriptor heap, as a convenience. But it is also fine for applications to directly create descriptors into shader visible descriptor heaps with no requirement to stage anything on the CPU.
       
      This flag only applies to CBV, SRV, UAV and samplers. It does not apply to other descriptor heap types (RTV, DSV) since shaders do not directly reference the other types.
  • UINT NodeMask: For single-adapter operation, set this to zero. If there are multiple adapter nodes, set a bit to identify the node (one of the device’s physical adapters) to which the descriptor heap applies. Each bit in the mask corresponds to a single node. Only one bit must be set.

The descriptor heap is created on line 146 using the ID3D12Device::CreateDescriptorHeap method.

LOAD THE SHADERS

By default, shaders are compiled into a Compiled Shader Object file (.cso) with the same name as the original .hlsl file.  The CSO file can be loaded directly into a binary object using the D3DReadFileToBlob function.When using the D3DReadFileToBlob function, or any function in the D3DCompiler API, do not forget to include the d3dcompiler.h header file in your source and link against the D3Dcompiler_47.lib library and copy the D3dcompiler_47.dll file to the same folder as the binary executable when distributing your project.

A redistributable version of the D3dcompiler_47.dll file can be found in the Windows 10 SDK installation folder at C:\Program Files (x86)\Windows Kits\10\Redist\D3D\.For more information, refer to the MSDN blog post at: https://blogs.msdn.microsoft.com/chuckw/2012/05/07/hlsl-fxc-and-d3dcompile/

// Load the vertex shader.
ComPtr<ID3DBlob> vertexShaderBlob;
ThrowIfFailed(D3DReadFileToBlob(L"VertexShader.cso", &vertexShaderBlob));
// Load the pixel shader.
ComPtr<ID3DBlob> pixelShaderBlob;
ThrowIfFailed(D3DReadFileToBlob(L"PixelShader.cso", &pixelShaderBlob));

By default, the shader compiler (FXC.exe) will generate the .cso file in the same directory as the output directory for the project. If the current working directory for the currently running process is configured to be the same folder where the executable is located, then it should be sufficient to load the shader from the .cso file using the path relative to the executable.

The D3DReadFileToBlob function takes the path to the file (as a wide character string) to load and returns a pointer to a ID3DBlob object that stores the compiled shader in binary format.There are methods to compile the HLSL shader files at runtime. For example, you can use the D3DCompileFromFile function to compile your HLSL shaders at runtime instead of using the FXC compiler integrated in Visual Studio.The compiled shader objects in binary form are used later to create the graphics pipeline state object. Before the graphics pipeline state object can be created, the input layout for the vertex shader is defined.

INPUT LAYOUT

The Input Layout describes the layout of the vertex buffers that are bound to to the Input Assembler stage of the rendering pipeline. For example, the vertex attributes that are passed to the vertex shader could look like this in the HLSL shader file:

struct VertexPosColor
{
    float3 Position : POSITION;
    float3 Color    : COLOR;
};

The Input Layout is specified using an array of D3D12_INPUT_ELEMENT_DESC structures. This structure has the following syntax [14]:

typedef struct D3D12_INPUT_ELEMENT_DESC {
  LPCSTR                     SemanticName;
  UINT                       SemanticIndex;
  DXGI_FORMAT                Format;
  UINT                       InputSlot;
  UINT                       AlignedByteOffset;
  D3D12_INPUT_CLASSIFICATION InputSlotClass;
  UINT                       InstanceDataStepRate;
} D3D12_INPUT_ELEMENT_DESC;

And has the following members:

  • LPCSTR SemanticName: The HLSL semantic associated with this element in a shader input-signature.
  • UINT SemanticIndex: 元素的語義索引。語義索引使用整數索引數修改語義。只有在有多個具有相同語義的元素的情況下,才需要語義索引。例如,4x4 矩陣將具有四個組件,每個組件都有語義名稱矩陣,但四個組件中每個組件將具有不同的語義索引(0、1、2 和 3)。
  • DXGI_FORMAT Format: A DXGI_FORMAT-typed value that specifies the format of the element data.
  • UINT InputSlot: An integer value that identifies the input-assembler. For more info, see Input Slots. Valid values are between 0 and 15.
  • UINT AlignedByteOffset: Offset, in bytes, between each element. Use D3D12_APPEND_ALIGNED_ELEMENT (0xffffffff) for convenience to define the current element directly after the previous one, including any packing if necessary.
  • D3D12_INPUT_CLASSIFICATION InputSlotClass: A value that identifies the input data class for a single input slot. Valid values for InputSlotClass are:
  • UINT InstanceDataStepRate: The number of instances to draw using the same per-instance data before advancing in the buffer by one element. This value must be 0 for an element that contains per-vertex data (the slot class is set to D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA).

The Input Layout for the simple vertex shader for this lesson is:

// Create the vertex input layout
D3D12_INPUT_ELEMENT_DESC inputLayout[] = {
    { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
    { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D12_APPEND_ALIGNED_ELEMENT, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },

};

 

ROOT SIGNATURE

根簽名描述了將會被傳遞至可編程階段渲染管線的參數。但在創建pipeline shader object之前,需要先指定根簽名。比如在這裏,根簽名包含了一個32bit的常數變量。創建根簽名之前,可以支持的最高版本的根簽名首先被詢問。Root signature version 1.1 should be preferred because it allows for driver level optimizations to be made. Root signature version 1.0 allowed both descriptors and the data pointed to by the descriptor to be changed after being recorded to a command list but before being executed on the command queue (the descriptors and data are volatile). Version 1.1 assumes that descriptors set on the root signature are static (that the descriptors won’t change after they have been recorded on the command list).如果descriptors在被記錄到指令集後又被更新,則必須在根簽名的descriptor range上設置D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE 。The default behaviour of root signature version 1.1 allows the driver to make optimizations (for example, copy the descriptors or data to the GPU before the command list is even executed) if it will improve performance.

// Create a root signature.
D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {};
featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;
if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData))))
{
    featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
}

Root signature version 1.1 is preferred but the driver supported version on the end-user’s computer must be verified using the ID3D12Device::CheckFeatureSupport method. If the check fails, the root signature version will fall-back to the root signature version 1.0.Next, the root signature flags are defined.

// Allow input layout and deny unnecessary access to certain pipeline stages.
D3D12_ROOT_SIGNATURE_FLAGS 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;

The supported root signature flags are [16]:

在這個例子中,只有頂點着色器階段需要根簽名參與。於是其他階段就被Deny掉來做一個小優化。根簽名包含了32bit的常數參數。CD3DX12_ROOT_PARAMETER1 結構是D3D12_ROOT_PARAMETER1的簡易初始化。

// A single 32-bit constant root parameter that is used by the vertex shader.
CD3DX12_ROOT_PARAMETER1 rootParameters[1];
rootParameters[0].InitAsConstants(sizeof(XMMATRIX) / 4, 0, 0, D3D12_SHADER_VISIBILITY_VERTEX);

 

  • UINT num32BitValues: The number of 32-bit constants. In this case, the XMMATRIX structure contains 16 32-bit floating-point values.
  • UINT shaderRegister: The shader register to bind to. This parameter is bound to b0 in the vertex shader.
  • UINT registerSpace: The register space to bind to. Since no shader register space was specified in the vertex shader, this defaults to space0.
  • D3D12_SHADER_VISIBILITY visibility: Specifies the shader stages that are allowed to access the contents at that root signature slot. In this case, the visibility of the 32-bit constants is restricted to the vertex shader stage.

After describing the parameters (and samplers) that are used by the root signature, the next step is to create the the root signature description.

在指定了根簽名需要的參數後,下一步是創建根簽名描述。CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC  結構是D3D12_VERSIONED_ROOT_SIGNATURE_DESC 的簡易初始化。

CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDescription;
rootSignatureDescription.Init_1_1(_countof(rootParameters), rootParameters, 0, nullptr, rootSignatureFlags);

 

  • UINT numParameters: The number of root parameters in the root signature. In this case, there is only a single root parameter.
  • D3D12_ROOT_PARAMETER1* _pParameters: An array of numParameters root parameters. The array of root parameters contains only a single 32-bit root constant parameter.
  • UINT numStaticSamplers: The number of static samplers in the root signature.
  • D3D12_STATIC_SAMPLER_DESC* _pStaticSamplers: An array of numStaticSamplers static samplers.
  • D3D12_ROOT_SIGNATURE_FLAGS flags: Flags that determine the root signature visibility to the various shader stages.

 

然後是序列化根簽名描述到一個二進制物體中,然後創建真正的根簽名。

// Serialize the root signature.
ComPtr<ID3DBlob> rootSignatureBlob;
ComPtr<ID3DBlob> errorBlob;
ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDescription,
    featureData.HighestVersion, &rootSignatureBlob, &errorBlob));
// Create the root signature.
ThrowIfFailed(device->CreateRootSignature(0, rootSignatureBlob->GetBufferPointer(),
    rootSignatureBlob->GetBufferSize(), IID_PPV_ARGS(&m_RootSignature)));

使用D3DX12SerializeVersionedRootSignature 來序列化根簽名。和預編譯HLSL類似,預編譯根簽名也是可以的。當有很多着色器都需要一個獨特的根簽名的時候,預編譯能節省很多時間,產品正式發佈時也需要預編譯。要在HLSL中指定根簽名請看 Specifying Root Signatures in HLSL.最後使用ID3D12Device::CreateRootSignature來從序列化根簽名中創建根簽名物體。

PIPELINE STATE OBJECT

下一步是創建Pipeline State Object (PSO),PSO使用Pipeline State Stream 結構來描述,後者包含了描述PSO的參數,比如頂點着色器,幾何着色器,像素着色器。但如果沒有用到就無需描述。下面是這個例子中需要定義的Pipeline State Stream。

For this simple demo, only the following stream tokens need to be defined in the Pipeline State Stream structure:

  • Root Signature
  • Input Layout
  • Primitive Topology
  • Vertex Shader
  • Pixel Shader
  • Depth-Stencil Format
  • Render Target Format(s)
struct PipelineStateStream
{
    CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE pRootSignature;
    CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT InputLayout;
    CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY PrimitiveTopologyType;
    CD3DX12_PIPELINE_STATE_STREAM_VS VS;
    CD3DX12_PIPELINE_STATE_STREAM_PS PS;
    CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT DSVFormat;
    CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS RTVFormats;
} pipelineStateStream;

 

Before the Pipeline State Object can be created, the number of render targets and the render target formats are defined.

在創建PSO之前,需要先指定Render targets的數量和類型。

D3D12_RT_FORMAT_ARRAY rtvFormats = {};
rtvFormats.NumRenderTargets = 1;
rtvFormats.RTFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;

PSO描述如下:

pipelineStateStream.pRootSignature = m_RootSignature.Get();
pipelineStateStream.InputLayout = { inputLayout, _countof(inputLayout) };
pipelineStateStream.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pipelineStateStream.VS = CD3DX12_SHADER_BYTECODE(vertexShaderBlob.Get());
pipelineStateStream.PS = CD3DX12_SHADER_BYTECODE(pixelShaderBlob.Get());
pipelineStateStream.DSVFormat = DXGI_FORMAT_D32_FLOAT;
pipelineStateStream.RTVFormats = rtvFormats;

然後使用 pipelineStateStream 結構來創建PSO。

D3D12_PIPELINE_STATE_STREAM_DESC pipelineStateStreamDesc = {
    sizeof(PipelineStateStream), &pipelineStateStream
};
ThrowIfFailed(device->CreatePipelineState(&pipelineStateStreamDesc, IID_PPV_ARGS(&m_PipelineState)));

在完成LoadContent方法之前,指令隊列上的指令集必須執行完成,來保證頂點和索引緩存已經被上傳到GPU資源處。

auto fenceValue = commandQueue->ExecuteCommandList(commandList);
commandQueue->WaitForFenceValue(fenceValue);
m_ContentLoaded = true;

 

DEPTH BUFFER

當descriptor heap創建好了,就可以安全創建depth buffer了。ResizeDepthBuffer可以創建全新的depth buffer,或是創建出用於替代之前depth buffer的depth buffer,但要確保沒有正在指令隊列上執行的指令集正在引用要被替換掉的depth buffer,於是首先要Flush掉應用程序上的每個指令隊列。

// Resize/Create the depth buffer.
    ResizeDepthBuffer(GetClientWidth(), GetClientHeight());
    return true;

}
void Tutorial2::ResizeDepthBuffer(int width, int height)
{
    if (m_ContentLoaded)
    {
        // Flush any GPU commands that might be referencing the depth buffer.
        Application::Get().Flush();
        width = std::max(1, width);
        height = std::max(1, height);
        auto device = Application::Get().GetDevice();

如果窗口最小化,那麼資源的大小爲0就會報錯,所以至少要是1x1。而應用類被DX12 device持有。

// Resize screen dependent resources.
// Create a depth buffer.
D3D12_CLEAR_VALUE optimizedClearValue = {};
optimizedClearValue.Format = DXGI_FORMAT_D32_FLOAT;
optimizedClearValue.DepthStencil = { 1.0f, 0 };
ThrowIfFailed(device->CreateCommittedResource(
    &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
    D3D12_HEAP_FLAG_NONE,
    &CD3DX12_RESOURCE_DESC::Tex2D(DXGI_FORMAT_D32_FLOAT, width, height,
        1, 0, 1, 0, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL),
    D3D12_RESOURCE_STATE_DEPTH_WRITE,
    &optimizedClearValue,
    IID_PPV_ARGS(&m_DepthBuffer)
));

這裏的ID3D12Device::CreateCommittedResource 方法用於創建紋理,而之前的用於創建緩存。D3D12_CLEAR_VALUE用於描述Clear顏色,同時用於Depth-stencil和顏色紋理。 CD3DX12_RESOURCE_DESC::Tex2D 方法用於幫助創建D3D12_RESOURCE_DESC , CD3DX12_RESOURCE_DESC::Tex2D具有如下結構。

The CD3DX12_RESOURCE_DESC::Tex2D method takes the following arguments [17]:

  • DXGI_FORMAT format: The format of the texture.
  • UINT64 width: Width of the texture in pixels.
  • UINT height: The height of the texture in pixels.
  • UINT16 arraySize: The number of texture elements. For a single texture this value must be 1.
  • UINT16 mipLevels: The number of mip levels. A value of 0 will cause the number of mip levels to be calculated automatically based on the maximum of either the width or the height of the texture.
  • UINT sampleCount: The number of milti-samples per pixel. The default value for sampleCount is 1.
  • UINT sampleQuality: The quality level for multi-sampling. The default value for sampleQuality is 0.
  • D3D12_RESOURCE_FLAGS flags: A bitwise combination of D3D12_RESOURCE_FLAGS enumeration values. Since this is a depth-stencil buffer, the D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL flag must be specified.
  • D3D12_TEXTURE_LAYOUT layout: Specifies the texture layout options. The default value is D3D12_TEXTURE_LAYOUT_UNKNOWN which allows the driver to choose the most optimized layout based on the properties of the texture.
  • UINT64 alignment: Specify the alignment of the texture. This can be 4KB, 64KB, or 4MB. The default value of 0 can be used to allow the API to automatically choose the alignment based on the texture properties. Multi-sample textures will be created with 4MB alignment and 64KB for single-sample textures. Smaller alignments are used if the texture is sufficiently small (if the number of 4KB tiles is less than 16 for the highest (largest) mip level of the texture).

然後爲Depth buffer創建depth-stencil view。

// Update the depth-stencil view.
        D3D12_DEPTH_STENCIL_VIEW_DESC dsv = {};
        dsv.Format = DXGI_FORMAT_D32_FLOAT;
        dsv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
        dsv.Texture2D.MipSlice = 0;
        dsv.Flags = D3D12_DSV_FLAG_NONE;
        device->CreateDepthStencilView(m_DepthBuffer.Get(), &dsv,
            m_DSVHeap->GetCPUDescriptorHandleForHeapStart());
    }
}

 D3D12_DEPTH_STENCIL_VIEW_DES用於描述Depth-stencil 資源,其具有如下結構:

typedef struct D3D12_DEPTH_STENCIL_VIEW_DESC {
  DXGI_FORMAT         Format;
  D3D12_DSV_DIMENSION ViewDimension;
  D3D12_DSV_FLAGS     Flags;
  union {
    D3D12_TEX1D_DSV         Texture1D;
    D3D12_TEX1D_ARRAY_DSV   Texture1DArray;
    D3D12_TEX2D_DSV         Texture2D;
    D3D12_TEX2D_ARRAY_DSV   Texture2DArray;
    D3D12_TEX2DMS_DSV       Texture2DMS;
    D3D12_TEX2DMS_ARRAY_DSV Texture2DMSArray;
  };
} D3D12_DEPTH_STENCIL_VIEW_DESC;

 

void CreateDepthStencilView(
  [in, optional]       ID3D12Resource                *pResource,
  [in, optional] const D3D12_DEPTH_STENCIL_VIEW_DESC *pDesc,
  [in]                 D3D12_CPU_DESCRIPTOR_HANDLE   DestDescriptor
);

 

  • ID3D12Resource *pResource: A pointer to the ID3D12Resource object that represents the depth stencil buffer.
     
    At least one of pResource or pDesc must be provided. A null pResource is used to initialize a null descriptor, which guarantees D3D11-like null binding behavior (reading 0s, writes are discarded), but must have a valid pDesc in order to determine the descriptor type.
  • D3D12_DEPTH_STENCIL_VIEW_DESC *pDesc: A pointer to a D3D12_DEPTH_STENCIL_VIEW_DESC structure that describes the depth-stencil view.
     
    A null pDesc is used to initialize a default descriptor, if possible. This behavior is identical to the D3D11 null descriptor behavior, where defaults are filled in. This behavior inherits the resource format and dimension (if not typeless) and DSVs target the first mip and all array slices. Not all resources support null descriptor initialization.
  • D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor: Describes the CPU descriptor handle that represents the start of the heap that holds the depth-stencil view.

Tutorial2::OnResize

這個方法很簡單,只是改變了m_Viewport變量的大小,而窗口類會自動調整交換鏈的back buffer的大小。

void Tutorial2::OnResize(ResizeEventArgs& e)
{
    if (e.Width != GetClientWidth() || e.Height != GetClientHeight())
    {
        super::OnResize(e);
        m_Viewport = CD3DX12_VIEWPORT(0.0f, 0.0f,
           static_cast<float>(e.Width), static_cast<float>(e.Height));
        ResizeDepthBuffer(e.Width, e.Height);
    }
}

 

Tutorial2::OnUpdate

OnUpdate在屏幕渲染前調用。每一幀立方體的旋轉角度和攝像機視角都可能更新,所以必須重新計算投影矩陣。

void Tutorial2::OnUpdate(UpdateEventArgs& e)
{
    static uint64_t frameCount = 0;
    static double totalTime = 0.0;
    super::OnUpdate(e);
    totalTime += e.ElapsedTime;
    frameCount++;
    if (totalTime > 1.0)
    {
        double fps = frameCount / totalTime;
        char buffer[512];
        sprintf_s(buffer, "FPS: %f\n", fps);
        OutputDebugStringA(buffer);
        frameCount = 0;
        totalTime = 0.0;
    }
// Update the model matrix.
float angle = static_cast<float>(e.TotalTime * 90.0);
const XMVECTOR rotationAxis = XMVectorSet(0, 1, 1, 0);
m_ModelMatrix = XMMatrixRotationAxis(rotationAxis, XMConvertToRadians(angle));

DirectX數學庫提供了好幾種用於計算不同類型的矩陣。也就是可用使用一個角度和旋轉軸以及XMMatrixRotationAxis來計算旋轉矩陣。

// Update the view matrix.
const XMVECTOR eyePosition = XMVectorSet(0, 0, -10, 1);
const XMVECTOR focusPoint = XMVectorSet(0, 0, 0, 1);
const XMVECTOR upDirection = XMVectorSet(0, 1, 0, 0);
m_ViewMatrix = XMMatrixLookAtLH(eyePosition, focusPoint, upDirection);
   // Update the projection matrix.
    float aspectRatio = GetClientWidth() / static_cast<float>(GetClientHeight());
    m_ProjectionMatrix = XMMatrixPerspectiveFovLH(XMConvertToRadians(m_FoV), aspectRatio, 0.1f, 100.0f);
}

A left-handed view matrix can be computed using the XMMatrixLookAtLH function from the position of the camera (eyePosition), a point to look at (focusPoint) and a local up vector (upDirection).A left-handed projection matrix can be computed from the field of view (m_FoV) in radians, the aspect ratio of the screen (aspectRatio), the distance to the near clipping plane (0.1f) and the distance to the far clipping plane (100.0f).See the article titled Coordinate Systems for more information on left, and right-handed coordinate system.See the article titled Understanding the View Matrix for more information about building view matrices.

Tutorial2::TransitionResource

TransitionResource用於在指令集上創建resource barrier。而CD3DX12_RESOURCE_BARRIER結構可以簡易初始化D3D12_RESOURCE_BARRIER結構。而ID3D12GraphicsCommandList::ResourceBarrier 用於在指令集上執行resource barrier,此方法可以接收resource barrier數組。在複製,繪製或dispatch之前,最好把所有的resource barrier都放進指令集中。

// Transition a resource
void Tutorial2::TransitionResource(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
    Microsoft::WRL::ComPtr<ID3D12Resource> resource,
    D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState)
{
    CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(
        resource.Get(),
        beforeState, afterState);
    commandList->ResourceBarrier(1, &barrier);

}

 

Tutorial2::ClearRTV

// Clear a render target.
void Tutorial2::ClearRTV(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
    D3D12_CPU_DESCRIPTOR_HANDLE rtv, FLOAT* clearColor)
{
    commandList->ClearRenderTargetView(rtv, clearColor, 0, nullptr);

}
void Tutorial2::ClearDepth(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
    D3D12_CPU_DESCRIPTOR_HANDLE dsv, FLOAT depth)
{
    commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, depth, 0, 0, nullptr);
}

 

 

Tutorial2::OnRender

  • Clear the color and depth buffers
  • For each object in the scene:
    • Transition any resources to the required state
    • Bind the Pipeline State Object (PSO)
    • Bind the Root Signature
    • Bind any resources (CBV, UAV, SRV, Samplers, etc..) to the shader stages
    • Set the primitive topology for the Input Assembler (IA)
    • Bind the vertex buffer to the IA
    • Bind the index buffer to the IA
    • Set the viewport(s) for the Rasterizer Stage (RS)
    • Set the scissor rectangle(s) for the RS
    • Bind the color and depth-stencil render targets to the Output Merger (OM)
    • Draw the geometry
  • Present the rendered image to the screen

不是每一步都需要爲每一個Scene object重複。例如,如果PSO不在draw calls之間變換,那麼就不需要被再次綁定。如果資源比如紋理或常數緩存需要被交換,或者根簽名需要在draw call之間變換,那麼資源才需要綁定。

void Tutorial2::OnRender(RenderEventArgs& e)
{
    super::OnRender(e);
    auto commandQueue = Application::Get().GetCommandQueue(D3D12_COMMAND_LIST_TYPE_DIRECT);
    auto commandList = commandQueue->GetCommandList();
    UINT currentBackBufferIndex = m_pWindow->GetCurrentBackBufferIndex();
    auto backBuffer = m_pWindow->GetCurrentBackBuffer();
    auto rtv = m_pWindow->GetCurrentRenderTargetView();
    auto dsv = m_DSVHeap->GetCPUDescriptorHandleForHeapStart();

OnRender方法需要做的第一件事就是接收從應用類傳來的消息指令。在LoadContent方法了,一個複製隊列 (D3D12_COMMAND_LIST_TYPE_COPY)用於把數據複製到GPU中。而在這裏,則需要一個D3D12_COMMAND_LIST_TYPE_DIRECT隊列來執行繪製指令。然後從指令隊列中獲取指令集,這時指令集已經準備好接受指令並且無需重設。而當前的back buffer 索引則從窗口類獲得,這個索引用於存儲Fence值。然後就是獲取back buffer資源,需要在被清除或展現到屏幕之前將其轉換到正確的狀態,而Depth Stencil View從depth-stencil descriptor heap中獲取。由於這兒只有一個depth-stencil buffer,所以descritor heap中的handle用 ID3D12DescriptorHeap::GetCPUDescriptorHandleForHeapStart 方法獲取。

CLEAR THE RENDER TARGETS

在屏幕被渲染之前,之前的內容需要被清理。

// Clear the render targets.
{
    TransitionResource(commandList, backBuffer,
        D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET);
    FLOAT clearColor[] = { 0.4f, 0.6f, 0.9f, 1.0f };
    ClearRTV(commandList, rtv, clearColor);
    ClearDepth(commandList, dsv);
}

然後是準備渲染管線。ID3D12GraphicsCommandList::SetPipelineState方法用於把PSO綁定值渲染管線。儘管在PSO創建期間已經把根簽名綁定至PSO了,根簽名仍然需要使用ID3D12GraphicsCommandList::SetGraphicsRootSignature 將其在指令集上設置,然後才能綁定任何資源。

commandList->SetPipelineState(m_PipelineState.Get());
commandList->SetGraphicsRootSignature(m_RootSignature.Get());

 

SETUP THE INPUT ASSEMBLER

commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
commandList->IASetVertexBuffers(0, 1, &m_VertexBufferView);
commandList->IASetIndexBuffer(&m_IndexBufferView);

The primitive topology type (point, line, triangle, or patch) was specified in the PSO but the primitive topology (for example triangle list, or triangle strip) also needs to be configured using the ID3D12GraphicsCommandList::IASetPrimitiveTopology method.The vertex buffer is bound to the Input Assembler using the ID3D12GraphicsCommandList::IASetVertexBuffers method. This method takes the following parameters [20]:

  • UINT StartSlot: Index into the device’s zero-based array to begin setting vertex buffers. In this case, only a single vertex buffer is bound to slot 0.
  • UINT NumViews: The number of views in the pViews array. In this case, there is only a single vertex buffer.
  • D3D12_VERTEX_BUFFER_VIEW* pViews: Specifies the vertex buffer views in an array of D3D12_VERTEX_BUFFER_VIEW structures.

The index buffer is bound to the Input Assembler using the ID3D12GraphicsCommandList::IASetIndexBuffer method. Only a single index buffer can be bound to the Input Assembler at at time.

SETUP THE RASTERIZER STATE

Viewport和scissor矩陣在光柵化階段設置,Rasterizer State。

commandList->RSSetViewports(1, &m_Viewport);
commandList->RSSetScissorRects(1, &m_ScissorRect);
commandList->OMSetRenderTargets(1, &rtv, FALSE, &dsv);

 

The viewport to render to is determined by the SV_ViewportArrayIndex semantic output by the geometry shader. If the geometry shader does not specify the semantic, the first viewport in the array is used.Since DirectX 12, the scissor rectangle must be explicitly specified using the ID3D12GraphicsCommandList::RSSetScissorRects method. Not specifying a scissor rectangle before rendering could result in a empty screen (only the clear color is visible). This method’s paramereters are similar to that of the ID3D12GraphicsCommandList::RSSetViewports method.

The render targets must be bound to the Output Merger (OM) stage before drawing.

在繪製之前,Render Targets必須綁定至OutPut Merger姐u但,使用ID3D12GraphicsCommandList::OMSetRenderTargets

void OMSetRenderTargets(
  [in]                 UINT                        NumRenderTargetDescriptors,
  [in, optional] const D3D12_CPU_DESCRIPTOR_HANDLE *pRenderTargetDescriptors,
  [in]                 BOOL                        RTsSingleHandleToDescriptorRange,
  [in, optional] const D3D12_CPU_DESCRIPTOR_HANDLE *pDepthStencilDescriptor
);

 

  • UINT NumRenderTargetDescriptors: The number of entries in the pRenderTargetDescriptors array.
  • D3D12_CPU_DESCRIPTOR_HANDLE* pRenderTargetDescriptors: Specifies an array of D3D12_CPU_DESCRIPTOR_HANDLE structures that describe the CPU descriptor handles that represents the start of the heap of render target descriptors.
  • BOOL RTsSingleHandleToDescriptorRangeTRUE means the handle passed in is the pointer to a contiguous range of NumRenderTargetDescriptors descriptors. This case is useful if the set of descriptors to bind already happens to be contiguous in memory (so all that’s needed is a handle to the first one). FALSE means that the pointer is the first element of an array of discontiguous NumRenderTargetDescriptors handles. The false case allows an application to bind a set of descriptors from different locations at once.
  • D3D12_CPU_DESCRIPTOR_HANDLE* pDepthStencilDescriptor: A pointer to a D3D12_CPU_DESCRIPTOR_HANDLE structure that describes the descriptor handle that represents the start of the heap that holds the depth-stencil descriptor.

UPDATE THE ROOT PARAMETERS

當根簽名改變的時候,之前綁定至渲染管線的參數需要被重新綁定。任何在兩次draw call之間變化的根參數也需要更新。比如這次的變換矩陣,於是就把此矩陣作爲32bit常數來送給頂點着色器。

// Update the MVP matrix
XMMATRIX mvpMatrix = XMMatrixMultiply(m_ModelMatrix, m_ViewMatrix);
mvpMatrix = XMMatrixMultiply(mvpMatrix, m_ProjectionMatrix);
commandList->SetGraphicsRoot32BitConstants(0, sizeof(XMMATRIX) / 4, &mvpMatrix, 0);
void SetGraphicsRoot32BitConstants(
  [in]       UINT RootParameterIndex,
  [in]       UINT Num32BitValuesToSet,
  [in] const void *pSrcData,
  [in]       UINT DestOffsetIn32BitValues
);

And takes the following parameters:

  • UINT RootParameterIndex: The slot number for binding. This value corresponds to the index in the root parameters array that was used to create the root signature.
  • UINT Num32BitValuesToSet: The number of 32-bit constants to set in the root signature.
  • void* pSrcData: A pointer to the source data for the group of constants to set.
  • UINT DestOffsetIn32BitValues: The offset, in 32-bit values, to set the first constant of the group in the root signature.

At this point, the pipeline is setup and ready to draw the cube.

DRAW

渲染過程的最後一步是在指令集上執行Draw方法。這會讓綁定至Input Assembler的頂點被推至圖形渲染管線。最後這將變爲一個渲染好的幾何體,被記錄到render target上,然後綁定至output merger 階段。

commandList->DrawIndexedInstanced(_countof(g_Indicies), 1, 0, 0, 0);
void DrawIndexedInstanced(
  [in] UINT IndexCountPerInstance,
  [in] UINT InstanceCount,
  [in] UINT StartIndexLocation,
  [in] INT  BaseVertexLocation,
  [in] UINT StartInstanceLocation
);

 

  • UINT IndexCountPerInstance: Number of indices read from the index buffer for each instance.
  • UINT InstanceCount: Number of instances to draw.
  • UINT StartIndexLocation: The location of the first index read by the GPU from the index buffer.
  • INT BaseVertexLocation: A value added to each index before reading a vertex from the vertex buffer.
  • UINT StartInstanceLocation: A value added to each index before reading per-instance data from a vertex buffer.

PRESENT

最好要將渲染好的圖像呈現到屏幕上。

// Present
    {
        TransitionResource(commandList, backBuffer,
            D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT);
        m_FenceValues[currentBackBufferIndex] = commandQueue->ExecuteCommandList(commandList);
        currentBackBufferIndex = m_pWindow->Present();
        commandQueue->WaitForFenceValue(m_FenceValues[currentBackBufferIndex]);
    }
}

 

在呈現之前,首先把back buffer資源改爲D3D12_RESOURCE_STATE_PRESENT狀態。然後再指令隊列上執行指令集。然後CommandQueue::ExecuteCommandList 返回Fence值,存儲再 m_FenceValues 數組中並等待下一幀。然後使用Window::Present方法來呈現back buffer 內容。這個方法將會返回下一幀的back buffer 所有。在下一幀重用這個back buffer之前,需要等到Fence值到達某個值。

Tutorial2-Screenshot.png

void Tutorial2::OnKeyPressed(KeyEventArgs& e)
{
    super::OnKeyPressed(e);
    switch (e.Key)
    {
    case KeyCode::Escape:
        Application::Get().Quit(0);
        break;
    case KeyCode::Enter:
        if (e.Alt)
        {
    case KeyCode::F11:
        m_pWindow->ToggleFullscreen();
        break;
        }
    case keyCode::V:
        m_pWindow->ToggleVSync();
        break;
    }
}
void Tutorial2::OnMouseWheel(MouseWheelEventArgs& e)
{
    m_FoV -= e.WheelDelta;
    m_FoV = clamp(m_FoV, 12.0f, 90.0f);
    char buffer[256];
    sprintf_s(buffer, "FoV: %f\n", m_FoV);
    OutputDebugStringA(buffer);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章