728x90

Deferred Context를 이용하여 텍스처를 수정하고 렌더링하는 과정에서 


D3D11 WARNING: ID3D11Device::RemoveContext: Deferred Context removal has been triggered for the following reason (어쩌구 저쩌구...)


라는 엄청 긴 메세지와 함께 프로그램은 실행되고 있지만, DebugView에서 엄청나게 많은 메세지를 찍어대면서 몇개의 영상이 멈추었다.




그래서 인터넷에 찾아보니 ID3D11CommandList는 SAFE_RELEASE()를 매크로를 사용하지말고 그냥 Release() 함수를 사용해야만 한다고 한다.


// Deferred Execute

for (int i = 0; i < DEFERRED_COUNT; ++i)

{

    if (m_pDX11DisplayProperty->m_pRenderingCommandList[i] != nullptr)

    {

        DeviceContext->ExecuteCommandList(m_pDX11DisplayProperty->m_pRenderingCommandList[i], TRUE);

        //SAFE_RELEASE(m_pDX11DisplayProperty->m_pRenderingCommandList[i]);     // <-- 이렇게 사용하면 안됨.

        m_pDX11DisplayProperty->m_pRenderingCommandList[i]->Release();

    }


    //m_pUpdateClass[i].m_pKeyedMutex->ReleaseSync(uRelKey);

}


출처 : https://community.amd.com/thread/128535



위에 이야기는 테스트 결과로 봤을 때 잘못된 정보였다 !!!!


결과만 말하면 FinishCommandList()가 ExecuteCommandList()보다 많이 호출되어 CommandList의 데이터가 소모가 되지 않고 쌓이기 때문에 Memory Leak 처럼 보인 것이다.


항상 ExecuteCommandList()가 FinishCommandList() 보다 더 많이 호출될 수 있게 하도록 하자.

728x90
728x90

DirectX 11에서는 Resource에 데이터를 넣는 함수는 4개가 존재한다.


UpdateSubresource(), Map() ~ Unmap(), CopyResource(), CopySubresourceRegion()


이 함수들은 자주 사용되는 만큼 어느 정도는 알고 사용해야 할 것 같아서 정리한다.




우선 함수의 속도를 보면 UpdateSubresource()는 웬만하면 매 프레임마다 사용하지 않는 것이 좋다는 생각이 들 것이다.



CPU -> GPU로 데이터를 복사할 때 사용하는 함수는 UpdateSubresource()와 Map() ~ Unmap()을 사용하게 된다. 


Map() ~ Unmap()의 경우 개발자가 직접 Lock() ~ Unlock()을 하며 memcpy()를 이용해서 Resource에 데이터를 직접 넣는 방법이고, UpdateSubresource()는 함수 호출만으로 내부에서 Lock() ~ Unlock()을 해주기 때문에 나의 경우 UpdateSubresource()를 더 즐겨 사용하는 편이다.


그리고 기본적으로 Lock() ~ Unlock()이 걸리게 되므로, 하나의 Resource에 Thread를 이용하여 동시에 CPU -> GPU로 복사는 불가능하다.



GPU -> GPU로 데이터를 복사할 때 사용하는 함수는 CopyResource()와 CopySubresourceRegion()가 있다. 


이 함수의 차이는 CopyResource()는 Full Image를 그대로 복사하고, CopySubresourceRegion()는 영역을 지정하여 복사가 가능한 것이다.


이 두개의 함수는 기본적으로 비동기 함수이다. 그렇기 때문에 하나의 Resource에 Thread를 이용하여 GPU -> GPU 복사는 얼마든지 가능하다.


테스트를 해보았을 때에는 Frame Rate는 거의 차이가 없었다.



국내 사이트에서는 자료가 없기에 해외 사이트를 검색하여 얻은 자료를 토대로 테스트 결과를 정리해보았다.


참조 사이트 : https://eatplayhate.me/2013/09/29/d3d11-texture-update-costs/

728x90
728x90

책들을 봐도 잘 이해가 안되는 것 같아서 따로 찾아보다가 좋은 예시를 찾았다...


큰 그룹으로 나누어 주는 API 는 ID3D11DeviceContext::Dispatch() 이다.

ipImmediateContextPtr->Dispatch( 3, 2, 1 );

이렇게 큰 블럭 단위로 나누고 난 후에 ComputeShader HLSL 에서는 이들을 세부적인 스레들로 분할하는 문법을 지정한다.


[numthreads(4, 4, 1)]
void MainCS( ... )
{
        ....
}


결과적으로 위의 그림처럼 스레드들이 생성되어서 병렬적으로 실행이 된다.
위에 나열된 숫자들은 스레드 ID 로써의 역활을 한다.
즉, 어떤 스레드의 ID 가 MainCS 함수에 파라메터로 넘어오면,
그 ID 를 통해서 해당 버퍼에 값을 작성하게 된다.

예를 들어, 

[numthreads( 256,1,1) ]

void VectorAdd( uint3 id: SV_DispatchThreadID )
{

  gBufOut[id] = gBuf1[id] + gBuf2[id];

}


아무리 스레드들이 복잡하게 동작하더라도, 위와 같이 ID 를 통해서 제어한다면
그 어떤 작업도 문제없이 할 수 있다.


728x90
728x90

DirectCompute에는 다양한 메모리 모형이 존재한다. 이것들을 한번 살펴보자.


레지스터 기반 메모리

계산 셰이더는 다른 프로그래밍 가능 셰이더 단계들과 동일한 하드웨어(프로그래밍이 가능한 처리기)에서 실행된다. 따라서 계산 셰이더 단계에서도 다른 프로그램 가능 셰이더 단계들에서와 동일한 일반적인 처리 패러다임이 적용되며, 공통 셰이더 코어를 구현한다. 계산 셰이더 단계는 다른 파이프라인 단계들과 비슷한 레지스터 기반 처리 개념을 사용한다.

계산 셰이더는 입력 특성 레지스터들(v#)과 텍스처 레지스터들(t#), 상수 버퍼 레지스터들(cb#), 순서 없는 접근 레지스터들(u#), 임시 레지스터들(r#, x#)을 지원한다.


임시 레지스터들은 셰이더 프로그램의 실행 도중 중간 계산 결과를 담는 데 주로 쓰인다.

임시 레지스터는 현재 실행 중인 스레드에서만 접근할 수 있으며, 일반적으로 접근이 극도로 빠르다.

공통 셰이더 코어에는 임시 레지스터를 최대 4096개까지(r#, x# 합쳐서) 지정할 수 있다.

일반적으로 셰이더 코어에 적재된 자료는 최대한 이 임시 레지스터들에 저장된다.


장치 메모리

레지스터 기반 메모리는 아주 빠르긴 하지만 용량이 매우 작다. 또한, 사용할 자료를 먼저 셰이더 코어에 적재한 후에야 레지스터들을 사용할 수 있으며, 스레드의 셰이더 프로그램 실행이 끝나면 레지스터 내용이 초기화된다. 계산 파이프라인을 본격적으로 활용하려면 레지스터 기반 메모리에 담을 수 있는 것보다 훨씬 큰 자원이 필요하며, 또한 셰이더 프로그램의 실행들 사이에서 계속 유지되는 자원도 필요하다. 그런 자원들은 장치 메모리에 담는다. 그리고 그런 자원들을 다른 종류의 메모리에 저장되는 자원과 구분해서 흔히 장치 메모리 자원이라고 부른다. 

728x90
728x90

DirectCompute의 스레드 적용 모형

DirectCompute에 관해 가장 먼저 살펴볼 것은 그 스레드 적용 모형과 실행 모형이다. GPU가 많은 수의 처리 코어들로 이루어져 있기 때문에 병렬 알고리즘 처리에 아주 적합하다는 점은 앞에서 이미 언급했다. 작업을 수행할 처리기가 아주 많기 때문에, 특정 알고리즘을 다수의 스레드들에 효과적으로 대응시키기 위한 적당한 방법론을 갖출 필요가 있다. 

전통적인 CPU 기반 알고리즘에 쓰이는 전형적인 다중스레드는 스레드들이 각자 개별적으로 실행되되 공유 메모리 공간과 수동적인 동기화를 통해서 서로 의사소통하는 방법을 사용한다. 이는 다중 프로세서 시스템들에서 오랫동안 쓰였다.

그러나 이러한 모형은 수천 개의 스레드가 동시에 돌아가야 하는 처리 패러다임에 그리 잘 맞지 않는다. DirectCompute는 일반성과 사용 편의의 균형을 도모하는 또 다른 종류의 스레드 적용 및 실행 모형을 사용한다. 이 모형에서는 스레드들을 자료 요소에 사상(대응)시키기가 더 쉽다. 이 덕분에 하나의 처리 과제를 더 작은 여러 조각으로 분할해서 GPU에서 실행하는 것이 수월해진다.


핵(Kernel) 기반 처리

계산 셰이더 단계는 핵(Kernel) 기반 처리 시스템을 구현한다. 계산 셰이더 프로그램 자체는 하나의 함수이며, 그 함수는 일종의 핵함수(Kernel Function)처럼 작용한다. 작업의 단위는 알고리즘마다 다르지만, 현재 적재된 핵의 각 인스턴스가 각각의 단일한 입력 자료 집합을 처리하는 식으로 알고리즘이 실행된다는 점은 동일하다. 계산 셰이더에 주어지는 입력자료는 계산 셰이더 단계에 연결된 자원에서 온 것이다. 이러한 구조에서는 하나의 알고리즘이 GPU상의 수천 개의 스레드들에서 실행되도록 프로그래밍하기가 아주 쉽고 직관적이다. 모든 스레드가 같은 핵(Kernel)을 실행하므로, 스레드들의 행동을 일일이 동기화하는 데 힘을 빼는 대신 원하는 자료를 각자 독립적으로 처리될 자료 항목들로 분할하는 방법을 찾는 데 주력하면 된다.


작업의 배분

작업을 시작할 때 사용할 수 있는 메서드는 ID3D11DeviceContext::Dispatch() 또는 ID3D11DeviceContext::DispatchIndirect()이다.

이 함수들은 렌더링 파이프라인의 여러 그리기 메서드(Draw로 시작하는 함수)들에 비견할 수 있다. 

Dispatch메서드는 부호 없는 정수 매개변수 3개를 받는데 이 세 매개변수는 원하는 처리 핵의 실행을 '배분(dispatch)' 할 스레드들의 그룹의 개수를 결정한다. 좀 더 구체적으로 이 세 매개변수는 인스턴스화할 스레드 그룹들을 담는 3차원 배열의 x, y, z 차원 크기로, 유효한 범위는 셋 모두 1에서 65535이다. 예를 들어 응용 프로그램이 Dispatch(4, 6, 2)를 호출했다면 4 x 6 x 2 = 48개의 스레드 그룹들이 만들어진다. 그 배열 안에서 각 스레드 그룹은 세 차원 색인(범위는 0 ~ 차원 크기 - 1)의  고유한 조합으로 이루어진 식별자로 식별된다.

이러한 배분 메서드를 호출할 때 지정하는 것은 인스턴스화할 스레드 그룹의 개수이지 개별 스레드들의 개수가 아님을 주의하기 바란다. 한 그룹당 인스턴스화될 스레드들의 개수는 계산 셰이더 프로그램의 주 함수 앞에서 numthreads라는 HLSL 함수 특성으로 지정한다. 배분 메서드에서처럼 이 함수 특성도 3차원 배열의 세 차원의 크기를 지정하나, 그 배열은 스레드 그룹들이 아니라 실제 스레드들을 담는 것이다. 

각 차원의 크기는 셰이더 모형에 따라 다른데, cs_5_0의 경우 x와 y가 1이상이어야 하고 z는 반드시 1 ~ 64 범위 이어야 한다. 또한 그룹의 전체 스레드 개수(X * Y * Z)가 1024를 넘으면 안된다.


[numthreads(10, 10, 2)]

이 예에서는 각 스레드 그룹마다 총 10 x 10 x 2 = 200개의 스레드가 인스턴스화된다. 이 스레드들의 개별 스레드 역시 차원 색인 ( 0 ~ 차원 크기 - 1) 세 개의 고유한 조합으로 이루어진 식별자로 식별된다. 앞에서처럼 Dispatch(4, 6, 2)라고 호출한 경우, 계산 셰이더 단계 전체적으로 인스턴스화되는 스레드는 48 x 200 = 6400개이다.


스레드 식별 체계

앞에서 처럼 총 6400개의 스레드가 실행된다면 이 스레드들은 자신이 처리할 자료를 어떻게 알게 될까? 모든 인스턴스가 같은 핵(셰이더 프로그램)을 실행하므로, 셰이더 프로그램 소스 코드 자체에서 자료를 지정할 수는 없다. 따라서 처리할 자료 집합을 스레드에게 알려주는 다른 어떤 메커니즘이 필요하다.

앞에서 이야기한 기하학적 스레드 조직화 방식이 바로 그러한 메커니즘의 기반이 된다. 스레드들을 그렇게 조직화하는 데에는 다 이유가 있었던 것이다. 앞에서 보았듯이, 하나의 배분 호출에 관여하는 스레드 그룹들 중 특정 스레드 그룹을 3차원 좌표 형태로 지정하는 것은 아주 간단한 문제이며, 특정 스레드 그룹의 특정 스레드를 지정하는 것 역시 마찬가지로 간단하다.

결과적으로, 한 배분 호출의 모든 스레드를 각각 고유한 식별자로 간단하게 식별할 수 있다.

셰이더 프로그램을 작성할 때 개발자는 셰이더 함수의 입력 매개변수로 쓰일 일단의 시스템 값 의미소들을 지정할 수 있는데, 그런 시스템 값 의미소들 중에는 현재 실행되는 셰이더 프로그램 인스턴스(스레드)를 식별하는 데 사용할 수 있는 것들이 있다. 


SV_GroupID : 이 배분 호출의 스레드 그룹들 중 현재 스레드가 속한 그룹의 3차원 식별자(uint3)

SV_GroupThreadID : 그 스레드 그룹 안에서의 현재 스레드의 3차원 식별자(uint3)

SV_DispatchThreadID : 전체 배분 안에서의 현재 스레드의 3차원 식별자(uint3)

SV_GroupIndex : 현재 스레드가 속한 스레드 그룹의 3차원 식별자를 1차원으로 직렬화한 색인(uint)


각 스레드의 실행마다 이러한 식별 정보가 주어지므로, 개발자는 이들을 이용해서 입력 자료 중 현재 스레드가 처리할 부분을 결정하도록 셰이더 프로그램을 작성하면 된다. 



이 코드가 하는 일은 1차원 버퍼의 자원의 값을 두 배로 증가 시키는 계산 셰이더이다.

이 코드는 스레드 그룹의 크기를 numthreads 함수 특성으로 선언한다. 그 다음 부분은 계산 셰이더의 처리 핵에 해당하는 주 함수이다. 이 함수의 매개 변수는 앞에서 말한 스레드 식별용 시스템 값 의미소 특성들 중 하나이다. 


728x90
728x90

앞에서 대략적인 렌더링 파이프라인에 대해 보았지만, DirectX11의 파이프라인에는 좀 더 광범위한 분야에 적용할 수 있는 유연한 계산을 위한 파이프라인 단계가 있다. 그 단계를 이용하면 GPU를 광선 추적이나 물리 시뮬레이션 같은 다양한 응용 분야에서 사용할 수 있고, 인공지능 계산에도 사용할 수 있다. 그것은 계산 셰이더(Compute Shader) 단계로 흔히 DirectCompute라고 부르는 기술이 바로 이 단계에서 구현된다.


DirectCompute

DirectCompute는 GPU가 제공하는 대규모 병렬 계산 능력을 통상적인 래스터 기반 렌더링 이외의 분야에 활용하기 위한 새로운 처리 패러다임이다. GPU는 병렬로 작동하는 아주 많은 수의 소규모 처리기들로 이루어져 있기 때문에 크게 병렬화할 수 있는 계산 과제에 아주 적합하다.

DirectCompute는 Direct3D 11 처리 환경에 직접 내장되어 있으며, Direct3D 렌더링 API와 기존 프레임워크의 상당 부분을 공유한다. 덕분에 DirectX11에 익숙한 응용 프로그램 개발자라면 응용 프로그램의 설정과 실행에 필요한 정도의 API 사용법은 따로 배우지 않아도 될 정도이다.


계산 셰이더 단계

계산 셰이더의 전체적인 사용 방법은 개념적으로 다른 프로그램 가능 셰이더 단계와 동일하다.

자원들을 순서 없는 접근 뷰(UAV)를 이용해서 계산 셰이더 단계에 연결할 수도 있다. 그러나 계산 셰이더 단계는 다른 프로그램 가능 파이프라인 단계들과 근본적으로 다르다. 가장 큰 차이점은 다른 파이프라인 단계의 출력을 입력으로 받지 않고, 출력을 다른 파이프라인 단계에 넘겨주지도 않는다. 몇 가지 시스템 값 의미소들은 입력 매개변수로 받을 수 있지만, 이는 셰이더 프로그램에서 사용할 수 있는 특성 형태의 자료에만 국한된다. 그 외의 모든 자료 입 · 출력은 자원을 통해서만 일어난다.

이러한 구조 때문에 계산 셰이더에서는 하나의 프로그램 안에서 알고리즘을 완결적으로 구현해야 한다. 




728x90
728x90

자원 뷰 (Resource View)

자원 뷰는 자원을 파이프라인에 묶는 데 사용되는 객체이다. 


자원 뷰의 종류

자원 뷰는 4개의 종류가 있다. 


렌더 타겟 뷰 ( ID3D11RenderTargetView )

깊이/스텐실 뷰 ( ID3D11DepthStencilView )

셰이더 자원 뷰 ( ID3D11ShaderResourceView )

순서 없는 접근 뷰 ( ID3D11UnordereAccessView )


같은 자원이라도 어떤 자원 뷰로 연결되는가에 따라 자원의 용도가 달란진다. 


렌더 타겟 뷰 ( ID3D11RenderTargetView )는 렌더링 파이프라인의 출력을 받을 자원을 연결하는 데 쓰인다. 전통적으로 렌더 타겟은 2차원 텍스처이나, 다른 종류의 자원을 렌더 타게으로 연결하는 것도 가능하다. 렌더 타겟 뷰의 구성 옵션으로는 자원의 DXGI 형식이 있으며, 그 외에도 자원의 종류에 따라 다양한 구성 옵션들이 존재한다. 렌더 대상 뷰는 또한 자원의 일부분을 파이프라인에 노출시키는 다양한 메서드들도 제공한다.


깊이/스텐실 뷰 ( ID3D11DepthStencilView )도 렌더 타겟 뷰처럼 렌더링 파이프라인의 출력을 받는 자원을 위한 것이다. 차이는, 렌더 대상 뷰가 색상 값들을 담는 버퍼를 위한 것인 반면 깊이/스텐실 뷰는 깊이와 스텐실 값들을 담는 버퍼를 위한 것이라는 점이다. 깊이/스텐실 버퍼 자원은 빈번하게 쓰이는 렌더링 연산이 깊이 판정과 스텐실 판정을 수행하는 데 사용되며, 이 때문에 파이프라인의 효율성에 아주 중요한 요인이 된다.


셰이더 자원 뷰 ( ID3D11ShaderResourceView )는 파이프라인의 프로그램 가능 셰이더 단계가 자원을 읽을 수 있게 한다. 이 뷰는 예전에 픽셀 셰이더에서 텍스처가 하던 역할, 즉 셰이더 프로그램 안에서 읽고 사용할 수는 있지만 기록하지는 못하는 자료에 해당하는 것이다.


순서 없는 접근 뷰 ( ID3D11UnordereAccessView )는 셰이더 프로그램안에서 자원을 읽음과 동시에 쓰기도 할 수 있다. 게다가, 출력 장소가 미리 정해져 있지 않기 때문에 셰이더 프로그램 안에서 자원 안의 임의의 위치에 scatter 연산을 수행하는 것도 가능하다. 이 자원 뷰는 픽셀 셰이더 단계와 계산 셰이더 단계에만 사용할 수 있다.


자원 뷰의 생성

자원 뷰의 생성에도 ID3D11Device가 사용된다. 이 인터페이스는 각 자원 종류별 생성 메서드를 제공하는데, 이 생성 메서드들은 모두 같은 패턴을 따른다. 자원 뷰 생성 메서드들은 모두 세 개의 매개변수를 받는데, 첫 매개변수는 자원 뷰를 적용할 자원(예를 들면 Texture)을 가리키는 포인터이다. 둘째 매개변수는 자원 뷰를 서술하는 구조체를 가리키는 포인터로, 그 구조체는 해당 종류의 자원 뷰를 위한 모든 옵션을 담는다. 셋째이자 마지막 매개변수는 해당 종류의 자원 뷰 객체에 대한 포인터로, 호출이 성공한 경우 그 포인터가 가리키는 곳에 자원 뷰 객체가 만들어진다.


자원 뷰의 사용

자원 뷰의 구조체는 사용 용도가 다르기 때문에 정의된 구조체도 모두 다르다. 

각 해당 자원에 대해 자원 뷰를 이용하여 접근하면 된다.


자원 뷰의 해제

자원과 마찬가지로 COM 인터페이스를 따라가므로, 참조를 해제해야 한다.

728x90
728x90

자원의 개요 

자원은 앞에서 말햇듯이 크게 두 가지의 종류로 나뉘는데, 버퍼(Buffer)텍스처(Texture)이다.




위의 그림은 자원 클래스들이 모두 ID3DResource라는 단일 공통 기반 클래스로부터 파생된 것임을 보여준다.


이러한 구조는 자원이라는 것이 결국은 파이프라인에 부착(연결)할 수 있고, 입력 또는 출력에 쓰이는 메모리 블록이라는 점을 생각하면 당연한 것이다. 다른 말로 하면 자원은 GPU가 사용하고 조작하도록 마련된 메모리 블록일 뿐이라는 것이다.


자원의 생성

모든 메모리 자원의 생성은 ID3D11Device 인터페이스가 책임진다. 생성된 자원을 파이프라인에 직접 연결할 수도 있고 자원 뷰를 통해서 부착할 수도 있다. 일단 연결이되면 이후 파이프라인 실행 과정 안에서 자원이 실제로 사용된다. 


자원의 생성에 쓰이는 ID3D11Device의 메서드는 자원의 종류마다 다르지만, 모두 동일한 일반 패턴을 따른다.


모든 자원 생성 메서드는 세 개의 매개변수를 받는다. 

첫 매개변수는 자원 생성에 관한 모든 옵션을 지정하는 구조체이다. 이를 자원 서술(Resource Description)이라고 부른다. 자원 종류마다 자원 서술 구조체가 다르다. 그러나 이 구조체들은 모두 동일한 목적(생성된 자원의 원하는 특성들을 정의)으로 사용된다.


둘째 매개변수는 D3D11_SUBRESOURCE_DATA 구조체를 가리키는 포인터인데, 이 구조체는 자원에 적재할 초기 자료를 제공하는 데 쓰인다. 예를 들어 정적 정점 자료를 담을 버퍼 자원을 만드는 경우 이 구조체를 이용해서 모형의 정점 자료를 버퍼 자원에 채워 넣을 수 있다. 


세번째 매개변수는 자원 종류에 맞는 자원 인터페이스를 가리키는 포인터의 포인터로, 자원 생성이 성공하면 해당 자원을 가리키는 포인터가 이 매개변수에 설정된다.




자원 용도 플래그

자원이 존재하는 '메모리'는 비디오 카드일 수도 있고, 시스템의 주 메모리일 수도 있다. 또한 Direct3D 실행 모듈이 자원을 비디오 카드 메모리에서 시스템 메모리로 또는 그 반대 방향으로 이동할 수도 있다. 때문에 응용 프로그램은 반드시 자원의 사용 방식에 대한 자신의 '의도'를 용도 명세 필드를 통해서 명시적으로 밝혀야 한다. 


enum D3D11_USAGE

{

D3D11_USAGE_DEFAULT,

D3D11_USAGE_IMMUTABLE,

D3D11_USAGE_DYNAMIC,

D3D11_USAGE_STAGING

}


 자원 용도

DEFAULT 

DYNAMIC 

 IMMUTABLE

 STAGING

 GPU 읽기 

 YES 

YES 

YES 

YES

GPU 쓰기

YES

 

 

 YES 

 CPU 쓰기 

 

 

 

YES

CPU 읽기

 

YES 

 

YSE


D3D11_USAGE_DEFAULT(기본 용도) : 표에서 보듯이 기본 용도 자원은 GPU의 읽기 쓰기만 가능하고 CPU의 접근은 모두 거부한다. 이 용도는 렌더 타겟 텍스처나 스트림 출력 정점 버퍼가 있다.


D3D11_USAGE_IMMUTABLE(불변 용도) : 가장 간단한 사용 패턴으로 오직 GPU가 읽을 수만 있다. GPU와 CPU의 쓰기를 모두 거부한다. 이 용도는 정적인 상수나, 정점, 색인 버퍼를 들 수 있다.


D3D11_USAGE_DYNAMIC(동적 용도) : CPU의 쓰기가 가능한 두 가지 용도 중 하나이다. GPU는 읽기만 가능하다. 이 사용 용도는 CPU가 자원의 내용을 생산하고 GPU가 자원을 소비한다. 

이 용도는 상수 버퍼에서 사용하면 좋다.


D3D11_USAGE_STAGING(예비 용도) : 이 예비 용도는 특별한 종류의 사용 패턴을 제공한다. 앞에서 말한 세 가지 용도는 렌더링 수행을 위한 전형적인 자원 사용 시나리오들에 해당하는 것이다. 그러나 GPU에서 자료를 계산, 조작하고 그것을 저장이나 추가 조사를 위해 CPU로 읽어 들어야 하는 경우도 있다. 그런 목적의 응용 프로그램에서는 다른 사용 패턴들에 CPU의 읽기 접근을 강제로 허용하는 대신 이 예비 용도의 자원을 중간 계산을 위한 장소로 활용하면 된다.


CPU 접근 플래그

자원의 용도를 지정했다면, 다음으로는 자원에 대한 CPU의 접근 방식을 지정해야 한다. 이 플래그는 CPU에만 국한 된다. 이 열거형의 두 값을 비트 단위 OR로 결합해서 사용하면 읽기와 쓰기를 모두 허용 할 수도 있다. 하지만 자원 용도 플래그에 맞추어서 설정해야만 한다.


enum D3D11_CPU_ACCESS_FLAG

{

D3D11_CPU_ACCESS_WRITE,

D3D11_CPU_ACCESS_READ

}


연결 플래그

자원 서술 구조체의 또 다른 공통 필드로 연결 플래그(Bind Flag)가 있다. 이 필드는 자원을 파이프라인의 어디에 연결할 것인지를 나타낸다. 이 값들을 비트 단위 OR로 결합함으로써 연결 가능 장소를 여러개 지정하는 것도 가능하다. 이 플래그를 제대로 지정하지 않고 자원을 생성하면 나중에 응용 프로그램이 자원을 파이프라인에 연결하려 할 때 오류가 발생한다.


enum D3D11_BIND_FLAG 

{

D3D11_BIND_VERTEX_BUFFER,

D3D11_BIND_INDEX_BUFFER,

D3D11_BIND_CONSTANT_BUFFER,

D3D11_BIND_SHADER_RESOURCE,

D3D11_BIND_STREAM_OUTPUT,

D3D11_BIND_RENDER_TARGET,

D3D11_BIND_DEPTH_STENCIL,

D3D11_BIND_UNORDERED_ACCESS,

}


자원을 파이프라인에 연결할 수 있는 지점은 총 여덟 가지이다. 처음 둘은 정점 버퍼와 색인 버퍼에 해당하는 것으로, 파이프라인의 기하구조 자료를 공급하기 위한 입력 조립기 단계에 부착할 자원에 쓰인다. 반면 여섯 번째와 일곱번째의 렌더 타겟 플래그와 깊이/스탠실 버퍼 플래그는 파이프라인의 렌더링 결과를 받는 출력 병합기 단계에 연결할 자원을 위한 것이다. 다섯 번째의 스트림 출력 플래그 역시 파이프라인으로부터의 출력을 위한 것이나, 래스터화된 이미지 자료가 아니라 기하구조 자료를 받는다는 점이 다르다. 반면 D3D11_BIND_CONSTANT_BUFFER, D3D11_BIND_SHADER_RESOURCE, 

D3D11_BIND_UNORDERED_ACCESS는 셰이더 프로그램 안에서 사용한다.


위의 그림에서 연결 플래그가 연결되는 부분을 확인 할 수 있다.


자원 해제

자원들은 모두 COM 인터페이스를 구현하는 객체의 형태이며, 따라서 해당객체의 계통 구조 안에는 IUnknown 인터페이스가 존재한다. 이는 자원들이 참조 계수 방식으로 관리된다는 뜻이며, 따라서 응용 프로그램이 자원을 다 사용하고 난 후에는 반드시 참조를 해제해 주어야 한다.


728x90

+ Recent posts