CPU与GPU间的交互

1.每个GPU中都至少有一个命令队列。CPU可以通过Direct3D API用命令列表向该队列提交命令,而这些命令则指挥GPU执行某些操作。在命令没有到达队列首部以前,用户所提交的命令是无法被执行的。如果命令队列内为空,则GPU会因为没有任务要去处理而处于空闲状态;但若命令队列被装的太满,则CPU将在某个时刻因提交命令的速度追上GPU执行命令的速度而进入空闲状态。值得一提的是,这两种情景其实都没有充分地利用系统资源。

2.GPU是系统中与CPU一起并行工作的第二种处理器。有时,我们需要对CPU与GPU进行同步。例如。若GPU命令队列中有一条引用某资源的命令,那么在GPU完成此命令的处理之前,CPU就不能修改或撤销这一资源。任何同步方法都会导致其中的一种处理器处于等待和空闲的状态,这意味着两种处理器并没有被充分利用,因此,我们应尽量减少同步的次数,并缩短同步的时间。

3.IID_PPV_ARGS辅助宏的内部通过__uuidof函数来获取指定COM对象对应的全局唯一标识符。定义如下所示:

#define IID_PPV_ARGS(ppType) __uuidof(**(ppType)), IID_PPV_ARGS_Helper(ppType)

4.命令分配器具有以下特性:
1>.记录在命令列表内的命令,实际上是存储在与之关联的命令分配器上。
2>.命令分配器被抽象为ID3D12CommandAllocator接口来表示。
3>.通过ID3D12Device::CreateCommandAllocator函数来创建命令分配器。
4>.在确定GPU执行完命令分配器中的所有命令时,可以通过ID3D12CommandAllocator::Reset函数来重置命令分配器,从而复用其内存。
5>.每个线程一般都仅使用属于自己的命令分配器。

5.命令列表具有以下特性:
1>.命令列表被抽象为ID3D12CommandList接口来表示。ID3D12GraphicsCommandList接口继承自ID3D12CommandList接口。
2>.通过ID3D12Device::CreateCommandList函数来创建命令列表。
3>.通过ID3D12GraphicsCommandList::Reset函数来将命令列表恢复为刚创建时的初始状态,从而复用其内存。此时相关的命令分配器仍在维护着其内存中被命令队列引用的系列命令。
4>.当创建或重置一个命令列表时,它会处于一种"打开"的状态。所以,当尝试为同一个命令分配器连续创建两个命令列表时,就会抛出异常错误。
5>.可以创建出多个关联于同一命令分配器的命令列表,但是不能同时用它们来记录命令。因此,当其中的一个命令列表在记录命令时,必须关闭同一命令分配器的其他命令列表。
6>.通过ID3D12GraphicsCommandList::RSSetViewports函数来向命令列表中添加设置视口命令。
7>.通过ID3D12GraphicsCommandList::RSSetScissorRects函数来向命令列表中添加设置裁剪矩形命令。
8>.通过ID3D12GraphicsCommandList::ClearRenderTargetView函数来向命令列表中添加用指定颜色来清除渲染目标视图命令。
9>.通过ID3D12GraphicsCommandList::ClearDepthStencilView函数来向命令列表中添加清除深度/模板视图命令。
10>.通过ID3D12GraphicsCommandList::OMSetRenderTargets函数来向命令列表中添加设置渲染流水线上使用的渲染目标和深度/模板缓冲区命令。
11>.通过ID3D12GraphicsCommandList::DrawIndexedInstanced函数来向命令列表中添加发起绘制调用命令。
12>.通过ID3D12GraphicsCommandList::Close函数来结束命令的记录。
13>.每个线程通常都只使用各自的命令列表。出于性能的原因,应用程序必须在初始化期间,指出用于并行记录命令的命令列表最大数量。

6.命令队列具有以下特性:
1>.命令队列被抽象为ID3D12CommandQueue接口来表示。
2>.D3D12_COMMAND_QUEUE_DESC结构体用来来描述命令队列。
3>.通过ID3D12Device::CreateCommandQueue函数来创建命令队列。
4>.通过ID3D12CommandQueue::ExecuteCommandLists函数来将命令列表中的命令添加到命令队列中。此时命令队列就会引用命令列表关联的命令分配器中的命令。
5>.多线程可以同时访问同一个命令队列,也能同时调用该命令队列的函数。

7.围栏具有以下特性:
1>.围栏被抽象为ID3D12Fence接口来表示。它的内部维护着一个类型为UINT64的围栏点。
2>.围栏用于实现GPU和CPU之间的同步:强制CPU等待,直到GPU完成所有命令的处理,达到某个指定的围栏点为止。
3>.通过ID3D12Device::CreateFence函数来创建围栏。
4>.通过ID3D12CommandQueue::Signal函数从GPU端设置围栏点,而ID3D12Fence::Signal函数则从CPU端设置围栏点。
5>.通过ID3D12Fence::GetCompleteValue函数来获取当前的围栏点。
6>.通过ID3D12Fence::SetEventOnCompletion函数来当GPU命中围栏点时,就激发事件。
7>.参考代码如下所示:

// 初始围栏点为0
UINT64 mCurrentFence = 0;
void D3DApp::FlushCommandQueue()
{
	// 增加围栏点,接下来将命令标记到此围栏点
	mCurrentFence++;

	// 向命令对列中添加一条用来设置新围栏点的命令
	// 由于这条命令要交由GPU处理,所以在GPU处理完命令队列中的此Signal()的
	// 所有命令之前,它并不会设置新的围栏点
	ThrowFailed(mCommandQueue->Signal(mFence.Get(), mCurrentFence));

	// 在CPU端等待GPU,直到后者执行完这个围栏点之前的所有命令
	if (mFence->GetCompleteValue() < mCurrentFence)
	{
		HANDLE eventHandle = CreateEventEx(nullptr, false, false, EVENT_ALL_ACCESS);
		
		// 若GPU命中当前的围栏,则激发预定事件
		ThrowFailed(mFence->SetEventOnCompletion(mCurrentFence, eventHandle));

		// 等待GPU命中围栏,激发事件
		WaitForSingleObject(eventHandle, INFINITE);
		CloseHandle(eventHandle);
	}
}

8.资源转换具有以下特性:
1>.当GPU的写操作还没完成抑或甚至还没开始,就开始读取资源,便会导致资源冒险。
2>.资源屏障用D3D12_RESOURCE_BARRIER结构体来表示。CD3D12_RESOURCE_BARRIER结构体继承自D3D12_RESOURCE_BARRIER。
3>.通过ID3D12GraphicsCommandList::ResourceBarrier函数来设置转换资源屏障数组,即可指定资源的转换。
4>.可以将此资源屏障转换看作是一条告知GPU某资源状态正在进行转换的命令。所以在执行后续的命令时,GPU便会采取必要的措施以防资源冒险。
5>.纹理从呈现状态转换为渲染目标状态的参考代码如下所示:

static inline CD3D12_RESOURCE_BARRIER Transition(
	_In_ ID3D12Resource* pResource,
	D3D12_RESOURCE_STATES stateBefore,
	D3D12_RESOURCE_STATES stateAfter,
	UINT subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES,
	D3D12_RESOURCE_BARRIER_FLAGS flags = D3D12_RESOURCE_BARRIER_FLAG_NONE)
{
	CD3D12_RESOURCE_BARRIER result;
	ZeroMemory(&result, sizeof(result));
	D3D12_RESOURCE_BARRIER &barrier = result;
	result.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
	result.Flag = flags;
	barrier.Transition.pResource = pResource;
	barrier.Transition.StateBefore= stateBefore;
	barrier.Transition.StateAfter= stateAfter;
	barrier.Transition.Subresource = subresource ;
	
	return result;
}

mCommandList->ResourceBarrier(1, &CD3D12_RESOURCE_BARRIER::Transition(
	CurrentBackBuffer, 
	D3D12_RESOURCE_STATE_PRESENT,
	 D3D12_RESOURCE_STATE_RENDER_TARGET));

你可能感兴趣的:(#,DirectX,12,3D游戏开发实战)