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 numElements
, elementSize
, 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:
D3D12_HEAP_PROPERTIES *pHeapProperties
: A pointer to aD3D12_HEAP_PROPERTIES
structure that provides properties for the resource’s heap.D3D12_HEAP_FLAGS HeapFlags
: Heap options, as a bitwise-OR’d combination ofD3D12_HEAP_FLAGS
enumeration constants.D3D12_RESOURCE_DESC *pDesc
: A pointer to aD3D12_RESOURCE_DESC
structure that describes the resource.D3D12_RESOURCE_STATES InitialResourceState
: The initial state of the resource, as a bitwise-OR’d combination ofD3D12_RESOURCE_STATES
enumeration constants.
When a resource is created together with aD3D12_HEAP_TYPE_UPLOAD
heap,InitialResourceState
must beD3D12_RESOURCE_STATE_GENERIC_READ
. When a resource is created together with aD3D12_HEAP_TYPE_READBACK
heap,InitialResourceState
must beD3D12_RESOURCE_STATE_COPY_DEST
.D3D12_CLEAR_VALUE *pOptimizedClearValue
: Specifies aD3D12_CLEAR_VALUE
that describes the default value for a clear color.pOptimizedClearValue
specifies a value for which clear operations are most optimal. When the created resource is a texture with either theD3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET
orD3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL
flags, applications should choose the value that the clear operation will most commonly be called with. Clear operations can be called with other values, but those operations will not be as efficient as when the value matches the one passed into resource creation.pOptimizedClearValue
must beNULL
when used withD3D12_RESOURCE_DIMENSION_BUFFER
.REFIID riidResource
: The globally unique identifier (GUID) for the resource interface. This is an input parameter. TheREFIID
, or GUID, of the interface to the resource can be obtained by using the__uuidof()
macro. For example,__uuidof(ID3D12Resource)
will get the GUID of the interface to a resource.void **ppvResource
: A pointer to memory that receives the requested interface pointer to the created resource object.ppvResource
can beNULL
, to enable capability testing. WhenppvResource
isNULL
, no object will be created andS_FALSE
will be returned (whenpResourceDesc
is valid).
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 theID3D12GraphicsCommandList
interface for the command list.ID3D12Resource *pDestinationResource
: A pointer to theID3D12Resource
interface that represents the destination resource.ID3D12Resource *pIntermediate
: A pointer to theID3D12Resource
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 toD3D12_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 lengthNumSubresources
) of pointers toD3D12_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:
- Upload the Vertex buffer data for a cube mesh
- Create a vertex buffer view
- Upload the index buffer data for a cube mesh
- Create an index buffer view
- Create a descriptor heap for the depth-stencil view
- Load the vertex shader
- Load the pixel shader
- Create the input layout for the vertex shader
- Create a root signature
- Create a graphics pipeline state object (PSO)
- 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
A 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 aD3D12_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
: ADXGI_FORMAT
-typed value for the index-buffer format. In this case, theDXGI_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
: AD3D12_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 ofD3D12_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 flagD3D12_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
: ADXGI_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. UseD3D12_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:D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA
: Input data is per-vertex data.D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA
: Input data is per-instance data.
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 toD3D12_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]:
D3D12_ROOT_SIGNATURE_FLAG_NONE
: The default behaviour.D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
: The app is opting in to using the Input Assembler (requiring an input layout that defines a set of vertex buffer bindings). Omitting this flag can result in one root argument space being saved on some hardware. Omit this flag if the Input Assembler is not required, though the optimization is minor.D3D12_ROOT_SIGNATURE_FLAG_DENY_VERTEX_SHADER_ROOT_ACCESS
: Denies the vertex shader access to the root signature.D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS
: Denies the hull shader access to the root signature.D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS
: Denies the domain shader access to the root signature.D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS
: Denies the geometry shader access to the root signature.D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS
: Denies the pixel shader access to the root signature.D3D12_ROOT_SIGNATURE_FLAG_ALLOW_STREAM_OUTPUT
: The root signature allows stream output. This flag can be specified for root signatures authored in HLSL, similar to how the other flags are specified.ID3D12Device::CreateGraphicsPipelineState
will fail if the geometry shader contains stream output but the root signature does not have this flag set. Omit this flag if stream output is not required.
在這個例子中,只有頂點着色器階段需要根簽名參與。於是其他階段就被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, theXMMATRIX
structure contains 16 32-bit floating-point values.UINT shaderRegister
: The shader register to bind to. This parameter is bound tob0
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 tospace0
.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;
CD3DX12_PIPELINE_STATE_STREAM_FLAGS
: Wrapper for aD3D12_PIPELINE_STATE_FLAGS
enumeration.CD3DX12_PIPELINE_STATE_STREAM_NODE_MASK
: Describes the pipeline state node mask as a UINT, which is used to identify the nodes (physical adapters of the device) that the PSO applies to in Multi-Adapter scenarios; each bit in the mask corresponds to a single node. For single-adapter scenarios, set this value to 0.CD3DX12_PIPELINE_STATE_STREAM_ROOT_SIGNATURE
: Wrapper for a pointer to aID3D12RootSignature
.CD3DX12_PIPELINE_STATE_STREAM_INPUT_LAYOUT
: Wrapper for aD3D12_INPUT_LAYOUT_DESC
. Specifies the input layout for the input assembly stage.CD3DX12_PIPELINE_STATE_STREAM_IB_STRIP_CUT_VALUE
: Wrapper for aD3D12_INDEX_BUFFER_STRIP_CUT_VALUE
enumeration. Used to specify the index buffer value to split line strips or triangle strips.CD3DX12_PIPELINE_STATE_STREAM_PRIMITIVE_TOPOLOGY
: Wrapper for aD3D12_PRIMITIVE_TOPOLOGY_TYPE
enumeration. Used to specify the primitive topology type (point, line, triangle or patch). Do not mistake this with theD3D_PRIMITIVE_TOPOLOGY
enumeration!CD3DX12_PIPELINE_STATE_STREAM_VS
: Wrapper for aD3D12_SHADER_BYTECODE
structure. Used to specify the compiled Vertex Shader (VS).CD3DX12_PIPELINE_STATE_STREAM_GS
: Wrapper for aD3D12_SHADER_BYTECODE
structure. Used to specify the compiled Geometry Shader (GS).CD3DX12_PIPELINE_STATE_STREAM_STREAM_OUTPUT
: Wrapper for aD3D12_STREAM_OUTPUT_DESC
structure. Describes the streaming output buffers for the Stream Output (SO) stage.CD3DX12_PIPELINE_STATE_STREAM_HS
: Wrapper for aD3D12_SHADER_BYTECODE
structure. Used to specify the compiled Hull Shader (HS).CD3DX12_PIPELINE_STATE_STREAM_DS
: Wrapper for aD3D12_SHADER_BYTECODE
structure. Used to specify the compiled Domain Shader (DS).CD3DX12_PIPELINE_STATE_STREAM_PS
: Wrapper for aD3D12_SHADER_BYTECODE
structure. Used to specify the compiled Pixel Shader (PS).CD3DX12_PIPELINE_STATE_STREAM_CS
: Wrapper for aD3D12_SHADER_BYTECODE
structure. Used to specify the compiled Compute Shader (CS).CD3DX12_PIPELINE_STATE_STREAM_BLEND_DESC
: Wraper for aD3D12_BLEND_DESC
structure. Used to describes the blend state.CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL
Wrapper for aD3D12_DEPTH_STENCIL_DESC
structure. Used to describe the depth-stencil state.CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL1
: Wrapper for aD3D12_DEPTH_STENCIL_DESC1
structure. Used to describe the depth-stencil state. This version adds the ability to enable depth-bounds testing.CD3DX12_PIPELINE_STATE_STREAM_DEPTH_STENCIL_FORMAT
: Wrapper for aDXGI_FORMAT
enumeration. Describes the format of the depth-stencil buffer.CD3DX12_PIPELINE_STATE_STREAM_RASTERIZER
: Wrapper for theD3D12_RASTERIZER_DESC
structure. Used to describe the Rasterizer State (RS)CD3DX12_PIPELINE_STATE_STREAM_RENDER_TARGET_FORMATS
: Wrapper for theD3D12_RT_FORMAT_ARRAY
structure which is a wrapper for an array of render target formats.CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_DESC
: Wrapper for theDXGI_SAMPLE_DESC
structure. Used to describe the multi-sampling. The render targets must also be created with multisampling enabled.CD3DX12_PIPELINE_STATE_STREAM_SAMPLE_MASK
: AUINT
that describes the sample mask for the blend state.CD3DX12_PIPELINE_STATE_STREAM_CACHED_PSO
: A wrapper for theD3D12_CACHED_PIPELINE_STATE
structure. The cached PSO can be used to quickly load the PSO after it has been created. Using cached PSO’s can improve load times.CD3DX12_PIPELINE_STATE_STREAM_VIEW_INSTANCING
: A wrapper for aD3D12_VIEW_INSTANCING_DESC
structure. Allows shaders to render to multiple views with a single draw call. Useful for stereo vision or cubemap generation.
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 forsampleCount
is 1.UINT sampleQuality
: The quality level for multi-sampling. The default value forsampleQuality
is 0.D3D12_RESOURCE_FLAGS flags
: A bitwise combination ofD3D12_RESOURCE_FLAGS
enumeration values. Since this is a depth-stencil buffer, theD3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL
flag must be specified.D3D12_TEXTURE_LAYOUT layout
: Specifies the texture layout options. The default value isD3D12_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;
DXGI_FORMAT Format
: The depth-stencil format. This should be the same format that was used to create the texture but it could be a different format if the view is for a Shader Resource.D3D12_DSV_DIMENSION ViewDimension
: One value from theD3D12_DSV_DIMENSION
enumeration. In this case, it is a 2D texture.D3D12_DSV_FLAGS Flags
: A combination ofD3D12_DSV_FLAGS
enumeration constants that are combined by using a bitwise OR operation. The resulting value specifies whether the texture is read only. Pass 0 to specify that it isn’t read only; otherwise, pass one or more of the members of theD3D12_DSV_FLAGS
enumerated type.- One of the following is specified depending on the value of the
ViewDimension
member:D3D12_TEX1D_DSV Texture1D
: AD3D12_TEX1D_DSV
structure that specifies a 1D texture subresource.D3D12_TEX1D_ARRAY_DSV Texture1DArray
: AD3D12_TEX1D_ARRAY_DSV
structure that specifies an array of 1D texture subresources.D3D12_TEX2D_DSV Texture2D
: AD3D12_TEX2D_DSV
structure that specifies a 2D texture subresource.D3D12_TEX2D_ARRAY_DSV Texture2DArray
: AD3D12_TEX2D_ARRAY_DSV
structure that specifies an array of 2D texture subresources.D3D12_TEX2DMS_DSV Texture2DMS
: AD3D12_TEX2DMS_DSV
structure that specifies a multisampled 2D texture.D3D12_TEX2DMS_ARRAY_DSV Texture2DMSArray
: AD3D12_TEX2DMS_ARRAY_DSV
structure that specifies an array of multisampled 2D textures.
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 theID3D12Resource
object that represents the depth stencil buffer.
At least one ofpResource
orpDesc
must be provided. A nullpResource
is used to initialize a null descriptor, which guarantees D3D11-like null binding behavior (reading 0s, writes are discarded), but must have a validpDesc
in order to determine the descriptor type.D3D12_DEPTH_STENCIL_VIEW_DESC *pDesc
: A pointer to aD3D12_DEPTH_STENCIL_VIEW_DESC
structure that describes the depth-stencil view.
A nullpDesc
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 thepViews
array. In this case, there is only a single vertex buffer.D3D12_VERTEX_BUFFER_VIEW* pViews
: Specifies the vertex buffer views in an array ofD3D12_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);
UINT NumViewports
: The number of viewports to bind. The range of valid values is (0 toD3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE
). Currently, the maximum number of viewports that can be bound is 16.D3D12_VIEWPORT* pViewports
: An array ofD3D12_VIEWPORT
structures to bind to the device.
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 thepRenderTargetDescriptors
array.D3D12_CPU_DESCRIPTOR_HANDLE* pRenderTargetDescriptors
: Specifies an array ofD3D12_CPU_DESCRIPTOR_HANDLE
structures that describe the CPU descriptor handles that represents the start of the heap of render target descriptors.BOOL RTsSingleHandleToDescriptorRange
:TRUE
means the handle passed in is the pointer to a contiguous range ofNumRenderTargetDescriptors
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 discontiguousNumRenderTargetDescriptors
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 aD3D12_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值到達某個值。
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);
}