본문 바로가기

DirectX11 3D/기본 문법

<DirectX11 3D> 31. Wrapper class - Vertex, Index, Constant

 

필요한 개념


- VertexBuffer :  정점 데이터의 정보를 저장

 

- IndexBuffer : 정점의 중복을 최소화하기 위해 그리는 번호를 지정

 

- Constant Buffer : 구조체를 변수로 묶어서 GPU로 보내기 위해 사용하는 버퍼
GPU 입장에서는 상수처럼 변수를 변경이 불가능(그래서 상수(Constant))
-> HLSL에서는 수정 불가지만, Effect에서는 값 수정이 가능하다.

-> 기존 HLSL에서는 VS, PS 접근 할 수 있는지도 지정했는데 Effect에서는 필요 없다.

 

간단하게 사용할때는 Shader->AsMatrix("World")->SetMatrix()
Shader에서 표현하면 전역변수 matrix world, float4, color등이 있다.
이것들이 다 cbuffer $Global영역에 들어간다.

 

 

- D3D11_USAGE : Cpu, Gpu 읽기, 쓰기 여부를 결정, 각 상태에 따라 렌더링 속도가 다름

 

* 속도 : IMMUTABLE > DEFAULT > DYNAMIC > STAGING

 

 

Flag/Attribute CPU Read CPU Write GPU Read GPU Write
IMMUTABLE(읽기) x X 0 x
DYNAMIC x 0 0 x
DEFAULT 0 x(*예외1) 0 0
STAGING 0 0 0 0

 

* 예외 1 : UpdateSubResource() 함수

 

<TIP>

- IndexBuffer -> IMMUTABLE 사용 (IndexBuffer는 한번 들어가면 절때 바뀌지 않아서)

- ConstantBuffer -> DYNAMIC 사용 (CPU에서 GPU로 계속 밀어 넣어주어야 해서)

 

 

 

.h


더보기
// 원래는 ID3D11Buffer(인터페이스)를 상속 받아 만든다.
//class VertexBuffer : public ID3D11Buffer
//{
//public:
//	VertexBuffer();
//	~VertexBuffer();
//
//private:
//};

// 그러나 이렇게 맴버로 만들어도 됨(우리에게 좀 더 편함)
// Wrapper class란? 맴버를 감싸서 추가 기능을 제공하기 위한 클래스
class VertexBuffer
{
public:
	VertexBuffer(void* data, UINT count, UINT stride, UINT slot = 0, bool bCpuWrite = false, bool bGpuWrite = false);
	~VertexBuffer();	
	
	UINT Count() { return count; }
	UINT Stride() { return stride; }
	ID3D11Buffer* Buffer() { return buffer; }

	void Render();

private:
	ID3D11Buffer* buffer;
	// 정점 정보를 들어올 데이터
	void* data;
	// 정점 개수
	UINT count;
	// 한 정점 크기
	UINT stride;
	UINT slot;

	// Cpu 혹은 Gpu에서 쓸수 있는지
	bool bCpuWrite;
	bool bGpuWrite;
};

///////////////////////////////////////////////////////////////////////////////

class IndexBuffer
{
public:
	IndexBuffer(void* data, UINT count);
	~IndexBuffer();

	UINT Count() { return count; }
	ID3D11Buffer* Buffer() { return buffer; }

	void Render();

private:
	ID3D11Buffer* buffer;

	void* data;
	UINT count;
};

///////////////////////////////////////////////////////////////////////////////

class ConstantBuffer
{
public:
	ConstantBuffer(void* data, UINT dataSize);
	~ConstantBuffer();

	ID3D11Buffer* Buffer() { return buffer; }

	// 데이터를 밀어주기 위해 사용
	void Render();

private:
	ID3D11Buffer* buffer;
	
	void* data;
	// 전체 데이터 크기
	UINT dataSize;
};

 

 

 

 

 

 

 

.Cpp


더보기
#include "Framework.h"
#include "Buffers.h"

VertexBuffer::VertexBuffer(void* data, UINT count, UINT stride, UINT slot, bool bCpuWrite, bool bGpuWrite)
	: data(data), count(count), stride(stride), slot(slot)
	, bCpuWrite(bCpuWrite), bGpuWrite(bGpuWrite)
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
	desc.ByteWidth = stride * count;
	desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;

	// Usage란? cpu 및 gpu의 읽기, 쓰기 상태를 알려줌, 각 상태에 따라 렌더링 속도가 다름
	// 속도 IMMUTABLE > DEFAULT > DYNAMIC > STAGING
	// IMMUTABLE
	// - GPU 읽기만 가능(렌더링에서만 접근 가능)	
	// - 최초 생성시에 입력된 데이터를 수정할 수 없음
	// - 가장 빠름
	// ex) 모델같은 경우 읽어주고, 모델을 수정할 이유가 없다.

	if (bCpuWrite == false && bGpuWrite == false)
		desc.Usage = D3D11_USAGE_IMMUTABLE; // 상수느낌(불변)

	// 어쩔 수 없이 정점을 바꿔야하는 경우(지형에서 brush로 지형을 바꾸는 경우라던가)
	// Dynamic
	// - CPU 쓰기(GPU로 보내는 것이 가능) -> Cpu에서 데이터를 써서 Gpu로 보낼수 있다는 얘기(데이터 수정 가능)
	// - GPU 읽기(렌더링에서만 접근 가능) -> GPU 읽기만 가능하다는 의미는 렌더링용으로만 사용하겠단 의미
	else if (bCpuWrite == true && bGpuWrite == false)
	{
		desc.Usage = D3D11_USAGE_DYNAMIC;
		// Dynamic을 해도 CPUAccessFlags가 없다면 IMMUTABLE과 같은 취급됨
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	}

	// Shader, GPU에서 뭔가를 처리했다면 Cpu로 데이터를 보내주기 위해 사용
	// DEFAULT
	// - CPU 읽기, GPU 읽기, 쓰기
	// - 단, 예외적으로 UpdateSubResource()함수를 통해 CPU 쓰기가 가능

	// UpdateSubResource()는 GPU로 복사하기 위한 락을 자기 스스로 걸고
	// 복사를 하고 풀어버림, 그래서 시점 제어 불가, 상대적으로 느림, 거의쓰지 않음

	else if (bCpuWrite == false && bGpuWrite == true)
	{
		desc.Usage = D3D11_USAGE_DEFAULT;
	}

	else
	{
		// STAGING
		// - 모든 것이 가능한 상황(모든것 읽기, 쓰기 가능), 가장 느림
		desc.Usage = D3D11_USAGE_STAGING;
		// D3D11_CPU_ACCESS_READ : GPU에서 CPU로 보낼 수 있도록
		// D3D11_CPU_ACCESS_WRITE : CPU에서 GPU로 보낼 수 있도록
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
	}

	// 밑에 것들은 GPU VRAM으로 복사해주기 위해 사용하는 부분
	D3D11_SUBRESOURCE_DATA sub_resource = { 0 };
	// 그릴 데이터의 시작 주소
	sub_resource.pSysMem = data;

	Check(D3D::GetDevice()->CreateBuffer(&desc, &sub_resource, &buffer));
}

VertexBuffer::~VertexBuffer()
{
	SafeRelease(buffer);
}


void VertexBuffer::Render()
{
	UINT offset = 0;
	D3D::GetDC()->IASetVertexBuffers(slot, 1, &buffer, &stride, &offset);
}

///////////////////////////////////////////////////////////////////////////////

IndexBuffer::IndexBuffer(void* data, UINT count)
	: data(data), count(count)
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));

	desc.ByteWidth = sizeof(UINT) * count;
	desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	// IndexBuffer는 한번 들어가면 절때 바뀌지 않는다.
	desc.Usage = D3D11_USAGE_IMMUTABLE;


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

	Check(D3D::GetDevice()->CreateBuffer(&desc, &subResource, &buffer));
}

IndexBuffer::~IndexBuffer()
{
	SafeRelease(buffer);
}

void IndexBuffer::Render()
{
	D3D::GetDC()->IASetIndexBuffer(buffer, DXGI_FORMAT_R32_UINT, 0);
}

///////////////////////////////////////////////////////////////////////////////

ConstantBuffer::ConstantBuffer(void* data, UINT dataSize) :
	data(data), dataSize(dataSize)
{
	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));

	desc.ByteWidth = dataSize;
	desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	// CPU에서 GPU로 계속 밀어 넣어주어야 함
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	Check(D3D::GetDevice()->CreateBuffer(&desc, nullptr, &buffer));
}

ConstantBuffer::~ConstantBuffer()
{
	SafeRelease(buffer);
}

void ConstantBuffer::Render()
{
	// CPU->CPU로 데이터 밀어넣기 위해 MAP 사용
	D3D11_MAPPED_SUBRESOURCE subResource;
	// 쓰기용으로
	// Map을 하면 GPU Buffer구역에 묶여 버림(다른 애들은 이 버퍼에 접근 불가->UnMap해줘야 함, 안그러면 터짐)
	// Gpu에 있는 버퍼의 시작주소가 subResource를 통해 리턴됨(pData)
	D3D::GetDC()->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
	{
		// D3D11_MAP을 Read로 주게 되면 반대로 Gpu데이터를 받을 수 있다. memcpy 순서를 바꿔야함
		// 1. 받을 데이터 주소 2. 복사할 데이터 3. 크기
		memcpy(subResource.pData, data, dataSize);
	}
	D3D::GetDC()->Unmap(buffer, 0);
}