이제 Structure 버퍼를 만들어서 사용해보자 

Structure Buffer는 정해진 구조체를 통해 배열을 만들어서 관리하는 버퍼를 통해 더 쉽게 이 작업을 해줄 수 있다. 

구현과 작동 자체는 RawBuffer와 유사하다. 

우선 Buffer 클래스를 만들어주자.

StructuredBuffer.h

#pragma once


class StructuredBuffer
{
public:
	StructuredBuffer(void* inputData, uint32 inputStride, uint32 inputCount, uint32 outputStride = 0, uint32 outputCount = 0);
	~StructuredBuffer();

public:
	void CreateBuffer();

private:
	void CreateInput();
	void CreateSRV();
	void CreateOutput();
	void CreateUAV();
	void CreateResult();

public:
	//데이터개수 * 데이터크기
	uint32 GetInputByteWidth() { return _inputStride * _inputCount; }
	//데이터개수 * 데이터크기
	uint32 GetOutputByteWidth() { return _outputStride * _outputCount; }

	void CopyToInput(void* data);
	void CopyFromOutput(void* data);

public:
	ComPtr<ID3D11ShaderResourceView> GetSRV() { return _srv; }
	ComPtr<ID3D11UnorderedAccessView> GetUAV() { return _uav; }

private:
	ComPtr<ID3D11Buffer> _input;
	ComPtr<ID3D11ShaderResourceView> _srv; // Input
	ComPtr<ID3D11Buffer> _output;
	ComPtr<ID3D11UnorderedAccessView> _uav; // Output
	ComPtr<ID3D11Buffer> _result;

private:
	void* _inputData;
	uint32 _inputStride = 0;
	uint32 _inputCount = 0;
	uint32 _outputStride = 0;
	uint32 _outputCount = 0;
};

StructuredBuffer.cpp

#include "pch.h"
#include "StructuredBuffer.h"

StructuredBuffer::StructuredBuffer(void* inputData, uint32 inputStride, uint32 inputCount, uint32 outputStride, uint32 outputCount)
	: _inputData(inputData), _inputStride(inputStride), _inputCount(inputCount), _outputStride(outputStride), _outputCount(outputCount)
{
	if (outputStride == 0 || outputCount == 0)
	{
		_outputStride = inputStride;
		_outputCount = inputCount;
	}

	CreateBuffer();
}

StructuredBuffer::~StructuredBuffer()
{

}

void StructuredBuffer::CreateBuffer()
{
	CreateInput();
	CreateSRV();
	CreateOutput();
	CreateUAV();
	CreateResult();
}

void StructuredBuffer::CreateInput()
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));

	desc.ByteWidth = GetInputByteWidth();
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;		//구조체에 맞게
	desc.StructureByteStride = _inputStride;
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	D3D11_SUBRESOURCE_DATA subResource = { 0 };
	subResource.pSysMem = _inputData;

	if (_inputData != nullptr)
		CHECK(DEVICE->CreateBuffer(&desc, &subResource, _input.GetAddressOf()));
	else
		CHECK(DEVICE->CreateBuffer(&desc, nullptr, _input.GetAddressOf()));
}

void StructuredBuffer::CreateSRV()
{
	D3D11_BUFFER_DESC desc;
	_input->GetDesc(&desc);

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	ZeroMemory(&srvDesc, sizeof(srvDesc));
	srvDesc.Format = DXGI_FORMAT_UNKNOWN;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
	srvDesc.BufferEx.NumElements = _inputCount;

	CHECK(DEVICE->CreateShaderResourceView(_input.Get(), &srvDesc, _srv.GetAddressOf()));
}

void StructuredBuffer::CreateOutput()
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(desc));

	desc.ByteWidth = GetOutputByteWidth();
	desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
	desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
	desc.StructureByteStride = _outputStride;

	CHECK(DEVICE->CreateBuffer(&desc, nullptr, _output.GetAddressOf()));
}

void StructuredBuffer::CreateUAV()
{
	D3D11_BUFFER_DESC desc;
	_output->GetDesc(&desc);

	D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
	ZeroMemory(&uavDesc, sizeof(uavDesc));
	uavDesc.Format = DXGI_FORMAT_UNKNOWN;
	uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
	uavDesc.Buffer.NumElements = _outputCount;

	CHECK(DEVICE->CreateUnorderedAccessView(_output.Get(), &uavDesc, _uav.GetAddressOf()));
}


void StructuredBuffer::CreateResult()
{
	D3D11_BUFFER_DESC desc;
	_output->GetDesc(&desc);

	desc.Usage = D3D11_USAGE_STAGING;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	desc.BindFlags = 0;
	desc.MiscFlags = 0;

	CHECK(DEVICE->CreateBuffer(&desc, NULL, _result.GetAddressOf()));
}


void StructuredBuffer::CopyToInput(void* data)
{
	D3D11_MAPPED_SUBRESOURCE subResource;
	DC->Map(_input.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
	{
		memcpy(subResource.pData, data, GetInputByteWidth());
	}
	DC->Unmap(_input.Get(), 0);
}

void StructuredBuffer::CopyFromOutput(void* data)
{
	DC->CopyResource(_result.Get(), _output.Get());

	D3D11_MAPPED_SUBRESOURCE subResource;
	DC->Map(_result.Get(), 0, D3D11_MAP_READ, 0, &subResource);
	{
		memcpy(data, subResource.pData, GetOutputByteWidth());
	}
	DC->Unmap(_result.Get(), 0);
}

 

쉐이더쪽 코드는 구조체를 통해 입출력 구조를 정해주고 이를 버퍼 변수로 지정해주고 출력에는 RW를 붙여서 Read + Write가 가능하도록 해주자. 그리고 지금은 입력값에 행렬을 받는데 이 행렬에 간단한 연산만 해서 반환하는 식으로 동작하게 해주자

StructuredBufferDemo.fx

struct InputDesc
{
    matrix input;
};

struct OutputDesc
{
    matrix result;
};

//하나의 배열로 보면된다
StructuredBuffer<InputDesc> Input;
RWStructuredBuffer<OutputDesc> Output;

//thread 개수 x*y*z 총합 
[numthreads(500,1,1)]       //x만 500개 행렬값
void CS(uint id : SV_GroupIndex)
{
    matrix result = Input[id].input * 2;
    
    Output[id].result = result;
}


technique11 T0
{
    Pass P0
    {
        SetVertexShader(NULL);
        SetPixelShader(NULL);
        //컴퓨트 쉐이더 세팅
        SetComputeShader(CompileShader(cs_5_0, CS()));
    }
};

 

이에 맞게 항등 행렬인 Matrix vector를 선언하고 이를 버퍼를 통해 작업할 수 있도록 메인 코드를 만들어주자

StructuredBufferDemo.fx

#include "pch.h"
#include "StructuredBufferDemo.h"
#include "StructuredBuffer.h"

void StructuredBufferDemo::Init()
{
	_shader = make_shared<Shader>(L"27. StructuredBufferDemo.fx");

	vector<Matrix> inputs(500, Matrix::Identity);

	auto buffer = make_shared<StructuredBuffer>(inputs.data(), sizeof(Matrix), 500, sizeof(Matrix), 500);

	_shader->GetSRV("Input")->SetResource(buffer->GetSRV().Get());
	_shader->GetUAV("Output")->SetUnorderedAccessView(buffer->GetUAV().Get());

	_shader->Dispatch(0, 0, 1, 1, 1);

	vector<Matrix> outputs(500);
	buffer->CopyFromOutput(outputs.data());
}

void StructuredBufferDemo::Update()
{

}

void StructuredBufferDemo::Render()
{

}

 

이렇게 해주고 코드끝부분에 Break Point를 잡고 실행시켜보면 

이렇게 행렬 x부분에 2가 곱해져서 반환되는 것을 볼 수 있다. 이런식으로 RawBuffer를 활용하는 방법보다

인스턴싱에서 매트릭스나 본 행렬 정보와 같은 정해진 구조를 여러개 넣어주는 경우가 많기 때문에 Sturctured Buffer

를 활용하는 방법이 좋을 것이다.

+ Recent posts