본문 바로가기

프로그래밍/iOS,macOS

[Metal] Compute Function 샘플분석

Compute Function

kernel 식별자를 사용하기에 커널 함수라고도 불림


kernel 키워드를 사용해 시작하고, 리턴은 void 형이어야 함.
max_total_threads_per_threadgroup 함수속성을 사용해 쓰레드 그룹간의 최대 쓰레드수를 지정가능
단, MTLDevice 의 maxThreadsPerThreadgroup 값보다 크게되면 오류발생할수 있음.

 

[[max_total_threads_per_threadgroup(x)]]
kernel void my_kernel()
{

}

함수인자

디바이스, 상수버퍼 : [[buffer(index)]]
텍스처 : [[texture(index]]
샘플러 : [[sampler(index]]
쓰레드그룹 버퍼 : [[threadgroup(index]



두벡터를 더해 출력하는 커널함수 

kernel void add_vectors( 
    const device float4 *inA[[buffer(0)]],
    const device float4 *inB[[buffer(1)]],
    device float4 *out[[buffer(2)]],
    uint id[[thrad_position_in_grid]] )
{
    out[id] = inA[id] + inB[id];
}

 

텍스처 출력도 가능

kernel void my_kernel (
    texture2d<float, access::read> inT [[texture(0)]],
    texture2d<float, access::write> outT [[texture(1)]],
    uint id[[thread_position_in_grid]] )
{
    float4 c = inT.read( id ).rgba;
    outT.write( c.rgba, id );
}

 

버퍼의 인덱스를 지정하지 않는 경우 인자 순서대로 타입별 인덱스가 할당됨

kernel void my_kernel( 
    texture2d<half> src,    // [[texture(0)]]
    texture2d<half, access::write> dst, // [[texture(1)]]
    sampler s,  // [[sampler(0)]]
    device myUserInfo *u  // [[buffer(0)]]
)

 


ComputeFunction 예제

https://medium.com/birdman-inc/how-to-add-effects-to-images-with-msl-metal-shading-language-a785b989f534

 

How to add effects to images with MSL (Metal Shading Language).

Overview

medium.com

 

쉐이더코드

#include <metal_stdlib>
using namespace metal;
kernel void my_kernel (
    texture2d<float, access::read> inT [[texture(0)]],
    texture2d<float, access::write> outT [[texture(1)]],
    uint id[[thread_position_in_grid]] )
{
    float4 c = inT.read( id ).rgba;
    outT.write( c.rgba, id );
}

 

 

swift 코드

파이프라인 구분
Vertex, Fragment : RenderPipeline
    MTLRenderPipelineState
    MTLRenderPipelineDescriptor
    
ComputeFunction : ComputePipeline
    MTLComputePipelineState
    MTLComputePipelineDescriptor

 

// 블로그에서 작성된 소스(단순 플로우 확인 용)

class MyClass {
    var device: MTLDevice?
    var library: MTLLibrary?
    var commandQueue: MTLCommandQueue?
    var computeFunction : MTLFunction?
    
    
    var computePipeline : MTLComputePipelineState?
    

    var inputTexture:MTLTexture?
    var outputTexture:MTLTexture?
 
 
    초기화를 진행한다.
    
    func init() {
        // 장치와 라이브러리
        device = MTLCreateSystemDefaultDevice()
        library = device.makeDefaultLibrary()
        
        // 커널함수
        computeFunction = library.makeFunction( name: "my_kernel")
        computePipeline = device.makeComputePipelineState( function: computeFunction )
        
        // 명령큐
        commandQueue = device.makeCommandQueue()
    }


    입력 텍스처 
    func inputImage(_ image: UIImage) -> UIImage {
    
        // 입력 텍스처
        inputTexture = makeMTLTexture( image )
        
        
        // 출력 텍스처
        let textureDescriptor = 
            MTLTextureDescriptor.texture2DDescriptor( 
                pixelFormat: MTLPixelFormat.rgba8Unorm,
                width:image.size.width,
                height:image.size.height,
                mipmapped:false
            )
        outTexture = self.device.makeTexture( descriptor: textureDescriptor)
        
        
           
        // command encoder 생성
        // MTLRenderCommandEncoder : vertex, fragment
        // MTLComputeCommandEncoder : kernel
        
        let buffer = commandQueue.makeCommandBuffer()
        let encoder = buffer?.makeComputeCommandEncoder()
        encoder.setComputePipelineState( computePipeline )
        encoder.setTexture( inputTexture, index:0)
        encoder.setTexture( outputTexture, index:1)
        
        let threadGroupCount = MTLSizeMake(16, 16, 1)
        encoder.dispatchThreadgroups( 
            MTLSizeMake( 
                Int(ceil(image.size.width / CGFloat(threadGroupCount.width ))),
                Int(ceil(image.size.height / CGFloat(threadGroupCount.height ))),
                1
            ), threadsPerThreadgroup: threadGroupCount )
        encoder.endEncoding()
        
        buffer.commit()
        buffer.waitUntilCompleted()
        return convert( from: outputTexture )
    }
    
    

    func makeMTLTexture( from image: UIImage )->MTLTexture {
        let textureLoader = MTKTextureLoader( device: self.device )
        let texture = textureLoader.newTexture( cgImage: image.cgImage, options: nil )
        return texture;
    }
    
    func conver( from texture: MTLTexture) -> UIImage {
        let w = texture.width
        let h = texture.height
        let bytesPerPixel:Int = 4
        let imageByteCount = w * h * bytesPerPixel
        let bytesPerRow = w * bytesPerPixel
        var src = [UInt8](repeating: 0, count: Int(imageByteCount ))
        
        let region = MTLRegionMake2D(0, 0, w, h)
        texture.getBytes( &src, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
        let bitmapInfo = CGBitmapInfo( rawValue:(CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedlast.rawValue))
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitsPerComponent = 8
        let context = CGContext( data: &src,
                        width: w,
                        height: h,
                        bitsPerCompoenent: bitsPerCompoenent,
                        bytesPerRow: bytesPerRow,
                        space: colorSpace,
                        bitmapInfo: bitmapInfo.rawValue)
        let cgImage = context?.makeImage()
        let image = UIImage( cgImage: cgImage)
        return image
    
    }
    
}

'프로그래밍 > iOS,macOS' 카테고리의 다른 글

[iOS] CoreAudio AudioUnit  (0) 2019.10.08
iOS 프레임워크 파이썬 스크립트  (0) 2019.07.23
[iOS] GPUImage  (0) 2019.07.06
xcodebuild  (0) 2019.06.06
CocoaPods 라이브러리 배포  (0) 2019.05.31
[Metal] MetalKit 플로우 분석  (0) 2019.05.27
swift , iOS 기본 사항만 빠르게 살펴보기  (0) 2019.05.23
swift - objective-c 혼합 사용  (0) 2019.05.22
ios pods google 특이한 에러  (0) 2017.01.26
cocoapods 관련 내용  (0) 2017.01.10