방법: 동적 리소스 사용
중요 API
앱이 해당 리소스의 데이터를 변경해야 하는 경우 동적 리소스를 만들고 사용합니다. 동적 사용을 위한 텍스처 및 버퍼를 만들 수 있습니다.
알아야 할 사항
기술
필수 구성 요소
C++에 익숙하다고 가정합니다. 그래픽 프로그래밍 개념에 대한 기본 경험도 있어야 합니다.
지시
1단계: 동적 사용량 지정
앱에서 리소스를 변경할 수 있도록 하려면 해당 리소스를 만들 때 동적 및 쓰기 가능한 리소스로 지정해야 합니다.
동적 사용량 지정하려면
- 리소스 사용량을 동적으로 지정합니다. 예를 들어, D3D11_USAGE_DYNAMIC 값을 꼭짓점 또는 상수 버퍼에 대한 D3D11_BUFFER_DESC의 Usage 멤버에 지정하고, 2D 텍스처에 대한 D3D11_TEXTURE2D_DESC의 Usage 멤버에 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가 버퍼를 사용하는 동안 정적 꼭짓점 버퍼에서 맵 호출하면 상당한 성능 저하가 발생합니다. 이 경우 맵 GPU가 버퍼에서 꼭짓점 또는 인덱스 데이터 읽기를 완료할 때까지 기다려야 Map 호출 앱으로 돌아갈 수 있으므로 상당한 지연이 발생합니다. 맵 호출한 다음 프레임당 여러 번 정적 버퍼에서 렌더링하면 맵 포인터를 반환하기 전에 명령을 완료해야 하므로 GPU가 렌더링 명령을 버퍼링할 수 없습니다. 버퍼링된 명령이 없으면 앱이 꼭짓점 버퍼 또는 인덱스 버퍼를 채우고 렌더링 명령을 실행한 후에야 GPU가 유휴 상태로 유지됩니다.
이상적으로 꼭짓점 또는 인덱스 데이터는 변경되지 않지만 항상 가능한 것은 아닙니다. 앱이 프레임마다 꼭짓점 또는 인덱스 데이터를 변경하거나 프레임당 여러 번 인덱싱해야 하는 경우가 많습니다. 이러한 경우 D3D11_USAGE_DYNAMIC사용하여 꼭짓점 또는 인덱스 버퍼를 만드는 것이 좋습니다. 이 사용 플래그를 설정하면 런타임이 맵 작업의 빈번한 수행에 최적화됩니다. D3D11_USAGE_DYNAMIC 버퍼가 자주 매핑되는 경우에만 유용합니다. 데이터가 상수로 유지되면 해당 데이터를 정적 꼭짓점 또는 인덱스 버퍼에 배치합니다.
동적 꼭짓점 버퍼를 사용할 때 성능 향상을 받으려면 앱이 적절한 D3D11_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 디바이스가 이러한 기능을 지원하는지를 확인하기 위해, ID3D11Device::CheckFeatureSupport를 D3D11_FEATURE_D3D11_OPTIONS와 함께 호출합니다. CheckFeatureSupport 는 D3D11_FEATURE_DATA_D3D11_OPTIONS 구조체의 멤버를 디바이스의 기능 정보로 채웁니다. 여기에 관련 멤버는 MapNoOverwriteOnDynamicConstantBuffer 및 MapNoOverwriteOnDynamicBufferSRV입니다.
관련 항목
-
Direct3D 11 사용하는 방법