精华内容
下载资源
问答
  • <div><p>I started to get this error in D3D12Multithreading the sample every time the index buffer is binded. <p>D3D12 ERROR: ID3D12CommandList::IASetIndexBuffer: pDesc->BufferLocation + SizeIn...
  • m getting this error when I try running D3D12ExecuteIndirect. This is also happening for my own application that uses ExecuteIndirect although I don't know if that is the issue. <p>Thanks <p>D3...
  • TerraFX.Interop.D3D12MemoryAllocator D3D12MemoryAllocator的互操作绑定。 程序包位于: : 或通过NuGet Feed URL: : 目录 行为守则 TerraFX和所有提供帮助的人员(包括问题,拉取请求,Wiki等)必须遵守 。 可...
  • Update D3D12

    2020-12-05 22:56:11
    <div><p>This PR updates the D3D12 file to support everything up to the update that added DXR and renderpasses <p>Most of this update is untested. Testing it would require using it in a D3D12 renderer....
  • <p>A minor, but quite confusing doc bug in D3D12_GPU_DESCRIPTOR_HANDLE and D3D12_CPU_DESCRIPTOR_HANDLE, says that the ptr element is "The size, in bytes, of the block of memory that the GPU ...
  • D3D12渲染技术概述

    千次阅读 2018-08-22 09:27:03
    从D3D9到D3D12逐步提升,现在很多以前的引擎还是停留在D3D9或者D3D11,D3D12用的比较少,相信不久的将来,引擎使用的D3D API都会更新到D3D12,为了普及D3D API的开发,会做一个系列文章用于介绍D3D12的特色和应用。...

    从D3D9到D3D12逐步提升,现在很多以前的引擎还是停留在D3D9,D3D12用的比较少,相信不久的将来,引擎使用的D3D API都会更新到D3D12,为了普及D3D API的开发,会做一个系列文章用于介绍D3D12的特色和应用。由浅入深,逐步讲解,不论做游戏还是做VR,AR都会涉及到渲染,市场上很多公司会针对自己的项目封装一些图形接口用于渲染,掌握D3D12渲染技术是必须的,回到正题,现在市面上或者自研引擎已经进行了DX API的升级,比如Unity引擎已经更新到了D3D11,下面我们先将D3D11和D3D12做一个对比,通过比较从总体上先对D3D12渲染技术有大概的认识,它们最大的区别还是渲染管线。

    渲染管线

    先看看D3D11的渲染管线:
    这里写图片描述

    再看看D3D12的渲染管线:
    这里写图片描述

    二者最大区别是:多了一个Root signature,它表示的是CBV/SRV/UAV/Sampler的布局,比如一组VS/PS,VS用了2个CBV,PS用了1个CBV、2个SRV、1个Sampler,这样的布局就能生成一个专用的root signature。root signature和view的值无关,只和布局有关,所以其实是可以共享的。 另外,Heap是D3D12新增的概念,不管是CBV、SRV还是UAV,都可以把handle放到heap里。如此一来,只要通过设置heap就能一次切换一批CBV/SRV/UAV/Sampler,而不需要一个一个设置,降低了CPU开销,就好像普通程序里常见指针数组。在此给读者介绍一下关于CBV/SRV/UAV/Sampler,如下图所示:
    这里写图片描述
    其中SRV表示的是shader-resource views,这些都是一些缩写。

    多线程

    我们先看看多线程资源的处理方式,如下图所示:
    这里写图片描述
    对于GPU的提升也是非常明显的,如下图所示:
    这里写图片描述
    为了解释上面的内容,我们看看D3D12的另一个功能 Command List,它的作用见下图所示:
    这里写图片描述
    CPU向GPU中加入一组Command到Queue中,GPU并不是立即执行,因为GPU可能忙于处理以前的Command,如果Command的Queue空了,那GPU也是处于空闲等待状态,如果Command的Queue太慢了,CPU要在某个点处于等待状态,等GPU去执行Command,这种情况是我们不愿意看到的,我们还是希望CPU和GPU能够协同工作,充分调动起来,这里就要用到D3D12的另一个特性Fence,专门用于协调CPU与GPU的,CPU 等待 GPU 处理完所有在隔离点之前的指令,然后刷新指令队列。

    Draw Call 模型

    我们测试游戏通常会看Draw Call的运行时显示的数量,D3D12有自己的Draw Call 模型,它跟以前相比最大的区别是:

    • 之前的多线程渲染模型是由一个单独的渲染线程用来提交Draw/Dispatch调用,其他一个或多个线程处理游戏逻辑。
    • 新的多线程模型,每个线程都可以提交Draw/Dispatch调用。

    它的模型如下图所示:
    这里写图片描述

    提交Draw Call的具体执行步骤:
    1、创建一个或多个3D/计算队列
    2、创建一个或多个命令列表
    3、为每个命令列表记录Draw Call
    4、执行命令列表

    • 小结:

      学习D3D12我们要先了解它的一些特性,后面我们会先从基础方面谈起,逐步深入,学习D3D API对于学习引擎有非常重要的帮助,可以帮助我们优化引擎,增加模块功能,封装功能模块库等等。

    展开全文
  • <div><p>I always forget whether its enable_d3d12_standalone or enable_standalone_d3d12 -- we should probably just make this enable_d3d12.</p><p>该提问来源于开源项目:HansKristian-Work/vkd3d-...
  • - <a href="https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-d3d12createrootsignaturedeserializer">D3D12CreateRootSignatureDeserializer - ...
  • <p>I was able to solve the first crash on D3D12Replay::FetchCounters but I'm stuck with the second crash. <p>The first crash happens because SetStablePowerState() is only supported on developer ...
  • I have the D3D12HeterogeneousMultiadapter sample always fail on D3D12CreateDevice(ppAdapters[i], D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_devices[i])), no meaningful message. The machine I ran ...
  • error: unable to create file Samples/Desktop/D3D12Raytracing/src/D3D12RaytracingRealTimeDenoisedAmbientOcclusion/RTAO/Shaders/Denoising/CalculateMeanVariance_SeparableFilterCS_CheckerboardSampling_...
  • * <code>native</code> module renamed to <code>resource, <code>d3d12</code> is included as <code>native</code> (might just call it d3d12 at the end once we got fully rid of winapi::um::d3d12) ...
  • t had any development on a D3D12 implementation/example and was wondering if this will be worked on in the near future or not? ImGUI has been working on a D3D12 Rendering implementation since early ...
  • <div><p>Currently the only existing D3D12 compute sample is the D3D12NBody simulation. For those interested in DX12 for purely compute reasons, particularly with no DX12 background, the sample can be ...
  • Previously, resource transitions between <code>D3D12_RESOURCE_STATE_RENDER_TARGET</code> and <code>D3D12_RESOURCE_STATE_PRESENT</code> were done at the start and end of every render pass. This is ...
  • D3D12渲染技术之初始化流程

    千次阅读 2018-08-23 14:12:47
    也就是我们通常说的初始化D3D12D3D12的初始化工作与以前的D3D9是完全不一样的,D3D12做了大幅的升级。但是每个D3D图形API都有自己的初始化流程,大家只要记住这个流程,学习起来就比较容易,就跟生产车架的流水线...

    掌握了矩阵向量运算后,接下来我们要做的事情利用D3D12图形库API编程,也就是我们通常说的初始化D3D12,D3D12的初始化工作与以前的D3D9是完全不一样的,D3D12做了大幅的升级。但是每个D3D图形API都有自己的初始化流程,大家只要记住这个流程,学习起来就比较容易,就跟生产车架的流水线作业一样的原理,先做什么后做什么。其他的内容向里面添加就可以了,我们先聊聊D3D12的初始化流程,然后再编程实现。

    D3D初始化

    1、创建窗体
    2、创建ID3D12Device设备
    3、创建 ID3D12Fence用于查询descriptor 大小
    4、检查设备是否支持4X MSAA
    5、创建指令队列,指令列表和主指令列表。
    6、创建交换链
    7、创建描述符堆(descriptor heaps)
    8、创建渲染目标视图。
    9、创建深度/模板缓冲区及其关联的深度/模板视图。
    10、设置视口

    共分为10步,严格来说,第一步不属于D3D12初始化里面的内容,因为我们要显示D3D12中的内容,必须要要有窗体作为载体,在这里就把他们加进来了,我们下面就按照这10步去编写程序实现我们的D3D12的初始化工作。

    • 创建窗体
      窗体是一个载体,用于显示D3D12内容的,首先需要注册窗体类,把它们里面的参数填满,代码如下所示:
    bool D3DApp::InitMainWindow()
    {
        WNDCLASS wc;
        wc.style         = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc   = MainWndProc; 
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = mhAppInst;
        wc.hIcon         = LoadIcon(0, IDI_APPLICATION);
        wc.hCursor       = LoadCursor(0, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
        wc.lpszMenuName  = 0;
        wc.lpszClassName = L"MainWnd";
    
        if( !RegisterClass(&wc) )
        {
            MessageBox(0, L"RegisterClass Failed.", 0, 0);
            return false;
        }
    
        // Compute window rectangle dimensions based on requested client area dimensions.
        RECT R = { 0, 0, mClientWidth, mClientHeight };
        AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false);
        int width  = R.right - R.left;
        int height = R.bottom - R.top;
    
        mhMainWnd = CreateWindow(L"MainWnd", mMainWndCaption.c_str(), 
            WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, mhAppInst, 0); 
        if( !mhMainWnd )
        {
            MessageBox(0, L"CreateWindow Failed.", 0, 0);
            return false;
        }
    
        ShowWindow(mhMainWnd, SW_SHOW);
        UpdateWindow(mhMainWnd);
    
        return true;
    }

    这个在DX9或者MFC中都会用到,这里就不介绍了,接下来开始进的入D3D12编程的正题了。

    • 创建ID3D12Device设备
      创建D3D12的设备,自然想到函数:D3D12CreateDevice,在D3D12中我们还需要做一件事情就是调用函数:CreateDXGIFactory1在这个函数中有DXGI字样,DXGI它是DirectX Graphics Infrastructure的缩写,DXGI 可以操控一些普通的图形功能,这其中包括全屏转换、枚举图形系统信息(比如显示适配器、显示器、被支持的显示模式(分辨率,刷新率等等)),它还定义了各种纹理格式(DXGI_FORMAT)。CreateDXGIFactory1就是DXGI下面的一个接口函数,可以枚举显示适配器。
      函数调用代码如下:
    CreateDXGIFactory1(IID_PPV_ARGS(&mdxgiFactory))

    其中 IID_PPV_ARGS 宏用来返回 COM 接口的 ID
    接下来调用D3D12CreateDevice函数,代码实现如下:

    HRESULT hardwareResult = D3D12CreateDevice(
            nullptr,             // default adapter
            D3D_FEATURE_LEVEL_11_0,
            IID_PPV_ARGS(&md3dDevice));
    
    
        if(FAILED(hardwareResult))
        {
            ComPtr<IDXGIAdapter> pWarpAdapter;
            ThrowIfFailed(mdxgiFactory->EnumWarpAdapter(IID_PPV_ARGS(&pWarpAdapter)));
    
            ThrowIfFailed(D3D12CreateDevice(
                pWarpAdapter.Get(),
                D3D_FEATURE_LEVEL_11_0,
                IID_PPV_ARGS(&md3dDevice)));
        }

    if条件语句内部就是枚举适配器的代码,设备创建好了以后,下面就是创建Fence了。

    • 创建 ID3D12Fence
      接下来我们需要创建Fence对象用于CPU/GPU同步,另外,一旦我们开始使用描述符,我们将需要知道它们的大小, 描述符大小可能因GPU而异,因此我们需要查询此信息, 我们缓存描述符的大小,以便在我们需要它时可用于各种描述符类型,代码如下所示:
        ThrowIfFailed(md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE,
            IID_PPV_ARGS(&mFence)));
    
        mRtvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
        mDsvDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
        mCbvSrvUavDescriptorSize = md3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
    • 检查设备是否支持4X MSAA
      我们之所以选择支持4X MSAA,在这里还是要求电脑硬件配置相对高一些比较好,因为所有支持Direct3D 11的设备都支持所有渲染目标格式的4X MSAA, 因此,保证在Direct3D 11硬件上可用,我们无需验证对它的支持。 但是,我们必须检查支持的质量等级,可以使用以下方法完成:
    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)));
    
        m4xMsaaQuality = msQualityLevels.NumQualityLevels;
        assert(m4xMsaaQuality > 0 && "Unexpected MSAA quality level.");
    • 创建指令队列,指令列表和主指令列表
      指令队列由ID3D12CommandQueue接口表示,指令分配器由ID3D12CommandAllocator接口表示,指令列表由ID3D12GraphicsCommandList接口表示,如下图所示:
      这里写图片描述
      CPU 通过 Direct3D API 的指令列表(Command Lists)向指令队列提交一个命令后,GPU并不会将这些命令立即执行,而是让它们置于队列之中等待处理,因为 GPU 此时可能正在处理队列中其它的命令。
      一旦指令队列为空,则 GPU 不再工作,因为没有指令可以处理。反过来,如果队列已满,则 CPU 不再工作,而是等待 GPU 处理队列中的指令。当然这两种情况哪个都是不可取的,理想情况是存在一种动态的平衡,让指令队列中始终存在可以被处理的指令却不能处于满格状态,这样 GPU 和 CPU 都能够被充分利用,这个需要用到我们上文提到的Fence。以下函数显示了我们如何创建命令队列,指令分配器和命令列表:
    void D3DApp::CreateCommandObjects()
    {
        D3D12_COMMAND_QUEUE_DESC queueDesc = {};
        queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
        queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
        ThrowIfFailed(md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
    
        ThrowIfFailed(md3dDevice->CreateCommandAllocator(
            D3D12_COMMAND_LIST_TYPE_DIRECT,
            IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf())));
    
        ThrowIfFailed(md3dDevice->CreateCommandList(
            0,
            D3D12_COMMAND_LIST_TYPE_DIRECT,
            mDirectCmdListAlloc.Get(), // Associated command allocator
            nullptr,                   // Initial PipelineStateObject
            IID_PPV_ARGS(mCommandList.GetAddressOf())));
    
        // Start off in a closed state.  This is because the first time we refer 
        // to the command list we will Reset it, and it needs to be closed before
        // calling Reset.
        mCommandList->Close();
    }

    注意,对于CreateCommandList,我们为管道状态对象参数指定null。 在本篇博客的示例程序中,我们不发出任何绘图命令,因此我们不需要有效的管道状态对象。

    • 创建交换链
      初始化过程的下一步是创建交换链, 这是通过填写DXGI_SWAP_CHAIN_DESC结构的实例来完成的,该结构描述了我们要创建的交换链的特征, 该结构定义如下:
     mSwapChain.Reset();
    
        DXGI_SWAP_CHAIN_DESC sd;
        sd.BufferDesc.Width = mClientWidth;
        sd.BufferDesc.Height = mClientHeight;
        sd.BufferDesc.RefreshRate.Numerator = 60;
        sd.BufferDesc.RefreshRate.Denominator = 1;
        sd.BufferDesc.Format = mBackBufferFormat;
        sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
        sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
        sd.SampleDesc.Count = m4xMsaaState ? 4 : 1;
        sd.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
        sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
        sd.BufferCount = SwapChainBufferCount;
        sd.OutputWindow = mhMainWnd;
        sd.Windowed = true;
        sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
        sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

    在D3D12中的实现效果如下所示:
    这里写图片描述
    利用两个缓冲区(前后缓冲)组成交换链的方式叫做双重缓冲(double buffering),也就是说,交换链不是必须要用两个缓冲区才可以,也可以用两个数量以上的缓冲,但是通常情况下两个就已经绰绰有余了。
    下面我们介绍这个结构体中的一些常用的属性:
    BufferDesc,此结构描述了我们要创建的后台缓冲区的属性, 我们关注的主要属性是宽度和高度,以及像素格式; 其他可以查看帮助文档。
    SampleDesc,多重采样的数量和质量等级; 对于单次采样,请指定样本计数为1,质量等级为0。
    BufferUsage,指定DXGI_USAGE_RENDER_TARGET_OUTPUT,因为我们将渲染到后台缓冲区(即,将其用作渲染目标)。
    BufferCount,交换链中使用的缓冲区数量; 为双缓冲指定两个。
    OutputWindow,我们正在渲染的窗口的句柄。
    Windowed,指定true以在窗口模式下运行,或指定为全屏模式时为false。
    SwapEffect,指定DXGI_SWAP_EFFECT_FLIP_DISCARD。
    Flags,可选标志, 如果指定DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH,则当应用程序切换到全屏模式时,它将选择与当前应用程序窗口尺寸最匹配的显示模式。 如果未指定此标志,则当应用程序切换到全屏模式时,它将使用当前桌面显示模式。

    在我们描述了交换链之后,我们可以使用IDXGIFactory :: CreateSwapChain方法创建它:

    ThrowIfFailed(mdxgiFactory->CreateSwapChain(
            mCommandQueue.Get(),
            &sd, 
            mSwapChain.GetAddressOf()));
    • 创建描述符堆
      我们需要创建描述符堆来存储我们的应用程序需要的描述符/视图, 描述符堆由ID3D12DescriptorHeap接口表示, 使用ID3D12Device :: CreateDescriptorHeap方法创建堆。 在本篇博客的示例程序中,我们需要SwapChainBufferCount许多渲染目标视图(RTV)来描述我们将渲染到的交换链中的缓冲区资源,以及一个深度/模板视图(DSV)来描述深度/模板缓冲区资源以进行深度测试。 因此,我们需要一个堆来存储SwapChainBufferCount RTV,需要一个堆来存储一个DSV, 这些堆使用以下代码创建:
    void D3DApp::CreateRtvAndDsvDescriptorHeaps()
    {
        D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc;
        rtvHeapDesc.NumDescriptors = SwapChainBufferCount;
        rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
        rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
        rtvHeapDesc.NodeMask = 0;
        ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
            &rtvHeapDesc, IID_PPV_ARGS(mRtvHeap.GetAddressOf())));
    
    
        D3D12_DESCRIPTOR_HEAP_DESC dsvHeapDesc;
        dsvHeapDesc.NumDescriptors = 1;
        dsvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
        dsvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
        dsvHeapDesc.NodeMask = 0;
        ThrowIfFailed(md3dDevice->CreateDescriptorHeap(
            &dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf())));
    }
    

    在我们的实现中,我们定义了两个变量:

        static const int SwapChainBufferCount = 2;
        int mCurrBackBuffer = 0;

    我们使用mCurrBackBuffer跟踪当前的后台缓冲区索引(回想一下前页和后台缓冲区在页面翻转中交换,所以我们需要跟踪哪个缓冲区是当前的后台缓冲区,以便我们知道要渲染哪个缓冲区)。

    在我们创建堆之后,我们需要能够访问它们存储的描述符,通过句柄引用描述符, 使用ID3D12DescriptorHeap :: GetCPUDescriptorHandleForHeapStart方法获取堆中第一个描述符的句柄, 以下函数分别获取当前后台缓冲区RTV和DSV:

    D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::CurrentBackBufferView()const
    {
        return CD3DX12_CPU_DESCRIPTOR_HANDLE(
            mRtvHeap->GetCPUDescriptorHandleForHeapStart(),
            mCurrBackBuffer,
            mRtvDescriptorSize);
    }
    D3D12_CPU_DESCRIPTOR_HANDLE D3DApp::DepthStencilView()const
    {
        return mDsvHeap->GetCPUDescriptorHandleForHeapStart();
    }
    • 创建渲染目标视图
      我们不直接将资源绑定到管道; 相反,我们必须为资源创建资源视图(描述符)并将视图绑定到管道, 特别是,为了将后台缓冲区绑定到管道的输出合并(因此Direct3D可以渲染到它上面),我们需要为后台缓冲区创建一个渲染目标视图, 第一步是获取存储在交换链中的缓冲区资源:
    HRESULT IDXGISwapChain::GetBuffer(
      UINT Buffer,
      REFIID riid,
      void **ppSurface);

    Buffer,标识我们想要获取的特定后台缓冲区的索引(如果有多个)。
    riid,我们想要获取指针的ID3D12Resource接口的COM ID。
    ppSurface,返回指向表示后台缓冲区的ID3D12Resource的指针。

    对IDXGISwapChain :: GetBuffer的调用会将COM引用计数增加到后台缓冲区,因此我们必须在完成后释放它, 如果使用ComPtr,则会自动完成此操作。
    要创建渲染目标视图,我们使用ID3D12Device :: CreateRenderTargetView方法:

    void ID3D12Device::CreateRenderTargetView(
      ID3D12Resource *pResource,
      const D3D12_RENDER_TARGET_VIEW_DESC *pDesc,
      D3D12_CPU_DESCRIPTOR_HANDLE DestDescriptor);

    pResource,指定将用作渲染目标的资源,在上面的示例中,它是后台缓冲区(即,我们正在为后台缓冲区创建渲染目标视图)。
    pDesc,指向D3D12_RENDER_TARGET_VIEW_DESC的指针, 除此之外,该结构描述了资源中元素的数据类型(格式), 如果资源是使用类型化格式(即非无类型)创建的,则此参数可以为null,这表示创建此资源的第一个mipmap级别的视图(后台缓冲区只有一个mipmap级别),格式为 资源创建的, 因为我们指定了后台缓冲区的类型,所以我们为这个参数指定了null。
    DestDescriptor,处理将存储创建的渲染目标视图的描述符。

    下面是调用这两种方法的示例,其中我们为交换链中的每个缓冲区创建一个RTV:

    ComPtr<ID3D12Resource> mSwapChainBuffer[SwapChainBufferCount];
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(
      mRtvHeap->GetCPUDescriptorHandleForHeapStart());
    for (UINT i = 0; i < SwapChainBufferCount; i++)
    {
      // Get the ith buffer in the swap chain.
      ThrowIfFailed(mSwapChain->GetBuffer(
        i, IID_PPV_ARGS(&mSwapChainBuffer[i])));
    
      // Create an RTV to it.
      md3dDevice->CreateRenderTargetView(
        mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
         // Next entry in heap.
      rtvHeapHandle.Offset(1, mRtvDescriptorSize);
    } 
    • 创建深度/模板缓冲和视图
      我们现在需要创建深度/模板缓冲区,深度缓冲区只是一个2D纹理,它存储最近的可见对象的深度信息(如果使用模板,则存储模板信息)。 纹理是一种GPU资源,因此我们通过填写描述纹理资源的D3D12_RESOURCE_DESC结构来创建一个,然后调用ID3D12Device :: CreateCommittedResource方法。 D3D12_RESOURCE_DESC结构定义如下:
    typedef struct D3D12_RESOURCE_DESC
      {
      D3D12_RESOURCE_DIMENSION Dimension;
      UINT64 Alignment;
      UINT64 Width;
      UINT Height;
      UINT16 DepthOrArraySize;
      UINT16 MipLevels;
      DXGI_FORMAT Format;
      DXGI_SAMPLE_DESC SampleDesc;
      D3D12_TEXTURE_LAYOUT Layout;
      D3D12_RESOURCE_MISC_FLAG MiscFlags;
      } D3D12_RESOURCE_DESC;

    Dimension,资源的维度,它是以下枚举类型之一:

    enum D3D12_RESOURCE_DIMENSION
      {
        D3D12_RESOURCE_DIMENSION_UNKNOWN = 0,
        D3D12_RESOURCE_DIMENSION_BUFFER = 1,
        D3D12_RESOURCE_DIMENSION_TEXTURE1D = 2,
        D3D12_RESOURCE_DIMENSION_TEXTURE2D = 3,
        D3D12_RESOURCE_DIMENSION_TEXTURE3D = 4
      } D3D12_RESOURCE_DIMENSION;

    Width,纹理的宽度,以纹素为单位。 对于缓冲区资源,这是缓冲区中的字节数。
    Height,纹理的高度,以纹素为单位。
    DepthOrArraySize,纹理的纹理深度,或纹理数组大小(对于1D和2D纹理)。
    MipLevels,mipmap等级的数量,为了创建深度/模板缓冲,我们的纹理只需要一个mipmap级别。
    Format,指定文本格式的DXGI_FORMAT枚举类型的成员,深度/模板缓冲区也需要一个格式;
    SampleDesc,多重采样数和质量等级, 回想一下,4X MSAA使用比屏幕分辨率大4倍的后缓冲和深度缓冲,以便存储每个子像素的颜色和深度/模板信息。 因此,用于深度/模板缓冲区的多重采样设置必须与用于渲染目标的设置相匹配。
    Layout,D3D12_TEXTURE_LAYOUT枚举类型的成员,指定纹理布局。 目前,我们不必担心布局,可以指定D3D12_TEXTURE_LAYOUT_UNKNOWN。
    MiscFlags,其它资源标志, 对于深度/模板缓冲区资源,请指定D3D12_RESOURCE_MISC_DEPTH_STENCIL。

    GPU资源存在于堆中,这些GPU本质上是具有某些属性的GPU内存块。 ID3D12Device :: CreateCommittedResource方法使用我们指定的属性创建并向特定堆提交资源。

    HRESULT ID3D12Device::CreateCommittedResource(
      const D3D12_HEAP_PROPERTIES *pHeapProperties,
      D3D12_HEAP_MISC_FLAG HeapMiscFlags,
      const D3D12_RESOURCE_DESC *pResourceDesc,
      D3D12_RESOURCE_USAGE InitialResourceState,
      const D3D12_CLEAR_VALUE *pOptimizedClearValue,
      REFIID riidResource,
      void **ppvResource);
    
    typedef struct D3D12_HEAP_PROPERTIES {
     D3D12_HEAP_TYPE     Type;
     D3D12_CPU_PAGE_PROPERTIES CPUPageProperties;
     D3D12_MEMORY_POOL    MemoryPoolPreference;
     UINT CreationNodeMask;
     UINT VisibleNodeMask;
    } D3D12_HEAP_PROPERTIES;

    pHeapProperties,我们要将资源提交到的堆的属性, 其中一些属性用于高级用法, 目前,我们需要担心的主要属性是D3D12_HEAP_TYPE,它可以是D3D12_HEAP_PROPERTIES枚举类型的以下成员之一:

    • D3D12_HEAP_TYPE_DEFAULT,默认堆, 这是我们提交将由GPU单独访问的资源地方,
      以深度/模板缓冲区为例:GPU读取和写入深度/模板缓冲区, CPU永远不需要访问它,因此深度/模板缓冲区将被放置在默认堆中。
    • D3D12_HEAP_TYPE_UPLOAD,上传堆, 这是我们提交资源的地方,我们需要将数据从CPU上传到GPU资源。
    • D3D12_HEAP_TYPE_READBACK,回读堆,这是我们提交需要由CPU读取的资源的地方。
    • D3D12_HEAP_TYPE_CUSTOM,有关高级使用方案,请参阅MSDN文档以获取更多信息。
      HeapMiscFlags,有关我们要将资源提交到的堆的其他标志, 它通常是D3D12_HEAP_MISC_NONE。
      pResourceDesc,指向描述我们要创建的资源的D3D12_RESOURCE_DESC实例的指针。
      InitialResourceState, 使用此参数可在创建资源时设置资源的初始状态,对于深度/模板缓冲区,初始状态将为D3D12_RESOURCE_USAGE_INITIAL,然后我们将其转换为D3D12_RESOURCE_USAGE_DEPTH,以便它可以作为深度/模板缓冲区绑定到管道。
      pOptimizedClearValue,指向D3D12_CLEAR_VALUE对象的指针,该对象描述用于清除资源的优化值, 可以为此值指定Null,以不指定优化的清除值。
    struct D3D12_CLEAR_VALUE
      {
        DXGI_FORMAT Format;
        union 
        {
            FLOAT Color[ 4 ];
          D3D12_DEPTH_STENCIL_VALUE DepthStencil;
        };
      }    D3D12_CLEAR_VALUE;

    riidResource,我们想要获取指针的ID3D12Resource接口的COM ID。
    ppvResource,返回指向ID3D12Resource的指针,该ID3D12Resource表示新创建的资源。

    此外,在使用深度/模板缓冲区之前,我们必须创建一个关联的深度/模板视图以绑定到管道, 这与创建渲染目标视图类似。 下面的代码示例显示了我们如何创建深度/模板纹理及其相应的深度/模板视图:

    D3D12_RESOURCE_DESC depthStencilDesc;
    depthStencilDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
    depthStencilDesc.Alignment = 0;
    depthStencilDesc.Width = mClientWidth;
    depthStencilDesc.Height = mClientHeight;
    depthStencilDesc.DepthOrArraySize = 1;
    depthStencilDesc.MipLevels = 1;
    depthStencilDesc.Format = mDepthStencilFormat;
    depthStencilDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
    depthStencilDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
    depthStencilDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
    depthStencilDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
    
    D3D12_CLEAR_VALUE optClear;
    optClear.Format = mDepthStencilFormat;
    optClear.DepthStencil.Depth = 1.0f;
    optClear.DepthStencil.Stencil = 0;
    ThrowIfFailed(md3dDevice->CreateCommittedResource(
      &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
      D3D12_HEAP_FLAG_NONE,
      &depthStencilDesc,
      D3D12_RESOURCE_STATE_COMMON,
      &optClear,
      IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf())));
    
    // Create descriptor to mip level 0 of entire resource using the
    // format of the resource.
    md3dDevice->CreateDepthStencilView(
      mDepthStencilBuffer.Get(),
      nullptr, 
      DepthStencilView());
    
    // Transition the resource from its initial state to be used as a depth buffer.
    mCommandList->ResourceBarrier(
      1, 
      &CD3DX12_RESOURCE_BARRIER::Transition(
        mDepthStencilBuffer.Get(),
        D3D12_RESOURCE_STATE_COMMON,
         D3D12_RESOURCE_STATE_DEPTH_WRITE));

    请注意,我们使用CD3DX12_HEAP_PROPERTIES辅助构造函数来创建堆属性结构,其实现方式如下:

    explicit CD3DX12_HEAP_PROPERTIES( 
        D3D12_HEAP_TYPE type, 
        UINT creationNodeMask = 1, 
        UINT nodeMask = 1 )
    {
      Type = type;
      CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
      MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
      CreationNodeMask = creationNodeMask;
      VisibleNodeMask = nodeMask;
    }

    CreateDepthStencilView的第二个参数是指向D3D12_DEPTH_STENCIL_VIEW_DESC的指针,除此之外,该结构描述了资源中元素的数据类型(格式), 如果资源是使用类型化格式创建的,则此参数可以为null,这表示创建此资源的第一个mipmap级别的视图(深度/模板缓冲区仅使用一个mipmap级别创建 )使用创建资源的格式, 因为我们指定了深度/模板缓冲区的类型,所以我们为此参数指定了null。

    • 设置视口
      通常我们喜欢将3D场景绘制到整个后台缓冲区,其中后台缓冲区大小对应于整个屏幕(全屏模式)或窗口的整个客户区域。 但是,有时我们只想将3D场景绘制到后缓冲区的子矩形中;
      这里写图片描述
      将3D场景绘制到后缓冲区的子矩形中, 然后将后缓冲区呈现给窗口的客户区域。我们绘制的后缓冲区的子矩形称为视口,它由以下结构描述:
    typedef struct D3D12_VIEWPORT {
      FLOAT TopLeftX;
      FLOAT TopLeftY;
      FLOAT Width;
      FLOAT Height;
      FLOAT MinDepth;
      FLOAT MaxDepth;
    } D3D12_VIEWPORT;

    前四个数据成员定义相对于后缓冲区的视口矩形(指定小数像素坐标,因为数据成员的类型为float)。 在Direct3D中,深度值以0到1的标准化范围存储在深度缓冲区中,MinDepth和MaxDepth成员用于将深度间隔[0,1]转换为深度间隔[MinDepth,MaxDepth]。 能够变换深度范围可以用于实现某些效果; 例如,您可以设置MinDepth = 0和MaxDepth = 0,以便使用此视口绘制的所有对象的深度值均为0,并显示在场景中所有其他对象的前面。 但是,通常将MinDepth设置为0并将MaxDepth设置为1,以便不修改深度值。这个通过Unity的相机视口就可以体验一下。

    一旦我们填写了D3D12_VIEWPORT结构,我们使用ID3D12CommandList :: RSSetViewports方法设置带有Direct3D的视口。 以下示例创建并设置一个绘制到整个后台缓冲区的视口:

    D3D12_VIEWPORT vp;
    vp.TopLeftX = 0.0f;
    vp.TopLeftY = 0.0f;
    vp.Width  = static_cast<float>(mClientWidth);
    vp.Height  = static_cast<float>(mClientHeight);
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    
    mCommandList->RSSetViewports(1, &vp);

    最后再给读者介绍一个技术点,我们可以定义相对于后缓冲区的裁剪矩形,使得该矩形外的像素被剔除(即,不被光栅化到后缓冲区)。 这可以用于优化。 例如,如果我们知道屏幕的某个区域将包含一个矩形UI元素,我们就不需要处理UI元素将模糊的3D世界的像素。
    裁剪矩形由D3D12_RECT结构定义,该结构的类型定义为以下结构:

    typedef struct tagRECT
    {
      LONG  left;
      LONG  top;
      LONG  right;
      LONG  bottom;
    } RECT;

    我们使用ID3D12CommandList :: RSSetScissorRects方法设置带有Direct3D的裁剪矩形,以下示例创建并设置一个覆盖后缓冲区左上象限的裁剪矩形:

    mScissorRect = { 0, 0, mClientWidth/2, mClientHeight/2 };
    mCommandList->RSSetScissorRects(1, &mScissorRect);

    与RSSetViewports类似,第一个参数是要绑定的裁剪矩形的数量(使用多个用于高级效果),第二个参数是指向矩形数组的指针。

    关于D3D12初始化工作就已经完成了,下篇博客我们基于这个流程搭建一个小小的框架用于实现我们的D3D12初始化。。。。。。

    展开全文
  • at TerraFX.Graphics.Providers.D3D12.D3D12GraphicsBuffer.get_D3D12Resource() in D:\terrafx\sources\Providers\Graphics\D3D12\D3D12GraphicsBuffer.cs:line 41 at TerraFX.Graphics.Providers.D3D12.D3D12...
  • D3D12_RASTERIZER_DESC结构表示的光栅化器状态组用于配置渲染管道的光栅化阶段: typedef struct D3D12_RASTERIZER_DESC { D3D12_FILL_MODE FillMode; // Default: D3D12_FILL_SOLID D3D12_CULL_MO...

    光栅化状态

    虽然渲染管道的许多部分都是可编程的,但某些部分只是可配置的, 由D3D12_RASTERIZER_DESC结构表示的光栅化器状态组用于配置渲染管道的光栅化阶段:

    typedef struct D3D12_RASTERIZER_DESC {
    D3D12_FILL_MODE FillMode;   // Default: D3D12_FILL_SOLID
      D3D12_CULL_MODE CullMode;   // Default: D3D12_CULL_BACK
      BOOL FrontCounterClockwise; // Default: false
      INT DepthBias;              // Default: 0
      FLOAT DepthBiasClamp;       // Default: 0.0f
      FLOAT SlopeScaledDepthBias; // Default: 0.0f
      BOOL DepthClipEnable;       // Default: true
      BOOL ScissorEnable;         // Default: false
      BOOL MultisampleEnable;     // Default: false
      BOOL AntialiasedLineEnable; // Default: false
      UINT ForcedSampleCount;     // Default: 0
    
      // Default: D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
      D3D12_CONSERVATIVE_RASTERIZATION_MODE ConservativeRaster;
    } D3D12_RASTERIZER_DESC;

    这个结构体有很多项,我们要使用它,就必须搞清楚这些项的含义,DX文档都有详细的介绍。本篇博客就不一一说明了,接下来我们看看如何使用。
    以下代码显示如何创建启用线框模式并禁用背面剔除的栅格化状态:

    CD3DX12_RASTERIZER_DESC rsDesc(D3D12_DEFAULT);
    rsDesc.FillMode = D3D12_FILL_WIREFRAME;
    rsDesc.CullMode = D3D12_CULL_NONE;

    CD3DX12_RASTERIZER_DESC是一个扩展类,它扩展了D3D12_RASTERIZER_DESC并添加了一些辅助构造函数。 特别是,它有一个构造函数,它接受类型为CD3D12_DEFAULT的对象,它只是一个用于重载的伪类型,表示光栅化器状态成员应初始化为默认值。 CD3D12_DEFAULT和D3D12_DEFAULT的定义如下:

    struct CD3D12_DEFAULT {};
    extern const DECLSPEC_SELECTANY CD3D12_DEFAULT D3D12_DEFAULT;

    管道状态

    前面,我们已经展示了如何描述输入布局描述,如何创建顶点和像素着色器,以及如何配置光栅化器状态组,我们还没有展示如何将这些对象绑定到图形管道以供实际使用。 控制图形管道状态的大多数对象被指定称为管道状态对象(PSO)的聚合,它由ID3D12PipelineState接口表示。 要创建PSO,我们首先通过填写D3D12_GRAPHICS_PIPELINE_STATE_DESC实例来描述它:

    typedef struct D3D12_GRAPHICS_PIPELINE_STATE_DESC
    {  ID3D12RootSignature *pRootSignature;
      D3D12_SHADER_BYTECODE VS;
      D3D12_SHADER_BYTECODE PS;
      D3D12_SHADER_BYTECODE DS;
      D3D12_SHADER_BYTECODE HS;
      D3D12_SHADER_BYTECODE GS;
      D3D12_STREAM_OUTPUT_DESC StreamOutput;
      D3D12_BLEND_DESC BlendState;
      UINT SampleMask;
      D3D12_RASTERIZER_DESC RasterizerState;
      D3D12_DEPTH_STENCIL_DESC DepthStencilState;
      D3D12_INPUT_LAYOUT_DESC InputLayout;
      D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType;
      UINT NumRenderTargets;
      DXGI_FORMAT RTVFormats[8];
      DXGI_FORMAT DSVFormat;
      DXGI_SAMPLE_DESC SampleDesc;
    } D3D12_GRAPHICS_PIPELINE_STATE_DESC;

    在我们填写了D3D12_GRAPHICS_PIPELINE_STATE_DESC实例之后,我们使用ID3D12Device :: CreateGraphicsPipelineState方法创建了一个ID3D12PipelineState对象:

    ComPtr<ID3D12RootSignature> mRootSignature;
    std::vector<D3D12_INPUT_ELEMENT_DESC> mInputLayout;
    ComPtr<ID3DBlob> mvsByteCode;
    ComPtr<ID3DBlob> mpsByteCode;
    …
    D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc;
    ZeroMemory(&psoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
    psoDesc.InputLayout = { mInputLayout.data(), (UINT)mInputLayout.size() };
    psoDesc.pRootSignature = mRootSignature.Get();
    psoDesc.VS = 
    { 
      reinterpret_cast<BYTE*>(mvsByteCode->GetBufferPointer()), 
      mvsByteCode->GetBufferSize() 
    };
    psoDesc.PS = 
    { 
      reinterpret_cast<BYTE*>(mpsByteCode->GetBufferPointer()), 
      mpsByteCode->GetBufferSize() 
    };
    psoDesc.RasterizerState = CD3D12_RASTERIZER_DESC(D3D12_DEFAULT);
    psoDesc.BlendState = CD3D12_BLEND_DESC(D3D12_DEFAULT);
    psoDesc.DepthStencilState = CD3D12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
    psoDesc.SampleMask = UINT_MAX;
    psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
    psoDesc.NumRenderTargets = 1;
    psoDesc.RTVFormats[0] = mBackBufferFormat;
    psoDesc.SampleDesc.Count = m4xMsaaState ? 4 : 1;
    psoDesc.SampleDesc.Quality = m4xMsaaState ? (m4xMsaaQuality - 1) : 0;
    psoDesc.DSVFormat = mDepthStencilFormat;
    
    ComPtr<ID3D12PipelineState> mPSO;
    md3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&mPSO)));

    这在一个ID3D12PipelineState对象中是相当多的状态,Direct3D可以验证所有状态是否兼容,并且驱动程序可以预先生成所有代码以编程硬件状态。在Direct3D 11状态模型中,这些渲染状态块分别设置,但是,如果一个状态发生变化,则可能还需要驱动程序将硬件重新编程为另一个依赖状态,由于许多状态已更改为配置管道,因此硬件状态可能会被重新编程,为了避免这种冗余,驱动程序通常推迟编程硬件状态,直到在整个流水线状态已发出绘制调用,它需要跟踪哪些状态已更改,然后生成代码以在运行时对硬件状态进行编程。在新的Direct3D 12模型中,驱动程序可以生成在初始化时编程管道状态所需的所有代码。

    注意,由于PSO验证和创建可能非常耗时,因此应在初始化时生成PSO。 一个例外可能是在第一次引用时根据需要在运行时创建PSO; 然后将其存储在一个集合(如哈希表)中,以便快速获取以供将来使用。

    并非所有渲染状态都封装在PSO中, 视口和裁剪矩形等某些状态是独立于PSO指定的。 这种状态可以有效地独立地设置到另一个流水线状态,因此通过将它们包括在PSO中没有什么优势。
    Direct3D基本上是一个状态机, 在我们改变之前,事情一直保持现状, 如果我们正在绘制的某些对象使用一个PSO,而我们正在绘制的其他对象需要不同的PSO,那么我们需要构建代码,如下所示:

    // Reset specifies initial PSO.
    mCommandList->Reset(mDirectCmdListAlloc.Get(), mPSO1.Get())
    /* …draw objects using PSO 1… */
    
    // Change PSO
    mCommandList->SetPipelineState(mPSO2.Get());
    /* …draw objects using PSO 2… */
    
    // Change PSO
    mCommandList->SetPipelineState(mPSO3.Get());
    /* …draw objects using PSO 3… */

    换句话说,当PSO绑定到命令列表时,它将不会更改,直到覆盖它(或重置命令列表)。

    展开全文
  • If I remove the D3D12EnableExperimentalFeatures the D3D12CreateDevice works fine, but then other crashes occur. <p>I am using a GTX 1050 TI with the latest nvidia driver and the lates windows insider ...
  • D3D12将资源状态管理从图形API层移交到应用层,迫使我们自己来管理资源的状态,我们不仅要正确的使用资源状态转换,还要保证转换的性能,这就需要我们深入的了解D3D12资源状态转换的一些规则。让我们先来看看D3D12都...

    D3D12将资源状态管理从图形API层移交到应用层,迫使我们自己来管理资源的状态,我们不仅要正确的使用资源状态转换,还要保证转换的性能,这就需要我们深入的了解D3D12资源状态转换的一些规则。让我们先来看看D3D12都有哪些应用场景。

    • Transition barrier 最常用的资源状态转换,比如RT->SRV。RT是写状态,SRV是读状态,因此我们需要建立一个转换屏障,保证写操作完成后再去读取资源。
    • Aliasing barrier D3D12允许一块堆内存被多个资源占用,这可以提高内存的利用率。因此我们需要一个别名屏障来保证同一时间只有一个资源在使用该内存。
    • Unordered access view (UAV) barrier 有的时候我们希望一个UAV先写完再读,虽然这样很低效,但是能保证结果是正确的。因此我们需要一个UAV屏障来保证写完再读。这个和转换屏障很类似,可以理解为转换屏障的UAV版本。

    Barrier在底层导致做了什么?

    • change state of memory for resource 首先它会改变内存的状态,比如从RT状态改变到SRV状态。写状态和读状态可能要求不同的数据格式,这取决于具体的资源类型及显卡的实现。
    • cache flushes 通常为了数据一致性,需要在转换的时候刷新缓存。
    • pipeline statlls 状态转换经常会造成gpu的空闲等待,比如在等待写完成。gpu内部其实也是分多个模块的,比如传输模块,计算模块。一个资源转换可能只占用传输模块,计算模块此时如果也被强制等待,就会造成gpu的资源浪费。再比如D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE和D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE。为什么将读状态拆分成像素着色器使用还是非像素着色器使用呢?一部分原因就是告诉显卡,如果发生资源状体转换,那么请只阻塞相应的模块,而没必要将整个gpu都阻塞。
    • 还有一些其他工作比如对内存的解压等等,为什么会有内存的解压,我没做过驱动层,只能感性理解,比如写内存的时候,不需要随机读取,因此我们可以压缩内存,减少带宽的使用,当读的时候就需要把它解压缩加速读取速度。这还取决于硬件是否支持。

    综上所述Barrier是一个很昂贵的api调用。如何优化Barrier的使用是一个复杂的问题,有几个基本的准则如下:

    尽量减少Barrier的使用

    尽量减少Barrier的使用,这貌似是一句废话,但是里面的细节却很重要。对于资源的状态转换,有些时候必须显示转换,有些时候不需显示转换,如果你显示转换了,那么就可能造成不必要的flushes和stalls。换句话说有些资源状态的转换是顺理成章的,应用层,驱动层,都不需要进行任何操作,硬件会很自然的正确使用资源,如果你此时强制使用了Barrier就可能强迫GPU进行不必要的flushes和stalls。那么问题来了,哪些资源转换是可以省略的?D3D12将这种省略的状态转换称为Implicit state transitions:"Resources can only be "promoted" out of D3D12_RESOURCE_STATE_COMMON. Similarly, resources will only "decay" to D3D12_RESOURCE_STATE_COMMON."对于隐式提升和衰减都是围绕D3D12_RESOURCE_STATE_COMMON状态进行的。为了方便理解隐式状态转换,D3D12将资源分成两类,一类是BufferSimultaneous-Access Textures,这类资源可以从common状态提升到任何状态,除了Depth-stencil resources,因为它必须是non-simultaneous-access textures。这类资源可以连续进行隐式资源转换,比如先转换成读,再转换成写,这些转换都不需要显式Barrier。另一类是Non-Simultaneous-Access Textures,这类资源只能隐式转换到某些状态,具体参考D3D12文档,而且一旦提升到写状态后,就不能继续隐式转换了,但是提升到读后是可以继续隐式转换的。当一个ExecuteCommandLists执行完毕后,以下情况的资源会自动退回到common状态(注意当ExecuteCommandLists调用多个CLS时是无法进行自动回退的。这里就有一个鱼和熊掌的问题了,D3D12要求我们尽可能的合并ExecuteCommandLists的调用,因为可以降低DDI层的消耗,因为调用这个API,操作系统会从用户态切换到系统态,另外驱动层也可以进行一些优化,比如去掉冗余的命令,让显卡异步调用起来。但是合并的ExecuteCommandLists的调用会阻止common状态的回退,导致我们可能需要显式状态转换)

    • 任何资源被拷贝队列使用
    • buffer资源或被标记为D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS状态的贴图资源在任何队列中使用
    • 任何资源隐式提升到只读状态

    其实可以简单理解Common状态的资源,这类资源需要满足以下两个条件:

    1) Have no pending write operations, cache flushes or layout changes.

    2) Have a layout that is intrinsically readable by any GPU operation.

    因为D3D12标准要求ExecuteCommandLists执行后不允许有任何未完成的操作包括cache flushes and layout changes。因此ExecuteCommandLists之后的资源只需要满足第2条就可以回退到common状态。

    因为Buffers and Simultaneous-Access Textures必须保证所有gpu操作都可以读,而且写操作不允许改变layout,因此只要它们没有被使用就会退回common状态。正是因为这个原因,这类资源可以连续进行隐式转换。

    总结能隐式转换的时候不要显式转换

    除非必须,不要转换到D3D12_RESOURCE_STATE_GENERIC_READ和D3D12_RESOURCE_STATE_COMMON状态

    转换到D3D12_RESOURCE_STATE_GENERIC_READ将会导致者整个gpu流水线的flushes和stalls。因为D3D12_RESOURCE_STATE_GENERIC_READ代表了整个流水线都可以Read。只有上传堆资源可以使用D3D12_RESOURCE_STATE_GENERIC_READ状态,因为上传只涉及到cpu的拷贝,占用的是cpu timeline。

    不允许进行read to read 的转换,这会增加转换的开销,可以将其转换到combine read state。

    我们希望D3D12_RESOURCE_STATE_COMMON状态可以隐式转换,因此不建议显示转换到common状态,除非必要的时候。一种情况是在copy queue中使用的资源,如果之前是在graphic or compute engine使用的资源,在copy queue使用之前需要将其转换到common state,反之亦然。另一种情况是cpu需要访问的贴图资源,需要将资源放入到 CPU-visible heap中,并且资源的状态需要是common状态。

    尽量batch Barrier

    可以减少DDI的调用,驱动层也可以尝试进行优化,比如去掉冗余的状态转换

    使用Split Barriers

    如果一个转换过程不需要立即转换,我们可以把这个转换过程的实际时间拉长,通过start和end告诉驱动,在start和end期间可以安排异步执行的任务。比如D3D12_RESOURCE_STATE_DEPTH_WRITE状态转换到D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE。深度写转换到pixel读的时候,占用大量的带宽资源,但是ALU相对空闲,如果不使用split barriers,gpu资源就会浪费ALU部分。如果使用split barriers,驱动程序就有可能会安排一些ALU的任务同步执行。

    我们可以利用D3D12的debug layer帮组我们正确的使用Barriers,比如AssertResourceState API。

    理解了以上内容,也就理解了AMD和Nivida对于barrier的使用建议,最后附上它们的使用建议。

    AMD Barriers

    Barriers are how dependencies between operations are conveyed to the API and driver. Barriers open up a whole new world of operations by allowing the application to decide if the GPU can overlap work. They are also an easy way to slow down the rendering by adding too many barriers or can cause corruptions from not having correct resource transitions. The validation layers can often help with identifying missing barriers.

    • Minimize the number of barriers used per frame.
      • Barriers can drain the GPU of work.
      • Don’t issue read to read barriers. Transition the resource into the correct state the first time.
    • Batch groups of barriers into a single call to reduce overhead of barriers.
      • This creates less calls into the driver and allows the driver to remove redundant operations.
    • Avoid GENERAL / COMMON layouts unless required.
      • Always use the optimized state for your usage.

    Nivida

    • Minimize the use of barriers and fences
      • We have seen redundant barriers and associated wait for idle operations as a major performance problem for DX11 to DX12 ports
        • The DX11 driver is doing a great job of reducing barriers – now under DX12 you need to do it
      • Any barrier or fence can limit parallelism
    • Make sure to always use the minimum set of resource usage flags
      • Stay away from using D3D12_RESOURCE_USAGE_GENERIC_READ unless you really need every single flag that is set in this combination of flags
      • Redundant flags may trigger redundant flushes and stalls and slow down your game unnecessarily
      • To reiterate: We have seen redundant and/or overly conservative barrier flags and their associated wait for idle operations as a major performance problem for DX11 to DX12 ports.
    • Specify the minimum set of targets in ID3D12CommandList::ResourceBarrier
      • Adding false dependencies adds redundancy
    • Group barriers in one call to ID3D12CommandList::ResourceBarrier
      • This way the worst case can be picked instead of sequentially going through all barriers
    • Use split barriers when possible
      • Use the _BEGIN_ONLY/_END_ONLY flags
      • This helps the driver doing a more efficient job
    • Do use fences to signal events/advance across calls to ExecuteCommandLists

    Dont's

    • Don’t insert redundant barriers
      • This limits parallelism
      • A transition from D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE to D3D12_RESOURCE_STATE_RENDER_TARGET and back without any draw calls in-between is redundant
      • Avoid read-to-read barriers
        • Get the resource in the right state for all subsequent reads
    • Don’t use D3D12_RESOURCE_USAGE_GENERIC_READ without good reason.
      • For transitions from write-to-read states, ensure the transition target is inclusive of all required read states needed before the next transition to write. This is done from the API by combining read state flags– and is preferred over transitioning from read-to-read in subsequent ResourceBarrier calls.
    • Don’t sequentially call ID3D12CommandList::ResourceBarrier with just one barrier
      • This doesn’t allow the driver to pick the worst case of a set of barriers
    • Don’t expect fences to trigger signals/advance at a finer granularity then once per ExecuteCommandLists call.
    展开全文
  • d3d12compute test failures

    2021-01-10 15:23:23
    host-d3d12compute. For a sample failure, see https://buildbot.halide-lang.org/master/#/builders/62/builds/241/steps/26/logs/stdio (this is of a testbranch but I verified separately that recent master ...
  • D3D12/DXGUID broken?

    2021-01-09 07:19:12
    <div><p>My current task is to implement a simple abstraction above D3D12 using pure C. <p>In the initialisation procedure I reference <code>IID_IDXGIAdapter4</code> (Querying interface from ...
  • <div><p>I see C+... Can you write D3D12 in C? Are there any official examples of using D3D12 with C? <p>Thanks!</p><p>该提问来源于开源项目:microsoft/DirectX-Graphics-Samples</p></div>
  • <div><p>I saw <a href="https://mobile.twitter.com/adamjmiles/status/1183846317694607364">this tweet</a> that got me thinking: why core Vulkan 1.0 is more verbose than D3D12? It does more things than ...
  • D3D12 ERROR: ID3D12Resource2::<final-release>: CORRUPTION: An ID3D12Resource object (0x000002FF41239E40:'Unnamed Object') is referenced by GPU operations in-flight on Command Queue (0x...
  • ) (subresource: 0) specified by transition barrier does not match with the state (0x80: D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) specified in the previous call to ResourceBarrier [ RESOURCE_...
  • kiero:适用于D3D9-D3D12,基于OpenGL和Vulkan的游戏的通用图形挂钩
  • D3D12 WARNING: ID3D12Resource::ID3D12Resource::Map: pReadRange is NULL and the heap page property is WRITE_BACK. This can be an indicator of inefficiencies that will result on mobile systems, as the ...
  • d share the World of Warcraft patches that you need to run WoW with D3D12. You decide if you wanna toss them in the game-specific and custom folders. <p>All these patches have been posted on the wine-...
  • Would be nice if you can add a simple stereoscopic sample using D3D12 using recommended best practices for creating the 2 back buffers,etc.. Thanks..</p><p>该提问来源于开源项目:microsoft/...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,316
精华内容 526
关键字:

d3d12