第四章d3d初始化

DX12与以前的版本相比,更接近于底层。

主要是考虑了两个同步:GPU与CPU的同步,和GPU与GPU的同步

1,GPU与CPU的同步

加入了命令队列和命令列表,命令管理器。

 

CPU好像列车长秘书,GPU好像列车。奇葩的是,这个列车有N个车头,也有N个驾驶员。列车可以上天入地。

列车长秘书有一个笔记本,他把列车长每天的命令写入活页,放入笔记本。这个笔记本就是命令队列,命令列表就是接收的列车长的当天一系列命令,命令管理器就是各个活页。当然,可能不止一个列车长秘书,但是,只有一只笔,必须等一个活页放入笔记本后,才能写下一条命令。这里暂时认为只有一个秘书。简化之。命令列表可以忘记,只要活页还在就行。好,先生成笔记本和活页和列车长今天的命令列表。另外,怕完不成任务,造成命令堆积。则放个围栏,等到完成当天任务时,秘书才能进入放笔记本的屋子,否则只能在围栏外等待。
    md3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence));
    md3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue));
    md3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(mDirectCmdListAlloc.GetAddressOf()));
    md3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, mDirectCmdListAlloc.Get(), nullptr, IID_PPV_ARGS(mCommandList.GetAddressOf()));

开始时命令队列是关着的。
    mCommandList->Close();

由于有多个车头(交换链mSwapChain),造几个车头
    mdxgiFactory->CreateSwapChain(mCommandQueue.Get(), &sd, mSwapChain.GetAddressOf());

接下来创建列车的上天入地功能,
    md3dDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(mRTVHeap.GetAddressOf()));
    md3dDevice->CreateDescriptorHeap(&dsvHeapDesc, IID_PPV_ARGS(mDsvHeap.GetAddressOf()));

这时候命令发的可能快了,先歇会,等执行完了再开始
    FlushCommandQueue();

接下来继续,调整火车头
    mSwapChain->ResizeBuffers(SwapChainBufferCount, mClientWidth, mClientHeight, mBackBufferFormat, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);

//根据不同的车头,换不同的风景,即渲染目标视图和深度/模板缓冲区
    CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHeapHandle(mRTVHeap->GetCPUDescriptorHandleForHeapStart());
    for (UINT i = 0; i < SwapChainBufferCount; i++)
    {
        //获得交换链内的第i个缓冲区
        mSwapChain->GetBuffer(i, IID_PPV_ARGS(&mSwapChainBuffer[i]));
        //为此缓冲区创建一个RTV
        md3dDevice->CreateRenderTargetView(mSwapChainBuffer[i].Get(), nullptr, rtvHeapHandle);
        //偏移到描述符堆中的下一个缓冲区
        rtvHeapHandle.Offset(1, mRtvDescriptorSize);
    }


    md3dDevice->CreateCommittedResource(&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &depthStencilDesc,
        D3D12_RESOURCE_STATE_COMMON,
        &optClear,
        IID_PPV_ARGS(mDepthStencilBuffer.GetAddressOf()));

//列车长今天的任务写完了,放下笔,把活页放入笔记本,开始执行。


    mCommandList->Close();
    mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

2,GPU与GPU同步(资源冒险)

 

这个笔记本,放着列车长的命令,将会送到驾驶员组里。由于只有一个列车,这些驾驶员都要开车执行任务,咋办呢?其他的好说,一个列车不能多个驾驶员同时开。

 

没事,在陆地行驶时,转换到陆地行驶模式,在这个模式下,只有陆地驾驶员开。到天空时,换为飞行模式,由飞行员开。但是,怎么才能知道目前列车占着没有?当然是驾驶员室门把手上放着请勿进入牌子了。


    mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(currentBackBuffer(),
        D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

    mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(currentBackBuffer(),
        D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

 

现在看看列车长每个命令都是怎么发布的.

秘书,换个活页记录.
    ThrowIfFailed(mDirectCmdListAlloc->Reset());

我开始要说命令了,记住.
    mCommandList->Reset(mDirectCmdListAlloc.Get(), nullptr);

先让驾驶员A到火星上
    mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(currentBackBuffer(),
        D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET));

//去火星有些注意事项,及要执行的任务。
    mCommandList->RSSetViewports(1, &mScreenViewport);
    mCommandList->RSSetScissorRects(1, &mScissorRect);
    mCommandList->ClearRenderTargetView(currentBackBufferView(), Colors::LightSteelBlue, 0, nullptr);
    mCommandList->ClearDepthStencilView(depthStencilView(), D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
    mCommandList->OMSetRenderTargets(1, &currentBackBufferView(), true, &depthStencilView());

  火星任务执行后,返回地球,开启陆地模式
    mCommandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(currentBackBuffer(),
        D3D12_RESOURCE_STATE_RENDER_TARGET, D3D12_RESOURCE_STATE_PRESENT));

  我的命令完了。
    mCommandList->Close();

开始执行吧
    ID3D12CommandList* cmdsLists[] = { mCommandList.Get() };
    mCommandQueue->ExecuteCommandLists(_countof(cmdsLists), cmdsLists);

   顺便请把电视换到我喜欢看的喜羊羊。
    ThrowIfFailed(mSwapChain->Present(0, 0));
    mCurrBackBuffer = (mCurrBackBuffer + 1) % SwapChainBufferCount;

任务完成前不要烦我,我要睡觉了
    FlushCommandQueue();

 

然后把门一关,看喜羊羊了。可以确保能看到任务完成。


    //增加围栏值,接下来命令标记到此围栏点
    mCurrentFence++;

    //向命令队列添加一条用来设置新围栏点的命令,这条命令由GPU处理,即由GPU端来修改围栏值,所以再GPU处理完命令队列中此signal()的所有命令前,并不会设置新的围栏点
    mCommandQueue->Signal(mFence.Get(), mCurrentFence);
    //在cpu段等待GPU,直到后者执行完这个围栏点之前的所有命令
    if (mFence->GetCompletedValue() < mCurrentFence)
    {
        HANDLE eventHandle = CreateEventEx(nullptr, nullptr, false, EVENT_ALL_ACCESS);
        //若GPU命中当前的围栏,即执行到SIGNAL()
        mFence->SetEventOnCompletion(mCurrentFence, eventHandle);
        //等待GPU命中围栏,激发事件
        WaitForSingleObject(eventHandle, INFINITE);
        CloseHandle(eventHandle);

    }

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值