• 티스토리 홈
  • 프로필사진
    유니얼
  • 방명록
  • 공지사항
  • 태그
  • 블로그 관리
  • 글 작성
유니얼
  • 프로필사진
    유니얼
    • 분류 전체보기 (295)
      • Unity (17)
        • 게임 개발 (5)
      • Unreal (24)
        • 게임 개발 (20)
      • DirectX (36)
      • 코딩테스트 (91)
        • 프로그래머스 (25)
        • 백준 (66)
      • Google Workspace (1)
      • Programing (102)
        • C# (68)
        • C++ (24)
        • JavaScript (10)
      • 게임 서버 프로그래밍 (17)
      • Web (6)
        • 슈퍼코딩 (6)
  • 방문자 수
    • 전체:
    • 오늘:
    • 어제:
  • 최근 댓글
    등록된 댓글이 없습니다.
  • 최근 공지
    등록된 공지가 없습니다.
# Home
# 공지사항
#
# 태그
# 검색결과
# 방명록
  • [DirectX11] StructuredBuffer
    2024년 03월 12일
    • 유니얼
    • 작성자
    • 2024.03.12.:35
    728x90

    DirectX 11로 게임 엔진 아키텍처 만들기

    DirectX11을 통한 게임 엔진 아키텍처의 설계와 구현 과정에서, 데이터의 효율적인 관리와 처리는 중요한 요소 중 하나입니다. 특히, GPU에서의 데이터 처리를 최적화하기 위해 StructuredBuffer의 이해와 활용은 필수적입니다. 이번 블로그 포스트에서는 StructuredBuffer에 대해 알아보고자 합니다.

    참고강의 링크:

    https://www.inflearn.com/course/directx11-%EA%B2%8C%EC%9E%84%EA%B0%9C%EB%B0%9C-%EB%8F%84%EC%95%BD%EB%B0%98/dashboard

     

    [게임 프로그래머 도약반] DirectX11 입문 강의 - 인프런

    게임 프로그래머 공부에 있어서 필수적인 DirectX 11 지식을 초보자들의 눈높이에 맞춰 설명하는 강의입니다., [사진][사진] [사진] 게임 개발자는 Unreal, Unity만 사용할 줄 알면 되는 거 아닌가요? 엔

    www.inflearn.com

    StructuredBuffer 소개

    StructuredBuffer는 DirectX11에서 제공하는 고급 데이터 버퍼 유형 중 하나로, 구조화된 데이터를 저장하고 GPU에서 직접 접근할 수 있도록 설계되었습니다. 이는 복잡한 데이터 구조를 효율적으로 GPU에 전달하고, 컴퓨트 셰이더 등에서 이를 활용할 수 있게 해줍니다. 예를 들어, 3D 모델의 정점 데이터, 물리 시뮬레이션에서의 입자 정보, 또는 렌더링에 사용되는 다양한 파라미터 등이 StructuredBuffer를 통해 관리될 수 있습니다.

    StructuredBuffer의 특징

    • 구조화된 데이터 저장: StructuredBuffer는 각종 구조체 형태의 데이터를 저장할 수 있어, 데이터의 의미와 사용 목적에 따라 효과적으로 구성할 수 있습니다.
    • GPU에서의 직접 접근: 컴퓨트 셰이더를 포함한 다양한 셰이더 단계에서 StructuredBuffer에 저장된 데이터에 직접 접근할 수 있습니다. 이를 통해 GPU에서 복잡한 계산과 처리가 가능해집니다.
    • 높은 성능: StructuredBuffer는 GPU 메모리를 효율적으로 활용하여 높은 성능의 데이터 접근과 처리를 가능하게 합니다.

    StructuredBuffer 활용 예제

    StructuredBuffer를 사용하여 간단한 데이터 구조를 정의하고, 이를 컴퓨트 셰이더에서 활용하는 예제를 살펴보겠습니다.

    위 예제에서는 Particle이라는 간단한 구조체를 정의하고, 이를 저장하는 StructuredBuffer를 선언합니다. 컴퓨트 셰이더에서는 StructuredBuffer에 저장된 각 Particle 데이터에 접근하여 필요한 계산을 수행할 수 있습니다.

    // 입력 데이터 구조체 정의: 4x4 행렬
    struct InputDesc
    {
    	matrix input;
    };
    
    // 출력 데이터 구조체 정의: 연산 결과를 담을 4x4 행렬
    struct OutputDesc
    {
    	matrix result;
    };
    
    // 입력 버퍼: 구조화된 버퍼로 정의된 InputDesc 구조체의 인스턴스들을 저장
    StructuredBuffer<InputDesc> Input;
    
    // 출력 버퍼: 쓰기 가능한 구조화된 버퍼로 OutputDesc 구조체의 인스턴스들을 저장
    RWStructuredBuffer<OutputDesc> Output;
    
    // 컴퓨트 셰이더의 엔트리 포인트
    // numthreads 어트리뷰트는 한 번에 실행될 쓰레드 그룹의 크기를 지정 (이 경우, x축으로 500개 쓰레드)
    [numthreads(500, 1, 1)]
    void CS(uint id : SV_GroupIndex) // 각 쓰레드의 고유 ID
    {
    	// 입력 버퍼에서 id에 해당하는 행렬을 가져와 2배로 확장
    	matrix result = Input[id].input * 2;
    
    	// 계산된 결과를 출력 버퍼의 해당 위치에 저장
    	Output[id].result = result;
    }
    
    // 셰이더 기술 세트 정의
    technique11 T0
    {
    	pass P0
    	{
    		// 이 테크닉에서는 버텍스 셰이더와 픽셀 셰이더는 사용하지 않으므로 NULL로 설정
    		SetVertexShader(NULL);
    		SetPixelShader(NULL);
    		// 컴파일된 컴퓨트 셰이더를 사용
    		SetComputeShader(CompileShader(cs_5_0, CS()));
    	}
    };

    StructuredBuffer

    StructuredBuffer 클래스는 Direct3D 11을 사용하여 구조화된 버퍼를 생성하고 관리하는 클래스입니다. 이 클래스는 GPU에서 처리할 데이터의 입력 및 출력을 위한 버퍼를 만듭니다. 주요 기능으로는 입력 데이터를 GPU에 전달하고 처리 결과를 읽을 수 있는 메커니즘을 제공합니다.

    StructuredBuffer.h

    #pragma once
    
    class TextureBuffer
    {
    public:
    	// 생성자: 원본 텍스처를 인자로 받습니다.
    	TextureBuffer(ComPtr<ID3D11Texture2D> src);
    	// 소멸자: 자원을 해제합니다.
    	~TextureBuffer();
    
    public:
    	// 버퍼를 생성하는 주 함수입니다.
    	void CreateBuffer();
    
    private:
    	// 입력 텍스처를 설정합니다.
    	void CreateInput(ComPtr<ID3D11Texture2D> src);
    	// 입력 텍스처에 대한 셰이더 리소스 뷰(SRV)를 생성합니다.
    	void CreateSRV();
    	// 출력 텍스처를 생성합니다.
    	void CreateOutput();
    	// 출력 텍스처에 대한 비순차적 액세스 뷰(UAV)를 생성합니다.
    	void CreateUAV();
    	// 처리 결과를 저장할 텍스처를 생성합니다.
    	void CreateResult();
    
    public:
    	// 텍스처의 너비, 높이, 배열 크기를 반환하는 접근자 함수들입니다.
    	uint32 GetWidth() { return _width; }
    	uint32 GetHeight() { return _height; }
    	uint32 GetArraySize() { return _arraySize; }
    
    	// 출력 텍스처와 그에 대한 SRV를 반환하는 접근자 함수입니다.
    	ComPtr<ID3D11Texture2D> GetOutput() { return (ID3D11Texture2D*)_output.Get(); }
    	ComPtr<ID3D11ShaderResourceView> GetOutputSRV() { return _outputSRV; }
    
    public:
    	// 입력 및 출력 텍스처에 대한 SRV와 UAV를 반환하는 접근자 함수입니다.
    	ComPtr<ID3D11ShaderResourceView> GetSRV() { return _srv; }
    	ComPtr<ID3D11UnorderedAccessView> GetUAV() { return _uav; }
    
    private:
    	// 입력 및 출력 텍스처와 관련 자원을 저장하는 멤버 변수입니다.
    	ComPtr<ID3D11Texture2D> _input;
    	ComPtr<ID3D11ShaderResourceView> _srv; // 입력 텍스처의 SRV
    	ComPtr<ID3D11Texture2D> _output;
    	ComPtr<ID3D11UnorderedAccessView> _uav; // 출력 텍스처의 UAV
    
    private:
    	// 텍스처의 너비, 높이, 배열 크기 및 포맷을 저장하는 멤버 변수입니다.
    	uint32 _width = 0;
    	uint32 _height = 0;
    	uint32 _arraySize = 0;
    	DXGI_FORMAT _format;
    	ComPtr<ID3D11ShaderResourceView> _outputSRV; // 출력 텍스처의 SRV
    };

    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()
    {
    	// Direct3D 자원 해제는 ComPtr에 의해 자동으로 관리되므로 별도로 할 작업이 없습니다.
    }
    
    // 버퍼 생성 전체 과정을 담당하는 함수
    void StructuredBuffer::CreateBuffer()
    {
    	CreateInput(); // 입력 데이터에 대한 버퍼 생성
    	CreateSRV();   // 입력 버퍼에 대한 SRV 생성
    	CreateOutput(); // 출력 데이터를 위한 버퍼 생성
    	CreateUAV();    // 출력 버퍼에 대한 UAV 생성
    	CreateResult(); // 결과 데이터를 위한 버퍼 생성 (주로 CPU에서 읽기 위함)
    }
    
    // 입력 버퍼 생성
    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; // CPU가 쓰기 가능
    	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // CPU 쓰기 액세스 허용
    
    	// 입력 데이터가 있는 경우 해당 데이터로 초기화
    	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()));
    }
    
    // 입력 버퍼에 대한 SRV 생성
    void StructuredBuffer::CreateSRV()
    {
    	// 입력 버퍼의 설명을 가져와서 SRV를 생성합니다.
    	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; // 요소의 갯수
    
    	// SRV 생성
    	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; // UAV에 바인딩될 예정
    	desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
    	desc.StructureByteStride = _outputStride; // 출력 구조체의 바이트 크기
    
    	// 출력 버퍼 생성
    	CHECK(DEVICE->CreateBuffer(&desc, nullptr, _output.GetAddressOf()));
    }
    
    // 출력 버퍼에 대한 UAV 생성
    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; // 출력 요소의 갯수
    
    	// UAV 생성
    	CHECK(DEVICE->CreateUnorderedAccessView(_output.Get(), &uavDesc, _uav.GetAddressOf()));
    }
    
    // 결과 버퍼 생성
    void StructuredBuffer::CreateResult()
    {
    	// 출력 버퍼를 기반으로 결과 버퍼의 설명 구조체를 설정합니다.
    	D3D11_BUFFER_DESC desc;
    	_output->GetDesc(&desc);
    
    	desc.Usage = D3D11_USAGE_STAGING; // CPU에서 읽을 수 있도록 설정
    	desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; // CPU 읽기 액세스
    	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); // 매핑 해제
    }

    프로젝트 호출

    StructuredBufferDemo.h

    #pragma once
    #include "IExecute.h"
    
    class StructuredBufferDemo : public IExecute
    {
    public:
    	void Init() override;
    	void Update() override;
    	void Render() override;
    
    private:
    	shared_ptr<Shader> _shader;
    };

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

    결론

    StructuredBuffer는 DirectX11에서 제공하는 강력한 데이터 버퍼 유형으로, 복잡한 구조의 데이터를 효율적으로 GPU에 전달하고 활용할 수 있게 해줍니다. 이를 통해 개발자는 더욱 복잡하고 세밀한 그래픽 및 계산 작업을 구현할 수 있으며, 게임 엔진의 성능과 기능을 향상시킬 수 있습니다. StructuredBuffer의 이해와 적절한 활용은 DirectX11 기반의 게임 엔진 아키텍처 설계와 개발에 있어 중요한 역량 중 하나입니다.

    반응형
    다음글
    다음 글이 없습니다.
    이전글
    이전 글이 없습니다.
    댓글
조회된 결과가 없습니다.
스킨 업데이트 안내
현재 이용하고 계신 스킨의 버전보다 더 높은 최신 버전이 감지 되었습니다. 최신버전 스킨 파일을 다운로드 받을 수 있는 페이지로 이동하시겠습니까?
("아니오" 를 선택할 시 30일 동안 최신 버전이 감지되어도 모달 창이 표시되지 않습니다.)
목차
표시할 목차가 없습니다.
    • 안녕하세요
    • 감사해요
    • 잘있어요

    티스토리툴바