GPU를 이용하여 GPU 내에서 텍스처를 생성하고 이를 활용해보겠습니다. 이를 위해 GPU 내에 텍스처를 사용하기 위한 공간을 만들어 주고, 이를 access::write를 매개변수로 하는 텍스처를 받아들이고 write 함수를 통해 위치에 맞는 색상을 그릴 수 있도록 합니다. 이 때, gridSize 및 index를 사용하여 텍스처의 위치를 결정짓습니다.
kernel void mandelbrot_set(texture2d< half, access::write > tex [[texture(0)]],
uint2 index [[thread_position_in_grid]],
uint2 gridSize [[threads_per_grid]])
{
// Scale
float x0 = 2.0 * index.x / gridSize.x - 1.5;
float y0 = 2.0 * index.y / gridSize.y - 1.0;
// Implement Mandelbrot set
float x = 0.0;
float y = 0.0;
uint iteration = 0;
uint max_iteration = 1000;
float xtmp = 0.0;
while(x * x + y * y <= 4 && iteration < max_iteration)
{
xtmp = x * x - y * y + x0;
y = 2 * x * y + y0;
x = xtmp;
iteration += 1;
}
// Convert iteration result to colors
half color = (0.5 + 0.5 * cos(3.0 + iteration * 0.15));
tex.write(half4(color, color, color, 1.0), index, 0);
}
연산 파이프라인은 렌더링 파이프라인보다 좀 더 간단한 편인데, 이는 단일 기능만을 포함하고 다른 상태 집합을 포함하지 않아도 되기 때문입니다. 이전에는 replaceRegion으로 메모리를 채웠다면, 이번에는 텍스처를 GPU 연산을 통해서 채워야 합니다. 따라서 다음과 같은 인코딩을 통해 파이프랑니을 설정하고 dispatchThreads() 메서드를 통해 커널을 실행합니다.
void Renderer::generateMandelbrotTexture()
{
MTL::CommandBuffer* pCommandBuffer = _pCommandQueue->commandBuffer();
assert(pCommandBuffer);
MTL::ComputeCommandEncoder* pComputeEncoder = pCommandBuffer->computeCommandEncoder();
pComputeEncoder->setComputePipelineState( _pComputePSO );
pComputeEncoder->setTexture( _pTexture, 0 );
MTL::Size gridSize = MTL::Size( kTextureWidth, kTextureHeight, 1 );
NS::UInteger threadGroupSize = _pComputePSO->maxTotalThreadsPerThreadgroup();
MTL::Size threadgroupSize( threadGroupSize, 1, 1 );
pComputeEncoder->dispatchThreads( gridSize, threadgroupSize );
pComputeEncoder->endEncoding();
pCommandBuffer->commit();
}
커널을 실행하기 위해 렌더러는 한 스레드가 처리하는 일의 크기를 MTL::Size를 통해 명시하고, 스레드의 수는 maxTotalThreadsPerThreadgroup을 통해 구합니다. 이는 dispatchThreads를 통해 인수로 전달해집니다.
이를 좀 더 진화시키면, 렌더링 명령을 실행하기 전에 연산 커널을 사용하여 각 프레임의 텍스처 이미지를 재생성하기 위해 이전 것을 증강시킬 수 있습니다. 이를 통해 텍스처가 변하게 구현가능합니다.
// Update texture:
generateMandelbrotTexture( pCmd );
// Begin render pass:
MTL::RenderPassDescriptor* pRpd = pView->currentRenderPassDescriptor();
MTL::RenderCommandEncoder* pEnc = pCmd->renderCommandEncoder( pRpd );
기본적으로 Metal은 버퍼와 텍스처에 대한 위험을 확인하기에 GPU가 렌더링하기 직전에 텍스처에 쓰기 위한 연산 작업을 수행할 때 명시적 동기화가 필요하지는 않습니다. Metal은 쓰기 작업을 감지하고 이 것이 끝난 후에야 텍스처를 그리도록 합니다.
'Projects > Metal - Document' 카테고리의 다른 글
| Using Argument Buffers (0) | 2022.08.26 |
|---|---|
| Tessellation & Terrains (1) | 2022.08.25 |
| Metal-cpp 07 : Texture Surfaces (0) | 2022.08.25 |
| Metal-cpp 05 : Render 3D with Perspective Projection (0) | 2022.08.25 |
| Metal-cpp 04 : Draw Multiple Instances of an Object (0) | 2022.08.25 |