【学习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);
}

 

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