DirectX 11에서 컴퓨팅 셰이더(compute shader)는 그래픽 처리 장치(GPU)에서 실행되는 셰이더 프로그램의 한 종류로, 그래픽을 렌더링하는 대신 범용 계산을 수행하도록 특별히 설계되었다.

 

목차

     

     


     

     

     

    Compute Shader

     

    DirectX 11에서 컴퓨팅 셰이더(compute shader)는 그래픽 처리 장치(GPU)에서 실행되는 셰이더 프로그램의 한 종류로, 그래픽을 렌더링하는 대신 범용 계산을 수행하도록 특별히 설계되었다.

    컴퓨팅 셰이더(compute shader)는 물리 시뮬레이션, 이미지 처리, 데이터 처리 등과 같은 매우 다양한 작업에 사용될 수 있다. 독립적으로 사용하거나 정점 셰이더, 픽셀 셰이더, 지오메트리 셰이더와 같은 다른 셰이더 프로그램과 함께 사용할 수 있다.

    컴퓨팅 셰이더(compute shader)는 병렬로 실행될 수 있는 스레드 모음인 하나 이상의 스레드 그룹에서 작동합니다. 각 스레드는 버퍼와 텍스처를 포함한 다양한 메모리에 액세스할 수 있으며 산술, 논리, 메모리 액세스와 같은 작업을 수행할 수 있습니다.

    DirectX 11에서 컴퓨팅 셰이더를 사용하려면 일반적으로 ID3D11Device 인터페이스를 사용하여 컴퓨팅 셰이더 코드를 입력으로 지정하는 셰이더 프로그램을 만듭니다. 그런 다음 ID3D11DeviceContext 인터페이스를 사용하여 버퍼 또는 텍스처와 같은 필요한 셰이더 리소스를 설정하고 ID3D11DeviceContext::Dispatch method를 사용하여 GPU에서 실행할 컴퓨팅 셰이더를 디스패치할 수 있다.

    컴퓨팅 셰이더(compute shader)를 사용할 때 명심해야 할 한 가지 중요한 점은 일반적으로 그래픽 렌더링에 있어 기존의 정점이나 픽셀 셰이더보다 효율성이 떨어진다는 것이다. 그러나 광범위한 비그래픽 작업에 매우 강력한 도구가 될 수 있다.

     

     

    Compute Shader와 Raw Buffer는 종종 많은 양의 데이터에 대한 범용 컴퓨팅 작업을 수행하기 위해 DirectX 11에서 함께 사용됩니다.

    Compute ShaderRaw Buffer에 저장된 데이터에 액세스하고 여러 스레드를 사용하여 병렬로 계산을 수행할 수 있다. 앞서 설명한 바와 같이 Raw Buffer는 미리 정의된 형식 없이 유연한 방식으로 데이터를 저장하는 데 사용할 수 있는 메모리 블록이다.

    DirectX 11에서 Compute Shader와 함께 Raw Buffer를 사용하려면 일반적으로 ID3D11Device 인터페이스를 사용하여 버퍼 개체를 만들고 버퍼 크기를 바이트 단위로 지정한다. 그런 다음 ID3D11DeviceContext 인터페이스를 사용하여 Raw Buffer를 셰이더 리소스로 설정하고 계산 셰이더를 사용하여 버퍼에 저장된 데이터에 대한 계산을 수행한다.

    Raw Buffer의 유연성을 통해 미리 정의된 형식과 호환되지 않는 사용자 지정 데이터 유형을 포함하여 광범위한 데이터 유형을 저장할 수 있습니다. 이를 통해 물리 시뮬레이션, 이미지 처리 및 데이터 처리와 같은 다양한 컴퓨팅 셰이더 애플리케이션에 유용하게 사용할 수 있다.

    전반적으로 Compute ShaderRaw Buffer는 DirectX 11의 GPU에서 범용 컴퓨팅을 수행하기 위한 강력한 조합을 제공한다.

     

     

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

     

    지난 시간에 만든 Compute Shade에 자료데이터를 넘기기 위해 RawBuffer를 만들었다.  이번에는 RawBuffer를 Compute Shade에 넘긴 후 Compute Shade 연산을 진행하고 결과를 뽑아내겠다. 그 후 Texture Buffer 생성 후 Pixel를 운용하여 이미지를 조작하겠다. Compute Shade에 Animation 연산을 넘겨주겠다.


     

    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;
        
        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 = 10 * 8 * 3;
    
    	struct Output
    	{
    		UINT GroupID[3];
    		UINT GroupThreadID[3];
    		UINT DispatchThreadID[3];
    		UINT GroupIndex;
    	};
    
    	//inputData: NULL, input Byte: 0, outputByte: sizeof(Output) * count
    	RawBuffer* rawBuffer = new RawBuffer(NULL, 0, sizeof(Output) * count);
    
    	shader->AsUAV("Output")->SetUnorderedAccessView(rawBuffer->UAV());
    	shader->Dispatch(0, 0, 1, 1, 1); //technique, pass, x, y, z
    
    
    	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\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
    		);
    	}
    	fclose(file); //파일 닫기
    }
    
    void RawBufferDemo::Update()
    {
    }
    
    void RawBufferDemo::Render()
    {
    }

     


     

    실행화면