DirectX 11에서 Thread Group은 GPU 상에서 함께 실행되는 셰이더 호출의 집합을 의미한다. Thread Group은 GPU 상에서 비그래픽 처리를 허용하는 그래픽 파이프라인의 프로그래밍 가능한 단계인 컴퓨팅 셰이더에 사용된다.

 

목차

     

     


     

     

     

    Thread Group

    DirectX 11에서 Thread Group은 GPU 상에서 함께 실행되는 셰이더 호출의 집합을 의미한다. Thread Group은 GPU 상에서 비그래픽 처리를 허용하는 그래픽 파이프라인의 프로그래밍 가능한 단계인 컴퓨팅 셰이더에 사용된다.

    Thread Group은 세 가지 값, 즉 각 차원을 따르는 그룹의 스레드 수(thread group dimensions), 그룹의 스레드 수(thread group size) 및 총 스레드 그룹 수(dispatch count)로 정의된다. 이러한 값은 계산 셰이더의 Dispatch() 메서드를 사용하여 설정된다.


     

    SV_GroupID

     

    SV_GroupID 시스템 값은 현재 스레드 그룹의 ID를 제공하는 HLSL(DirectX에서 사용되는 셰이더 언어)의 기본 제공 변수이다. 이 변수는 스레드 동기화 및 메모리 액세스와 같은 동일한 그룹 내 스레드 간의 조정이 필요한 그룹 수준 작업을 수행하는 데 사용할 수 있다.

    SV_GroupID는 각 차원에서 현재 스레드 그룹의 인덱스를 나타내는 3개의 값을 가지는 벡터이다. 예를 들어 스레드 그룹 차원이 (8, 8, 1)로 설정된 경우 SV_GroupID 변수는 x 및 y 차원에서 현재 스레드 그룹의 인덱스를 나타내는 (0, 0, 0) ~ (7, 7, 0) 범위의 값을 가진다.

    전체적으로 스레드 그룹 및 SV_GroupID는 GPU에서 효율적인 병렬 처리를 가능하게 하는 컴퓨팅 셰이더의 중요한 개념이다.

     

    Microsoft 공식문서

    https://learn.microsoft.com/ko-kr/windows/win32/direct3dhlsl/sv-groupid

     

    SV_GroupID - Win32 apps

    컴퓨팅 셰이더가 실행되는 스레드 그룹의 인덱스입니다.

    learn.microsoft.com

     

     

    Shader
      RawBuffer.fx
    Framework
      Render
      Buffer.h .cpp
    UnitTest
      DirectCompute
      RawBufferDemo.h .cpp
      Main.h .cpp

     


     

    RawBuffer.fx

     

    RawBuffer.fx

    더보기
    ByteAddressBuffer Input; //SRV
    RWByteAddressBuffer Output; //UAV
    
    struct Group
    {
        uint3 GroupID;
        uint3 GroupThreadID;
        uint3 DispatchThreadID;
        uint GroupIndex;
        float RetValue;
    };
    
    struct ComputeInput
    {
        uint3 GroupID : SV_GroupID;
        uint3 GroupThreadID : SV_GroupThreadID;
        uint3 DispatchThreadID : SV_DispatchThreadID;
        uint GroupIndex : SV_GroupIndex;
    };
    
    //thread 하나하나가 함수를 물고 진행한다.
    [numthreads(10, 8, 3)] //x, y, z
    void CS(ComputeInput input)
    {
        Group group;
        group.GroupID = asuint(input.GroupID); //asuint는 uint로 캐스팅해주는 함수
        group.GroupThreadID = asuint(input.GroupThreadID);
        group.DispatchThreadID = asuint(input.DispatchThreadID);
        group.GroupIndex = asuint(input.GroupIndex);
        
        uint index = input.GroupID.x * 10 * 8 * 3 + input.GroupIndex;
        uint outAddress = index * 11 * 4; //thread 개수 늘려줌.
        
        uint inAddress = index * 4;
        group.RetValue = asfloat(Input.Load(inAddress));
        
        Output.Store3(outAddress + 0, asuint(group.GroupID)); //12
        Output.Store3(outAddress + 12, asuint(group.GroupThreadID)); //24
        Output.Store3(outAddress + 24, asuint(group.DispatchThreadID)); //36
        Output.Store(outAddress + 36, asuint(group.GroupIndex)); //40
        Output.Store(outAddress + 40, asuint(group.RetValue));
    
    }
    
    technique11 T0
    {
        pass P0
        {
            SetVertexShader(NULL);
            SetPixelShader(NULL);
    		//cs_5_0는 DX11에서 사용하는 Shader 버젼
            SetComputeShader(CompileShader(cs_5_0, CS()));
        }
    }

     


     

    RawBufferDemo

     

    RawBufferDemo.h

    더보기
    #pragma once
    #include "Systems/IExecute.h"
    
    class RawBufferDemo : public IExecute
    {
    public:
    	virtual void Initialize() override;
    	virtual void Ready() override {}
    	virtual void Destroy() override {}
    	virtual void Update() override;
    	virtual void PreRender() override {}
    	virtual void Render() override;
    	virtual void PostRender() override {}
    	virtual void ResizeScreen() override {}
    
    private:
    };

     

     

    RawBufferDemo.cpp

    더보기
    #include "stdafx.h"
    #include "RawBufferDemo.h"
    
    void RawBufferDemo::Initialize()
    {
    	Shader* shader = new Shader(L"60_RawBuffer.fx");
    
    
    	UINT count = 2 * 10 * 8 * 3; //x2 //shader->Dispatch에서 x를 2로 늘려주었다.
    
    	struct Output
    	{
    		UINT GroupID[3];
    		UINT GroupThreadID[3];
    		UINT DispatchThreadID[3];
    		UINT GroupIndex;
    		float RetValue;
    	};
    	//RawBuffer* rawBuffer = new RawBuffer(NULL, 0, sizeof(Output) * count);
    
    	struct Input
    	{
    		float Value = 0.0f;
    	};
    	Input* input = new Input[count];
    
    	for (int i = 0; i < count; i++)
    		input[i].Value = Math::Random(0.0f, 10000.0f);
    
    	RawBuffer* rawBuffer = new RawBuffer(input, sizeof(Input) * count, sizeof(Output) * count);
    
    
    	shader->AsSRV("Input")->SetResource(rawBuffer->SRV());
    	shader->AsUAV("Output")->SetUnorderedAccessView(rawBuffer->UAV());
    	shader->Dispatch(0, 0, 2, 1, 1);
    
    
    	Output* output = new Output[count];
    	rawBuffer->CopyFromOuput(output);
    
    	FILE* file;
    	fopen_s(&file, "../Raw.csv", "w");
    
    	for (UINT i = 0; i < count; i++)
    	{
    		Output temp = output[i];
    
    		fprintf
    		(
    			file,
    			"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%f\n",
    			i,
    			temp.GroupID[0], temp.GroupID[1], temp.GroupID[2],
    			temp.GroupThreadID[0], temp.GroupThreadID[1], temp.GroupThreadID[2],
    			temp.DispatchThreadID[0], temp.DispatchThreadID[1], temp.DispatchThreadID[2],
    			temp.GroupIndex, temp.RetValue
    		);
    	}
    	fclose(file);
    }
    
    void RawBufferDemo::Update() { }
    
    void RawBufferDemo::Render() { }

    UINT count = 2 * 10 * 8 * 3;

    • shader->Dispatch에서 x를 2로 늘려주었기 때문에 x2를 하였다.

    Input* input = new Input[count];

    for (int i = 0; i < count; i++)
        input[i].Value = Math::Random(0.0f, 10000.0f);

    • input을 배열로 만들어 적용.

     


     

    실행화면