[DirectX] 구조화 버퍼


광원이 늘어나거나 줄어들 경우?


광원이 늘어나거나 줄어들 경우에는 어떻게 해야할까?

상수레지스터는 크기 제한이 있으나 텍스쳐는 무엇이 들어올지 알 수 없기 때문에 포인터로 가리키는 개념이다.

그래서 레지스터에서 참조 형태로 접근이 가능하다.

이게 된다면 어떻게 되느냐, LightInfo의 크기 별로 버퍼를 생성할 수 있다.

상수버퍼처럼 크기를 고정시키는게 아니다. 그래서 가변적으로 바인딩을 할 수 있다.

cbuffer LIGHT : register(b2)
{
    tLightInfo  arrInfo[10];
    int         iLightCount;
    int3        iLightPadding;
}

Texture2D g_tex_0 : register(t0);
...

StructuredBuffer<tLightInfo> g_buffer_0 : register(t8);

SamplerState g_sam_0 : register(s0);
SamplerState g_sam_1 : register(s1);

그래서 구조화 버퍼에다가 값을 넣어서 텍스쳐 레지스터에다가 바인딩을 거는 형식으로 광원을 생성해볼 수 있다.

구조화 버퍼의 클래스 구성


// 헤더파일

#pragma once
#include "CEntity.h"

class CStructuredBuffer :
    public CEntity
{
private:
    ComPtr<ID3D11Buffer>                m_SB;
    ComPtr<ID3D11ShaderResourceView>    m_SRV;
    D3D11_BUFFER_DESC                   m_tDesc;

    UINT                                m_iElementSize;
    UINT                                m_iElementCount;

public:
    void Create(UINT _iElementSize, UINT _iElementCount);
    void SetData(void* _pSrc, UINT _iSize = 0);

    // PIPELINE_STAGE
    void UpdateData(UINT _iRegisterNum, UINT _iPipeLineStage);

    UINT GetElementSize() { return m_iElementSize; }
    UINT GetElementCount() { return m_iElementCount; }



    CLONE_DISABLE(CStructuredBuffer);
public:
    CStructuredBuffer();
    ~CStructuredBuffer();
};
// cpp 파일

#include "pch.h"
#include "CStructuredBuffer.h"

#include "CDevice.h"

CStructuredBuffer::CStructuredBuffer()
	: m_iElementSize(0)
	, m_iElementCount(0)
{
}

CStructuredBuffer::~CStructuredBuffer()
{
}

void CStructuredBuffer::Create(UINT _iElementSize, UINT _iElementCount)
{
	m_SB = nullptr;
	m_SRV = nullptr;


	m_iElementSize = _iElementSize;
	m_iElementCount = _iElementCount;

	UINT iBufferSize = m_iElementSize * _iElementCount;

	// 16바이트 단위 메모리 정렬
	assert(!(iBufferSize % 16));

	// 상수버퍼 생성
	m_tDesc.ByteWidth = iBufferSize;				// 버퍼 크기
	m_tDesc.StructureByteStride = m_iElementSize;	// 데이터 간격

	m_tDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;	// Texture 레지스터에 바이딩하기 위한 플래그
	m_tDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;	// 구조화 버퍼 체크

	m_tDesc.Usage = D3D11_USAGE_DYNAMIC;
	m_tDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	
	if (FAILED(DEVICE->CreateBuffer(&m_tDesc, nullptr, m_SB.GetAddressOf())))
	{
		assert(nullptr);
	}


	// ShaderResourceView 생성
	D3D11_SHADER_RESOURCE_VIEW_DESC	m_SRVDesc = {};

	m_SRVDesc.ViewDimension = D3D_SRV_DIMENSION_BUFFEREX;
	m_SRVDesc.BufferEx.NumElements = m_iElementCount;

	if (FAILED(DEVICE->CreateShaderResourceView(m_SB.Get(), &m_SRVDesc, m_SRV.GetAddressOf())))
	{
		assert(nullptr);
	}
}

void CStructuredBuffer::SetData(void* _pSrc, UINT _iSize)
{
	D3D11_MAPPED_SUBRESOURCE tSub = {};
	CONTEXT->Map(m_SB.Get(), 0, D3D11_MAP::D3D11_MAP_WRITE_DISCARD, 0, &tSub);

	memcpy(tSub.pData, _pSrc, _iSize);	

	CONTEXT->Unmap(m_SB.Get(), 0);
}

void CStructuredBuffer::UpdateData(UINT _iRegisterNum, UINT _iPipeLineStage)
{
	if (PIPELINE_STAGE::PS_VERTEX & _iPipeLineStage)
	{
		CONTEXT->VSSetShaderResources(_iRegisterNum, 1, m_SRV.GetAddressOf());
	}

	if (PIPELINE_STAGE::PS_HULL & _iPipeLineStage)
	{
		CONTEXT->HSSetShaderResources(_iRegisterNum, 1, m_SRV.GetAddressOf());
	}

	if (PIPELINE_STAGE::PS_DOMAIN & _iPipeLineStage)
	{
		CONTEXT->DSSetShaderResources(_iRegisterNum, 1, m_SRV.GetAddressOf());
	}

	if (PIPELINE_STAGE::PS_GEOMETRY & _iPipeLineStage)
	{
		CONTEXT->GSSetShaderResources(_iRegisterNum, 1, m_SRV.GetAddressOf());
	}

	if (PIPELINE_STAGE::PS_PIXEL & _iPipeLineStage)
	{
		CONTEXT->PSSetShaderResources(_iRegisterNum, 1, m_SRV.GetAddressOf());
	}
}

구조화 버퍼를 생성할 때

  • 텍스쳐가 바인드로 쉐이더 리소스를 쓰므로 Bing Flag로 D3D11_BIND_SHADER_RESOURCE로 잡아준다.
  • 텍스쳐인지 구조화 버퍼인지를 구분해주기 위해서 MiscFlag를 설정해준다. (D3D11_RESOURCE_MISC_BUFFER_STRUCTURED)
  • 쉐이더 리소스뷰를 반드시 활용한다.

나머지는 상수 버퍼를 생성할 때와 설정이 같다.

구조화 버퍼 적용하기


CStructuredBuffer.cpp 파일 중에서


렌더 매니저의 init함수에서 구조화 버퍼를 생성해서 적용할 수 있다.

그리고 이제는 상수버퍼로 데이터를 보내는 것이 아니라 LightBuffer에다가 데이터를 보내도록 조치할 것이다.

// CStructuredBuffer.cpp 에서
void CStructuredBuffer::SetData(void* _pSrc, UINT _iSize)
{
	D3D11_MAPPED_SUBRESOURCE tSub = {};
	CONTEXT->Map(m_SB.Get(), 0, D3D11_MAP::D3D11_MAP_WRITE_DISCARD, 0, &tSub);

	memcpy(tSub.pData, _pSrc, _iSize);	

	CONTEXT->Unmap(m_SB.Get(), 0);
}

SetData함수에서 구조화 버퍼를 Map, UnMap을 사용하면서 동적으로 사용할 수 있도록 한다.


새로운 상수버퍼 적용하기


그리고 구조화 버퍼안에 광원의 개수는 몇개인지 정보를 따로 보내줘야 한다.

목적은 효율적인 메모리 관리이다.

이는 새로운 상수버퍼에서 실행할 것이다.

value.fx 파일 중에서


cbuffer GLOBAL : register(b2)
{
    float2 g_Resolution; // 해상도
    float  g_DT; // Delta Time
    float  g_AccTime;
    uint   g_Light2DCount; // 2차원 광원의 개수
    uint   g_Light3DCount; // 3차원 광원의 개수
    int2   g_globalpadding; // 16바이트 단위로 채우기 위한 패딩
}

이제 b2는 전역 상수버퍼로서 역할을 수행할 것이다.

define.h 파일 중에서


그리고 이를 맞추기 위해서 define.h파일에서도 수정을 한다.

enum class CB_TYPE
{
	TRANSFORM,	// b0
	MATERIAL,	// b1
	GLOBAL,		// b2
	END,
};


struct.h 파일 중에서


struct tGlobal
{
	Vec2  Resolution;
	float tDT;
	float tAccTime;
	UINT  Light2DCount;
	UINT  Light3DCount;
	int	  globalpadding[2];
};

extern tGlobal GlobalData;

글로벌 헤더에 입력된 값을 구조체로 맞춰준다.

CDevice.cpp 파일 중에서


그리고 글로벌 버퍼를 CDevice.cpp에서 생성해준다.

void CDevice::CreateConstBuffer()
{
    m_arrConstBuffer[(UINT)CB_TYPE::TRANSFORM] = new CConstBuffer((UINT)CB_TYPE::TRANSFORM);
    m_arrConstBuffer[(UINT)CB_TYPE::TRANSFORM]->Create(sizeof(tTransform), 1);

    m_arrConstBuffer[(UINT)CB_TYPE::MATERIAL] = new CConstBuffer((UINT)CB_TYPE::MATERIAL);
    m_arrConstBuffer[(UINT)CB_TYPE::MATERIAL]->Create(sizeof(tMtrlConst), 1);

    m_arrConstBuffer[(UINT)CB_TYPE::GLOBAL] = new CConstBuffer((UINT)CB_TYPE::GLOBAL);
    m_arrConstBuffer[(UINT)CB_TYPE::GLOBAL]->Create(sizeof(tGlobal), 1);
}


CRenderMgr.cpp 파일 중에서


GlobalData버퍼의 Light2DCount에다가 광원의 개수를 채워 넣는다.

그리고 디바이스로부터 전역 상수버퍼를 가져와서 데이터를 업데이트시킨다.

void CRenderMgr::UpdateData()
{
    // GlobalData 에 광원 개수정보 세팅
    GlobalData.Light2DCount = m_vecLight2D.size();

    // 구조화버퍼의 크기가 모자라면 더 크게 새로 만든다.
    if (m_Light2DBuffer->GetElementCount() < m_vecLight2D.size())
    {
        m_Light2DBuffer->Create(sizeof(tLightInfo), m_vecLight2D.size());
    }

    // 구조화버퍼로 광원 데이터를 옮긴다.
    m_Light2DBuffer->SetData(m_vecLight2D.data(), sizeof(tLightInfo) * m_vecLight2D.size());
    m_Light2DBuffer->UpdateData(8, PIPELINE_STAGE::PS_PIXEL);


    // 전역 상수 데이터 바인딩
    CConstBuffer* pGlobalBuffer = CDevice::GetInst()->GetConstBuffer(CB_TYPE::GLOBAL);
    pGlobalBuffer->SetData(&GlobalData, sizeof(tGlobal));
    pGlobalBuffer->UpdateData();
}

© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2