• Home
  • About
    • 게임 개발자 유정룡 photo

      게임 개발자 유정룡

      포트폴리오

    • Learn More
    • Email
    • Github
    • Bitbucket
  • Projects
    • All Projects
    • All Tags

Direct3D11 공부 28일차(DirectCompute)

06 Jul 2021

Reading time ~2 minutes

드디어 스레드다 스레드~~

Multi Threaded Programming

이 멀티 스레드를 하다보면 나오는 문제는 MultiThread 여기 가면 어떤 문제가 있는지 문제를 해결하는 방법에 관해 잘 정리해 뒀다.

DirectCompute

그래픽 처리만 했던 지금까지와 달리 GPU를 CPU처럼 연산을 할 수 있게 해주는 방법이다. OpenCL이나 Cuda, 그리고 블록체인 채굴(제발 채굴좀 그만해라 ㅡㅡ)에 사용하는 병렬 처리 방식이다.

출처

이러한 방식으로 처리 된다.

GPU에서 연산을 한 뒤, CPU에 값을 복사해온다.

이 GPU에게 주는 버퍼는 Raw Buffer(raw 바이트 단위로 순차적으로 쌓여있는 순수한 데이터)로 넘겨준다.

CsResource

주고 받는 리소스를 만들어주는 클래스이다.

class CsResource
{
public:
	CsResource();
	virtual ~CsResource();

protected:
	virtual void CreateInput() {}
	virtual void CreateSRV() {}

	virtual void CreateOutput() {}
	virtual void CreateUAV() {}

	virtual void CreateResult() {}

	void CreateBuffer();

protected:
	ID3D11Resource* input = NULL;
	ID3D11ShaderResourceView* srv = NULL; // input

	ID3D11Resource* output = NULL;
	ID3D11UnorderedAccessView* uav = NULL; // output 

	ID3D11Resource* result = NULL;
};

주고 받는 리소스를 갖고있으며 받을때는 GPU에서 주는 데이터의 순서를 모르기 때문에 Unordered로 해서 받아온다. 그런 뒤 프로그래머가 나눠준다.

RawBuffer

class RawBuffer : public CsResource
{
public:
	RawBuffer(void* inputData, UINT inputByte, UINT outputByte);
	~RawBuffer();

private:
	void CreateInput()	 override;
	void CreateSRV()	 override;

	void CreateOutput()	 override;
	void CreateUAV()	 override;

	void CreateResult()	 override;

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

private:
	void* inputData;
	UINT inputByte;
	UINT outputByte;
};
void RawBuffer::CreateInput()
{
	if (inputByte < 1) { return; }

	ID3D11Buffer* buffer = NULL;

	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));
	desc.ByteWidth = inputByte;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

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

	Check(D3D::GetDevice()->CreateBuffer(&desc, inputData != NULL ? &subResource : NULL, &buffer));
	
	input = (ID3D11Resource *)buffer;
}

GPU에 넣어주는 버퍼를 만들어준다.

void RawBuffer::CreateSRV()
{
	if (inputByte < 1) { return; }

	ID3D11Buffer* buffer = (ID3D11Buffer*)input;

	D3D11_BUFFER_DESC desc;
	buffer->GetDesc(&desc);

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	ZeroMemory(&srvDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
	srvDesc.Format = DXGI_FORMAT_R32_TYPELESS;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
	srvDesc.BufferEx.Flags = D3D11_BUFFEREX_SRV_FLAG_RAW;
	srvDesc.BufferEx.NumElements = desc.ByteWidth / 4;

	Check(D3D::GetDevice()->CreateShaderResourceView(buffer, &srvDesc, &srv));
}

GPU에 ResourceView를 만들어 보내는데 갖고올때 현재 buffer를 갖고와서 넘겨준다.

void RawBuffer::CreateOutput()
{
	ID3D11Buffer* buffer = NULL;

	D3D11_BUFFER_DESC desc;
	ZeroMemory(&desc, sizeof(D3D11_BUFFER_DESC));

	desc.ByteWidth = outputByte;
	desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
	desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;

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

	output = (ID3D11Resource *)buffer;
}

GPU에서 쓴 버퍼를 갖고온다.

void RawBuffer::CreateUAV()
{
	ID3D11Buffer* buffer = (ID3D11Buffer *)output;

	D3D11_BUFFER_DESC desc;
	buffer->GetDesc(&desc);


	D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
	ZeroMemory(&uavDesc, sizeof(D3D11_UNORDERED_ACCESS_VIEW_DESC));
	uavDesc.Format = DXGI_FORMAT_R32_TYPELESS;
	uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
	uavDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
	uavDesc.Buffer.NumElements = desc.ByteWidth / 4;

	Check(D3D::GetDevice()->CreateUnorderedAccessView(buffer, &uavDesc, &uav));
}

CreateUnorderedAccessView 를 사용해서 갖고온다.

void RawBuffer::CreateResult()
{
	ID3D11Buffer* buffer;

	D3D11_BUFFER_DESC desc;
	((ID3D11Buffer*)output)->GetDesc(&desc);
	desc.Usage = D3D11_USAGE_STAGING;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;

	// unordered access view에 연결하기 위해 0
	// 0 == D3D11_USAGE_DEFULAT
	desc.BindFlags = 0;
	desc.MiscFlags = 0;

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

	result = (ID3D11Resource*)buffer;
}

현재 GPU에 써놓은 버퍼를 갖고온다.

void RawBuffer::CopyToInput(void * data)
{
	D3D11_MAPPED_SUBRESOURCE subResource;
	D3D::GetDC()->Map(input, 0, D3D11_MAP_WRITE_DISCARD, 0, &subResource);
	{
		memcpy(subResource.pData, data, inputByte);
	}
	D3D::GetDC()->Unmap(input, 0);
}
void RawBuffer::CopyFromOutput(void * data)
{
	D3D11_MAPPED_SUBRESOURCE subResource;
	D3D::GetDC()->Map(output, 0, D3D11_MAP_READ, 0, &subResource);
	{
		memcpy(subResource.pData, data, outputByte);
	}
	D3D::GetDC()->Unmap(output, 0);
}

값을 복사해온다.



DirectX Share Tweet +1