最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

【知乎】DirectX 12 簡明入門教程

2023-08-03 10:23 作者:ACFUN-AK  | 我要投稿


【譯】DirectX 12 簡明入門教程

RYHarrison

編程/攝影/翻譯

原文鏈接:https://alain.xyz/blog/raw-directx12
作者:Alain Galvan

本文將介紹如何編寫一個(gè)簡單的 Hello Triangle DirectX 12 應(yīng)用程序。DirectX 12 是最新的底層圖形 API,用來編寫 Windows 應(yīng)用程序,支持光線追蹤、計(jì)算、光柵化等特性。

Github倉庫

下載范例

DirectX 12是微軟專有計(jì)算機(jī)圖形 API 的最新版本,用于 Windows 和 Xbox 平臺(tái)。與 Vulkan、Apple Metal 和 WebGPU 等較新的圖形 API 一樣,DirectX 12旨在讓開發(fā)者可以更接近 GPU 的底層架構(gòu),從而能開發(fā)出不再復(fù)雜、運(yùn)行更快的驅(qū)動(dòng)程序。這意味著應(yīng)用程序開發(fā)者能直接創(chuàng)建管線狀態(tài),指定使用隊(duì)列執(zhí)行哪些命令,并能選擇使用多線程來構(gòu)建這些數(shù)據(jù)結(jié)構(gòu)。

DirectX 專注于實(shí)時(shí)渲染,因此專為游戲開發(fā)人員計(jì)算機(jī)輔助設(shè)計(jì)(CAD)軟件工程師而設(shè)計(jì)。作為計(jì)算機(jī)圖形 API 的行業(yè)標(biāo)準(zhǔn),幾乎所有兼容的硬件都能對(duì) DirectX 提供強(qiáng)大支持,使它成為商業(yè)項(xiàng)目的標(biāo)準(zhǔn)。

從?Marmoset Toolbag、Adobe PhotoShop、Unreal Engine?等三維或圖像創(chuàng)作軟件,再到像 Activision Blizzard 的?OverWatch?和?Call of Duty、Epic 的?Fortnite?的商業(yè)游戲、以及 Valve 的?Steam?上的絕大多數(shù)游戲等等,DirectX 這個(gè)圖形 API 是最受歡迎并且無處不在,盡管它對(duì)某些平臺(tái)不兼容。

當(dāng)然,這并不是說 DirectX 在 3D 渲染之外的領(lǐng)域沒有用途。Google Chrome?使用 DirectX 渲染 HTML 和 SVG 元素。Tensorflow 采用了?DirectX 12 后端為使用 DirectML 執(zhí)行機(jī)器學(xué)習(xí)提供支持。采用計(jì)算管線(compute pipeline)的 GPGPU 計(jì)算可以執(zhí)行各種工作負(fù)載,包括物理仿真、構(gòu)造 BVH 等等。

DirectX 12 目前支持以下設(shè)備:

  • Windows 7 - 11(Windows 7 可以通過微軟的?D3D12 On 7?部分支持DirectX 12)

  • ?? Xbox One - Xbox Series X/S

雖然 DirectX 12 的官方語言或許是 C 和 C++,但還有不少語言同樣支持 DirectX 12:

  • C

  • C++

  • C#

  • Rust(借助?dx-sys)

我已經(jīng)準(zhǔn)備了一個(gè)Github倉庫,其中包含入門所需的一切內(nèi)容。我將用 C++ 介紹一個(gè) Hello Triangle 應(yīng)用程序,這個(gè)程序使用了光柵圖形管線創(chuàng)建三角形并將它渲染到屏幕上。

安裝

首先安裝:

  • Git

  • CMake

  • 集成開發(fā)環(huán)境(IDE),比如 Visual Studio、XCode,或者是編譯器,比如 GCC

然后在任何終端(如 VS Code 的集成終端)中,輸入以下內(nèi)容。

# ? Clone 代碼倉git clone https://github.com/alaingalvan/directx12-seed --recurse-submodules# ? 定位至文件夾cd directx12-seed# ? 如果你忘記了 `recurse-submodules` ,你始終可以運(yùn)行:git submodule update --init# ? 創(chuàng)建一個(gè) build 文件夾,并將你的項(xiàng)目文件存放在里面:# ?? 在 Windows 上構(gòu)建 Visual Studio 64位版本的解決方案cmake . -B build -A x64# ? 在 Mac OS 上構(gòu)建 XCode 項(xiàng)目cmake . -B build -G Xcode# ? 在 Linux 上構(gòu)建 .make 文件cmake . -B build# ? 適用在任何平臺(tái)上進(jìn)行構(gòu)建cmake --build build

概述

DirectX 12 的開發(fā)文檔建議使用ComPtr<T>來作為std::shared_ptr<T>的替代,這樣做的好處是可以更好地調(diào)試,更容易初始化 DirectX 12 的數(shù)據(jù)結(jié)構(gòu)。

無論你是否選擇使用ComPtr<T>,使用 DirectX 12 渲染光柵圖形的步驟與其他現(xiàn)代圖形 API?非常相似:

  1. 初始化 API:創(chuàng)建IDXGIFactory?、IDXGIAdapter、ID3D12Device、ID3D12CommandQueueID3D12CommandAllocator、ID3D12GraphicsCommandList?。

  2. 設(shè)置幀后臺(tái)緩沖:為后臺(tái)緩沖區(qū)創(chuàng)建IDXGISwapChain、ID3D12DescriptorHeap,為ID3D12Resource后臺(tái)緩沖區(qū)渲染目標(biāo)視圖,創(chuàng)建ID3D12Fence以檢測(cè)幀何時(shí)完成渲染。

  3. 初始化資源:創(chuàng)建三角形數(shù)據(jù),例如ID3D12Resource頂點(diǎn)緩沖區(qū)、ID3D12Resource索引緩沖區(qū)等,創(chuàng)建ID3D12Fence以檢測(cè)上傳到 GPU 內(nèi)存的時(shí)間。加載著色器ID3DBlob、常量緩沖區(qū)ID3D12Resource及其ID3D12DescriptorHeap,描述使用ID3D12RootSignature可以訪問哪些資源,并構(gòu)建ID3D12PipelineState

  4. 編碼指令:ID3D12GraphicsCommandList編寫你希望執(zhí)行的管線指令,確保將ResourceBarrier放在適當(dāng)?shù)奈恢谩?/p>

  5. 渲染:更新 GPU 常量緩沖區(qū)數(shù)據(jù)(Uniforms),使用ExecuteCommandLists將命令提交至ID3D12CommandQueue,Present交換鏈,然后等待下一幀。

  6. 銷毀:使用Release()來銷毀正在使用的數(shù)據(jù)結(jié)構(gòu),或者使用ComPtr<T>對(duì)數(shù)據(jù)結(jié)構(gòu)解除分配。

下面我將對(duì)?Github 倉庫中代碼片段進(jìn)行解釋,省略了某些部分,并且成員變量 (mMemberVariable) 內(nèi)聯(lián)聲明,不帶前綴m,以便你可以更容易地分辨它們的類型,這里給出的示例代碼可以獨(dú)立運(yùn)行。

創(chuàng)建窗口(Window Creation)

我們使用?CrossWindow?(譯者注:原文給出的鏈接失效了,可以從這個(gè)?github倉庫?來訪問)來創(chuàng)建支持跨平臺(tái)的窗口。創(chuàng)建 Win32 窗口并對(duì)其進(jìn)行更新,操作非常簡單:

#include "CrossWindow/CrossWindow.h"#include "Renderer.h"#include <iostream>void xmain(int argc, const char** argv){ ? ?// ?? 創(chuàng)建窗口 ? ?xwin::WindowDesc wdesc; ? ?wdesc.title = "DirectX 12 Seed"; ? ?wdesc.name = "MainWindow"; ? ?wdesc.visible = true; ? ?wdesc.width = 640; ? ?wdesc.height = 640; ? ?wdesc.fullscreen = false; ? ?xwin::Window window; ? ?xwin::EventQueue eventQueue; ? ?if (!window.create(wdesc, eventQueue)) ? ?{ return; }; ? ?// ? 創(chuàng)建一個(gè)渲染器 ? ?Renderer renderer(window); ? ?// ? 引擎循環(huán) ? ?bool isRunning = true; ? ?while (isRunning) ? ? ? ?{ ? ? ? ? ? ?bool shouldRender = true; ? ? ? ? ? ?// ?? 更新事件隊(duì)列 ? ? ? ? ? ?eventQueue.update(); ? ? ? ? ? ?// ? 事件迭代: ? ? ? ? ? ?while (!eventQueue.empty()) ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ?// 更新事件 ? ? ? ? ? ? ? ? ? ?const xwin::Event& event = eventQueue.front(); ? ? ? ? ? ? ? ? ? ?// ? 響應(yīng)窗口改變大小: ? ? ? ? ? ? ? ? ? ?if (event.type == xwin::EventType::Resize) ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ?const xwin::ResizeData data = event.data.resize; ? ? ? ? ? ? ? ? ? ? ? ?renderer.resize(data.width, data.height); ? ? ? ? ? ? ? ? ? ? ? ?shouldRender = false; ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ?// ? 響應(yīng)窗口關(guān)閉: ? ? ? ? ? ? ? ? ? ?if (event.type == xwin::EventType::Close) ? ? ? ? ? ? ? ? ? ?{ ? ? ? ? ? ? ? ? ? ? ? ?window.close(); ? ? ? ? ? ? ? ? ? ? ? ?shouldRender = false; ? ? ? ? ? ? ? ? ? ? ? ?isRunning = false; ? ? ? ? ? ? ? ? ? ?} ? ? ? ? ? ? ? ? ? ?eventQueue.pop(); ? ? ? ? ? ? ? ?} ? ? ? ? ? ?// ? 更新可視化部分 ? ? ? ? ? ?if (shouldRender) ? ? ? ? ? ?{ ? ? ? ? ? ? ? ?renderer.render(); ? ? ? ? ? ?} ? ? ? ?}}

如果你想替代 CrossWindow,可以使用其它庫,比如 GLFW,SFML,SDL,QT,或者直接調(diào)用 Win32 或 UWP API 接口。

初始化 API Initialize API)

工廠(Factory)

工廠是 DirectX 12 API 的入口,它允許你查找那些支持 DirectX 12 命令的適配器(譯者注:可以理解為顯卡)。

你還可以在工廠附近創(chuàng)建一個(gè)調(diào)試控制器(Debug Controller),它可以啟用 API 使用情況驗(yàn)證。這只能在 debug 模式中使用。

  • 參考:IDXGIFactory7

// ? 聲明 DirectX 12 句柄(Handle)IDXGIFactory4* factory;ID3D12Debug1* debugController;// ? 創(chuàng)建 FactoryUINT dxgiFactoryFlags = 0;#if defined(_DEBUG)// ? 創(chuàng)建一個(gè) Debug Controller 來追蹤錯(cuò)誤ID3D12Debug* dc;ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&dc)));ThrowIfFailed(dc->QueryInterface(IID_PPV_ARGS(&debugController)));debugController->EnableDebugLayer();debugController->SetEnableGPUBasedValidation(true);dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG;dc->Release();dc = nullptr;#endifHRESULT result = CreateDXGIFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory));

適配器(Adapter)

Adapter 提供了有關(guān) DirectX 設(shè)備物理屬性的信息。你可以查詢當(dāng)前 GPU 的名稱、制造商、顯存容量以及其它更多信息。

有兩種類型的適配器:軟件適配器和硬件適配器。微軟 Windows 系統(tǒng)總是會(huì)包含一個(gè)基于軟件層面的DirectX 實(shí)現(xiàn),讓你可以在沒有專用硬件(如獨(dú)立或集成 GPU )的情況下使用 API 。

  • 參考:IDXGIAdapter4

// ? 聲明句柄IDXGIAdapter1* adapter;// ? 創(chuàng)建 Adapterfor (UINT adapterIndex = 0; ? ? DXGI_ERROR_NOT_FOUND != factory->EnumAdapters1(adapterIndex, &adapter); ? ? ++adapterIndex){ ? ?DXGI_ADAPTER_DESC1 desc; ? ?adapter->GetDesc1(&desc); ? ?// ? 不要選用 Basic Render Driver adapter. ? ?if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) ? ?{ ? ? ? ?continue; ? ?} ? ?// ?? 檢查適配器是否支持 Direct3D 12, 如果支持就選擇它來供程序運(yùn)行并返回 ? ?if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?_uuidof(ID3D12Device), nullptr))) ? ?{ ? ? ? ?break; ? ?} ? ?// ? 否則的話,不再使用這個(gè)迭代器 adapter,所以要進(jìn)行釋放 ? ?adapter->Release();}

設(shè)備(Device)

設(shè)備是 DirectX 12 API 的主要入口點(diǎn),可用來訪問 API 的內(nèi)部。這是訪問重要數(shù)據(jù)結(jié)構(gòu)和函數(shù)(如管線、著色器 blob、渲染狀態(tài)、資源屏障等)的關(guān)鍵。

  • 參考:IDXGIDevice4

// ? 聲明句柄ID3D12Device* device;// ? 創(chuàng)建 DeviceThrowIfFailed(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_12_0, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?IID_PPV_ARGS(&device)));

調(diào)試 DirectX 創(chuàng)建的數(shù)據(jù)結(jié)構(gòu)可能非常困難。而一個(gè) Debug Device 對(duì)象允許你使用 DirectX 12 的調(diào)試模式。這樣,你將能夠防止數(shù)據(jù)泄露,或者能驗(yàn)證程序是否正確創(chuàng)建或使用 API。

  • 參考:ID3D12DebugDevice

// ? 聲明句柄ID3D12DebugDevice* debugDevice;#if defined(_DEBUG)// ? 獲取 debug deviceThrowIfFailed(device->QueryInterface(&debugDevice));#endif

命令隊(duì)列(Command Queue)

命令隊(duì)列讓你可以一次性提交多組 draw call(稱為命令列表 command lists)來按順序來執(zhí)行命令,從而讓 GPU 保持充分工作并優(yōu)化執(zhí)行速度。

  • 參考:ID3D12CommandQueue

// ? 聲明句柄ID3D12CommandQueue* commandQueue;// ? 創(chuàng)建 Command QueueD3D12_COMMAND_QUEUE_DESC queueDesc = {};queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;ThrowIfFailed(device->CreateCommandQueue(&queueDesc, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IID_PPV_ARGS(&commandQueue)));

命令分配器(Command Allocator)

命令分配器讓你可以創(chuàng)建命令列表,你可以在其中定義希望 GPU 執(zhí)行的功能。

  • 參考:ID3D12CommandAllocator

// ? 聲明句柄ID3D12CommandAllocator* commandAllocator;// ? 創(chuàng)建命令分配器ThrowIfFailed(device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IID_PPV_ARGS(&commandAllocator)));

同步(Synchronization)

DirectX 12 具有大量同步圖元(synchronization primitives),可以告知驅(qū)動(dòng)程序如何使用資源、GPU 何時(shí)完成任務(wù)等。

圍欄(Fence)能讓程序知道 GPU 在什么時(shí)候執(zhí)行了哪些特定任務(wù),無論是上傳了哪些資源到 GPU 專用內(nèi)存,還是程序什么時(shí)候完成向屏幕的提交(present),都能獲取到這些信息。

  • 參考:ID3D12Fence1

// ? 聲明句柄UINT frameIndex;HANDLE fenceEvent;ID3D12Fence* fence;UINT64 fenceValue;// ? 創(chuàng)建 fenceThrowIfFailed(device->CreateFence(0, D3D12_FENCE_FLAG_NONE, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?IID_PPV_ARGS(&fence)));

屏障(Barrier)能讓驅(qū)動(dòng)程序知道如何在即將提交的命令中使用資源。比如說,程序正在寫入紋理,并且想要將這個(gè)紋理復(fù)制到另一個(gè)紋理(例如交換鏈的渲染附件),這會(huì)很有用。

// ? 聲明句柄ID3D12GraphicsCommandList* commandList;// ? 創(chuàng)建 BarrierD3D12_RESOURCE_BARRIER barrier = {};barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;barrier.Transition.pResource = texResource;barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_UNORDERED_ACCESS;barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;commandList->ResourceBarrier(1, &barrier);

交換鏈(Swap Chain)

交換鏈能處理交換和分配后臺(tái)緩沖區(qū),用來顯示正在渲染到給定窗口的內(nèi)容。

// ? 聲明數(shù)據(jù)變量unsigned width = 640;unsigned height = 640;// ? 聲明句柄static const UINT backbufferCount = 2;UINT currentBuffer;ID3D12DescriptorHeap* renderTargetViewHeap;ID3D12Resource* renderTargets[backbufferCount];UINT rtvDescriptorSize;// ?? 聲明交換鏈IDXGISwapChain3* swapchain;D3D12_VIEWPORT viewport;D3D12_RECT surfaceSize;surfaceSize.left = 0;surfaceSize.top = 0;surfaceSize.right = static_cast<LONG>(width);surfaceSize.bottom = static_cast<LONG>(height);viewport.TopLeftX = 0.0f;viewport.TopLeftY = 0.0f;viewport.Width = static_cast<float>(width);viewport.Height = static_cast<float>(height);viewport.MinDepth = .1f;viewport.MaxDepth = 1000.f;if (swapchain != nullptr){ ? ?// 通過交換鏈創(chuàng)建渲染目標(biāo)附件(Render Target Attachments) ? ?swapchain->ResizeBuffers(backbufferCount, width, height, ? ? ? ? ? ? ? ? ? ? ? ? ? ? DXGI_FORMAT_R8G8B8A8_UNORM, 0);}else{ ? ?// ?? 創(chuàng)建交換鏈 ? ?DXGI_SWAP_CHAIN_DESC1 swapchainDesc = {}; ? ?swapchainDesc.BufferCount = backbufferCount; ? ?swapchainDesc.Width = width; ? ?swapchainDesc.Height = height; ? ?swapchainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; ? ?swapchainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; ? ?swapchainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; ? ?swapchainDesc.SampleDesc.Count = 1; ? ?IDXGISwapChain1* newSwapchain = ? ? ? ?xgfx::createSwapchain(window, factory, commandQueue, &swapchainDesc); ? ?HRESULT swapchainSupport = swapchain->QueryInterface( ? ? ? ?__uuidof(IDXGISwapChain3), (void**)&newSwapchain); ? ?if (SUCCEEDED(swapchainSupport)) ? ?{ ? ? ? ?swapchain = (IDXGISwapChain3*)newSwapchain; ? ?}}frameIndex = swapchain->GetCurrentBackBufferIndex();// 描述并創(chuàng)建渲染目標(biāo)視圖(render target view, RTV) 描述符堆D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};rtvHeapDesc.NumDescriptors = backbufferCount;rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;ThrowIfFailed(device->CreateDescriptorHeap( ? ?&rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap)));rtvDescriptorSize = ? ?device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);// ?? 創(chuàng)建幀資源D3D12_CPU_DESCRIPTOR_HANDLE ? ?rtvHandle(renderTargetViewHeap->GetCPUDescriptorHandleForHeapStart());// 為每幀創(chuàng)建RTVfor (UINT n = 0; n < backbufferCount; n++){ ? ?ThrowIfFailed(swapchain->GetBuffer(n, IID_PPV_ARGS(&renderTargets[n]))); ? ?device->CreateRenderTargetView(renderTargets[n], nullptr, rtvHandle); ? ?rtvHandle.ptr += (1 * rtvDescriptorSize);}

初始化資源(Initialize Resources)

描述符堆(Descriptor Heaps)

描述符堆是用來處理內(nèi)存分配的,這些內(nèi)存存儲(chǔ)著色器引用的對(duì)象描述。

  • 參考:ID3D12DescriptorHeap

ID3D12DescriptorHeap* renderTargetViewHeap;D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};rtvHeapDesc.NumDescriptors = backbufferCount;rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;ThrowIfFailed(device->CreateDescriptorHeap( ? ?&rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap)));

根簽名(Root Signature)

根簽名定義了著色器可以訪問資源類型的對(duì)象,包括常量緩沖區(qū)、結(jié)構(gòu)化緩沖區(qū)、采樣器、紋理、結(jié)構(gòu)化緩沖區(qū),等等。

  • 參考:ID3D12RootSignature

// ? 聲明句柄ID3D12RootSignature* rootSignature;// ? 判斷是否能得到1.1版本的根簽名: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;}// ? 私有的GPU資源D3D12_DESCRIPTOR_RANGE1 ranges[1];ranges[0].BaseShaderRegister = 0;ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;ranges[0].NumDescriptors = 1;ranges[0].RegisterSpace = 0;ranges[0].OffsetInDescriptorsFromTableStart = 0;ranges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;// ? GPU資源組D3D12_ROOT_PARAMETER1 rootParameters[1];rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;rootParameters[0].DescriptorTable.NumDescriptorRanges = 1;rootParameters[0].DescriptorTable.pDescriptorRanges = ranges;// ? ?所有布局D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;rootSignatureDesc.Desc_1_1.Flags = ? ?D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;rootSignatureDesc.Desc_1_1.NumParameters = 1;rootSignatureDesc.Desc_1_1.pParameters = rootParameters;rootSignatureDesc.Desc_1_1.NumStaticSamplers = 0;rootSignatureDesc.Desc_1_1.pStaticSamplers = nullptr;ID3DBlob* signature;ID3DBlob* error;try{ ? ?// ? 創(chuàng)建根簽名 ? ?ThrowIfFailed(D3D12SerializeVersionedRootSignature(&rootSignatureDesc, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &signature, &error)); ? ?ThrowIfFailed(device->CreateRootSignature(0, signature->GetBufferPointer(), ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?signature->GetBufferSize(), ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?IID_PPV_ARGS(&rootSignature))); ? ?rootSignature->SetName(L"Hello Triangle Root Signature");}catch (std::exception e){ ? ?const char* errStr = (const char*)error->GetBufferPointer(); ? ?std::cout << errStr; ? ?error->Release(); ? ?error = nullptr;}if (signature){ ? ?signature->Release(); ? ?signature = nullptr;}

雖然程序運(yùn)行起來沒有問題,但如果使用無綁定資源(bindless resources),那么開發(fā)起來要容易得多,Matt Pettineo(@MyNameIsMJP)在《Ray Tracing Gems II》中寫過關(guān)于這方面的內(nèi)容。

堆(Heaps)

堆是 GPU 顯存中的對(duì)象。你可以使用堆將頂點(diǎn)緩沖區(qū)或紋理等資源上傳到 GPU 顯存中。

  • 參考:ID3D12Resource

// ? 上傳:// ? 聲明句柄ID3D12Resource* uploadBuffer;std::vector<unsigned char> sourceData;D3D12_HEAP_PROPERTIES uploadHeapProps = {D3D12_HEAP_TYPE_UPLOAD, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? D3D12_CPU_PAGE_PROPERTY_UNKNOWN, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? D3D12_MEMORY_POOL_UNKNOWN, 1u, 1u};D3D12_RESOURCE_DESC uploadBufferDesc = {D3D12_RESOURCE_DIMENSION_BUFFER, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?65536ull, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?65536ull, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1u, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?1, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DXGI_FORMAT_UNKNOWN, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?{1u, 0u}, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?D3D12_TEXTURE_LAYOUT_ROW_MAJOR, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?D3D12_RESOURCE_FLAG_NONE};result = device->CreateCommittedResource( ? ?&uploadHeapProps, D3D12_HEAP_FLAG_NONE, &uploadBufferDesc, ? ?D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, __uuidof(ID3D12Resource), ? ?((void**)&uploadBuffer));uint8_t* data = nullptr;D3D12_RANGE range{0, SIZE_T(chunkSize)};auto hr = spStaging -> Map(0, &range, reinterpret_cast<void**>(&data));if (FAILED(hr)){ ? ?std::cout << "Could not map resource";}// 拷貝數(shù)據(jù)if (resourceDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER){ ? ?memcpy(data, sourceData.data(), sourceData.size());}// ? 回讀:// ? 聲明句柄ID3D12Resource* readbackBuffer;D3D12_HEAP_PROPERTIES heapPropsRead = {D3D12_HEAP_TYPE_READBACK, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? D3D12_CPU_PAGE_PROPERTY_UNKNOWN, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? D3D12_MEMORY_POOL_UNKNOWN, 1u, 1u};D3D12_RESOURCE_DESC resourceDescDimBuffer = { ? ?D3D12_RESOURCE_DIMENSION_BUFFER, ? ?D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT, ? ?2725888ull, ? ?1u, ? ?1, ? ?1, ? ?DXGI_FORMAT_UNKNOWN, ? ?{1u, 0u}, ? ?D3D12_TEXTURE_LAYOUT_ROW_MAJOR, ? ?D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE};result = device->CreateCommittedResource( ? ?&heapPropsRead, D3D12_HEAP_FLAG_NONE, &resourceDescDimBuffer, ? ?D3D12_RESOURCE_STATE_COPY_DEST, nullptr, __uuidof(ID3D12Resource), ? ?((void**)&readbackBuffer));

通過創(chuàng)建自己的堆,你可以更加精細(xì)地管理內(nèi)存。不過,可管理的堆可能很少,因此你可以改用內(nèi)存分配庫。

頂點(diǎn)緩沖區(qū)(Vertex Buffer)

頂點(diǎn)緩沖區(qū)將每個(gè)頂點(diǎn)信息作為屬性存儲(chǔ)在頂點(diǎn)著色器中。所有緩沖區(qū)都是 DirectX 12 中的ID3D12Resource對(duì)象,無論是頂點(diǎn)緩沖區(qū)、索引緩沖區(qū)、常量緩沖區(qū)等。

  • 參考:D3D12_VERTEX_BUFFER_VIEW

// ? 聲明數(shù)據(jù)結(jié)構(gòu)struct Vertex{ ? ?float position[3]; ? ?float color[3];};Vertex vertexBufferData[3] = {{{1.0f, -1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?{{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}}, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?{{0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}};// ? 聲明句柄ID3D12Resource* vertexBuffer;D3D12_VERTEX_BUFFER_VIEW vertexBufferView;const UINT vertexBufferSize = sizeof(vertexBufferData);D3D12_HEAP_PROPERTIES heapProps;heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;heapProps.CreationNodeMask = 1;heapProps.VisibleNodeMask = 1;D3D12_RESOURCE_DESC vertexBufferResourceDesc;vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;vertexBufferResourceDesc.Alignment = 0;vertexBufferResourceDesc.Width = vertexBufferSize;vertexBufferResourceDesc.Height = 1;vertexBufferResourceDesc.DepthOrArraySize = 1;vertexBufferResourceDesc.MipLevels = 1;vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;vertexBufferResourceDesc.SampleDesc.Count = 1;vertexBufferResourceDesc.SampleDesc.Quality = 0;vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;ThrowIfFailed(device->CreateCommittedResource( ? ?&heapProps, D3D12_HEAP_FLAG_NONE, &vertexBufferResourceDesc, ? ?D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&vertexBuffer)));// ? 向頂點(diǎn)緩沖區(qū)拷貝三角形數(shù)據(jù)UINT8* pVertexDataBegin;// ? 不會(huì)在 CPU 中讀取這些資源D3D12_RANGE readRange;readRange.Begin = 0;readRange.End = 0;ThrowIfFailed(vertexBuffer->Map(0, &readRange, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?reinterpret_cast<void**>(&pVertexDataBegin)));memcpy(pVertexDataBegin, vertexBufferData, sizeof(vertexBufferData));vertexBuffer->Unmap(0, nullptr);// ? 初始化頂點(diǎn)緩沖視圖.vertexBufferView.BufferLocation = vertexBuffer->GetGPUVirtualAddress();vertexBufferView.StrideInBytes = sizeof(Vertex);vertexBufferView.SizeInBytes = vertexBufferSize;

索引緩沖區(qū)(Index Buffer)

索引緩沖區(qū)包含要繪制的每個(gè)三角形、線、點(diǎn)的各個(gè)索引。

  • 參考:D3D12_INDEX_BUFFER_VIEW

// ? 聲明數(shù)組uint32_t indexBufferData[3] = {0, 1, 2};// ? 聲明句柄ID3D12Resource* indexBuffer;D3D12_INDEX_BUFFER_VIEW indexBufferView;const UINT indexBufferSize = sizeof(indexBufferData);D3D12_HEAP_PROPERTIES heapProps;heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;heapProps.CreationNodeMask = 1;heapProps.VisibleNodeMask = 1;D3D12_RESOURCE_DESC vertexBufferResourceDesc;vertexBufferResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;vertexBufferResourceDesc.Alignment = 0;vertexBufferResourceDesc.Width = indexBufferSize;vertexBufferResourceDesc.Height = 1;vertexBufferResourceDesc.DepthOrArraySize = 1;vertexBufferResourceDesc.MipLevels = 1;vertexBufferResourceDesc.Format = DXGI_FORMAT_UNKNOWN;vertexBufferResourceDesc.SampleDesc.Count = 1;vertexBufferResourceDesc.SampleDesc.Quality = 0;vertexBufferResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;vertexBufferResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;ThrowIfFailed(device->CreateCommittedResource( ? ?&heapProps, D3D12_HEAP_FLAG_NONE, &vertexBufferResourceDesc, ? ?D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&indexBuffer)));// ? 向 DirectX 12 顯存拷貝數(shù)據(jù):UINT8* pVertexDataBegin;// ? 不會(huì)在 CPU 中讀取這些資源D3D12_RANGE readRange;readRange.Begin = 0;readRange.End = 0;ThrowIfFailed(indexBuffer->Map(0, &readRange, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? reinterpret_cast<void**>(&pVertexDataBegin)));memcpy(pVertexDataBegin, indexBufferData, sizeof(indexBufferData));indexBuffer->Unmap(0, nullptr);// ? 初始化索引緩沖區(qū)視圖indexBufferView.BufferLocation = indexBuffer->GetGPUVirtualAddress();indexBufferView.Format = DXGI_FORMAT_R32_UINT;indexBufferView.SizeInBytes = indexBufferSize;

常量緩沖區(qū)(Constant Buffer)

常量緩沖區(qū)描述了在程序繪制時(shí)將發(fā)送到著色器階段的數(shù)據(jù)。通常,你在這里存放著模型視圖投影矩陣或任何特定的變量數(shù)據(jù),如顏色。

// ? 聲明數(shù)據(jù)結(jié)構(gòu)struct{ ? ?glm::mat4 projectionMatrix; ? ?glm::mat4 modelMatrix; ? ?glm::mat4 viewMatrix;} cbVS;// ? 聲明句柄ID3D12Resource* constantBuffer;ID3D12DescriptorHeap* constantBufferHeap;UINT8* mappedConstantBuffer;// ? 創(chuàng)建常量緩沖區(qū)D3D12_HEAP_PROPERTIES heapProps;heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;heapProps.CreationNodeMask = 1;heapProps.VisibleNodeMask = 1;D3D12_DESCRIPTOR_HEAP_DESC heapDesc = {};heapDesc.NumDescriptors = 1;heapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;heapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;ThrowIfFailed(device->CreateDescriptorHeap(&heapDesc, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IID_PPV_ARGS(&constantBufferHeap)));D3D12_RESOURCE_DESC cbResourceDesc;cbResourceDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;cbResourceDesc.Alignment = 0;cbResourceDesc.Width = (sizeof(cbVS) + 255) & ~255;cbResourceDesc.Height = 1;cbResourceDesc.DepthOrArraySize = 1;cbResourceDesc.MipLevels = 1;cbResourceDesc.Format = DXGI_FORMAT_UNKNOWN;cbResourceDesc.SampleDesc.Count = 1;cbResourceDesc.SampleDesc.Quality = 0;cbResourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;cbResourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE;ThrowIfFailed(device->CreateCommittedResource( ? ?&heapProps, D3D12_HEAP_FLAG_NONE, &cbResourceDesc, ? ?D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&constantBuffer)));constantBufferHeap->SetName(L"Constant Buffer Upload Resource Heap");// ? 創(chuàng)建常量緩沖區(qū)視圖(CBV)D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};cbvDesc.BufferLocation = constantBuffer->GetGPUVirtualAddress();cbvDesc.SizeInBytes = ? ?(sizeof(cbVS) + 255) & ~255; // CB size is required to be 256-byte aligned.D3D12_CPU_DESCRIPTOR_HANDLE ? ?cbvHandle(constantBufferHeap->GetCPUDescriptorHandleForHeapStart());cbvHandle.ptr = cbvHandle.ptr + device->GetDescriptorHandleIncrementSize( ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) * ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?0;device->CreateConstantBufferView(&cbvDesc, cbvHandle);// ? 不會(huì)在 CPU 中讀取這些資源D3D12_RANGE readRange;readRange.Begin = 0;readRange.End = 0;ThrowIfFailed(constantBuffer->Map( ? ?0, &readRange, reinterpret_cast<void**>(&mappedConstantBuffer)));memcpy(mappedConstantBuffer, &cbVS, sizeof(cbVS));constantBuffer->Unmap(0, &readRange);

頂點(diǎn)著色器(Vertex Shader)

頂點(diǎn)著色器是逐頂點(diǎn)執(zhí)行的,它非常適合對(duì)給定對(duì)象進(jìn)行轉(zhuǎn)換處理,比如根據(jù)混合形狀執(zhí)行每個(gè)頂點(diǎn)動(dòng)畫、GPU 蒙皮,等等。

cbuffer cb : register(b0){ ? ?row_major float4x4 projectionMatrix : packoffset(c0); ? ?row_major float4x4 modelMatrix : packoffset(c4); ? ?row_major float4x4 viewMatrix : packoffset(c8);};struct VertexInput{ ? ?float3 inPos : POSITION; ? ?float3 inColor : COLOR;};struct VertexOutput{ ? ?float3 color : COLOR; ? ?float4 position : SV_Position;};VertexOutput main(VertexInput vertexInput){ ? ?float3 inColor = vertexInput.inColor; ? ?float3 inPos = vertexInput.inPos; ? ?float3 outColor = inColor; ? ?float4 position = mul(float4(inPos, 1.0f), mul(modelMatrix, mul(viewMatrix, projectionMatrix))); ? ?VertexOutput output; ? ?output.position = position; ? ?output.color = outColor; ? ?return output;}

你可以使用傳統(tǒng)的 DirectX 著色器編譯器(已包含在 DirectX 11/12 API 中)來編譯 shader ,但最好使用較新的官方編譯器。

dxc.exe -T lib_6_3 -Fo assets/triangle.vert.dxil assets/triangle.vert.hlsl

然后,你就能以為二進(jìn)制文件形式加載著色器:

inline std::vector<char> readFile(const std::string& filename){ ? ?std::ifstream file(filename, std::ios::ate | std::ios::binary); ? ?bool exists = (bool)file; ? ?if (!exists || !file.is_open()) ? ?{ ? ? ? ?throw std::runtime_error("failed to open file!"); ? ?} ? ?size_t fileSize = (size_t)file.tellg(); ? ?std::vector<char> buffer(fileSize); ? ?file.seekg(0); ? ?file.read(buffer.data(), fileSize); ? ?file.close(); ? ?return buffer;};// ? 聲明句柄D3D12_SHADER_BYTECODE vsBytecode;std::string compiledPath;std::vector<char> vsBytecodeData = readFile(compCompiledPath);vsBytecode.pShaderBytecode = vsBytecodeData.data();vsBytecode.BytecodeLength = vsBytecodeData.size();

像素著色器(Pixel Shader)

像素著色器是按輸出的每個(gè)像素來執(zhí)行的,包括與這個(gè)像素坐標(biāo)對(duì)應(yīng)的其他附件。

struct PixelInput{ ? ?float3 color : COLOR;};struct PixelOutput{ ? ?float4 attachment0 : SV_Target0;};PixelOutput main(PixelInput pixelInput){ ? ?float3 inColor = pixelInput.color; ? ?PixelOutput output; ? ?output.attachment0 = float4(inColor, 1.0f); ? ?return output;}

管線狀態(tài)(Pipeline State)

管線狀態(tài)描述了執(zhí)行給定的光柵繪制指令所用到的全部內(nèi)容。

  • 參考:ID3D12GraphicsCommandList5

// ? 聲明句柄ID3D12PipelineState* pipelineState;// ?? 定義圖形管線D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};// ? 輸入裝配布局D3D12_INPUT_ELEMENT_DESC inputElementDescs[] = { ? ?{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, ? ? D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}, ? ?{"COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, ? ? D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};psoDesc.InputLayout = {inputElementDescs, _countof(inputElementDescs)};// ? 資源psoDesc.pRootSignature = rootSignature;// ? 頂點(diǎn)著色器D3D12_SHADER_BYTECODE vsBytecode;vsBytecode.pShaderBytecode = vertexShaderBlob->GetBufferPointer();vsBytecode.BytecodeLength = vertexShaderBlob->GetBufferSize();psoDesc.VS = vsBytecode;// ?? 像素著色器D3D12_SHADER_BYTECODE psBytecode;psBytecode.pShaderBytecode = pixelShaderBlob->GetBufferPointer();psBytecode.BytecodeLength = pixelShaderBlob->GetBufferSize();psoDesc.PS = psBytecode;// ? 光柵化D3D12_RASTERIZER_DESC rasterDesc;rasterDesc.FillMode = D3D12_FILL_MODE_SOLID;rasterDesc.CullMode = D3D12_CULL_MODE_NONE;rasterDesc.FrontCounterClockwise = FALSE;rasterDesc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;rasterDesc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;rasterDesc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;rasterDesc.DepthClipEnable = TRUE;rasterDesc.MultisampleEnable = FALSE;rasterDesc.AntialiasedLineEnable = FALSE;rasterDesc.ForcedSampleCount = 0;rasterDesc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;psoDesc.RasterizerState = rasterDesc;psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;// ? 顏色/混合D3D12_BLEND_DESC blendDesc;blendDesc.AlphaToCoverageEnable = FALSE;blendDesc.IndependentBlendEnable = FALSE;const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = { ? ?FALSE, ? ?FALSE, ? ?D3D12_BLEND_ONE, ? ?D3D12_BLEND_ZERO, ? ?D3D12_BLEND_OP_ADD, ? ?D3D12_BLEND_ONE, ? ?D3D12_BLEND_ZERO, ? ?D3D12_BLEND_OP_ADD, ? ?D3D12_LOGIC_OP_NOOP, ? ?D3D12_COLOR_WRITE_ENABLE_ALL,};for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) ? ?blendDesc.RenderTarget[i] = defaultRenderTargetBlendDesc;psoDesc.BlendState = blendDesc;// ? 深度/緩沖狀態(tài)psoDesc.DepthStencilState.DepthEnable = FALSE;psoDesc.DepthStencilState.StencilEnable = FALSE;psoDesc.SampleMask = UINT_MAX;// ?? 輸出psoDesc.NumRenderTargets = 1;psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;psoDesc.SampleDesc.Count = 1;// ? 創(chuàng)建光柵管線狀態(tài)try{ ? ?ThrowIfFailed(device->CreateGraphicsPipelineState( ? ? ? ?&psoDesc, IID_PPV_ARGS(&pipelineState)));}catch (std::exception e){ ? ?std::cout << "Failed to create Graphics Pipeline!";}

編碼指令(Encoding Commands)

為了執(zhí)行 draw call,你需要一個(gè)編寫命令的地方。命令列表(Command List)可以對(duì) GPU 要執(zhí)行的許多命令進(jìn)行編碼,包括設(shè)置屏障(barrier)、設(shè)置根簽名等等。

  • 參考:ID3D12GraphicsCommandList5

// ? 聲明句柄ID3D12CommandAllocator* commandAllocator;ID3D12PipelineState* initialPipelineState;ID3D12GraphicsCommandList* commandList;// ? 創(chuàng)建命令列表ThrowIfFailed(device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?commandAllocator, initialPipelineState, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?IID_PPV_ARGS(&commandList)));

然后,對(duì)這些命令進(jìn)行編碼并提交:

// ? 重置命令列表并添加新的命令ThrowIfFailed(commandAllocator->Reset());// ?? 開始調(diào)用光柵圖形管線ThrowIfFailed(commandList->Reset(commandAllocator, pipelineState));// ? 設(shè)置資源commandList->SetGraphicsRootSignature(rootSignature);ID3D12DescriptorHeap* pDescriptorHeaps[] = {constantBufferHeap};commandList->SetDescriptorHeaps(_countof(pDescriptorHeaps), pDescriptorHeaps);D3D12_GPU_DESCRIPTOR_HANDLE ? ?cbvHandle(constantBufferHeap->GetGPUDescriptorHandleForHeapStart());commandList->SetGraphicsRootDescriptorTable(0, cbvHandle);// ?? 指派back buffer,用作render targetD3D12_RESOURCE_BARRIER renderTargetBarrier;renderTargetBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;renderTargetBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;renderTargetBarrier.Transition.pResource = renderTargets[frameIndex];renderTargetBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;renderTargetBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;renderTargetBarrier.Transition.Subresource = ? ?D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;commandList->ResourceBarrier(1, &renderTargetBarrier);D3D12_CPU_DESCRIPTOR_HANDLE ? ?rtvHandle(rtvHeap->GetCPUDescriptorHandleForHeapStart());rtvHandle.ptr = rtvHandle.ptr + (frameIndex * rtvDescriptorSize);commandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);// ? 記錄光柵命令.const float clearColor[] = {0.2f, 0.2f, 0.2f, 1.0f};commandList->RSSetViewports(1, &viewport);commandList->RSSetScissorRects(1, &surfaceSize);commandList->ClearRenderTargetView(rtvHandle, clearColor, 0, nullptr);commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);commandList->IASetVertexBuffers(0, 1, &vertexBufferView);commandList->IASetIndexBuffer(&indexBufferView);commandList->DrawIndexedInstanced(3, 1, 0, 0, 0);// ?? 指派back buffer 隨即提交(present)D3D12_RESOURCE_BARRIER presentBarrier;presentBarrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;presentBarrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;presentBarrier.Transition.pResource = renderTargets[frameIndex];presentBarrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;presentBarrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;presentBarrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;commandList->ResourceBarrier(1, &presentBarrier);ThrowIfFailed(commandList->Close());

渲染(Render)

在 DirectX 12 中渲染非常簡單:你需要在刷新時(shí)改動(dòng)常量緩沖區(qū)數(shù)據(jù)、提交要執(zhí)行的命令列表、提交交換鏈來更新 Win32 或 UWP 窗口,同時(shí)向程序發(fā)出已完成提交的信號(hào)。

// ? 聲明句柄std::chrono::time_point<std::chrono::steady_clock> tStart, tEnd;float elapsedTime = 0.0f;void render(){ ? ?// ? 將幀率鎖定至60fps ? ?tEnd = std::chrono::high_resolution_clock::now(); ? ?float time = ? ? ? ?std::chrono::duration<float, std::milli>(tEnd - tStart).count(); ? ?if (time < (1000.0f / 60.0f)) ? ?{ ? ? ? ?return; ? ?} ? ?tStart = std::chrono::high_resolution_clock::now(); ? ?// ?? 更新 Uniforms ? ?elapsedTime += 0.001f * time; ? ?elapsedTime = fmodf(elapsedTime, 6.283185307179586f); ? ?cbVS.modelMatrix = Matrix4::rotationY(elapsedTime); ? ?D3D12_RANGE readRange; ? ?readRange.Begin = 0; ? ?readRange.End = 0; ? ?ThrowIfFailed(constantBuffer->Map( ? ? ? ?0, &readRange, reinterpret_cast<void**>(&mappedConstantBuffer))); ? ?memcpy(mappedConstantBuffer, &cbVS, sizeof(cbVS)); ? ?constantBuffer->Unmap(0, &readRange); ? ?setupCommands(); ? ?ID3D12CommandList* ppCommandLists[] = {commandList}; ? ?commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); ? ?// ? 提交,然后等待GPU執(zhí)行 ? ?swapchain->Present(1, 0); ? ?const UINT64 fence = fenceValue; ? ?ThrowIfFailed(commandQueue->Signal(fence, fence)); ? ?fenceValue++; ? ?if (fence->GetCompletedValue() < fence) ? ?{ ? ? ? ?ThrowIfFailed(fence->SetEventOnCompletion(fence, fenceEvent)); ? ? ? ?WaitForSingleObject(fenceEvent, INFINITE); ? ?} ? ?frameIndex = swapchain->GetCurrentBackBufferIndex();}

銷毀句柄(Destroy Handles)

如果你使用的是ComPtr<T>數(shù)據(jù)結(jié)構(gòu),那么就像使用共享指針一樣,你無需擔(dān)心什么時(shí)候銷毀已創(chuàng)建的句柄。如果沒使用ComPtr<T>,那你可以調(diào)用內(nèi)置在每個(gè) DirectX 數(shù)據(jù)結(jié)構(gòu)中的Release()函數(shù)進(jìn)行銷毀。

結(jié)論

DirectX 12 是一個(gè)功能豐富、性能強(qiáng)大的計(jì)算機(jī)圖形 API,非常適合商業(yè)項(xiàng)目。它在設(shè)計(jì)上遵循現(xiàn)代圖形 API,同時(shí)它也是硬件驅(qū)動(dòng)程序工程師和商業(yè)項(xiàng)目工程師主要在維護(hù)的 API。這篇文章回顧了基于光柵的繪圖,但沒有涉及到更多方面,而它們同樣值得探討,例如:

  • DirectML:硬件加速機(jī)器學(xué)習(xí)模型。

  • DirectX Raytracing:硬件加速光線追蹤和場(chǎng)景遍歷。

  • 計(jì)算著色器(Compute Shader):基于 GPGPU 執(zhí)行任意任務(wù),例如圖像處理、物理等。

  • 網(wǎng)格著色器(Mesh Shader):一種替代傳統(tǒng)基于光柵渲染技術(shù)構(gòu)建基元的方法。

  • 可變速率著色(Variable Rate Shading,VRS):對(duì)給定命令集的著色速率進(jìn)行更精細(xì)的控制。

我還寫了更多關(guān)于 DirectX 的文章:

  • Raw DirectX 12 book?介紹了計(jì)算管線、光線追蹤管線等。

  • Raw DirectX 11 blog post?回顧了早期的 DirectX 11,并且可以把這篇文章作為學(xué)習(xí) DirectX 很不錯(cuò)的入門材料,因?yàn)檫@里面的許多概念都沿用到了 DirectX 12。

更多資源

你一定不要錯(cuò)過下面這些文章、工具和項(xiàng)目:

文章

  • 在微軟 DirectX 12文檔中有一個(gè)頁面,包含了初始化 DirectX 的所用到的全部數(shù)據(jù)結(jié)構(gòu);

  • 微軟在這個(gè)鏈接里有關(guān)于 D3D12 和 11 的規(guī)范文檔;

  • Jendrik Illner(@jendrikillner)寫了一篇?DirectX 12 學(xué)習(xí)計(jì)劃;

  • Braynzarsoft,一個(gè) DirectX 教程社區(qū);

  • Riko Ophorst?的一篇?DirectX 12 光線追蹤論文;

  • Riccardo Loggini 的 D3D12 博客文章;

  • 英特爾關(guān)于 D3D12 博客文章;

  • NVIDIA 關(guān)于 D3D12 和相關(guān)主題的博客文章;

  • Diligent Graphics (@diligentengine) 寫了篇關(guān)于 DirectX 12 的文章;

  • Jeremiah van Oosten的?DirectX 12 系列教程;

  • Alex Tardif?(@longbool)的?A Gentle Introduction to D3D12;

范例

  • 微軟官方的?DirectX 12 范例代碼倉庫;

  • 英特爾官方的?GitHub Organization Game Tech Dev;

  • NVIDIA 的官方?GitHub Organization GameWorks;

  • Matth?us G. Chajdas(@NIV_Anteru)發(fā)布的?HelloD3D12,它是 AMD 的 GPUOpen 庫和 SDK 的一部分;

工具

  • 由 Adam Sawicki(@Reg__)開發(fā)的?D3D12 Memory Allocator?,他同樣還是 Vulkan Memory Allocator 的開發(fā)者;

  • Tim Jones(@tim_jones) 發(fā)布了一個(gè)VS Code 插件?HLSL Tools,可讓你更輕松地對(duì)編寫 shader;

你可以在這個(gè)Github倉庫中找到本篇文章提到的所有源代碼。

發(fā)布于 2023-01-29 19:41?IP 屬地上海?directx 12是什么意思?directx12有什么功能和效果?

來源: 酷狗科技網(wǎng)

時(shí)間:2022-08-09 15:57:54

directx 12是什么意思?

DirectX全稱為Direct eXtension,是由微軟公司創(chuàng)建的多媒體編程接口。

DirectX由C++編程語言實(shí)現(xiàn),遵循COM。被廣泛使用于Microsoft Windows、Microsoft Xbox和Microsoft Xbox 360電子游戲開發(fā),并且只能支持這些平臺(tái),DirectX 12是目前DirectX最新的版本,大家熟悉的XP系統(tǒng)內(nèi)置的版本為DirectX 9.0,Win7/8系統(tǒng)則內(nèi)置版本為DirectX 11,而Win10正式版中,將內(nèi)置最新的DirectX12,而且DX12只會(huì)支持Windows10,

微軟在2014年的GDC上正式發(fā)布。全球首款支持DirectX 12的游戲是奇點(diǎn)灰燼。

directx12有什么功能和效果?

像之前的DirectX規(guī)范一樣,DX12實(shí)際上也可以分為多個(gè)功能不同的功能層(Feature Level),不過DX12這次還多了一個(gè)底層優(yōu)化,所以DX12規(guī)范可以視作三個(gè)不同層級(jí):

·D3D 12 Low Level API:這部分實(shí)際上是見諸報(bào)道最多的一部分,DX12相比DX11性能大提升就是底層優(yōu)化的功勞,這也是DX12最吸引人的一點(diǎn),不論是對(duì)游戲開發(fā)者還是對(duì)游戲玩家來說都是如此。底層優(yōu)化部分包括Low Overhead(低開銷)、更多的控制及異步計(jì)算(Async Compute)等多個(gè)部分,低開銷類似AMD提出的Mantle優(yōu)化,后者也是大幅改善了游戲的多線程效率,降低了驅(qū)動(dòng)層開銷,現(xiàn)在這部分已經(jīng)可以使用3DMark的Driver Overhead做測(cè)試了。

·DX12 Feature Level 12_0:前面的底層優(yōu)化部分實(shí)際上是幫助DX12打通了“經(jīng)脈”,提高了開發(fā)者的潛力,但那些并不涉及具體的招式——Feature Level 12_0這部分開始涉及更新的3D渲染方法,包括平鋪資源(Tiled Resoure)、歸類UAV訪問、無綁定(Bindless)等等,其中多項(xiàng)功能實(shí)際上DX11.1中就有了,不過DX11中多是T1級(jí)別的,現(xiàn)在的則是T2級(jí)別的。

·DX12高級(jí)功能Feature Level 12_1:跟以往的DX11.1/11.2一樣,DX12還有比Feature Level 12_0更高級(jí)的Feature Level 12_1功能,包括立體平鋪資源(Volume Tiled Resources)、保守光柵(Conservative Rasterization)、光柵順序視圖(Raster Order Views)等,這些功能通常屬于可選支持,但它們可以更好地提升開發(fā)者的效率或者游戲畫質(zhì),同時(shí)對(duì)顯卡的要求也更高。

以上三部分是DX12規(guī)范的主要內(nèi)容,但這些還不是DX12的全部功能,還記得之前曝光過的DX12黑科技——A、N顯卡混合交火嗎?微軟確實(shí)在DX12中嘗試了不同顯卡的混搭技術(shù),該技術(shù)名為Muti-Adapter(多顯卡適配器),它就可以把不同架構(gòu)的GPU聯(lián)合起來渲染。

directx12官方版擁有更加強(qiáng)效的驅(qū)動(dòng)效率,可以允許游戲開發(fā)者對(duì)特定的硬件進(jìn)行優(yōu)化,能夠?yàn)?Windows 系統(tǒng)提供高性能的硬件加速多煤體支持,讓游戲最大程度的與電腦硬件相配合。


【知乎】DirectX 12 簡明入門教程的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
阜平县| 横山县| 肃宁县| 广灵县| 万安县| 高青县| 九龙县| 英德市| 西和县| 广汉市| 伊金霍洛旗| 固原市| 文登市| 平邑县| 庆云县| 惠来县| 广元市| 马关县| 虞城县| 垣曲县| 贵州省| 肥西县| 宁乡县| 铁岭市| 财经| 五常市| 东乡| 滦平县| 个旧市| 永川市| 盐城市| 和龙市| 城市| 清镇市| 乌拉特后旗| 天柱县| 荣昌县| 和林格尔县| 永善县| 中牟县| 建始县|