Buffer
현재까지 정점이나 인덱스를 만들때마다 Buffer를 만들어 GPU에게 정보를 넘겨줬다.
하지만, 너무 자주 사용하는데 일일이 만들어주는데 번거로웠다. 이번엔 각 버퍼를 관리할 버퍼 클래스를 만들었다.
VertexBuffer
VertexBuffer.h
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;
bool bCpuWrite;
bool bGpuWrite;
};
어떤 형으로 받을지 모르기 때문에 void* 형으로 데이터를 받는다.
VertexBuffer.cpp
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;
if (bCpuWrite == false && bGpuWrite == false)
{
// 속도 1
desc.Usage = D3D11_USAGE_IMMUTABLE; // GPU 읽기
}
else if (bCpuWrite == true && bGpuWrite == false)
{
// 속도 3
desc.Usage = D3D11_USAGE_DYNAMIC; // CPU쓰기, GPU 읽기
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
}
else if (bCpuWrite == false && bGpuWrite == true)
{
// 속도 : 2
// CPU 쓰기 가능한 상황 - UpdateSubresource
desc.Usage = D3D11_USAGE_DEFAULT;
}
else
{
// 속도 4
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
}
D3D11_SUBRESOURCE_DATA subResource = { 0 };
subResource.pSysMem = data;
Check(D3D::GetDevice()->CreateBuffer(&desc, &subResource, &buffer));
}
VertexBuffer::~VertexBuffer()
{
SafeRelease(buffer);
}
void VertexBuffer::Render()
{
UINT offset = 0;
D3D::GetDC()->IAGetVertexBuffers(slot, 1, &buffer, &stride, &offset);
}
지금까지 선언한것과 많이 다르게 길다.
똑같이 desc를 만들어서 보내지만, CPU와 GPU가 ReadOnly인지 Write까지 할 수 있는지 한번에 정해서 보내준다. 왜?? 안전성 문제도 있고 이게 더 속도가 잘 나오기 때문이다.
IndexBuffer
IndexBuffer.h
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;
};
IndexBuffer.cpp
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;
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);
}
인덱스 버퍼는 자료 형도 UINT로 고정되어 있고 별로 특별한점은 없다.
ConstantBuffer
쉐이더에 값을 넘겨줄때마다 쉐이더의 변수를 계속해서 참조를 하게 되면 새로 쉐이더에 있는 구조체를 만들어 읽기 때문에 변수가 많으면 많을수록 속도가 많이 느려진다.
그래서 register에 구역을 나눠서 contantbuffer로 바꿔 전달해줘야 좀 더 빠르다.
ContantBuffer.h
class ConstantBuffer
{
public:
ConstantBuffer(void* data, UINT dataSize);
~ConstantBuffer();
ID3D11Buffer* Buffer() { return buffer; }
void Render();
private:
ID3D11Buffer* buffer;
void* data;
UINT dataSize;
};
ContantBuffer.cpp
ConstantBuffer::ConstantBuffer(void * data, UINT dataSize)
{
D3D11_BUFFER_DESC desc;
ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
desc.ByteWidth = dataSize;
desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
Check(D3D::GetDevice()->CreateBuffer(&desc, NULL, &buffer));
}
ConstantBuffer::~ConstantBuffer()
{
SafeRelease(buffer);
}
void ConstantBuffer::Render()
{
D3D11_MAPPED_SUBRESOURCE subResource;
D3D::GetDC()->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
{
memcpy(subResource.pData, data, dataSize);
}
D3D::GetDC()->Unmap(buffer, 0);
}
전달해주는 방식은 CPU에서 값을 쓰고 GPU에서 읽어야 하기 때문에 D3D11_USAGE_DYNAMIC으로 만들어준다.
그리고 지금까진 subResource를 만들어서 넘겨줬지만, 이것이 속도가 느리다는 단점이 있다. 그래서 이번엔 map을 만들어 넘겨준다.
이 map 에도 둥요한게 있다. map을 만들면 꼭 unmap을 시켜줘야 map을 할동안 이 데이터에 다른 접근을 거부하고 unmap을 해줘야 허가 해주기 때문이다.