【学习DirectX12(5)】渲染——第二部分

 

COMMANDQUEUE::GETCOMMANDLIST

GetCommandList可以返回一个指令集并直接用于发布GPU绘制or dispatch指令。这个指令集一开始就处在记录状态所有开发者无需重设它就可以直接使用它。但指令集需要一个指令分配器与之相关联。但是指令集又没有直接的方法去查询究竟应该是哪个指令分配器,所以一开始创建的时候就必须指定一个未经使用的指令分配器。但只要这个指令分配器不在当前这个指令队列上工作,那么这个指令分配器就可以立马重用。


Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> CommandQueue::GetCommandList()
{
    Microsoft::WRL::ComPtr<ID3D12CommandAllocator> commandAllocator;
    Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList;
if ( !m_CommandAllocatorQueue.empty() && IsFenceComplete(m_CommandAllocatorQueue.front().fenceValue))
{
    commandAllocator = m_CommandAllocatorQueue.front().commandAllocator;
    m_CommandAllocatorQueue.pop();
    ThrowIfFailed(commandAllocator->Reset());
}
else
{
    commandAllocator = CreateCommandAllocator();
}
    // Associate the command allocator with the command list so that it can be   
 // retrieved when the command list is executed.    
ThrowIfFailed(commandList->SetPrivateDataInterface(__uuidof(ID3D12CommandAllocator), commandAllocator.Get()));    
return commandList;}

使用ID3D12Object::SetPrivateDataInterface方法来将指令分配器和指令集关联起来。当指令集执行后,指令分配器就可以回到指令分配器队列中。而为ID3D12Object指定COM物体时也是使用ID3D12Object物体时也是使用ID3D12Object::SetPrivateDataInterface,然后COM物体的内部计数指针会增加,除非ID3D12Object物体被销毁,或COM被别人或空指针代替。

COMMANDQUEUE::EXECUTECOMMANDLIST

uint64_t CommandQueue::ExecuteCommandList(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList)
{
    commandList->Close();
    ID3D12CommandAllocator* commandAllocator;
    UINT dataSize = sizeof(commandAllocator);
    ThrowIfFailed(commandList->GetPrivateData(__uuidof(ID3D12CommandAllocator), &dataSize, &commandAllocator));
    ID3D12CommandList* const ppCommandLists[] = {
        commandList.Get()
    };
    m_d3d12CommandQueue->ExecuteCommandLists(1, ppCommandLists);
    uint64_t fenceValue = Signal();
    m_CommandAllocatorQueue.emplace(CommandAllocatorEntry{ fenceValue, commandAllocator });
    m_CommandListQueue.push(commandList);
 // The ownership of the command allocator has been transferred to the ComPtr
    // in the command allocator queue. It is safe to release the reference
    // in this temporary COM pointer here.
    commandAllocator->Release();
    return fenceValue;
}

在指令集可以被执行之前,首先应该关闭,用ID3D12GraphicsCommandList::Close。然后用ID3DObject::GetPrivateData来获取指令分配器。由于ID3DCommandQueueList::ExecuteCommandLists方法需要一个数组的ID3D12CommmandList,所以创建一个数组。当开始执行指令集之前的一瞬间,然后就用Signal把一个fence给与指令分配器。然后指令分配器和指令集将被推进至各自的队列里用于重新使用。

 

The Game Class

Game类是个抽象基础层,需要如下功能

  1. 初始化DX12 Runtime,并且创建用于渲染的窗口
  2. 加载/unload指定的内容
  3. 销毁指定的内容且释放内存
  4. 处理窗口消息

Game Class

The Game Header

/**
 *   @brief The Game class is the abstract base class for DirecX 12 demos.
 */
#pragma once
#include <Events.h>
#include <memory> // for std::enable_shared_from_this
#include <string> // for std::wstring
class Window;
class Game : public std::enable_shared_from_this<Game>
{
public:
    /**
     * Create the DirectX demo using the specified window dimensions.
     */
    Game(const std::wstring& name, int width, int height, bool vSync);
    virtual ~Game();
/*  Initialize the DirectX Runtime.*/
virtual bool Initialize();
/* Load content required for the demo.*/
virtual bool LoadContent() = 0;
/* Unload demo specific content that was loaded in LoadContent.*/
virtual void UnloadContent() = 0;
/* Destroy any resource that are used by the game. */
virtual void Destroy();

 The vSync option specifies whether rendering should be synchronized to the vertical refresh rate of the screen or not.

Game类提供Initialize的默认实现,仅仅是创建了一个用于渲染图形的创建并注册了了窗口回调函数。而默认销毁函数仅仅是销毁窗口。而LoadContent和UnLoadContent必须由子类提供。 The default implementations for the window callback functions is to do nothing.

protected:
    friend class Window;
    /**
     *  Update the game logic.
     */
    virtual void OnUpdate(UpdateEventArgs& e);
    /**
     *  Render stuff.
     */
    virtual void OnRender(RenderEventArgs& e);
    /**
     * Invoked by the registered window when a key is pressed
     * while the window has focus.
     */
    virtual void OnKeyPressed(KeyEventArgs& e);
/**
     * Invoked when a key on the keyboard is released.
     */
    virtual void OnKeyReleased(KeyEventArgs& e);
    /**
     * Invoked when the mouse is moved over the registered window.
     */
    virtual void OnMouseMoved(MouseMotionEventArgs& e);
    /**
     * Invoked when a mouse button is pressed over the registered window.
     */
    virtual void OnMouseButtonPressed(MouseButtonEventArgs& e);
    /**
     * Invoked when a mouse button is released over the registered window.
     */
    virtual void OnMouseButtonReleased(MouseButtonEventArgs& e);
    /**
     * Invoked when the mouse wheel is scrolled while the registered window has focus.
     */
    virtual void OnMouseWheel(MouseWheelEventArgs& e);
    /*
     * Invoked when the attached window is resized.
     */
    virtual void OnResize(ResizeEventArgs& e);
    /**
     * Invoked when the registered window instance is destroyed.
     */
    virtual void OnWindowDestroy();

OnUpdate函数将会处理窗口接受到的WM_PAINT消息。然后OnUpdate函数回调将会使用OnRender方法。The Game class also provides protected access to the window instance that is created by default in the Game::Initialize method.

   std::shared_ptr<Window> m_pWindow;
private:
    std::wstring m_Name;
    int m_Width;
    int m_Height;
    bool m_vSync;
};

Game::Initialize

The purpose of the Game::Initialize method is to initialize the game specific state. GPU resources should be allocated in the LoadContent method which should be provided by the child class.

bool Game::Initialize()
{
    // Check for DirectX Math library support.
    if (!DirectX::XMVerifyCPUSupport())
    {
        MessageBoxA(NULL, "Failed to verify DirectX Math library support.", "Error", MB_OK | MB_ICONERROR);
        return false;
    }
    m_pWindow = Application::Get().CreateRenderWindow(m_Name, m_Width, m_Height, m_vSync);
    m_pWindow->RegisterCallbacks(shared_from_this());
    m_pWindow->Show();
    return true;
}

Game::Destroy

void Game::Destroy()
{
    Application::Get().DestroyWindow(m_pWindow);
    m_pWindow.reset();
}

 

 

The Tutorial Class

这个教程的Tutorial2类不仅重写了Game类的LoadContent和UnLoadContent,还提供了一些帮助函数用于上传顶点和索引数据到GPU并且执行资源转换。

The Tutorial Header

#pragma once
#include <Game.h>
#include <Window.h>
#include <DirectXMath.h>
class Tutorial2 : public Game
{
public:
    using super = Game;
    Tutorial2(const std::wstring& name, int width, int height, bool vSync = false);
    /* Load content required for the demo.*/
    virtual bool LoadContent() override;
    /* Unload demo specific content that was loaded in LoadContent. */
    virtual void UnloadContent() override;
protected:  
  /**     *  Update the game logic.     */   
 virtual void OnUpdate(UpdateEventArgs& e) override;  
  /**     *  Render stuff.     */   
 virtual void OnRender(RenderEventArgs& e) override; 
   /**     * Invoked by the registered window when a key is pressed     * while the window has focus.     */  
  virtual void OnKeyPressed(KeyEventArgs& e) override;   
 /**     * Invoked when the mouse wheel is scrolled while the registered window has focus.     */ 
   virtual void OnMouseWheel(MouseWheelEventArgs& e) override;   
 virtual void OnResize(ResizeEventArgs& e) override; 
private:
    // Helper functions
    // Transition a resource
    void TransitionResource(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
        Microsoft::WRL::ComPtr<ID3D12Resource> resource,
        D3D12_RESOURCE_STATES beforeState, D3D12_RESOURCE_STATES afterState);
    // Clear a render target view.
    void ClearRTV(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
        D3D12_CPU_DESCRIPTOR_HANDLE rtv, FLOAT* clearColor);
    // Clear the depth of a depth-stencil view.
    void ClearDepth(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
        D3D12_CPU_DESCRIPTOR_HANDLE dsv, FLOAT depth = 1.0f );
    // Create a GPU buffer.
    void UpdateBufferResource(Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList2> commandList,
        ID3D12Resource** pDestinationResource, ID3D12Resource** pIntermediateResource,
        size_t numElements, size_t elementSize, const void* bufferData,
        D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE );
    // Resize the depth buffer to match the size of the client area.
    void ResizeDepthBuffer(int width, int height);

windows.h是WINAPI,而DirectXMath尽管是WIN 10 SDK的,但仍然被当作DirectX的一部分。 The vSync option determines if the renderer should synchronize with vertical refresh rate of the screen.当游戏逻辑部分需要更新时使用OnUpdate,而当屏幕渲染需要更新时使用OnRender。

TransitionResouce和ClearRTV方法在之前的课程已经说过了,而ClearDepth方法与ClearRTV方法非常类似,但是这是清除depth-stencil view的。而UpdateBufferResouce方法将同时创建一个足够大的资源来存储缓存数据,但是也将创建一个intermediate upload buffer用于把缓存数据从CPU传回到GPU,但要在图形指令集完成把资源加载到GPU之后。因此除非全部加载完成,否则这个函数的指针不能被销毁。

depth buffer储存像素的深度值,远处为1,近处为0。

 

uint64_t m_FenceValues[Window::BufferCount] = {};
// Vertex buffer for the cube.
Microsoft::WRL::ComPtr<ID3D12Resource> m_VertexBuffer;
D3D12_VERTEX_BUFFER_VIEW m_VertexBufferView;
// Index buffer for the cube.
Microsoft::WRL::ComPtr<ID3D12Resource> m_IndexBuffer;
D3D12_INDEX_BUFFER_VIEW m_IndexBufferView;
// Depth buffer.
Microsoft::WRL::ComPtr<ID3D12Resource> m_DepthBuffer;
// Descriptor heap for depth buffer.
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_DSVHeap;
// Root signature
Microsoft::WRL::ComPtr<ID3D12RootSignature> m_RootSignature;
// Pipeline state object.
Microsoft::WRL::ComPtr<ID3D12PipelineState> m_PipelineState;
D3D12_VIEWPORT m_Viewport;
D3D12_RECT m_ScissorRect;
float m_FoV;
DirectX::XMMATRIX m_ModelMatrix;
DirectX::XMMATRIX m_ViewMatrix;
DirectX::XMMATRIX m_ProjectionMatrix;
bool m_ContentLoaded;
};

 The number of frames that need to be tracked is stored in the Window::BufferCount static constant.In order to demonstrate a basic rendering pipeline, a cube is rendered using a vertex buffer and index buffer. For each of these resources, a view is created which describes the buffer resources to the rendering pipeline.The depth buffer is stored in the m_DepthBuffer member variable. Similar to the render target views for the swap chain, the depth buffer requires a depth-stencil view. The depth-stencil view is created in a descriptor heap.

The root signature describes the parameters passed to the various stages of the rendering pipeline and the pipeline state object describes the rendering pipeline itself.The viewport and scissor rect variables are used to initialize the rasterizer stage of the rendering pipeline.The m_FoV variable is used to store the current field of view of the camera. For this demo, the middle mouse wheel is used to “zoom-in” to the cube in the center of the scene.

The modelview and projection matrices are used to compute the MVP matrix that is used in the vertex shader to rotate the cube, position the camera, and project the vertices into clip-space.The m_ContentLoaded is used to indicate when the game content has been loaded.

Tutorial2 Preamble

首先包含头文件

#include <Tutorial2.h>
#include <Application.h>
#include <CommandQueue.h>
#include <Helpers.h>
#include <Window.h>
#include <wrl.h>
using namespace Microsoft::WRL;
#include <d3dx12.h>
#include <d3dcompiler.h>
#include <algorithm> // For std::min and std::max.
#if defined(min)
#undef min
#endif
#if defined(max)
#undef max
#endif
using namespace DirectX;

The CommandQueue.h header file contains the definition of the CommandQueue class that was described in detail in the section titled Command Queue.The Helpers.h header file contains helper functions and macros that are used throughout the demo source code.The Window.h header file contains the Window class definition (not to be mistaken with the Windows.h WIN32 API header file).The wrl.h header file contains the Windows Runtime C++ Template Library. It is required for the ComPtr template class.The d3dx12.h header file included on line 11 is not considered to be part of the standard DirectX 12 API and must be downloaded separately from the Microsoft GitHub page (https://github.com/Microsoft/DirectX-Graphics-Samples/blob/master/Libraries/D3DX12/d3dx12.h).The d3dcompiler.h header file contains functions that are required to load (precompiled) shaders from disc.When using runtime compiled HLSL shaders using any of the D3DCompiler functions, do not forget to link against the D3Dcompiler_47.lib library and copy the D3dcompiler_47.dll 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/

In order to avoid polluting the global namespace, all of the DirectX Math types are declared in the DirectX namespace. To avoid having to type “DirectX::” for all DirectX math types, the DirectX namespace is imported into the current source file on line 23.Avoid importing namespaces in header files. Importing namespaces in your own C++ source files is okay because then you are only polluting your own compilation units. If you import namespaces in your header files, you are polluting everybody’s (who is dependent on your header files) compilation units.

Starting with the C++17 standard, the algorithm header file also contains the clamp function. Until C++17 standard is fully implemented in Visual Studio, an implementation of the clamp function will need to be provided manually 😭.

// Clamp a value between a min and max range.
template<typename T>
constexpr const T& clamp(const T& val, const T& min, const T& max)
{
    return val < min ? min : val > max ? max : val;
}
// Vertex data for a colored cube.
struct VertexPosColor
{
    XMFLOAT3 Position;
    XMFLOAT3 Color;
};
static VertexPosColor g_Vertices[8] = {
    { XMFLOAT3(-1.0f, -1.0f, -1.0f), XMFLOAT3(0.0f, 0.0f, 0.0f) }, // 0
    { XMFLOAT3(-1.0f,  1.0f, -1.0f), XMFLOAT3(0.0f, 1.0f, 0.0f) }, // 1
    { XMFLOAT3( 1.0f,  1.0f, -1.0f), XMFLOAT3(1.0f, 1.0f, 0.0f) }, // 2
    { XMFLOAT3( 1.0f, -1.0f, -1.0f), XMFLOAT3(1.0f, 0.0f, 0.0f) }, // 3
    { XMFLOAT3(-1.0f, -1.0f,  1.0f), XMFLOAT3(0.0f, 0.0f, 1.0f) }, // 4
    { XMFLOAT3(-1.0f,  1.0f,  1.0f), XMFLOAT3(0.0f, 1.0f, 1.0f) }, // 5
    { XMFLOAT3( 1.0f,  1.0f,  1.0f), XMFLOAT3(1.0f, 1.0f, 1.0f) }, // 6
    { XMFLOAT3( 1.0f, -1.0f,  1.0f), XMFLOAT3(1.0f, 0.0f, 1.0f) }  // 7
};

之前定义的顶点是不能字节绘制几何网格的。而 Input Assembler 可以渲染一组点,支持如下方式,但是还不能生成几何体。

Primitive Topologies

The D3D_PRIMITIVE_TOPOLOGY enumeration specifies how the Input Assembler stage interprets the vertex data that is bound to the rendering pipeline.

  • D3D_PRIMITIVE_TOPOLOGY_POINTLIST: A disconnected list of points.
  • D3D_PRIMITIVE_TOPOLOGY_LINELIST: A list of disconnected lines.
  • D3D_PRIMITIVE_TOPOLOGY_LINESTRIP: A list of connected lines.
  • D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST: A list of disconnected triangles.
  • D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP: A list of connected triangles. Vertex [Math Processing Error]n and vertex [Math Processing Error]n−2,∀n>1 are implicitly connected to form the triangles.
  • D3D_PRIMITIVE_TOPOLOGY_LINELIST_ADJ: A list of disconnected lines. Additional adjacency information is available to the Geometry shader stage.
  • D3D_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ: A list of connected lines. Additional adjacency information is available to the Geometry shader stage.
  • D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ: A list of disconnected triangles. Additional adjacency information is available to the Geometry shader stage.
  •  
  • D3D_PRIMITIVE_TOPOLOGY_TRIANGRIP_ADJ: A list of connected triangles. Additional adjacency information is available to the Geometry shader stage.

 

之前说过,经过Input Assembler后,这些顶点变成了点和线,但是还不能生成网格。只能由三角形生成网格,于是上顶点索引。

static WORD g_Indicies[36] =
{
    0, 1, 2, 0, 2, 3,
    4, 6, 5, 4, 7, 6,
    4, 5, 1, 4, 1, 0,
    3, 2, 6, 3, 6, 7,
    1, 5, 6, 1, 6, 2,
    4, 0, 3, 4, 3, 7
};

如上图所示,在DX12中,三角形分为前向和后向的,也就是front-face和back-face。前向则是正对着摄像机的,后向就算愿你摄像机的。如果一个多边形是后向的,那么就需要告诉Rasterizer阶段去cull。在DX12中,前向的多边形的索引是顺时针的,而后向是逆时针的。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值