[D3D] StructureBuffer

vector·2025년 12월 5일

이제 자주 사용할 StructureBuffer를 만들어보자

StructureBuffer는 정해진 구조체를 통해서 배열을 만들어 관리하는 버퍼를 통해 더 쉽게 작업 가능하다.

구현과 작동은 RawBuffer와 유사하다.

StructuredBuffer

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);
}

27.StructuredBufferDemo.fx

  • 구조체를 통해서 입출력 만들기

struct InputDesc
{
    matrix input;
};

struct OutputDesc
{
    matrix result;
};

StructuredBuffer<InputDesc> Input;
RWStructuredBuffer<OutputDesc> Output;

[numthreads(500, 1, 1)]
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()));
    }
};

StructuredBufferDemo.cpp

#include "pch.h"
#include "StructuredBufferDemo.h"
#include "RawBuffer.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));

	_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(){}

결과

  • 쉐이더에서 input값에 2를 곱한대로 output값에 2가 곱해진 상태로 들어간 거 확인

참고 강의

https://www.inflearn.com/courses/lecture?courseId=329791&tab=curriculum&type=LECTURE&unitId=161166&subtitleLanguage=ko

profile
게임 클라이언트 프로그래머 준비중 (공부 및 기록용)

0개의 댓글