방법: 컴퓨팅 셰이더 만들기
컴퓨팅 셰이더는 일반화된 입력 및 출력 메모리 액세스를 사용하여 거의 모든 유형의 계산을 지원하는 Microsoft HLSL(High Level Shader Language) 프로그래밍 가능한 셰이더입니다. 이 항목에서는 컴퓨팅 셰이더를 만드는 방법을 보여줍니다. 컴퓨팅 셰이더 기술을 DirectCompute 기술이라고도 합니다.
컴퓨팅 셰이더를 만들려면 다음을 수행합니다.
D3DCompileFromFile을 호출하여 HLSL 셰이더 코드를 컴파일합니다.
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS; #if defined( DEBUG ) || defined( _DEBUG ) flags |= D3DCOMPILE_DEBUG; #endif // Prefer higher CS shader profile when possible as CS 5.0 provides better performance on 11-class hardware. LPCSTR profile = ( device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0 ) ? "cs_5_0" : "cs_4_0"; const D3D_SHADER_MACRO defines[] = { "EXAMPLE_DEFINE", "1", NULL, NULL }; ID3DBlob* shaderBlob = nullptr; ID3DBlob* errorBlob = nullptr; HRESULT hr = D3DCompileFromFile( srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE, entryPoint, profile, flags, 0, &shaderBlob, &errorBlob );
ID3D11Device::CreateComputeShader를 사용하여 컴퓨팅 셰이더를 만듭니다.
ID3D11ComputeShader* g_pFinalPassCS = NULL; pd3dDevice->CreateComputeShader( pBlobFinalPassCS->GetBufferPointer(), pBlobFinalPassCS->GetBufferSize(), NULL, &g_pFinalPassCS );
다음 코드 예제에서는 컴퓨팅 셰이더를 컴파일하고 만드는 방법을 보여줍니다.
참고
이 예제 코드의 경우 경로에 있는 %PROGRAM_FILE%\Windows Kits\8.0\Redist\D3D\arch> 폴더의 Windows SDK< 8.0 및 d3dcompiler_44.dll 파일이 필요합니다.
#define _WIN32_WINNT 0x600
#include <stdio.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#pragma comment(lib,"d3d11.lib")
#pragma comment(lib,"d3dcompiler.lib")
HRESULT CompileComputeShader( _In_ LPCWSTR srcFile, _In_ LPCSTR entryPoint,
_In_ ID3D11Device* device, _Outptr_ ID3DBlob** blob )
{
if ( !srcFile || !entryPoint || !device || !blob )
return E_INVALIDARG;
*blob = nullptr;
UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
flags |= D3DCOMPILE_DEBUG;
#endif
// We generally prefer to use the higher CS shader profile when possible as CS 5.0 is better performance on 11-class hardware
LPCSTR profile = ( device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0 ) ? "cs_5_0" : "cs_4_0";
const D3D_SHADER_MACRO defines[] =
{
"EXAMPLE_DEFINE", "1",
NULL, NULL
};
ID3DBlob* shaderBlob = nullptr;
ID3DBlob* errorBlob = nullptr;
HRESULT hr = D3DCompileFromFile( srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
entryPoint, profile,
flags, 0, &shaderBlob, &errorBlob );
if ( FAILED(hr) )
{
if ( errorBlob )
{
OutputDebugStringA( (char*)errorBlob->GetBufferPointer() );
errorBlob->Release();
}
if ( shaderBlob )
shaderBlob->Release();
return hr;
}
*blob = shaderBlob;
return hr;
}
int main()
{
// Create Device
const D3D_FEATURE_LEVEL lvl[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 };
UINT createDeviceFlags = 0;
#ifdef _DEBUG
createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
ID3D11Device* device = nullptr;
HRESULT hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, lvl, _countof(lvl),
D3D11_SDK_VERSION, &device, nullptr, nullptr );
if ( hr == E_INVALIDARG )
{
// DirectX 11.0 Runtime doesn't recognize D3D_FEATURE_LEVEL_11_1 as a valid value
hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, &lvl[1], _countof(lvl) - 1,
D3D11_SDK_VERSION, &device, nullptr, nullptr );
}
if ( FAILED(hr) )
{
printf("Failed creating Direct3D 11 device %08X\n", hr );
return -1;
}
// Verify compute shader is supported
if ( device->GetFeatureLevel() < D3D_FEATURE_LEVEL_11_0 )
{
D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS hwopts = { 0 } ;
(void)device->CheckFeatureSupport( D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &hwopts, sizeof(hwopts) );
if ( !hwopts.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x )
{
device->Release();
printf( "DirectCompute is not supported by this device\n" );
return -1;
}
}
// Compile shader
ID3DBlob *csBlob = nullptr;
hr = CompileComputeShader( L"ExampleCompute.hlsl", "CSMain", device, &csBlob );
if ( FAILED(hr) )
{
device->Release();
printf("Failed compiling shader %08X\n", hr );
return -1;
}
// Create shader
ID3D11ComputeShader* computeShader = nullptr;
hr = device->CreateComputeShader( csBlob->GetBufferPointer(), csBlob->GetBufferSize(), nullptr, &computeShader );
csBlob->Release();
if ( FAILED(hr) )
{
device->Release();
}
printf("Success\n");
// Clean up
computeShader->Release();
device->Release();
return 0;
}
앞의 코드 예제는 ExampleCompute.hlsl 파일에서 컴퓨팅 셰이더 코드를 컴파일합니다. 다음은 ExampleCompute.hlsl의 코드입니다.
//--------------------------------------------------------------------------------------
// File: BasicCompute11.hlsl
//
// This file contains the Compute Shader to perform array A + array B
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#ifdef USE_STRUCTURED_BUFFERS
struct BufType
{
int i;
float f;
#ifdef TEST_DOUBLE
double d;
#endif
};
StructuredBuffer<BufType> Buffer0 : register(t0);
StructuredBuffer<BufType> Buffer1 : register(t1);
RWStructuredBuffer<BufType> BufferOut : register(u0);
[numthreads(1, 1, 1)]
void CSMain( uint3 DTid : SV_DispatchThreadID )
{
BufferOut[DTid.x].i = Buffer0[DTid.x].i + Buffer1[DTid.x].i;
BufferOut[DTid.x].f = Buffer0[DTid.x].f + Buffer1[DTid.x].f;
#ifdef TEST_DOUBLE
BufferOut[DTid.x].d = Buffer0[DTid.x].d + Buffer1[DTid.x].d;
#endif
}
#else // The following code is for raw buffers
ByteAddressBuffer Buffer0 : register(t0);
ByteAddressBuffer Buffer1 : register(t1);
RWByteAddressBuffer BufferOut : register(u0);
[numthreads(1, 1, 1)]
void CSMain( uint3 DTid : SV_DispatchThreadID )
{
#ifdef TEST_DOUBLE
int i0 = asint( Buffer0.Load( DTid.x*16 ) );
float f0 = asfloat( Buffer0.Load( DTid.x*16+4 ) );
double d0 = asdouble( Buffer0.Load( DTid.x*16+8 ), Buffer0.Load( DTid.x*16+12 ) );
int i1 = asint( Buffer1.Load( DTid.x*16 ) );
float f1 = asfloat( Buffer1.Load( DTid.x*16+4 ) );
double d1 = asdouble( Buffer1.Load( DTid.x*16+8 ), Buffer1.Load( DTid.x*16+12 ) );
BufferOut.Store( DTid.x*16, asuint(i0 + i1) );
BufferOut.Store( DTid.x*16+4, asuint(f0 + f1) );
uint dl, dh;
asuint( d0 + d1, dl, dh );
BufferOut.Store( DTid.x*16+8, dl );
BufferOut.Store( DTid.x*16+12, dh );
#else
int i0 = asint( Buffer0.Load( DTid.x*8 ) );
float f0 = asfloat( Buffer0.Load( DTid.x*8+4 ) );
int i1 = asint( Buffer1.Load( DTid.x*8 ) );
float f1 = asfloat( Buffer1.Load( DTid.x*8+4 ) );
BufferOut.Store( DTid.x*8, asuint(i0 + i1) );
BufferOut.Store( DTid.x*8+4, asuint(f0 + f1) );
#endif // TEST_DOUBLE
}
#endif // USE_STRUCTURED_BUFFERS