1. 개발 현황

인스턴싱을 활용하여 효율적인 렌더링 환경 구축

DX12의 DrawIndexedInstanced 함수 내 StartInstanceLocation 활용에 오류가 있어, constant buffer를 활용하여 Start Location을 전달하는 방법으로 동작 구현

2. 상세 개발 내용

// Defaults for number of lights.
#ifndef NUM_DIR_LIGHTS
#define NUM_DIR_LIGHTS 3
#endif

#ifndef NUM_POINT_LIGHTS
#define NUM_POINT_LIGHTS 0
#endif

#ifndef NUM_SPOT_LIGHTS
#define NUM_SPOT_LIGHTS 0
#endif

#define MaxLights 16
#include "LightingUtil.hlsli"

struct InstanceData
{
    float4x4 World;
    float4x4 TexTransform;
    float4x4 WorldInvTranspose; // Geometery Shader 동작 간 법선 벡터 변환 시 직교 성질 유지를 위함
    uint MaterialIndex;
    float2 DisplacementMapTexelSize;
    float GridSpatialStep;
    float bPerObjectPad1;
};

struct MaterialData
{
    float4 DiffuseAlbedo;
    float3 FresnelR0;
    float Roughness;
    float4x4 MatTransform;
    uint DiffuseMapIndex;
    uint MatPad0;
    uint MatPad1;
    uint MatPad2;
};

#ifdef TEX_SIZE
Texture2D gDiffuseMap[TEX_SIZE] : register(t0, space0);
#else
Texture2D gDiffuseMap[16] : register(t0, space0);
#endif

#ifdef TEX_ARRAY_SIZE
Texture2DArray gTreeMapArray[TEX_ARRAY_SIZE] : register(t0, space1);
#else
Texture2DArray gTreeMapArray[2] : register(t0, space1);
#endif

// Put in space1, so the texture array does not overlap with these resources.  
// The texture array will occupy registers t0, t1, ..., t3 in space0.
StructuredBuffer<InstanceData> gInstanceData : register(t0, space2); 
StructuredBuffer<MaterialData> gMaterialData : register(t1, space2);
Texture2D gDisplacementMap : register(t2, space2);

SamplerState gsamPointWrap : register(s0);
SamplerState gsamPointClamp : register(s1);
SamplerState gsamLinearWrap : register(s2);
SamplerState gsamLinearClamp : register(s3);
SamplerState gsamAnisotropicWrap : register(s4);
SamplerState gsamAnisotropicClamp : register(s5);

// Constant data that varies per material.
cbuffer cbPass : register(b0)
{
    float4x4 gView;
    float4x4 gInvView;
    float4x4 gProj;
    float4x4 gInvProj;
    float4x4 gViewProj;
    float4x4 gInvViewProj;
    float3 gEyePosW;
    float cbPerPassPad1;
    float2 gRenderTargetSize;
    float2 gInvRenderTargetSize;
    float gNearZ;
    float gFarZ;
    float gTotalTime;
    float gDeltaTime;
    float4 gAmbientLight;

    	// Allow application to change fog parameters once per frame.
	// For example, we may only use fog for certain times of day.
    float4 gFogColor;
    float gFogStart;
    float gFogRange;
    float2 cbPerObjectPad2;
    
    // Indices [0, NUM_DIR_LIGHTS) are directional lights;
    // indices [NUM_DIR_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHTS) are point lights;
    // indices [NUM_DIR_LIGHTS+NUM_POINT_LIGHTS, NUM_DIR_LIGHTS+NUM_POINT_LIGHT+NUM_SPOT_LIGHTS)
    // are spot lights for a maximum of MaxLights per object.
    Light gLights[MaxLights];
};

cbuffer cbInstance : register(b1)
{
    uint gBaseInstanceIndex;
}
void MyApp::BuildRootSignature()
{
	// Create root CBVs.
	D3D12_DESCRIPTOR_RANGE TexTable // register t0[16] (Space0)
	{
		/* D3D12_DESCRIPTOR_RANGE_TYPE RangeType	*/.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
		/* UINT NumDescriptors						*/.NumDescriptors = (UINT)TEXTURE_FILENAMES.size() + SRV_USER_SIZE,
		/* UINT BaseShaderRegister					*/.BaseShaderRegister = 0,
		/* UINT RegisterSpace						*/.RegisterSpace = 0,
		/* UINT OffsetInDescriptorsFromTableStart	*/.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
	};
	D3D12_DESCRIPTOR_RANGE TexArrayTable // register t0[0] (Space1)
	{
		/* D3D12_DESCRIPTOR_RANGE_TYPE RangeType	*/.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
		/* UINT NumDescriptors						*/.NumDescriptors = (UINT)TEXTURE_ARRAY_FILENAMES.size(),
		/* UINT BaseShaderRegister					*/.BaseShaderRegister = 0,
		/* UINT RegisterSpace						*/.RegisterSpace = 1,
		/* UINT OffsetInDescriptorsFromTableStart	*/.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
	};
	D3D12_DESCRIPTOR_RANGE DisplacementMapTable // register t2 (Space2)
	{
		/* D3D12_DESCRIPTOR_RANGE_TYPE RangeType	*/.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
		/* UINT NumDescriptors						*/.NumDescriptors = 1,
		/* UINT BaseShaderRegister					*/.BaseShaderRegister = 2,
		/* UINT RegisterSpace						*/.RegisterSpace = 2,
		/* UINT OffsetInDescriptorsFromTableStart	*/.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
	};
	

	// Root parameter can be a table, root descriptor or root constants.
	CD3DX12_ROOT_PARAMETER slotRootParameter[7];

	/*D3D12_SHADER_VISIBILITY
	{
		D3D12_SHADER_VISIBILITY_ALL = 0,
		D3D12_SHADER_VISIBILITY_VERTEX = 1,
		D3D12_SHADER_VISIBILITY_HULL = 2,
		D3D12_SHADER_VISIBILITY_DOMAIN = 3,
		D3D12_SHADER_VISIBILITY_GEOMETRY = 4,
		D3D12_SHADER_VISIBILITY_PIXEL = 5,
		D3D12_SHADER_VISIBILITY_AMPLIFICATION = 6,
		D3D12_SHADER_VISIBILITY_MESH = 7
	} 	D3D12_SHADER_VISIBILITY;*/

	// Perfomance TIP: Order from most frequent to least frequent.
	slotRootParameter[0].InitAsConstantBufferView(1);		// register b1 (gBaseInstanceIndex)
	slotRootParameter[1].InitAsShaderResourceView(0, 2);	// InstanceData t0 (Space2)
	slotRootParameter[2].InitAsShaderResourceView(1, 2);	// MaterialData t1 (Space2)
	slotRootParameter[3].InitAsConstantBufferView(0);		// register b0
	slotRootParameter[4].InitAsDescriptorTable(1, &TexTable, D3D12_SHADER_VISIBILITY_PIXEL);
	slotRootParameter[5].InitAsDescriptorTable(1, &TexArrayTable, D3D12_SHADER_VISIBILITY_PIXEL);
	slotRootParameter[6].InitAsDescriptorTable(1, &DisplacementMapTable, D3D12_SHADER_VISIBILITY_ALL);

	auto staticSamplers = D3DUtil::GetStaticSamplers();

	// A root signature is an array of root parameters.
	CD3DX12_ROOT_SIGNATURE_DESC rootSigDesc(7, slotRootParameter, (UINT)staticSamplers.size(), staticSamplers.data(), D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT);

	// create a root signature with a single slot which points to a descriptor range consisting of a single constant buffer
	Microsoft::WRL::ComPtr<ID3DBlob> serializedRootSig = nullptr;
	Microsoft::WRL::ComPtr<ID3DBlob> errorBlob = nullptr;
	HRESULT hr = D3D12SerializeRootSignature(&rootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf());
	if (errorBlob != nullptr)
		::OutputDebugStringA((char*)errorBlob->GetBufferPointer());
	ThrowIfFailed(hr);

	ThrowIfFailed(mDevice->CreateRootSignature(0, serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(), IID_PPV_ARGS(mRootSignature.GetAddressOf())));
}
	for (size_t i = 0; i < mAllRitems.size(); ++i)
	{
		auto& ri = mAllRitems[i];
		if (!(ri->LayerFlag & (1 << (int)flag)))
			continue;

		if (flag == RenderLayer::Normal
			|| flag == RenderLayer::NormalWireframe
			|| flag == RenderLayer::TreeSprites
			|| flag == RenderLayer::TreeSpritesWireframe)
			ri->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
		else if (flag == RenderLayer::Tessellation
			|| flag == RenderLayer::TessellationWireframe)
			ri->PrimitiveType = D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST;
		else
			ri->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;

		D3D12_VERTEX_BUFFER_VIEW vbv = ri->Geo->VertexBufferView();
		D3D12_INDEX_BUFFER_VIEW ibv = ri->Geo->IndexBufferView();
		mCommandList->IASetVertexBuffers(0, 1, &vbv);
		mCommandList->IASetIndexBuffer(&ibv);
		mCommandList->IASetPrimitiveTopology(ri->PrimitiveType);
		std::cout << ri->StartInstanceLocation << std::endl;

		D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = currInstanceCB->GetGPUVirtualAddress() + i * objCBByteSize;
		mCommandList->SetGraphicsRootConstantBufferView(0, objCBAddress);

		mCommandList->DrawIndexedInstanced(ri->IndexCount, ri->InstanceCount, ri->StartIndexLocation, ri->BaseVertexLocation, ri->StartInstanceLocation);
		// mCommandList->DrawIndexedInstanced(ri->IndexCount, ri->InstanceCount, ri->StartIndexLocation, ri->BaseVertexLocation, 0);
	}

3. TODO

컬링을 적용하여 더 효율적인 렌더링 환경 구축

4. 참고자료

https://www.gamedev.net/forums/topic/662594-startinstancelocation-and-sv-instanceid/

https://github.com/microsoft/DirectXShaderCompiler/issues/2946

 

[SPIR-V] SV_InstanceID behavior different than in DX12 · Issue #2946 · microsoft/DirectXShaderCompiler

When using SV_InstanceID, the SPIR-V codegen emits gl_InstanceIndex, which is not correct. The DX12 InstanceID always counts from 0, even if the StartInstanceLocation parameter is non-zero. gl_Inst...

github.com

 

728x90
반응형

+ Recent posts