Hardware Instancing
- Hareware Instancing이란 Second Vertex Buffer를 활용하여 해당 Vertex Buffer를 필요로하는 Instance들의 추가적인 변환 Data를 Instance Buffer를 통해 Vertex Shader 측에서 한번의 Draw Call로 여러개의 Instance를 그리는 방식이다.
Instancing 장점
1) 같은 Vertex Buffer를 사용함에도 매번 해당 Instance Data를 Constant Buffer에 Update하여 Draw Call을 호출하는 것은병목 현상을 유발하는데, 이러한 Instance Data를 Instance Buffer에 Copy하여 Cache해둔 Vertex Buffer와 수정된Instance Buffer를 통해 Vertex Shader 측에서한번에 그리는 방식을 사용해야한다.
2) Rendering 병목현상을 예방하기위한 최적의 방법은 비슷한 Rendering 과정 ( Vertex Buffer, Shader, Material ...) 을 묶어 한번에 그리는 방식을Batch라고 한다. 이러한 Rendering이 필요한 각각의 Object를 하나씩 그리지 않고 여러개를 묶어 그려오버헤드를 줄이는 것이 중요하다.
3) Batch Rendering과 Instancing을 사용한다면 RenderState 설정 비용 감소, Draw Call 비용 감소,Texture 설정 비용 감소 등 여러가지 최적화가 가능하다.
// Vertex Shader Update..
CB_StaticMesh objectBuf;
objectBuf.gWorld = world;
objectBuf.gInvWorld = invWorld;
objectBuf.gView = view;
objectBuf.gProj = proj;
m_Mesh_VS->ConstantBufferUpdate(&objectBuf);
m_Mesh_VS->Update();
// Pixel Shader Update..
CB_Material materialBuf;
materialBuf.gAddColor = matSub->AddColor;
materialBuf.gEmissiveFactor = matSub->EmissiveFactor;
materialBuf.gRoughnessFactor = matSub->RoughnessFactor;
materialBuf.gMetallicFactor = matSub->MetallicFactor;
materialBuf.gLimLightFactor = matSub->LimLightFactor;
materialBuf.gLimLightColor = matSub->LimLightColor;
materialBuf.gLimLightWidth = matSub->LimLightWidth;
...
m_Deferred_PS->ConstantBufferUpdate(&materialBuf);
m_Deferred_PS->ConstantBufferUpdate(&cameraBuf);
m_Deferred_PS->Update();
// Draw..
g_Context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
g_Context->IASetVertexBuffers(0, 1, &mesh->m_VertexBuf, &mesh->m_Stride, &mesh->m_Offset);
g_Context->IASetIndexBuffer(mesh->m_IndexBuf, DXGI_FORMAT_R32_UINT, 0);
g_Context->DrawIndexed(mesh->m_IndexCount, 0, 0);
#include "Input_Header.hlsli"
cbuffer cbStaticMesh : register(b0)
{
float4x4 gWorld : packoffset(c0);
float4x4 gInvWorld : packoffset(c4);
float4x4 gView : packoffset(c8);
float4x4 gProj : packoffset(c12);
};
MeshVertexOut StaticMesh_VS(MeshVertexIn vin)
{
MeshVertexOut vout;
vout.PosW = mul(gWorld, float4(vin.PosL, 1.0f)).xyz;
vout.PosV = mul(gView, float4(vout.PosW, 1.0f)).xyz;
vout.PosH = mul(gProj, float4(vout.PosV, 1.0f));
...
return vout;
}
- Hardware Instancing 사용하지 않을 경우 Object당 1번씩 Constant Buffer Update와 Draw Call이 호출된다.
// Instance Update..
for (int i = 0; i < m_RenderCount; i++)
{
m_RenderData = meshlist[i];
if (m_RenderData->m_Draw == false) continue;
// 해당 Instance Data 삽입..
m_MeshData.World = m_RenderData->m_ObjectData->World;
m_MeshData.InvWorld = m_RenderData->m_ObjectData->InvWorld;
m_MeshInstance[m_InstanceCount++] = m_MeshData;
}
if (m_InstanceCount == 0) return;
// Instance Buffer Update..
// Mapping SubResource Data..
D3D11_MAPPED_SUBRESOURCE mappedResource;
ZeroMemory(&mappedResource, sizeof(D3D11_MAPPED_SUBRESOURCE));
// GPU Access Lock Buffer Data..
g_Context->Map(m_Mesh_IB->InstanceBuf->Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
// Copy Resource Data..
memcpy(mappedResource.pData, &m_MeshInstance[0], (size_t)m_Mesh_IB->Stride * (size_t)m_InstanceCount);
// GPU Access UnLock Buffer Data..
g_Context->Unmap(m_Mesh_IB->InstanceBuf->Get(), 0);
// Vertex Shader Update..
CB_InstanceStaticMesh objectBuf;
objectBuf.gView = view;
objectBuf.gProj = proj;
m_MeshInst_VS->ConstantBufferUpdate(&objectBuf);
m_MeshInst_VS->Update();
// Pixel Shader Update..
CB_Material materialBuf;
materialBuf.gAddColor = matSub->AddColor;
materialBuf.gEmissiveFactor = matSub->EmissiveFactor;
materialBuf.gRoughnessFactor = matSub->RoughnessFactor;
materialBuf.gMetallicFactor = matSub->MetallicFactor;
materialBuf.gLimLightFactor = matSub->LimLightFactor;
materialBuf.gLimLightColor = matSub->LimLightColor;
materialBuf.gLimLightWidth = matSub->LimLightWidth;
...
m_Deferred_PS->ConstantBufferUpdate(&materialBuf);
m_Deferred_PS->ConstantBufferUpdate(&cameraBuf);
m_Deferred_PS->Update();
ID3D11Buffer* vertexBuffers[2] = { mesh->m_VertexBuf, m_Mesh_IB->InstanceBuf->Get() };
UINT strides[2] = { mesh->m_Stride, m_Mesh_IB->Stride };
UINT offsets[2] = { 0,0 };
// Draw..
g_Context->IASetVertexBuffers(0, 2, vertexBuffers, strides, offsets);
g_Context->IASetIndexBuffer(mesh->m_IndexBuf, DXGI_FORMAT_R32_UINT, 0);
g_Context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
g_Context->DrawIndexedInstanced(mesh->m_IndexCount, m_InstanceCount, 0, 0, 0);
#include "Input_Header.hlsli"
#include "Instance_Header.hlsli"
cbuffer cbInstanceStaticMesh : register(b0)
{
float4x4 gView : packoffset(c0);
float4x4 gProj : packoffset(c4);
};
MeshVertexOut StaticMesh_Instance_VS(MeshVertexIn vin, MeshInstanceIn instance)
{
MeshVertexOut vout;
vout.PosW = mul(instance.World, float4(vin.PosL, 1.0f)).xyz;
vout.PosV = mul(gView, float4(vout.PosW, 1.0f)).xyz;
vout.PosH = mul(gProj, float4(vout.PosV, 1.0f));
...
return vout;
}
- Hardware Instancing 사용할 경우 동일한 Vertex Buffer를 사용하는 Object들을 한 Batch로 묶어 Instance Buffer에 Data를 수정한 후 Cache된 Vertex Buffer와 Instance Buffer를 Vertex Shader 측에서 받아 1번의 Draw Call을 통하여 그린다.
적용 화면
'Graphics' 카테고리의 다른 글
[DirectX 11] Game Engine Render Resource 동기화 방식 및 Resource 관리 방식 (0) | 2022.03.16 |
---|---|
[DirectX 11] Game Engine & Graphic Engine 분리 및 Render Data 전달 방식 (0) | 2022.03.07 |
[DirectX 11] Deferred Fog (2) | 2022.02.21 |
[DirectX 11] Bloom (1) | 2022.02.17 |
[DirectX 11] Lambert / Half Lambert (0) | 2022.02.07 |