기존의 상수버퍼는 하드코딩되어있어서, 업데이트를 할 때마나 데이터 하나하나씩 map, unmap과정을 거쳐주어야 했다.

상수버퍼가 1개일 때는 별 상관 없겠지만, 여러개를 추가하려고 했더니 너무 번거롭고 귀찮은 부분이 많아서 구조를 수정해주었다.

struct ConstantBufferData
{
	Microsoft::WRL::ComPtr<ID3D11Buffer> ConstantBuffer;
	
	void* Data = nullptr;
	UINT DataSize = 0;
};

 

먼저, 상수버퍼의 데이터를 담을 구조체를 선언해주었다.

다양한 자료형에 대응해야하기 때문에 void*타입으로 데이터를 담도록 하였다.

또한, 상수버퍼를 업데이트할 때 데이터의 size가 필요한데 void*타입으로는 이를 계산할 수 없으므로 미리 계산하여 구조체에 함께 저장하도록 하였다.

std::list<ConstantBufferData> ConstantBuffers;

 

 

각 렌더러는 위와 같이, 상수버퍼를 저장하도록 하였다.

template <typename DataType>
void CreateConstantBuffer(DataType& _Data)
{
    D3D11_BUFFER_DESC CBDesc;
    CBDesc.ByteWidth = sizeof(DataType);
    CBDesc.Usage = D3D11_USAGE_DYNAMIC;
    CBDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    CBDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    CBDesc.MiscFlags = 0;
    CBDesc.StructureByteStride = 0;

    D3D11_SUBRESOURCE_DATA InitData;
    InitData.pSysMem = &_Data;
    InitData.SysMemPitch = 0;
    InitData.SysMemSlicePitch = 0;

    Microsoft::WRL::ComPtr<ID3D11Buffer> NewBuffer;

    HRESULT Result =
        EngineBase::GetInstance().GetDevice()->CreateBuffer(&CBDesc, &InitData, NewBuffer.GetAddressOf());

    if (Result != S_OK)
    {
        std::cout << Name << " :" << "CreateConstantBuffer() failed." << std::hex << "\nResult : " << Result << std::endl;
    };

    ConstantBufferData NewBufferData;
    NewBufferData.ConstantBuffer = NewBuffer;
    NewBufferData.Data = reinterpret_cast<void*>(&_Data);
    NewBufferData.DataSize = sizeof(_Data);

    ConstantBuffers.push_back(NewBufferData);
}

 

상수버퍼를 생성하는 함수이다. 코드는 전반적으로 동일하지만, 마지막에 list에 상수버퍼데이터를 삽입하는 과정만 추가해주었다.

 

for (const ConstantBufferData& _Data : ConstantBuffers)
{
    D3D11_MAPPED_SUBRESOURCE Ms;

    EngineBase::GetInstance().GetContext()->Map(_Data.ConstantBuffer.Get(), NULL, D3D11_MAP_WRITE_DISCARD, NULL, &Ms);
    memcpy(Ms.pData, _Data.Data, _Data.DataSize);
    EngineBase::GetInstance().GetContext()->Unmap(_Data.ConstantBuffer.Get(), NULL);
}

 

업데이트는 위와 같이 list의 원소를 순회하며 하나씩 업데이트하도록 해주었다.

 

int Index = 0;
for (const ConstantBufferData& _Data : ConstantBuffers)
{
    EngineBase::GetInstance().GetContext()->VSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
    EngineBase::GetInstance().GetContext()->PSSetConstantBuffers(Index, 1, _Data.ConstantBuffer.GetAddressOf());
    Index++;
}

 

상수버퍼를 세팅하는 부분에선 위와 같이 해주었다.

여기서 본인은 동일한 상수버퍼를 버텍스쉐이더와 픽셀쉐이더에 모두 세팅하도록 해주었다. 구조체에 VS의 버퍼인지 PS의 버퍼인지 아니면 둘 다 사용하는 버퍼인지 enum 을 사용하여 분류해줄 수도 있지만, 쉐이더에서 레지스터의 번호와 상수버퍼를 일치시키는 부분에 있어서 위의 코드처럼 하는 것이 더 유리할 것이라고 생각하였다.

 

예를 들면, 위에서 버퍼를 세팅할 때, list에서 원소의 위치를 기반으로 슬롯 번호를 세팅해주고 있다.

이 때, list의 첫번째 원소는 vs의 버퍼이고 두번째 원소는 ps의 버퍼라고 했을 때, 각각 쉐이더에선 register(b0)을 사용하게 될 것이다. 상수버퍼가 많아지면 레지스터 번호를 매칭하는 것이 다소 복잡해질 수 있다.

 

하지만, 위처럼  PS와 VS를 동시에 세팅해주면, 첫번째원소는 b0을 사용하고, 두번쨰 원소는 b1을 사용하게 된다. 원소의 위치를 기반으로 레지스터 번호를 쉽게 매칭할 수 있다.

 

하지만 이 방법도 좋은 방법은 아니라고 생각한다. 왜냐하면 이렇게 하면 사용가능한 상수버퍼의 수가 줄어들기 때문이다.

PS에서만 사용하는 상수버퍼와 VS에서만 사용하는 상수버퍼를 분리하여 세팅하면 각 쉐이더당 16개의 상수버퍼를 등록할 수 있다. 하지만, 위의 코드처럼 세팅하게 되면 PS와 VS를 합쳐서 16개밖에 사용하지 못한다.

 

그래서 아래와 같은 방법도 생각해보았다.

PS에서 사용하는 상수버퍼와 VS에서 사용하는 상수버퍼를 다른 자료구조에 분리해서 저장한 뒤 사용하는 방식이다.

그런데 이렇게 하면 VS와 PS에서 둘 다 사용하는 상수버퍼를 관리하는 것이 다소 까다로워진다.

 

그래서 본인은 위의 코드와 같은 방식을 일단 사용하기로 하였다. 왜냐하면 16개 이상의 상수버퍼를 사용할 일이 없을 것 같았기 때문이다. 

 

바뀐 구조를 테스트하기 위해 상수버퍼를 하나 추가해보았다.

상수버퍼의 x값보다 텍스쳐좌표의 x값이 더 크면 파란색을 출력하고, 더 작다면 빨간색을 출력하도록 하였다.

cbuffer UV : register(b1)
{
    float4 UV;
};

struct PixelShaderInput
{
    float4 pos : SV_POSITION;
    float3 color : COLOR;
    float2 TexCoord : TEXCOORD;
};

float4 main(PixelShaderInput _Input) : SV_TARGET
{
    if(UV.x > _Input.TexCoord.x)
    {
        return float4(1.0f, 0.0f, 0.0f, 1.0f);
    }
    else
    {
        return float4(0.0f, 0.0f, 1.0f, 1.0f);
    }
}

 

위는 간단하게 작성해본 쉐이더 코드이다.

 

 

결과는 아주 만족스럽게 잘 나온다.

x값을 기준으로 했는데 특정 면에서는 좌우가 아니라 상하가 분리되는 이유는 박스를 만들때 버텍스를 그렇게 만들어서 그렇다... 아무튼 잘된다!

+ Recent posts