방법: 동적 리소스 사용
중요 API
앱이 해당 리소스의 데이터를 변경해야 하는 경우 동적 리소스를 만들고 사용합니다. 동적 사용을 위한 텍스처 및 버퍼를 만들 수 있습니다.
알아야 하는 작업
기술
필수 구성 요소
사용자가 C++에 익숙하다고 가정합니다. 그래픽 프로그래밍 개념에 대한 기본 경험도 필요합니다.
지침
1단계: 동적 사용량 지정
앱에서 리소스를 변경할 수 있도록 하려면 리소스를 만들 때 해당 리소스를 동적 및 쓰기 가능으로 지정해야 합니다.
동적 사용을 지정하려면
- 리소스 사용량을 동적으로 지정합니다. 예를 들어 꼭짓점 또는 상수 버퍼의 D3D11_BUFFER_DESCUsage 멤버에 D3D11_USAGE_DYNAMIC 값을 지정하고 2D 텍스처에 대한 D3D11_TEXTURE2D_DESCUsage 멤버에 D3D11_USAGE_DYNAMIC 값을 지정합니다.
- 리소스를 쓰기 가능으로 지정합니다. 예를 들어 D3D11_BUFFER_DESC 또는D3D11_TEXTURE2D_DESCCPUAccessFlags 멤버에 D3D11_CPU_ACCESS_WRITE 값을 지정합니다.
- 리소스 설명을 생성 함수에 전달합니다. 예를 들어 D3D11_BUFFER_DESC 주소를 ID3D11Device::CreateBuffer 에 전달하고 D3D11_TEXTURE2D_DESC 주소를 ID3D11Device::CreateTexture2D에 전달합니다.
동적 꼭짓점 버퍼를 만드는 예제는 다음과 같습니다.
// Create a vertex buffer for a triangle.
float2 triangleVertices[] =
{
float2(-0.5f, -0.5f),
float2(0.0f, 0.5f),
float2(0.5f, -0.5f),
};
D3D11_BUFFER_DESC vertexBufferDesc = { 0 };
vertexBufferDesc.ByteWidth = sizeof(float2)* ARRAYSIZE(triangleVertices);
vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
vertexBufferDesc.MiscFlags = 0;
vertexBufferDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA vertexBufferData;
vertexBufferData.pSysMem = triangleVertices;
vertexBufferData.SysMemPitch = 0;
vertexBufferData.SysMemSlicePitch = 0;
DX::ThrowIfFailed(
m_d3dDevice->CreateBuffer(
&vertexBufferDesc,
&vertexBufferData,
&vertexBuffer2
)
);
2단계: 동적 리소스 변경
리소스를 만들 때 동적 및 쓰기 가능으로 지정한 경우 나중에 해당 리소스를 변경할 수 있습니다.
동적 리소스의 데이터를 변경하려면
- 리소스에 대한 새 데이터를 설정합니다. D3D11_MAPPED_SUBRESOURCE 형식 변수를 선언하고 0으로 초기화합니다. 이 변수를 사용하여 리소스를 변경합니다.
- 변경하려는 데이터에 대한 GPU(그래픽 처리 장치) 액세스를 사용하지 않도록 설정하고 데이터가 포함된 메모리에 대한 포인터를 가져옵니다. 이 포인터를 가져오려면 ID3D11DeviceContext::Map을 호출할 때 MapType 매개 변수에 D3D11_MAP_WRITE_DISCARD 전달합니다. 이 포인터를 이전 단계의 D3D11_MAPPED_SUBRESOURCE 변수 주소로 설정합니다.
- 메모리에 새 데이터를 씁니다.
- 새 데이터 쓰기가 완료되면 ID3D11DeviceContext::Unmap 을 호출하여 데이터에 대한 GPU 액세스를 다시 활성화합니다.
동적 꼭짓점 버퍼를 변경하는 예제는 다음과 같습니다.
// This might get called as the result of a click event to double the size of the triangle.
void TriangleRenderer::MapDoubleVertices()
{
D3D11_MAPPED_SUBRESOURCE mappedResource;
ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
float2 vertices[] =
{
float2(-1.0f, -1.0f),
float2(0.0f, 1.0f),
float2(1.0f, -1.0f),
};
// Disable GPU access to the vertex buffer data.
auto m_d3dContext = m_deviceResources->GetD3DDeviceContext();
m_d3dContext->Map(vertexBuffer2.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
// Update the vertex buffer here.
memcpy(mappedResource.pData, vertices, sizeof(vertices));
// Reenable GPU access to the vertex buffer data.
m_d3dContext->Unmap(vertexBuffer2.Get(), 0);
}
설명
동적 텍스처 사용
형식 및 크기당 동적 텍스처를 하나만 만드는 것이 좋습니다. 모든 수준 매핑의 추가 오버헤드로 인해 동적 Mipmap, 큐브 및 볼륨을 만들지 않는 것이 좋습니다. mipmap의 경우 최상위 수준에서만 D3D11_MAP_WRITE_DISCARD 지정할 수 있습니다. 모든 수준은 최상위 수준만 매핑하여 삭제됩니다. 이 동작은 볼륨 및 큐브에 대해 동일합니다. 큐브의 경우 최상위 수준 및 얼굴 0이 매핑됩니다.
동적 버퍼 사용
GPU가 버퍼를 사용하는 동안 정적 꼭짓점 버퍼에서 Map 을 호출하면 상당한 성능 저하가 발생합니다. 이 경우 Map 은 GPU가 버퍼에서 꼭짓점 또는 인덱스 데이터 읽기를 마칠 때까지 기다려야 맵 이 호출 앱으로 돌아갈 수 있으므로 상당한 지연이 발생합니다. 또한 맵을 호출한 다음 프레임당 여러 번 정적 버퍼에서 렌더링하면 GPU가 맵 포인터를 반환하기 전에 명령을 완료해야 하므로 렌더링 명령을 버퍼링할 수 없습니다. 버퍼링된 명령이 없으면 앱이 꼭짓점 버퍼 또는 인덱스 버퍼를 채우고 렌더링 명령을 실행한 후에야 GPU가 유휴 상태로 유지됩니다.
이상적으로 꼭짓점 또는 인덱스 데이터는 변경되지 않지만 항상 가능한 것은 아닙니다. 앱이 프레임당 여러 번 꼭짓점을 변경하거나 프레임마다 데이터를 인덱싱해야 하는 경우가 많습니다. 이러한 경우 D3D11_USAGE_DYNAMIC 사용하여 꼭짓점 또는 인덱스 버퍼를 만드는 것이 좋습니다. 이 사용 플래그를 사용하면 런타임이 잦은 맵 작업에 최적화됩니다. D3D11_USAGE_DYNAMIC 버퍼가 자주 매핑되는 경우에만 유용합니다. 데이터가 일정하게 유지되면 해당 데이터를 정적 꼭짓점 또는 인덱스 버퍼에 배치합니다.
동적 꼭짓점 버퍼를 사용할 때 성능 향상을 받으려면 앱이 적절한 D3D11_MAP 값으로 Map을 호출해야 합니다. D3D11_MAP_WRITE_DISCARD 앱이 버퍼에 이전 꼭짓점 또는 인덱스 데이터를 유지할 필요가 없다는 것을 나타냅니다. D3D11_MAP_WRITE_DISCARD 사용하여 Map을 호출할 때 GPU가 버퍼를 계속 사용하는 경우 런타임은 이전 버퍼 데이터 대신 새 메모리 영역에 대한 포인터를 반환합니다. 이를 통해 앱이 데이터를 새 버퍼에 저장하는 동안, GPU는 기존 데이터를 계속 사용할 수 있습니다. 앱에서 추가적인 메모리 관리는 필요 없으며, GPU에서 사용이 끝나면 이전 버퍼가 자동으로 재사용되거나 제거됩니다.
참고
D3D11_MAP_WRITE_DISCARD 사용하여 버퍼를 매핑하는 경우 런타임은 항상 전체 버퍼를 삭제합니다. 0이 아닌 오프셋 또는 제한된 크기 필드를 지정하여 버퍼의 매핑되지 않은 영역에 정보를 유지할 수 없습니다.
스프라이트를 렌더링하기 위해 4개의 꼭짓점을 추가하는 등 앱이 맵당 저장해야 하는 데이터의 양이 작은 경우가 있습니다. D3D11_MAP_WRITE_NO_OVERWRITE 앱이 동적 버퍼에서 이미 사용 중인 데이터를 덮어쓰지 않음을 나타냅니다. Map 호출은 이전 데이터에 대한 포인터를 반환합니다. 그러면 앱이 꼭짓점 또는 인덱스 버퍼의 사용되지 않는 지역에 새 데이터를 추가할 수 있습니다. 앱은 GPU에서 계속 사용 중일 수 있으므로 그리기 작업에 사용되는 꼭짓점 또는 인덱스를 수정해서는 안 됩니다. 그런 다음 동적 버퍼가 가득 찬 후 앱에서 D3D11_MAP_WRITE_DISCARD 사용하여 GPU가 완료된 후 이전 꼭짓점 또는 인덱스 데이터를 삭제하는 새 메모리 영역을 수신하는 것이 좋습니다.
비동기 쿼리 메커니즘은 꼭짓점이 GPU에서 여전히 사용되고 있는지 확인하는 데 유용합니다. 꼭짓점을 사용하는 마지막 그리기 호출 후 D3D11_QUERY_EVENT 형식의 쿼리를 실행합니다. ID3D11DeviceContext::GetData가 S_OK 반환할 때 꼭짓점은 더 이상 사용되지 않습니다. D3D11_MAP_WRITE_DISCARD 또는 맵 값이 없는 버퍼를 매핑하는 경우 항상 꼭짓점이 GPU와 올바르게 동기화되도록 보장됩니다. 그러나 지도 값 없이 매핑하면 앞에서 설명한 성능 저하가 발생합니다.
참고
Windows 8 시작하는 Direct3D 11.1 런타임을 사용하면 동적 상수 버퍼와 동적 버퍼의 SRV(셰이더 리소스 뷰)를 D3D11_MAP_WRITE_NO_OVERWRITE 매핑할 수 있습니다. Direct3D 11 및 이전 런타임은 꼭짓점 또는 인덱스 버퍼에 대한 부분 업데이트 매핑을 덮어쓰지 않는 것으로 제한되었습니다. Direct3D 디바이스가 이러한 기능을 지원하는지 확인하려면 D3D11_FEATURE_D3D11_OPTIONS사용하여 ID3D11Device::CheckFeatureSupport를 호출합니다. CheckFeatureSupport 는 D3D11_FEATURE_DATA_D3D11_OPTIONS 구조체의 멤버를 디바이스의 기능으로 채웁니다. 여기에 관련된 멤버는 MapNoOverwriteOnDynamicConstantBuffer 및 MapNoOverwriteOnDynamicBufferSRV입니다.
관련 항목