Compute shader

ㅋㅋ·2022년 7월 28일
0

DirectX12강의

목록 보기
31/39

GPGPU
일반적인 연산 용도로 GPU를 사용

이펙트나 애니메이션 등 계산 시 유용

CPU와 GPU 통신 속도에 제한이 있기 때문에

CPU 부담이 큰 일을 처리할 때만 사용


compute 쉐이더 파일

RWTexture2D<float4> g_rwtex_0 : register(u0);

[numthreads(1024, 1, 1)]
void CS_Main(int3 threadIndex : SV_DispatchThreadID)
{
    if (threadIndex.y % 2 == 0)
        ...
    else
        ...
}

u 레지스터는 일반적인 그래픽 연산에 사용하는 레지스터가 아님

이미지출처 numthreads

GPU는 group들로 이루어져 있고 gropu은 group thread로 이루어져 있음

numthreads의 인자들로 gropu thread가 만들어지게 되고

Dispatch라는 GPU 함수가 불릴 때 인자들로 group의 형태가 만들어 진다.


command queue 클래스 수정

일반적인 그래픽 용도의 커맨드 큐와 연산용 커맨드 큐는 만들 때 옵션 설정이 다르다

class ComputeCommandQueue
{
public:
	
    ...

private:
	ComPtr<ID3D12CommandQueue>			_cmdQueue;
	ComPtr<ID3D12CommandAllocator>		_cmdAlloc;
	ComPtr<ID3D12GraphicsCommandList>	_cmdList;

	ComPtr<ID3D12Fence>					_fence;
	uint32								_fenceValue = 0;
	HANDLE								_fenceEvent = INVALID_HANDLE_VALUE;
};

기존 구현했던 커맨드 큐를 그래픽스 커맨드 큐로 바꾸고

연산 시 사용할 compute command queue 클래스 구현

해당 큐는 리소스를 필요로 하지 않음


void ComputeCommandQueue::Init(ComPtr<ID3D12Device> device)
{
	D3D12_COMMAND_QUEUE_DESC computeQueueDesc = {};
	computeQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;
	computeQueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
	device->CreateCommandQueue(&computeQueueDesc, IID_PPV_ARGS(&_cmdQueue));
	device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_COMPUTE, IID_PPV_ARGS(&_cmdAlloc));
	device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_COMPUTE, _cmdAlloc.Get(), nullptr, IID_PPV_ARGS(&_cmdList));

	...
    
}

커맨드 큐를 만들 때 타입을 D3D12_COMMAND_LIST_TYPE_COMPUTE으로 하여

큐, 리스트와 얼로케이터 생성


root signature 클래스 수정

class RootSignature
{
	...
    
	ComPtr<ID3D12RootSignature>	GetComputeRootSignature() { return _computeRootSignature; }
    
    ...
    
	void CreateComputeRootSignature();

private:
	ComPtr<ID3D12RootSignature> _computeRootSignature;
	
    ...
};

연산용으로 사용될 공간을 관리할 root signature 정의


void RootSignature::CreateComputeRootSignature()
{
	...

	D3D12_ROOT_SIGNATURE_DESC sigDesc = CD3DX12_ROOT_SIGNATURE_DESC(_countof(param), param);
	sigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_NONE;

	...
}

root signature의 descriptor를 만들 때 플래그로 D3D12_ROOT_SIGNATURE_FLAG_NONE을 사용



class ComputeDescriptorHeap
{
public:
	...
    
	void SetUAV(D3D12_CPU_DESCRIPTOR_HANDLE srcHandle, UAV_REGISTER reg);

	void CommitTable();

	...
    
	D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(UAV_REGISTER reg);
	
    ...
};

Unordered Access View 레지스터를 사용할 descriptor heap 클래스 정의 및 구현


shader 클래스 수정

enum class SHADER_TYPE : uint8
{
	DEFERRED,
	FORWARD,
	LIGHTING,
	COMPUTE,
};

쉐이더 타입에 compute 추가

class Shader : public Object
{
public:
	...
    
	void CreateComputeShader(const wstring& path, const string& name, const string& version);

	...

private:
	...

	ComPtr<ID3DBlob>					_csBlob;
	D3D12_COMPUTE_PIPELINE_STATE_DESC   _computePipelineDesc = {};
};

compute 쉐이더를 사용할 변수 및 생성 함수 정의


texture 클래스 수정

if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
{
	...
    
	uavHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
	uavHeapDesc.NumDescriptors = 1;
	uavHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
	uavHeapDesc.NodeMask = 0;
	DEVICE->CreateDescriptorHeap(&uavHeapDesc, IID_PPV_ARGS(&_uavHeap));

	...
}

텍스쳐 생성 시 UAV 사용 플래그가 있다면 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV 타입으로 생성

void Texture::Create(DXGI_FORMAT format, uint32 width, uint32 height,
	const D3D12_HEAP_PROPERTIES& heapProperty, D3D12_HEAP_FLAGS heapFlags,
	D3D12_RESOURCE_FLAGS resFlags, Vec4 clearColor)
{
	...

	D3D12_CLEAR_VALUE optimizedClearValue = {};
	D3D12_CLEAR_VALUE* pOptimizedClearValue = nullptr;

	D3D12_RESOURCE_STATES resourceStates = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON;

	if (resFlags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)
	{
		...
        
		pOptimizedClearValue = &optimizedClearValue;
	}
	else if (resFlags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)
	{
		...
        
		pOptimizedClearValue = &optimizedClearValue;
	}

	HRESULT hr = DEVICE->CreateCommittedResource(
		&heapProperty,
		heapFlags,
		&desc,
		resourceStates,
		pOptimizedClearValue,
		IID_PPV_ARGS(&_tex2D));

	...
}

UAV로 사용할 텍스쳐의 경우 clear value가 null이어야 함


constant buffer 클래스 수정


material 클래스 수정

class Material : public Object
{
public:
	...
    
	void PushComputeData();
	void Dispatch(uint32 x, uint32 y, uint32 z);

	...
};

material 데이터들을 GPU에 업로드하고 실제 GPU가 compute 쉐이더를 계산하도록하는

Dispatch 함수 정의

void Material::Dispatch(uint32 x, uint32 y, uint32 z)
{
	PushComputeData();
	GEngine->GetComputeDescHeap()->CommitTable();

	COMPUTE_CMD_LIST->Dispatch(x, y, z);
	GEngine->GetComputeCmdQueue()->FlushComputeCommandQueue();
}

compute 시 사용할 데이터를 업로드,

커맨드 큐의 dispatch를 사용하여 요청


compute shader 사용

{
	shared_ptr<Shader> shader = GET_SINGLE(Resources)->Get<Shader>(L"ComputeShader");

	shared_ptr<Texture> texture = GET_SINGLE(Resources)->CreateTexture(L"UAVTexture",
		DXGI_FORMAT_R8G8B8A8_UNORM, 1024, 1024,
		CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE,
		D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS);

	...
    
	GEngine->GetComputeDescHeap()->SetUAV(texture->GetUAVHandle(), UAV_REGISTER::u0);

	material->Dispatch(1, 1024, 1);
}

텍스쳐 생성 시

플래그가 D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET가 아닌

D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS 사용

0개의 댓글