DirectX12学习笔记(四)Direct3D Initialization

目录4.1 PRELIMINARIES4.1.1Direct3D4.1.2 COM(Component Object Model)4.1.3 Textures Formats4.1.4 The Swap Chain and Page Flipping4.1.5 Depth Buffering4.1.6 Resources and Descriptors4.1.7 ...
摘要由CSDN通过智能技术生成

目录

4.1 PRELIMINARIES

4.1.1 Direct3D

4.1.2 COM(Component Object Model)

4.1.3 Textures Formats

4.1.4 The Swap Chain and Page Flipping

4.1.5 Depth Buffering

4.1.6 Resources and Descriptors

4.1.7 Multisampling Theory

4.1.8 Multisampling in Direct3D

4.1.9 Feature Levels

4.1.10 DirectX Graphics Infrastructure

4.1.11 Checking Feature Support

4.1.12 Residency

4.2 CPU/GPU INTERACTION

4.2.1 The Command Queue and Command Lists

4.2.2 CPU/GPU Synchronization

4.2.3 Resource Transitions

4.2.4 Multithreading with Commands

4.3 INITIALIZING DIRECT3D

4.3.1 Create the Device

4.3.2 Create the Fence and Descriptor Sizes

 4.3.3 Check 4X MSAA Quality Support

4.3.4 Create Command Queue and Command List

4.3.5 Describe and Create the Swap Chain

 4.3.6 Create the Descriptor Heaps

4.3.7 Create the Render Target View

 4.3.8 Create the Depth/Stencil Buffer and View

4.3.9 Set the Viewport

4.3.10 Set the Scissor Rectangles

4.4 TIMING AND ANIMATION

4.4.1 The Performance Timer

4.4.2 Game Timer Class

4.4.3 Time Elapsed Between Frames

4.4.4 Total Time

4.5 THE DEMO APPLICATION FRAMEWORK

4.6 DEBUGGING DIRECT3D APPLICATIONS


4.1 PRELIMINARIES

4.1.1 Direct3D

是一种底层图形API,经应用程序来控制和对GPU编程进行硬件加速的3D渲染。Direct3D 12添加了一些新的特性,主要的改进是显著减少了CPU开销并提高了对多线程的支持。

4.1.2 COM(Component Object Model)

为了让DirectX成为独立于编程语言并向下兼容。一般用接口的形式来使用COM。我们不使用new来直接创建COM,而是用特定的函数或其它COM的接口获取指向COM接口的指针。COM对象是引用计数,调用接口之后要再调用Release方法而不是delete——COM对象在他们的引用计数为0后会释放自己的内存。

为了管理COM对象的生命周期,Windows Runtime Library(WRL)提供了Microsoft::WRL::ComPtr类,可以看作是COM的智能指针。当一个ComPtr实例超出范围时它会自动调用包含的COM对象的Release方法。

书里会用到3个主要的ComPtr方法:

  1. Get:返回一个指向COM接口的指针。一般用于传递参数给接受原生COM接口指针的函数。
  2. GetAddressOf:返回指向COM接口的指针的地址。
  3. Rest:设置ComPtr实例为nullptr并减少包含的COM接口的引用计数。也可以直接赋值ComPtr对象为nullptr。

4.1.3 Textures Formats

纹理不仅可以作为图片的数据,也可以作为1-3维的数据数组。纹理不能保存任意的数据格式,在DXGI_FORMAT描述了它可以用的枚举类型(DXGI_FORMAT也描述了顶点数据类型和索引数据类型)。其中也有无类型的格式,我们只申请了内存,在纹理绑定到管线时才会解释数据,如4个个16bit通道的DXGI_FORMAT_R16G16B16A16_TYPELESS。

4.1.4 The Swap Chain and Page Flipping

为了避免动画的闪烁问题,最好的解决办法是在一张叫做back buffer的离屏缓冲上绘制完整的一帧内容,绘制完成后才在屏幕上显示这完整的一帧。为了实现这个方案我们需要两张缓冲,在屏幕显示的叫front buffer。

front和back buffer来自一个交换链。在Direct3D中,交换链通过IDXGISwapChain来表示。这个接口储存了front和back buffer纹理,并提供IDXGISwapChain::ResizeBuffers和IDXGISwapChain::Present来重置尺寸和在屏幕上呈现。

用两个缓冲称为双缓冲,类似也可以用三缓冲。

4.1.5 Depth Buffering

用来储存每个像素的深度信息。0.0代表视锥体最近的物体,1.0代表最远的物体。depth buffering和back buffer每一个元素是一一对应的,所以两者需要相同的分辨率(MSAA除外)。

depth buffering通过深度测试来工作,小于depth buffer的值的像素才能被写入back buffer,当然我们也可以改变这个规则。depth buffer是一张纹理,所以也需要特定的格式来创建:

  1. XGI_FORMAT_D32_FLOAT_S8X24_UINT:32位浮点数的depth buffer,8位uint[0-255]留给stencil buffer,以及填充未使用的24位。
  2. DXGI_FORMAT_D32_FLOAT:32位浮点数的depth buffer。
  3. DXGI_FORMAT_D24_UNORM_S8_UINT:24位浮点数的depth buffer,8位uint[0-255]stencil buffer。
  4. DXGI_FORMAT_D16_UNORM:16位被映射到[0,255]的depth buffer。

我们使用第三种24位深度和8位模板,也因此称他为深度/模板缓存depth/stencil buffer。

4.1.6 Resources and Descriptors

在渲染过程中,GPU会写入和读取资源。在我们发出Draw命令之前,我们需要绑定资源到将调用draw call的管线。Gpu不直接绑定资源,而是用过descriptor对象来引用。descriptor可以堪为是一种描述GPU资源的轻量结构体,本质上它是一种间接的层级,给一个resource descriptor,GPU就可以得到实际的资源数据,并知道它的必要信息。我们通过将在draw call引用的descriptor把资源绑定到渲染管线。

为什么要这样额外的层级descriptor?因为GPU资源本质上是通用的内存块,所以它们可以在渲染管线不同的阶段被使用。比如一张纹理被用来做rander target,然后又被作为shader的资源。一个资源它本身不能被说明它到底是render target还是depth/stencil buffer或者shader resource,第二我们可能只想把资源的一个子区域绑定到渲染管线上,第三资源可以是typeless类型,GPU无法知道它的格式。

view是descriptor的同义词,在以前版本的Direct3D使用,在某些Direct3D 12 API也仍然使用。本书中这两个词可以相互转换使用:如constant buffer view和constant buffer descriptor是同一个事物。

Descriptors拥有类型,定义它将如何被使用。这本书会用到:

  1. CBV/SRV/UAV constant buffers/shader resources/unordered access view resources.
  2. Sampler descriptors 采样器资源。
  3. RTV render target资源
  4. DSV depth/stencil资源

一个descriptor heap是descriptors的数组。它是我们应用程序使用的每一种特定类型的所有descriptors的内存支持,我们需要一个单独的descriptor heap来对应每种类型的descriptor。当然也可以创建相同descriptor的多个heap。

也可以有descriptors引用同一个资源,比如用多个descriptors来引用一个资源的不用子区域,可以如之前说的用在渲染不同阶段。如果创建了一个typeless的资源,它可以被视为浮点或者整型,这就需要两个descriptors。

descriptors应该在初始化的时候创建,这是因为需要做一些类型检查和验证。并且descriptors在初始化创建比在运行时创建更好。

2009年8月的SDK文档说:“创建一个全类型的资源限制资源的格式。这使运行时能够优化访问。“只有在真正需要它们提供的灵活性时,才应该创建typeless的资源,否则应该创建一个全类型的资源。

4.1.7 Multisampling Theory

抗锯齿,介绍了SSAA(4倍back buffer,4倍depth/stencil buffer)和MSAA(1倍back buffer,4倍depth/stencil buffer)。

4.1.8 Multisampling in Direct3D

typedef struct DXGI_SAMPLE_DESC
{
    UINT Count;
    UINT Quality;
} DXGI_SAMPLE_DESC;

Count指定每个像素多少次采样,Quality指定预期的质量级别(“质量水平”的含义可能因硬件制造商而异)。质量级别的范围取决于纹理格式和每个像素的采样数量。

我们可以对给定纹理格式和采样数量来查询质量级别: 

typedef struct D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS {
    DXGI_FORMAT    Format;
    UINT           SampleCount;
    D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG Flags;
    UINT           NumQualityLevels;
} D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS;

D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS msQualityLevels;
msQualityLevels.Format = mBackBufferFormat;
msQualityLevels.SampleCount = 4;
msQualityLevels.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
msQualityLevels.NumQualityLevels = 0;

ThrowIfFailed(md3dDevice->CheckFeatureSupport(
    D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS,
    &msQualityLevels,
    sizeof(msQualityLevels)));

 第二个参数即是输入又是输出,输入时我们必须定义它的纹理格式,采样数量和flag,输出时函数会填上它的最大质量级别。我们就可以知道有效的质量级别范围是0到NumQualityLevels–1。

每个像素采样数量最大值定义为:

    #define D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT (32)

如果不想用msaa,可以设置采样数量为1,质量等级为0。所有Direct3D 11(不是12么?)设备对所有render target formats支持4xmsaa。

交换链缓冲区和深度缓冲区都需要填充DXGI_SAMPLE_DESC,back buffer和depth buffer都必须使用相同的msaa设置。

4.1.9 Feature Levels

Direct3D 11 介绍了feature levels的概念(在代码中表现为D3D_FEATURE_LEVEL枚举类型),它大致对应Direct3D 9-11的各种版本:

enum D3D_FEATURE_LEVEL
{
    D3D_FEATURE_LEVEL_9_1 = 0x9100,
    D3D_FEATURE_LEVEL_9_2 = 0x9200,
    D3D_FEATURE_LEVEL_9_3 = 0x9300,
    D3D_FEATURE_LEVEL_10_0 = 0xa000,
    D3D_FEATURE_LEVEL_10_1 = 0xa100,
    D3D_FEATURE_LEVEL_11_0 = 0xb000,
    D3D_FEATURE_LEVEL_11_1 = 0xb100
}D3D_FEATURE_LEVEL;

feature levels规定了一系列严格的功能,如如果一个GPU支持Direct3D 11,那么它必须支持整个Direct3D 11的功能集,很少有例外(如msaa采样数量仍需要查询,因为它们可以在不用的Direct3D 11硬件上变化)。特性集使开发更加容易——一旦我们了解支持的特性集,我们就知道了可以使用的Direct3D功能。

如果用户的硬件不支持某一个feature level,应用程序可以退回到旧的feature level。比如,为了支持更广泛的受众,应用程序你支持Direct3D 11、10和9.3的硬件。应用程序将检查最新到最旧的feature level的支持。在本书中,我们总是要求支持D3D_FEATURE_LEVEL_11_0的feature level,但现实的应用程序还必须考虑支持旧的硬件来最大化他们的用户。

4.1.10 DirectX Graphics Infrastructure

DirectX Graphics Infrastructure (DXGI)是和Direct3D一起用的API。它的基本思想是一些图形相关的任务对多个图形APIs是相同的,如2D和3D的API都需要交换链,因此交换链接口IDXGISwapChain实际上是DXGI的API。DXGI处理其它相同的图形功能,如全屏模式转换,枚举图形系统信息如显示适配器、显示器和支持的现实模式(分辨率,刷新率等),定义了各种支持的surface模式(DXGI_FORMAT)。

简短描述下初始化Direct3D时用到的DXGI概念和接口。IDXGIFactory主要用来创建IDXGISwapChain和枚举显示适配器。显示适配器实行图形功能,通常他是一个物理硬件(图形显卡),但系统也可以有一个软件显示适配器来模拟图形功能。一个系统可以有多个适配器,适配器用IDXGIAdapter接口表示。我们可以这样枚举系统的所有适配器

void D3DApp::LogAdapters()
{
    UINT i = 0;
    IDXGIAdapter* adapter = nullptr;
    std::vector<IDXGIAdapter*> adapterList;
    // 用mdxgiFactory枚举每个适配器到adapter
    while(mdxgiFactory->EnumAdapters(i, &adapter) != DXGI_ERROR_NOT_FOUND)
    {
        DXGI_ADAPTER_DESC desc;
        // 获取描述信息
        adapter->GetDesc(&desc);
        std::wstring text = L"***Adapter: ";
        text += desc.Description;
        text += L"\n";
        OutputDebugString(text.c_str());
        adapterList.push_back(adapter);
        ++i;
    }
    for(size_t i = 0; i < adapterList.size(); ++i)
    {
        // 枚举适配器关联的output
        LogAdapterOutputs(adapterList[i]);
        ReleaseCom(adapterList[i]);
    }
}

一个系统可以有多个显示器,一个显示器是一个display output的示例。一个output由IDXGIOutput接口表示。每个适配器都和一个列表的output相关联。如有两个显卡和三个显示器的系统,两个显示器连到了第一个显卡,第三个显示器连到第二个显卡。我们可以这样枚举一个适配器关联的所有output

void D3DApp::LogAdapterOutputs(IDXGIAdapter* adapter)
{
    UINT i = 0;
    IDXGIOutput* output = nullptr;
    while(adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND)
    {
        DXGI_OUTPUT_DESC desc;
        output->GetDesc(&desc);
        std::wstring text = L"***Output: ";
        text += desc.DeviceName;
        text += L"\n";
        OutputDebugString(text.c_str());
        // 输出DXGI_FORMAT_B8G8R8A8_UNORM下支持的显示模式
        LogOutputDisplayModes(output, DXGI_FORMAT_B8G8R8A8_UNORM);
        ReleaseCom(output);
        ++i;
    }
}

根据文档,“Microsoft Basic Render Driver”表示没有display output。

每一个显示器支持一系列的显示模式,由DXGI_MODE_DESC中一下数据表示:

typedef struct DXGI_MODE_DESC
{
    UINT Width; // Resolution width
    UINT Height; // Resolution height
    DXGI_RATIONAL RefreshRate;
    DXGI_FORMAT Format; // Display format
    DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; //Progressive vs. interlaced
    DXGI_MODE_SCALING Scaling; // How the image is stretched
    // over the monitor.
} DXGI_MODE_DESC;

typedef struct DXGI_RATIONAL
{
    UINT Numerator;
    UINT Denominator;
} DXGI_RATIONAL;

typedef enum DXGI_MODE_SCANLINE_ORDER
{
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED = 0,
    DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE = 1,
    DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST = 2,
    DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST = 3    
} DXGI_MODE_SCANLINE_ORDER;

typedef enum DXGI_MODE_SCALING
{
    DXGI_MODE_SCALING_UNSPECIFIED = 0,
    DXGI_MODE_SCALING_CENTERED = 1,
    DXGI_MODE_SCALING_STRETCHED = 2
} DXGI_MODE_SCALING;

固定显示模式(DXGI_FORMAT,如上面代码中的DXGI_FORMAT_B8G8R8A8_UNORM)的格式,我们可以得到一个在这个格式下output支持的所有显示模式的列表

void D3DApp::LogOutputDisplayModes(IDXGIOutput* output, DXGI_FORMAT format)
{
    UINT count = 0;
    UINT flags = 0;
    // Call with nullptr to get list count.
    output->GetDisplayModeList(format, flags, &count, nullptr);
    std::vector<DXGI_MODE_DESC> modeList(count);
    output->GetDisplayModeList(format, flags, &count, &modeList[0]);
	
    for(auto& x : modeList)
    {
        UINT n = x.RefreshRate.Numerator;
        UINT d = x.RefreshRate.Denominator;
		
        std::wstring text = L"Width = " + std::to_wstring(x.Width) + L" " +
        L"Height = " + std::to_wstring(x.Height) + L" " +
        L"Refresh = " + std::to_wstring(n) + L"/" + std::to_wstring(d) + L"\n";
		
        ::OutputDebugString(text.c_str());
    }
}

输出如下:

Width = 1920 Height = 1080 Refresh = 59950/1000
Width = 1920 Height = 1200 Refresh = 59950/1000

进入全屏模式时,枚举显示模式变得尤为重要,为了获得全屏的最佳性能,制定的显示模式(包括刷新率)必须与显示器支持的显示模式完全匹配。

4.1.11 Checking Feature Support

我们已经用了ID3D12Device::CheckFeatureSupport方法来检查当前图形驱动对msaa的支持,这只是我们能用这个函数检查的其中一个feature。它的参数如下:

HRESULT ID3D12Device::CheckFeatureSupport(
    D3D12_FEATURE Feature,
    void *pFeatureSupportData,
    UINT FeatureSupportDataSize);

1.Feature:D3D12_FEATURE枚举类型的一个成员,识别我们想要检查支持的features类型:

  1. D3D12_FEATURE_D3D12_OPTIONS:各种DirectX 12 features的支持。
  2. D3D12_FEATURE_ARCHITECTURE:硬件架构的支持。
  3. D3D12_FEATURE_FEATURE_LEVELS:feature level的支持。
  4. D3D12_FEATURE_FORMAT_SUPPORT:给定纹理类型的feature的支持。
  5. D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS:mass的支持。

2.pFeatureSupportData:指向数据结构的指针,用于检索feature支持信息。它的类型取决于你指定的Feature,指定什么类型就传递那个类型的实例。

3.FeatureSupportDataSize:传递数据类型的大小。

ID3D12Device::CheckFeatureSupport函数检查大量feature的支持,很多是本书没有使用的高级特性。以feature levels举例:

typedef struct D3D12_FEATURE_DATA_FEATURE_LEVELS {
    UINT NumFeatureLevels;
    const D3D_FEATURE_LEVEL *pFeatureLevelsRequested;
    D3D_FEATURE_LEVEL MaxSupportedFeatureLevel;
} D3D12_FEATURE_DATA_FEATURE_LEVELS;

D3D_FEATURE_LEVEL featureLevels[3] =
{
    D3D_FEATURE_LEVEL_11_0, // First check D3D 11 support
    D3D_FEATURE_LEVEL_10_0, // Next, check D3D 10 support
    D3D_FEATURE_LEVEL_9_3 // Finally, check D3D 9.3 support
};

D3D12_FEATURE_DATA_FEATURE_LEVELS featureLevelsInfo;
featureLevelsInfo.NumFeatureLevel
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值