Learning

This process started with the idea to reduce & reuse as much data as possible. When researching this I came upon the term GPU Instancing. This basically means that the GPU keep reusing 1 instance of a mesh to draw all the instances needed in the scene. You can mark some data that you pass as ‘Per Instance Data’ which means that the GPU will only load this data once but the GPU will draw it multiple times.

Code snippets

Create Instance & Constant buffer
HRESULT Mesh::CreateInstanceBuffer(ID3D11Device* pDevice)
{
	// ********************************
	// Create instance buffer: Data
	// ********************************
	Scene* pScene{ Locator::GetSceneManagerService()->GetActiveGameScene() };
	std::vector<GameObject*> pGameObjects{ pScene->GetObjects() };
	for (GameObject* pGameObject : pGameObjects)
	{
		ModelComponent* pModelComponent{ pGameObject->GetComponent<ModelComponent>() };
		if (!pModelComponent)
			continue;

		Mesh* pMesh{ pModelComponent->GetMesh() };
		if (pMesh != this)
			continue;

		TransformComponent* pTransformComponent{ pGameObject->GetComponent<TransformComponent>() };
		m_InstancePositionData.push_back(pTransformComponent->GetWorldPosition());
	}

	// If there is only 1 instance of that mesh than clear out the data vector so it won't render as an instance
	if (m_InstancePositionData.size() <= 1)
	{
		m_InstancePositionData.clear();		
		return S_OK;
	}

	// ********************************
	// Create instance buffer: DirectX
	// ********************************
	D3D11_BUFFER_DESC instBuffDesc;
	ZeroMemory(&instBuffDesc, sizeof(instBuffDesc));
	instBuffDesc.Usage = D3D11_USAGE_DEFAULT;
	instBuffDesc.ByteWidth = sizeof(DirectX::XMFLOAT3) * m_AmountInstances;
	instBuffDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	instBuffDesc.CPUAccessFlags = 0;
	instBuffDesc.MiscFlags = 0;

	D3D11_SUBRESOURCE_DATA instData;
	ZeroMemory(&instData, sizeof(instData));
	instData.pSysMem = m_InstancePositionData.data();

	/* CreateBuffer - Parameters */
	const D3D11_BUFFER_DESC* pInstanceBufferDesc{ &instBuffDesc };
	const D3D11_SUBRESOURCE_DATA* pInitialData_InstanceBuffer{ &instData };
	ID3D11Buffer** ppInstanceBuffer{ &m_pInstanceBuffer };

	// Explanation for all parameters in link below
	// Reference: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-createbuffer
	return pDevice->CreateBuffer(pInstanceBufferDesc, pInitialData_InstanceBuffer, ppInstanceBuffer);
}
HRESULT Mesh::CreateConstantBuffer(ID3D11Device* pDevice)
{
	// ********************************
	// Create instance buffer: DirectX
	// ********************************
	D3D11_BUFFER_DESC constantBuffDesc;
	ZeroMemory(&constantBuffDesc, sizeof(constantBuffDesc));
	constantBuffDesc.Usage = D3D11_USAGE_DEFAULT;
	constantBuffDesc.ByteWidth = sizeof(DirectX::XMFLOAT4);
	constantBuffDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	constantBuffDesc.CPUAccessFlags = 0;
	constantBuffDesc.MiscFlags = 0;

	/* CreateBuffer - Parameters */
	const D3D11_BUFFER_DESC* pCBufferDesc{ &constantBuffDesc };
	const D3D11_SUBRESOURCE_DATA* pInitialData_CBuffer{ nullptr };
	ID3D11Buffer** ppCBuffer{ &m_pConstantBuffer };

	// Explanation for all parameters in link below
	// Reference: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11device-createbuffer
	return pDevice->CreateBuffer(pCBufferDesc, pInitialData_CBuffer, ppCBuffer);
}
Update constant buffer
void Mesh::UpdateBuffer()
{ 
	ID3D11DeviceContext* pDeviceContext{ Locator::GetGraphicsService()->GetDeviceContext() };

	// ********************************
	// Update sub resource
	// ********************************
	/* CreateBuffer - Parameters */
	ID3D11Resource*  pDstResource{ m_pConstantBuffer };
	UINT			 DstSubresource{ 0 };
	const D3D11_BOX* pDstBox{ NULL };
	const void*		 pSrcData{ &m_InstancePositionData };
	UINT             SrcRowPitch{ 0 };
	UINT             SrcDepthPitch{ 0 };

	// Explanation for all parameters in link below
	// Reference: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-updatesubresource
	pDeviceContext->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch);
}
Load 3D mesh
Mesh* ResourceManager::Load3DMesh(const std::string& name, const std::string& path)
{
	int meshID{};
	// Check if the Mesh exists already and return the id if it does
	if (MeshAlreadyParsed(name, meshID))
	{
		Mesh* pMesh{ m_p3DMeshes[meshID]->pMesh };

		// Check if the instance counter is on 0 if so set to 1 to start
		if (pMesh->GetAmountInstances() <= 0)
			pMesh->IncrementInstanceCount();

		pMesh->IncrementInstanceCount();
		return pMesh;
	}

	// If it does not exist
	Mesh* pMesh{ new Mesh(path) };
	pMesh->Initialize();

	// Store the mesh
	MeshData* pMeshData{ new MeshData() };
	pMeshData->ID = GetFreeMeshID();
	pMeshData->name = name;
	pMeshData->pMesh = pMesh;
	m_p3DMeshes.push_back(pMeshData);

	// Return the pointer to the mesh
	return pMesh;
}
Rendering & GPU Instancing
void RenderComponent::Render(ID3D11DeviceContext* pDeviceContext, Material* pEffect, Mesh* p3DMesh)
{
	if (p3DMesh->GetAmountInstances() > 0)
		RenderInstanced(pDeviceContext, pEffect, p3DMesh);
	else
		RenderNormal(pDeviceContext, pEffect, p3DMesh);
}
void RenderComponent::RenderNormal(ID3D11DeviceContext* pDeviceContext, Material* pEffect, Mesh* p3DMesh)
{
	ID3DX11EffectTechnique* pTechnique{ pEffect->GetTechnique() };

	D3DX11_TECHNIQUE_DESC techDesc{};
	pTechnique->GetDesc(&techDesc);
	for (UINT p{}; p < techDesc.Passes; ++p)
	{
		// COMMENT HERE
		pTechnique->GetPassByIndex(p)->Apply(0, pDeviceContext);

		// ****************************************
		// Set up the DrawIndexed function
		// ****************************************
		/* DrawIndexed - Parameters */
		UINT IndexCount{ p3DMesh->GetAmountIndices() };
		UINT StartIndexLocation{ 0 };
		INT  BaseVertexLocation{ 0 };

		// Explanation for all parameters in link below
		// Reference: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-drawindexed
		pDeviceContext->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation);
	}
}
void RenderComponent::RenderInstanced(ID3D11DeviceContext* pDeviceContext, Material* pEffect, Mesh* p3DMesh)
{
	if (p3DMesh->GetIsRendered())
		return;

	ID3DX11EffectTechnique* pTechnique{ pEffect->GetTechnique() };

	D3DX11_TECHNIQUE_DESC techDesc{};
	pTechnique->GetDesc(&techDesc);
	for (UINT p{}; p < techDesc.Passes; ++p)
	{
		// COMMENT HERE
		pTechnique->GetPassByIndex(p)->Apply(0, pDeviceContext);

		// ****************************************
		// Set up the DrawIndexedInstanced function
		// ****************************************
		/* DrawIndexedInstanced - Parameters */
		UINT IndexCountPerInstance{ p3DMesh->GetAmountIndices() };
		UINT InstanceCount{ p3DMesh->GetAmountInstances() };
		UINT StartIndexLocation{ 0 };
		INT  BaseVertexLocation{ 0 };
		UINT StartInstanceLocation{ 0 };

		// Explanation for all parameters in link below
		// Reference: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/nf-d3d11-id3d11devicecontext-drawindexedinstanced
		pDeviceContext->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation);
	}

	p3DMesh->SetIsRendered(true);
}

Credits

Introduction to geomtry instancing

Instancing (with indexed primitives)