본문 바로가기

그래픽스

[DX12] Compute Shader

Compute Shader은 지금까지 해왔던 Vertex , Index Buffer 입력, VS, Rasterize, PS 의 과정을 거치지 않고 독립적으로 실행된다. CPU의 부담을 덜기 위해 GPU에게 떠넘기는 것이다. CPU에서 RAM, GPU에서 VRAM 끼리 전달 속도는 빠르지만 CPU에서 GPU 속도는 느리기 때문에 GPU가 독립적으로 계산해서 자생하도록 하는 것이다. 

 

https://butter-shower.tistory.com/41

GPU에서 스레드는 하나의 일감을 의미한다. 그리고 여러개의 스레드들은 하나의 블록으로 관리하고 그 블록들이 또 여러개 모여있다.

 

RWTexture2D<float4> g_rwtex_0 : register(u0);

// 쓰레드 그룹당 쓰레드 개수
// max : 1024 (CS_5.0)
// - 하나의 쓰레드 그룹은 하나의 다중처리기에서 실행
[numthreads(1024, 1, 1)]
void CS_Main(int3 threadIndex : SV_DispatchThreadID)
{
    if (threadIndex.y % 2 == 0)
        g_rwtex_0[threadIndex.xy] = float4(1.f, 0.f, 0.f, 1.f);
    else
        g_rwtex_0[threadIndex.xy] = float4(0.f, 1.f, 0.f, 1.f);
}

 

새로 추가된 compute shader이다. RWTexture은 읽고 쓸 수 있는 Texture로 전에 사용했던 Texture과 달리 쉐이더 코드에서 Texture을 변경할 수 있다. numthreads는 스레드의 개수를 의미하는데 3차원으로 x축, y축, z축을 곱한다. 위에서는 (1024, 1, 1)을 곱하므로 1024개가 된다. threadIndex.xy를 픽셀 좌표처럼 표현하기 위해 1024개로 설정했다. 

 

msdn

 

numthreads의 모습인데 GroupThreadID로 현재 그룹에서 스레드의 ID, GroupID로 현재 그룹의 ID, DispatchThreadID로 GroupID*스레드크기 + GroupThreadID로 전체적인 번호를 알 수 있다. 

 

enum class UAV_REGISTER : uint8
{
	u0 = static_cast<uint8>(SRV_REGISTER::END),
	u1,
	u2,
	u3,
	u4,

	END,
};
	computeQueueDesc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE;

 

레지스터는 UAV를 새로 만들고 기존에 사용하던 CommandQueue를 GraphicsCommandQueue로 바꾸고 ComputeCommandQueue를 추가한다. 초기 설정시에 Type을 Compute로 설정하는 것 외에는 큰 차이가 없다.  RootSignature도 새롭게 추가한 ComputeCommandQueue에 맞춰 추가해주고 TableDescriptorHeap도 

SetUAV를 추가한 ComputeDescriptorHeap을 만들어준다. 

 

enum class SHADER_TYPE : uint8
{
	DEFERRED,
	FORWARD,
	LIGHTING,
	COMPUTE,
};
	// GraphicsShader
	ComPtr<ID3DBlob>					_vsBlob;
	ComPtr<ID3DBlob>					_psBlob;
	ComPtr<ID3DBlob>					_errBlob;	
	D3D12_GRAPHICS_PIPELINE_STATE_DESC  _graphicsPipelineDesc = {};

	// ComputeShader
	ComPtr<ID3DBlob>					_csBlob;
	D3D12_COMPUTE_PIPELINE_STATE_DESC   _computePipelineDesc = {};
void Shader::CreateComputeShader(const wstring& path, const string& name, const string& version)
{
	_info.shaderType = SHADER_TYPE::COMPUTE;

	CreateShader(path, name, version, _csBlob, _computePipelineDesc.CS);
	_computePipelineDesc.pRootSignature = COMPUTE_ROOT_SIGNATURE.Get();

	HRESULT hr = DEVICE->CreateComputePipelineState(&_computePipelineDesc, IID_PPV_ARGS(&_pipelineState));
	assert(SUCCEEDED(hr));
}

 

Shader클래스에도 Compute Type Shader을 추가하고 Compute Shader을 생성하는 부분을 추가한다.

 

		// UAV 용 Texture 생성
		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);
            //Flag가 ALLOW_UNORDERED_ACCESS(UAV용)로 되어있음
		if (desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)
		{
			// UAV
			D3D12_DESCRIPTOR_HEAP_DESC uavHeapDesc = {};
			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));

			_uavHeapBegin = _uavHeap->GetCPUDescriptorHandleForHeapStart();

			D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
			uavDesc.Format = _image.GetMetadata().format;
			uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2D;

			DEVICE->CreateUnorderedAccessView(_tex2D.Get(), nullptr, &uavDesc, _uavHeapBegin);
		}

 

그리고 Resource에 Compute Shader 와 material을 추가해준 후 SceneManager에서 UAV용 Texture을 추가하려니 오류가 발생한다. 그래서 Texture에서 UAV 부분을 추가한다. Material과 ConstantBuffer에서 PushData를 할 때도 DescriptorHeap이 Graphics, Compute 두 종류로 나뉘었으므로 그 부분에 나눠서 처리를 하도록 바꾼다. 그래서 최종적으로 새로운 방식인 Compute Shader을 사용할 수 있게 된다. 

'그래픽스' 카테고리의 다른 글

[DX12] Instancing  (0) 2022.07.30
[DX12] Particle System  (0) 2022.07.29
[DX12] Deferred Rendering  (0) 2022.07.28
[DX12] Render Target  (0) 2022.07.28
[DX12] Orthographic Projection  (0) 2022.07.28