CyberConnect2에서는 2013년부터 DirectX11세대용 멀티플랫폼엔진 개발을 시작하였으며, 제작 시 발생하였던 문제점을 DirectX9와의 차이점을 바탕으로 공유하고자 합니다.
이 세션은 DirectX11의 개발이 처음이거나 관심 있으신 분을 대상으로 합니다. Tessellation 이나 OIT와 같은 최신기술은 다루지 않으므로 주의하시기 바랍니다.
2. 세션 개요
세션에서 다루는 내용
• CyberConnect2의 DirectX11엔진 이야기
• DirectX11 API설계
• 게임 내용, 최신 테크닉은 다루지 않습니다
세션의 대상
• DirectX9세대의 Graphics API를 사용해 본 적이 있는 분
• 앞으로 DirectX11를 사용하실 분
9. 방향성
Engine 소개
멀티 플랫폼, 멀티 코어 대응
• 기본적으로 Windows, PlayStation4, XBOX ONE 대응
• 멀티코어를 고려한 설계
Engine 규모
• 프로젝트 규모상 상용엔진 수준은 무리
• 필요한 기능만 최소한으로 제작중
10. 방향성
Engine 소개
기능
• 최소한 지금까지 가능했던 일은 구현 가능하도록 설계
• 기존 엔진에서 불편했던 부분은 개선, 좋았던 부분은 채용, 가능하면
코드 재사용
차세대 대비
• 차세대를 준비하기 위한 최소한의 환경 구축
11. 제작 이유
Engine 소개
기존 엔진의 문제점
• PlayStation2 시절의 낡은 설계
• 리소스 파이프라인에 불필요한 작업이 많았음
• 부족한 기능을 구현하기 위해 game에서 억지로 구현한 부분이 점차
많아지기 시작함
• 외부 엔진이라 대응이 늦거나 불가능한 일이 잦았음
콘솔 개발킷의 부족
• 기존 엔진 및 각종 툴이 콘솔기기에서 밖에 동작하지 않아 콘솔 개
발킷이 없으면 디버깅이 불가능
• Windows에서 개발하고 싶다는 요청이 많아짐
12. 제작 이유
Engine 소개
내부 기술력 강화
• 외부 엔진만 사용해서는 노하우 쌓기가 쉽지 않다고 판단
차세대기로 갈아타기 위한 좋은 타이밍
• 차세대기 발매
• 하드웨어의 진화
• 지금까지의 설계로는 하드웨어의 스펙을 전부 활용하기 힘들어짐
20. DirectX11변경점
고정 파이프라인 삭제
Device 분리
CAPS비트 삭제
새로운 shader stage 추가
Multithread Rendering
Dynamic Shader Linkage
Tesselation, OIT
BC6, BC7 texture format
Etc.
21. DirectX11변경점
이 중 DirectX11 을 사용하여 rendering하기 위해 반드시 필
요한 부분을 DirectX9과 비교
독자적으로 대응한 방법에 대한 소개도 있습니다만 정답은
아니므로 어디까지나 참고
22. Device 분리
DirectX11변경점
DirectX9의 IDirect3DDevice9 은 DirectX11에서부터 ID3D11Device 와
ID3D11DeviceContext 로 분리되었음
ID3D11Device은 주로 resource 생성을 담당
• thread-safe
ID3D11DeviceContext은 주로 rendering을 담당
• thread-unsafe
자료 소스코드 중 pDevice 는 IDirect3DDevice9, pImmediateContext 은
ID3D11DeviceContext 의 객체이므로 참고
24. Constant Buffer?
Constant Buffer
DirectX10부터 추가된 shader parameter 설정을 위한 버퍼
DirectX9에서는 개별적으로 설정했지만 DirectX11에서는 버
퍼 단위의 오브젝트로 변경
DirectX11에서 shader 에 값을 전달하기 위해 반드시 필요
25. DirectX9
Constant Buffer
HLSL(sm3.0)까지의 shader 변수정의
• C++ global variable 처럼 정의
HLSL
float3 g_vLightPos;
float3 g_vLightDir;
float4x4 g_mView;
float4x4 g_mProj;
float4x4 g_mWorld;
float4x4 g_mBone[128];
float4 g_vMaterial;
27. DirectX11
Constant Buffer
cbuffer키워드 안에 변수를 묶어서 정의. C언어의 구조체와 비슷
한 형태
선언 뒤에 바인드될 레지스터의 번호를 설정. 16번까지 설정 가
능
- 설정하지 않을 경우, 0번부터 차례대로 부여됨
cbuffer CB : register(b0)
{
float3 vLightPos;
float3 vLightDir;
float4x4 mView;
float4x4 mProj;
float4x4 mWorld;
float4x4 mBone[128];
float4 vMaterial;
};
HLSL
28. DirectX11
Constant Buffer
1. CreateBuffer로 constant buffer용 버퍼 생성
2. UpdateSubresource(Map)로 버퍼의 내용을 update
• 한번의 update로 constant buffer 내용 전부를 덮어씌우므로 주의
3. XXSetconstantBuffers로 레지스터에 바인드
ex) VSSetConstantBuffers, PSSetConstantBuffer 등
29. 주의점
Constant Buffer
update 사이즈와 호출회수를 최대한으로 줄여야 함
• 성능 저하에 민감함
가장 실수하기 쉬운 부분
world matrix만 덮어씌워도
8424bytes update해야함
cbuffer CB : register(b0)
{
float3 vLightPos;
float3 vLightDir;
float4x4 mView;
float4x4 mProj;
float4x4 mWorld;
float4x4 mBone[128];
float4 vMaterial;
};
HLSL
30. Constant Buffer
shader로 전달할 데이터 크기 이상의 constant buffer 를
update 하는 것은 잘못된 설계
필요한 부분만 update 할 수 있도록 constant buffer 를 나
누어 설계. update 빈도에 따라 나누길 추천
한 번 update 한 constant buffer는 최대한 돌려쓰거나 모아
서 rendering
31. Constant Buffer
Application HLSL
Global
cbuffer CB : register(b0)
{
float3 vLightPos;
float3 vLightDir;
float4x4 mView;
float4x4 mProj;
float4x4 mWorld;
float4x4 mBone[128];
float4 vMaterial;
};
Camera
Object
Material
32. Constant Buffer
Application HLSL
Global
Camera
cbuffer CB : register(b0)
{
float3 vLightPos;
float3 vLightDir;
float4x4 mView;
float4x4 mProj;
float4x4 mWorld;
float4x4 mBone[128];
float4 vMaterial;
};
8424bytes
25272bytes
Object
Material
Material
Material
34. Constant Buffer
처음부터 constant buffer의
update 빈도에 따라 sorting
하여 rendering 할 수 있도
록 설계 하는 것이 중요
내부 엔진에서는 다음의 4개
의 빈도로 나누어서 설계
- Global
- Camera
- Object
- Material
Global
Camera
Object
Material
Object
Material
Material
Camera
Object
Material
40. VertexInputLayout
HRESULT CreateInputLayout(
[in] const D3D11_INPUT_ELEMENT_DESC *pInputElementDescs,
[in] UINT NumElements,
[in] const void *pShaderBytecodeWithInputSignature,
[in] SIZE_T BytecodeLength,
[out] ID3D11InputLayout **ppInputLayout
);
pShaderBytecodeWithInputSignature [in]
void
A pointer to the compiled shader. The compiled shader code contains a input signature which
is validated against the array of elements.
BytecodeLength [in]
SIZE_T
Size of the compiled shader.
45. VertexInputLayout
vertex buffer 의 구성을 알고 있어도 같은 구성의 semantic
을 가지고 있는 vertex shader를 컴파일 하지 않으면
VertexInputLayout 생성이 불가능
vertex buffer 의 구성만으로 생성이 가능 했던 DirectX9에
비해 엄격해졌음
46. VertexInputLayout
vertex buffer 와 vertex shader semantic 의 binding이 결정
된 시점에 VertexInputLayout 동적생성
한번 생성된 객체는 vertex buffer 와 vertex shader
semantic 을 key로 가지는 hash table 관리
⇒ 동적 생성, hash table 검색이 느리고, rendering 할때마
다 체크하지 않으면 안되기 때문에 비효율적
47. VertexInputLayout
DirectX11에서는 shader reflection기능이 대폭 강화됨
• semantic 의 type, num, offset 이외에도 constant buffer 의 name,
variable type 등 대부분의 정보를 프로그램에서 얻어 낼 수 있음
vertex shader를 reflection하여 semantic과 일치하는
VertexInputLayout를 미리 생성. 즉, vertex buffer 기준이 아
닌 shader semantic기준으로VertexInputLayout를 생성
49. VertexInputLayout
vertex buffer 와 상관없이 미리 VertexInputLayout 을 생성하기
때문에 vertex buffer 와의 불일치가 발생하는 경우가 있음
VertexInputLayout vertex buffer
position color 문제 없음
assert
normal position normal color
position normal
position normal color assert ?
position normal color
position color
50. VertexInputLayout
vertex buffer 가 GPU에 binding될 때 VertexInputLayout
정보를 참조하여 필요한 semantic 만 binding
VertexInputLayout position color
vertex buffer position normal color
GPU
51. VertexInputLayout
vertex buffer binding
vertex buffer 를 나누어 생성
GPU
1.0f 1.0f 1.0f
normal 1.0f 1.0f 1.0f
1.0f 1.0f 1.0f 1.0f
position
color
UINT posStride = sizeof(float)*3;
UINT posOffset = 0;
pImmediateContext->IASetVertexBuffers( 0, 1, &posVB, &posStride, &posOffset );
UINT colorStride = sizeof(float)*4;
UINT colorOffset = 0;
pImmediateContext->IASetVertexBuffers( 1, 1, &colorVB, &colorStride, &colorOffset );
54. DirectX9
State Management
개별적으로 설정
한번 설정된 값은 변경하지 않는 이상 지속됨
pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
Render(); // z enable – On
pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
Render(); // z enable – On, z write - Off
55. DirectX11
State Management
CreateXXXState 함수로 생성하며 오브젝트로 변경됨
개별 설정 불가능. 오브젝트 단위로 설정
state를 변경하는 경우 오브젝트를 재생성할 필요가 있음
texture sampler도 render state와 동일한 설계로 변경됨
56. DirectX11
State Management
data create set
Rasterizer D3D11_RASTERIZER_DESC CreateRasterizerState RSSetState
Blend D3D11_BLEND_DESC CreateBlendState OMSetBlendState
DepthStencil D3D11_DEPTH_STENCIL_DESC CreateDepthStencilState OMSetDepthStencilState
58. DirectX11
State Management
변경 예
1. z enable – on
2. z enable – on, z write – off
pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
Render(); // z enable – On
pDevice->SetRenderState( D3DRS_ZWRITEENABLE, FALSE );
Render(); // z enable – On, z write - Off
59. DirectX11
State Management
ID3D11DepthStencilState* depthStencilState = NULL;
D3D11_DEPTH_STENCIL_DESC desc;
desc.DepthEnable = TRUE;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
/* set desc.... */
device->CreateDepthStencilState(&desc, &depthStencilState);
pImmediateContext->OMSetDepthStencilState( depthStencilState );
Render(); // z enable – On
SAFE_RELEASE( depthStencilState );
desc.DepthEnable = TRUE;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
/* set desc.... */
pDevice->CreateDepthStencilState(&desc, &depthStencilState);
pImmediateContext->OMSetDepthStencilState( depthStencilState );
Render(); // z enable – On, z write - Off
60. State Management
DirectX9 처럼 global render state 에 모든 설정을 한 후, rendering 직
전에 오브젝트 생성
⇒ 동적생성으로 인한 속도저하, multithread 대응 불가능, release 타이
밍을 잡기 쉽지 않음
사전에 사용될 가능성이 있는 render state를 전부 미리 생성해 둠
⇒ program에서 동적으로 생성되는 타입의 render state에 대응하기 쉽
지 않음
ex) wireframe, blend 등
61. State Management
DirectX11은 한번 생성되었던 render state 오브젝트를 내부적으로 관리
하고 있음
2번째 생성하는 경우에 이전에 생성된 render state 오브젝트의
reference count를 올리고 해당 오브젝트의 pointer를 반환함
즉, 한번 생성된 오브젝트를 해제하지 않고 놔두면 2번째 부터는 고속으
로 생성 가능
62. State Management
한번 생성된 render state는 해제하지 않고 stl set 에 저장.
2번째 같은 오브젝트가 생성된 경우 reference count를 1로
유지하기 위해 Release호출
Create - solid
Create – wireframe
solid
1
solid
1
wireframe
1
render state
count
render state
count
63. State Management
Create – alpha
solid wireframe
1 1
alpha
1
Create – wireframe
solid wireframe
1 2
alpha
1
solid wireframe
1 1
alpha
1
Release
render state
count
render state
count
render state
count
64. State Management
차후 CreateXXXState 하기 위한 copy연산을 줄이기 위해
render state parameter를 hash key로 관리하는 시스템
도입 검토중
D3D11_DEPTH_STENCIL_DESC desc;
ZeroMemory( &desc, sizeof( D3D11_DEPTH_STENCIL_DESC ) );
desc.DepthEnable = EngineToDX11(ENGINE_DEPTH_ON);
desc.DepthWriteMask = EngineToDX11(ENGINE_ZWRITE_ON);
desc.DepthFunc = EngineToDX11(ENGINE_COMPARISON_LESS);
desc.StencilEnable = EngineToDX11(ENGINE_STENCIL_ON);
/* set desc.... */
69. ID3D11DeviceContext
Multithread
immediate context
• rendering 명령을 실행하는 오브젝트
• 1개만 생성 가능하며 thread unsafe
deferred context
• immediate context의 multithread 대응 오브젝트. 여러 개 생성 가능
하며 각 thread 에서 rendering 명령을 실행
• immediate context와 사용가능한 함수는 완전 일치
• 각 thread에서 모든 rendering 명령의 실행이 끝난 후에
FinishCommandList 를 호출하여 command list 를 얻어옴. 이후
command list를 immediate context 에서 실행
70. Deferred Context
Multithread
1. 생성
ID3D11DeviceContext* pImmediateContext;
ID3D11DeviceContext* pDeferredContext;
/* pImmediateContext생성 후 */
pDevice->CreateDeferredContext( 0, &pDeferredContext );
2. 각 thread 마지막에 실행
/* pDeferredContext를 이용한 rendering 완료후 */
pDeferredContext->FinishCommandList( FALSE, &pCommandList );
3. immediate context에서 command list 실행
pImmediateContext->ExecuteCommandList( pCommandList, FALSE );
72. Draw Command
Multithread
내부 엔진에서는 중간 단계 rendering command 를 자체적
으로 구현
모든 command 에는 priority 를 붙여서 생성
각 thread에서 생성된 command 를 sorting 한 후
rendering thread 에 전달
rendering thread에서 각각의 command를 merge sort 한
후 이를 순차적으로 실행
75. Draw Command
Multithread
장점
• priority를 이용해 유연성있게 rendering 순서를 조절 가능
• Graphics API에서 multithread를 대응하지 않는 경우 대응이 쉬움
• Draw Command의 기록을 남겨 replay나 디버깅에 활용
단점
• Graphics API단에서의 병렬화가 아니므로, 일부 명령어는 병렬화 되
지 않을 가능성이 있음
76. Multithread
처음부터 multithread를 고려하여 설계하는 것이 중요
• Single-thread기준으로 작성된 프로젝트를 multithread 구조로 변경
하기 매우 힘듦
Multithread를 고려하지 않으면 속도 올리기가 쉽지 않음
하드웨어의 스펙을 100% 활용합시다!
82. Sampler 정의
Shader
DirectX11의 경우, shader에 적힌 sampler 속성은 effect를 사용
하지 않는 한 적용되지 않으므로 주의
effect를 사용하지 않는 경우는 프로그램에서 설정
DirectX9
sampler2D g_sampler = sampler_state
{
Texture = <g_texture>;
MinFilter = Point;
MagFilter = Linear;
MipFilter = Linear;
};
SamplerState g_sampler
{
Filter = MIN_MAG_MIP_LINEAR;
};
DirectX11
86. Texture Array
Etc.
다수의 texture를 한번에 shader로 전달하는 것이 가능해짐
Texture이외의 material parameter가 같은 경우, texture 변경 없이 한번
에 rendering 할 수 있음
리소스 파이프라인 변경 없이 texture atlas의 효과를 얻을 수 있음
87. Format
Etc.
D3DFMT_XXX가 DXGI_FORMAT_XXX로 변경
D3DFMT_XXX에서 추가, 변경, 삭제 된 포맷이 있
으므로 주의
지원되지 않는 포맷을 리소스에서 사용하고 있는
경우 차후 문제가 생길 여지가 있으므로 주의
88. Primitive
Etc.
D3DPT_TRIANGLEFAN가 삭제됨
프로그램에서 geometry를 생성하는 타입은 소스코드 수정
리소스에서 사용되는 경우 지원되는 primitive 포맷으로 export를 다시
하거나 로딩 시에 변환
90. 결론
DirectX11 은 DirectX9 과 비교하여 많은 점이 변경 되었음.
기존 엔진&라이브러리를 이식하는 경우 생각보다 쉽지 않
음
단, 그만큼 차세대에 어울리는 설계가 되어 있으므로,
application에서도 graphics API를 이해하고 그 성능을 십
분 활용할 수 있는 설계가 필요함