이 항목에서는 Direct3D 12 앱에서 명령 목록 및 번들을 기록하는 방법에 대해 설명합니다. 명령 목록과 번들을 모두 사용하면 앱이 GPU(그래픽 처리 장치)에서 나중에 실행하기 위한 그리기 또는 상태 변경 호출을 기록할 수 있습니다.
명령 목록 외에도 API는 번들이라고 하는 두 번째 수준의 명령 목록을 추가하여 GPU 하드웨어에 있는 기능을 악용합니다. 번들의 목적은 앱이 나중에 실행하기 위해 소수의 API 명령을 함께 그룹화할 수 있도록 하는 것입니다. 번들 생성 시 드라이버는 가능한 한 많은 사전 처리를 수행하여 나중에 실행하기 위해 저렴하게 만듭니다. 번들은 여러 번 사용하고 재사용할 수 있도록 설계되었습니다. 반면 명령 목록은 일반적으로 한 번만 실행됩니다. 그러나 명령 목록 여러 번 실행할 수 있습니다(애플리케이션이 새 실행을 제출하기 전에 이전 실행이 완료되었는지 확인하는 한).
일반적으로 API 호출이 번들로 묶이고, 이러한 API 호출과 번들이 명령 목록으로, 명령 목록이 단일 프레임으로 결합되는 과정은 다음의 다이어그램에 나타나 있습니다. 이 다이어그램에서는 번들 1이 명령 목록 1 및 명령 목록 2에서 재사용되는 것을 볼 수 있으며, 다이어그램의 API 메서드 이름은 단지 예시일 뿐, 다양한 API 호출을 사용 가능합니다.
으로 빌드하는
번들 및 직접 명령 목록을 만들고 실행하는 데는 여러 가지 제한 사항이 있으며, 이러한 차이점은 이 항목 전체에서 설명합니다.
명령 목록 만들기
직접 명령 목록과 번들은 ID3D12Device::CreateCommandList 또는 ID3D12Device4::CreateCommandList1을(를) 호출하여 생성됩니다.
ID3D12Device4::CreateCommandList1 사용하여 새 목록을 만들고 즉시 닫는 대신, 미리 닫힌 명령 목록을 만듭니다. 이렇게 하면 할당자 및 PSO로 목록을 생성하지만 실제로 사용하지 않는 비효율성을 방지할 수 있습니다.
ID3D12Device::CreateCommandList 다음 매개 변수를 입력으로 사용합니다.
D3D12_COMMAND_LIST_TYPE
D3D12_COMMAND_LIST_TYPE 열거형은 생성되는 명령 목록의 형식을 나타냅니다. 직접 명령 목록, 번들, 컴퓨팅 명령 목록 또는 복사 명령 목록일 수 있습니다.
ID3D12CommandAllocator
명령 할당자를 사용하면 앱이 명령 목록에 할당된 메모리를 관리할 수 있습니다. 명령 할당자는 CreateCommandAllocator호출하여 생성됩니다. 명령 목록을 만들 때 D3D12_COMMAND_LIST_TYPE지정된 할당자의 명령 목록 형식은 생성되는 명령 목록의 형식과 일치해야 합니다. 지정된 할당자는 한 번에 명령 목록을 기록하는 하나 이상 연결할 수 있지만, 하나의 명령 할당자를 사용하여 GraphicsCommandList 개체 수를 만들 수 있습니다.
명령 할당자가 할당한 메모리를 회수하기 위해 앱은 id3D12CommandAllocator::Reset호출합니다. 이렇게 하면 할당자를 새 명령에 다시 사용할 수 있지만 기본 크기는 줄어들지 않습니다. 그러나 이렇게 하기 전에 앱은 GPU가 할당자와 연결된 명령 목록을 더 이상 실행하지 않는지 확인해야 합니다. 그렇지 않으면 호출이 실패합니다. 또한 이 API는 자유 스레드가 아니므로 여러 스레드에서 동시에 동일한 할당자에서 호출할 수 없습니다.
ID3D12PipelineState
명령 목록의 초기 파이프라인 상태입니다. Microsoft Direct3D 12에서 대부분의 그래픽 파이프라인 상태는 ID3D12PipelineState 개체를 사용하여 명령 목록 내에서 설정됩니다. 앱은 일반적으로 앱 초기화 중에 많은 수의 개체를 만든 다음 ID3D12GraphicsCommandList::SetPipelineState사용하여 현재 바인딩된 상태 개체를 변경하여 상태가 업데이트됩니다. 파이프라인 상태 개체에 대한 자세한 내용은 Direct3D 12그래픽 파이프라인 상태 관리를 참조하세요.
번들은 부모인 직접 명령 목록의 이전 호출에 의해 설정된 파이프라인 상태를 상속하지 않습니다.
이 매개 변수가 NULL이면 기본 상태가 사용됩니다.
명령 목록 기록
만든 직후 명령 목록은 기록 상태에 있습니다. 또한 ID3D12GraphicsCommandList::Reset호출하여 기존 명령 목록을 다시 사용할 수 있습니다. 그러면 명령 목록이 기록 상태로 유지됩니다. ID3D12CommandAllocator::Reset와는 달리, 명령 목록이 실행되는 동안에도 Reset를 호출할 수 있습니다. 일반적인 패턴은 명령 목록을 제출한 다음 즉시 다시 설정하여 할당된 메모리를 다른 명령 목록에 다시 사용하는 것입니다. 각 명령 할당자와 연결된 하나의 명령 목록만 한 번에 기록 상태일 수 있습니다.
명령 목록이 기록 상태가 되면 ID3D12GraphicsCommandList 인터페이스의 메서드를 호출하여 목록에 명령을 추가하기만 하면 됩니다. 이러한 방법 중 상당수는 Microsoft Direct3D 11 개발자에게 친숙한 일반적인 Direct3D 기능을 지원합니다. 다른 API는 Direct3D 12의 새로운 기능입니다.
명령 목록에 명령을 추가한 후 닫기호출하여 명령 목록을 기록 상태에서 전환합니다.
명령 할당자는 증가할 수 있지만 축소되지 않습니다. 앱의 효율성을 극대화하려면 할당자를 풀링하고 다시 사용하는 것이 좋습니다. 한 번에 하나의 목록만 지정된 할당자에 기록하는 경우 다시 설정하기 전에 동일한 할당자에 여러 목록을 기록할 수 있습니다. 각 목록은 ID3D12CommandQueue::ExecuteCommandLists가실행할를 나타내는 할당자의 일부를 소유하고 있는 것으로 시각화할 수 있습니다.
간단한 할당자 풀링 전략은 약 numCommandLists * MaxFrameLatency 할당자를 목표로 해야 합니다. 예를 들어 6개의 목록을 기록하고 최대 3개의 지연 프레임을 허용하는 경우 18~20개의 할당자를 예상할 수 있습니다. 같은 스레드에서 여러 목록에 대해 할당자를 재사용하는 보다 고급 풀링 전략은 numRecordingThreads * MaxFrameLatency 할당자를 목표로 할 수 있습니다. 이전 예제를 사용하여 스레드 A에 2개, 스레드 B에 2개, 스레드 C에서 1개, 스레드 D에 1개의 목록을 기록한 경우 현실적으로 12-14개의 할당자를 목표로 할 수 있습니다.
펜스를 사용하여 지정된 할당자를 다시 사용할 수 있는 시기를 확인합니다.
명령 목록은 실행 후 즉시 다시 설정할 수 있으므로, ID3D12CommandQueue::ExecuteCommandLists을 호출할 때마다 간단하게 풀링하여 풀에 다시 추가할 수 있습니다.
본보기
다음 코드 조각은 명령 목록의 생성 및 기록을 보여 줍니다. 이 예제에는 다음과 같은 Direct3D 12 기능이 포함되어 있습니다.
- 파이프라인 상태 개체 - 명령 목록 내에서 렌더링 파이프라인의 상태 매개 변수 대부분을 설정하는 데 사용됩니다. 자세한 내용은 Direct3D 12그래픽 파이프라인 상태 관리를 참조하세요.
- 설명자 힙 - 앱은 설명자 힙을 사용하여 메모리 리소스에 대한 파이프라인 바인딩을 관리합니다.
- 리소스 장벽 - 이는 리소스를 렌더 타겟 뷰에서 셰이더 리소스 뷰와 같이 한 상태에서 다른 상태로 관리하며 전환하는 데 사용됩니다. 자세한 내용은 리소스 장벽을 사용하여 리소스 상태를 동기화하는참조하세요.
예를 들어
void D3D12HelloTriangle::LoadAssets()
{
// Create an empty root signature.
{
CD3DX12_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Init(0, nullptr, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ThrowIfFailed(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature)));
}
// Create the pipeline state, which includes compiling and loading shaders.
{
ComPtr<ID3DBlob> vertexShader;
ComPtr<ID3DBlob> pixelShader;
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
UINT compileFlags = 0;
#endif
ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr));
ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr));
// Define the vertex input layout.
D3D12_INPUT_ELEMENT_DESC inputElementDescs[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
};
// Describe and create the graphics pipeline state object (PSO).
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {};
psoDesc.InputLayout = { inputElementDescs, _countof(inputElementDescs) };
psoDesc.pRootSignature = m_rootSignature.Get();
psoDesc.VS = { reinterpret_cast<UINT8*>(vertexShader->GetBufferPointer()), vertexShader->GetBufferSize() };
psoDesc.PS = { reinterpret_cast<UINT8*>(pixelShader->GetBufferPointer()), pixelShader->GetBufferSize() };
psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
psoDesc.DepthStencilState.DepthEnable = FALSE;
psoDesc.DepthStencilState.StencilEnable = FALSE;
psoDesc.SampleMask = UINT_MAX;
psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
psoDesc.NumRenderTargets = 1;
psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
psoDesc.SampleDesc.Count = 1;
ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState)));
}
// Create the command list.
ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList)));
// Command lists are created in the recording state, but there is nothing
// to record yet. The main loop expects it to be closed, so close it now.
ThrowIfFailed(m_commandList->Close());
// Create the vertex buffer.
{
// Define the geometry for a triangle.
Vertex triangleVertices[] =
{
{ { 0.0f, 0.25f * m_aspectRatio, 0.0f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
{ { 0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { -0.25f, -0.25f * m_aspectRatio, 0.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } }
};
const UINT vertexBufferSize = sizeof(triangleVertices);
// Note: using upload heaps to transfer static data like vert buffers is not
// recommended. Every time the GPU needs it, the upload heap will be marshalled
// over. Please read up on Default Heap usage. An upload heap is used here for
// code simplicity and because there are very few verts to actually transfer.
ThrowIfFailed(m_device->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&m_vertexBuffer)));
// Copy the triangle data to the vertex buffer.
UINT8* pVertexDataBegin;
CD3DX12_RANGE readRange(0, 0); // We do not intend to read from this resource on the CPU.
ThrowIfFailed(m_vertexBuffer->Map(0, &readRange, reinterpret_cast<void**>(&pVertexDataBegin)));
memcpy(pVertexDataBegin, triangleVertices, sizeof(triangleVertices));
m_vertexBuffer->Unmap(0, nullptr);
// Initialize the vertex buffer view.
m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress();
m_vertexBufferView.StrideInBytes = sizeof(Vertex);
m_vertexBufferView.SizeInBytes = vertexBufferSize;
}
// Create synchronization objects and wait until assets have been uploaded to the GPU.
{
ThrowIfFailed(m_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence)));
m_fenceValue = 1;
// Create an event handle to use for frame synchronization.
m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (m_fenceEvent == nullptr)
{
ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
}
// Wait for the command list to execute; we are reusing the same command
// list in our main loop but for now, we just want to wait for setup to
// complete before continuing.
WaitForPreviousFrame();
}
}
명령 목록이 만들어지고 기록되면 명령 큐를 사용하여 실행할 수 있습니다. 자세한 내용은 명령 목록 실행 및 동기화를 참조하세요.
참조 수 계산
대부분의 D3D12 API는 COM 규칙에 따라 참조 계산을 계속 사용합니다. 이에 대한 주목할 만한 예외는 D3D12 그래픽 명령 목록 API입니다. ID3D12GraphicsCommandList 모든 API에는 해당 API에 전달된 개체에 대한 참조가 없습니다. 즉, 애플리케이션은 소멸된 리소스를 참조하는 실행을 위해 명령 목록이 제출되지 않도록 할 책임이 있습니다.
명령 목록 오류
ID3D12GraphicsCommandList 대부분의 API는 오류를 반환하지 않습니다. 명령 목록을 만드는 동안 발생한 오류는 ID3D12GraphicsCommandList::close때까지 지연됩니다. 한 가지 예외는 DXGI_ERROR_DEVICE_REMOVED로, 이는 더 연기됩니다. 이는 많은 매개 변수 유효성 검사 오류가 자동으로 삭제되고 호출자에게 반환되지 않는 D3D11과 다릅니다.
애플리케이션은 다음 API 호출에서 DXGI_DEVICE_REMOVED 오류를 볼 수 있습니다.
- 모든 리소스 생성 방법
- ID3D12Resource::Map
- IDXGISwapChain1::Present1
- getDeviceRemovedReason
명령 목록 API 제한
일부 명령 목록 API는 특정 유형의 명령 목록에서만 호출할 수 있습니다. 아래 표에서는 각 명령 목록 유형에서 호출할 수 있는 명령 목록 API를 보여 줍니다. 또한 D3D12 렌더링 패스호출하는 데 유효한 API를 보여 줍니다.
| API 이름 | 그래픽 | 계산 | 복사 | 묶음 | 렌더 패스에서 |
|---|---|---|---|---|---|
| AtomicCopyBufferUINT | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| AtomicCopyBufferUINT64 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 쿼리 시작 | ✓ 유효 | ✓ 유효 | |||
| BeginRenderPass | ✓ 유효 | ||||
| 레이 트레이싱 가속 구조 빌드 | ✓ 유효 | ✓ 유효 | |||
| 클리어뎁스스텐실뷰 (ClearDepthStencilView) | ✓ 유효 | ||||
| ClearRenderTargetView | ✓ 유효 | ||||
| ClearState (클리어스테이트) | ✓ 유효 | ✓ 유효 | |||
| ClearUnorderedAccessViewFloat | ✓ 유효 | ✓ 유효 | |||
| ClearUnorderedAccessViewUint | ✓ 유효 | ✓ 유효 | |||
| CopyBufferRegion | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| CopyRaytracingAccelerationStructure | ✓ 유효 | ✓ 유효 | |||
| CopyResource | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| CopyTextureRegion | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 카피타일즈 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| DiscardResource | ✓ 유효 | ✓ 유효 | |||
| 보내다 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| DispatchRays | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| DrawIndexedInstanced | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| DrawInstanced | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 레이 트레이싱 가속 구조 후처리 정보 방출 (EmitRaytracingAccelerationStructurePostbuildInfo) | ✓ 유효 | ✓ 유효 | |||
| EndQuery (쿼리 종료) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| EndRenderPass | ✓ 유효 | ✓ 유효 | |||
| ExecuteBundle | ✓ 유효 | ✓ 유효 | |||
| ExecuteIndirect | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| ExecuteMetaCommand | ✓ 유효 | ✓ 유효 | |||
| IASetIndexBuffer | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 기본 토폴로지 설정 (IASetPrimitiveTopology) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| IASetVertexBuffers | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| InitializeMetaCommand | ✓ 유효 | ✓ 유효 | |||
| OMSetBlendFactor | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| OMSetDepthBounds | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| OMSetRenderTargets | ✓ 유효 | ||||
| OMSetStencilRef | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| ResolveQueryData | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| ResolveSubresource | ✓ 유효 | ||||
| 리졸브서브리소스리전 | ✓ 유효 | ||||
| 자원 장벽 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| RSSetScissorRects | ✓ 유효 | ✓ 유효 | |||
| RSSetShadingRate | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| RSSetShadingRateImage | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| RSSetViewports | ✓ 유효 | ✓ 유효 | |||
| SetComputeRoot32BitConstant | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetComputeRoot32BitConstants | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetComputeRootConstantBufferView | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetComputeRootDescriptorTable (셋컴퓨트루트디스크립터테이블 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| 계산 루트 셰이더 리소스 뷰 설정 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetComputeRootSignature | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetComputeRootUnorderedAccessView | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetDescriptorHeaps | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| `SetGraphicsRoot32BitConstant` (그래픽 루트 32비트 상수 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetGraphicsRoot32BitConstants (세트그래픽스루트32비트상수) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetGraphicsRootConstantBufferView (그래픽 루트 상수 버퍼 뷰 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetGraphicsRootDescriptorTable | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetGraphicsRootShaderResourceView (그래픽 루트 셰이더 리소스 뷰 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetGraphicsRootSignature(그래픽 루트 서명 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetGraphicsRootUnorderedAccessView (그래픽 루트 비정렬 액세스 뷰 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 파이프라인 상태 설정 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | |
| SetPipelineState1 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetPredication | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| SetProtectedResourceSession(보호된 자원 세션 설정) | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 샘플 위치 설정 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 뷰인스턴스마스크설정 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ||
| 에스오셋타겟 | ✓ 유효 | ✓ 유효 | |||
| 버퍼 즉시 쓰기 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 | ✓ 유효 |
번들 제한 사항
제한 사항으로 인해 Direct3D 12 드라이버는 기록적인 시간에 번들과 관련된 대부분의 작업을 수행할 수 있으므로 오버헤드가 낮은 ExecuteBundle API를 실행할 수 있습니다. 번들에서 참조하는 모든 파이프라인 상태 개체에는 동일한 렌더링 대상 형식, 깊이 버퍼 형식 및 샘플 설명이 있어야 합니다.
다음 명령 목록 API 호출은 D3D12_COMMAND_LIST_TYPE_BUNDLE 유형으로 생성된 명령 목록에서 허용되지 않습니다.
- Any Clear 메서드
- 임의의 복사 메서드
- DiscardResource
- ExecuteBundle
- ResourceBarrier
- ResolveSubresource
- SetPredication
- 쿼리시작
- 쿼리종료
- SOSetTargets
- OMSetRenderTargets
- RSSetViewports
- RSSetScissorRects
SetDescriptorHeaps 번들에서 호출할 수 있지만 번들 설명자 힙은 호출 명령 목록 설명자 힙과 일치해야 합니다.
번들에서 이러한 API가 호출되면 런타임은 호출을 삭제합니다. 디버그 계층은 이 문제가 발생할 때마다 오류를 발생합니다.
관련 항목
Direct3D 12에서 작업 제출하기