Metal - `include` 或 `import` 函数

Posted

技术标签:

【中文标题】Metal - `include` 或 `import` 函数【英文标题】:Metal - `include` or `import` function 【发布时间】:2017-01-10 01:09:49 【问题描述】:

是否可以将metal 文件导入或包含到另一个金属文件中?假设我有一个包含所有数学函数的金属文件,如果我的金属项目中需要它,我只会包含或导入它。可能吗?

我试过了:

#include "sdf.metal"

我得到了错误:

metallib:乘以定义的符号_Z4vmaxDv2_f 命令/应用程序/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/usr/bin/metallib 退出代码 1 失败

更新:

这是我的两个着色器文件:

SDF.metal:

#ifndef MYAPP_METAL_CONSTANTS
#define MYAPP_METAL_CONSTANTS


#include <metal_stdlib>

namespace metal 

float kk(float2 v) 
    return max(v.x, v.y);


float kkk(float3 v) 
    return max(max(v.x, v.y), v.z);




#endif

还有 Shaders.metal:

#include <metal_stdlib>
#include "SDF.metal"
using namespace metal;


float fBoxCheap(float3 p, float3 b)  //cheap box
    return kkk(abs(p) - b);



float map( float3 p )

    float box2 = fBoxCheap(p-float3(0.0,3.0,0.0),float3(4.0,3.0,1.0));



    return box2;


float3 getNormal( float3 p )

    float3 e = float3( 0.001, 0.00, 0.00 );

    float deltaX = map( p + e.xyy ) - map( p - e.xyy );
    float deltaY = map( p + e.yxy ) - map( p - e.yxy );
    float deltaZ = map( p + e.yyx ) - map( p - e.yyx );

    return normalize( float3( deltaX, deltaY, deltaZ ) );


float trace( float3 origin, float3 direction, thread float3 &p )

    float totalDistanceTraveled = 0.0;

    for( int i=0; i <64; ++i)
    
        p = origin + direction * totalDistanceTraveled;

        float distanceFromPointOnRayToClosestObjectInScene = map( p );
        totalDistanceTraveled += distanceFromPointOnRayToClosestObjectInScene;

        if( distanceFromPointOnRayToClosestObjectInScene < 0.0001 )
        
            break;
        

        if( totalDistanceTraveled > 10000.0 )
        
            totalDistanceTraveled = 0.0000;
            break;
        
    

    return totalDistanceTraveled;


float3 calculateLighting(float3 pointOnSurface, float3 surfaceNormal, float3 lightPosition, float3 cameraPosition)

    float3 fromPointToLight = normalize(lightPosition - pointOnSurface);
    float diffuseStrength = clamp( dot( surfaceNormal, fromPointToLight ), 0.0, 1.0 );

    float3 diffuseColor = diffuseStrength * float3( 1.0, 0.0, 0.0 );
    float3 reflectedLightVector = normalize( reflect( -fromPointToLight, surfaceNormal ) );

    float3 fromPointToCamera = normalize( cameraPosition - pointOnSurface );
    float specularStrength = pow( clamp( dot(reflectedLightVector, fromPointToCamera), 0.0, 1.0 ), 10.0 );

    // Ensure that there is no specular lighting when there is no diffuse lighting.
    specularStrength = min( diffuseStrength, specularStrength );
    float3 specularColor = specularStrength * float3( 1.0 );

    float3 finalColor = diffuseColor + specularColor;

    return finalColor;


kernel void compute(texture2d<float, access::write> output [[texture(0)]],
                    constant float &timer [[buffer(1)]],
                    constant float &mousex [[buffer(2)]],
                    constant float &mousey [[buffer(3)]],
                    uint2 gid [[thread_position_in_grid]])

    int width = output.get_width();
    int height = output.get_height();
    float2 uv = float2(gid) / float2(width, height);
    uv = uv * 2.0 - 1.0;
    // scale proportionately.
    if(width > height) uv.x *= float(width)/float(height);
    if(width < height) uv.y *= float(height)/float(width);


    float posx = mousex * 2.0 - 1.0;
    float posy = mousey * 2.0 - 1.0;

    float3 cameraPosition = float3( posx * 0.01,posy * 0.01, -10.0 );


    float3 cameraDirection = normalize( float3( uv.x, uv.y, 1.0) );

    float3 pointOnSurface;
    float distanceToClosestPointInScene = trace( cameraPosition, cameraDirection, pointOnSurface );

    float3 finalColor = float3(1.0);
    if( distanceToClosestPointInScene > 0.0 )
    
        float3 lightPosition = float3( 5.0, 2.0, -10.0 );
        float3 surfaceNormal = getNormal( pointOnSurface );
        finalColor = calculateLighting( pointOnSurface, surfaceNormal, lightPosition, cameraPosition );
    
    output.write(float4(float3(finalColor), 1), gid);


更新2:

还有我的MetalView.swift

import MetalKit

public class MetalView: MTKView, NSWindowDelegate 

    var queue: MTLCommandQueue! = nil
    var cps: MTLComputePipelineState! = nil

    var timer: Float = 0
    var timerBuffer: MTLBuffer!

    var mousexBuffer: MTLBuffer!
    var mouseyBuffer: MTLBuffer!
    var pos: NSPoint!
    var floatx: Float!
    var floaty: Float!

    required public init(coder: NSCoder) 
        super.init(coder: coder)
        self.framebufferOnly = false
        device = MTLCreateSystemDefaultDevice()
        registerShaders()
    


    override public func drawRect(dirtyRect: NSRect) 
        super.drawRect(dirtyRect)
        if let drawable = currentDrawable 
            let command_buffer = queue.commandBuffer()
            let command_encoder = command_buffer.computeCommandEncoder()
            command_encoder.setComputePipelineState(cps)
            command_encoder.setTexture(drawable.texture, atIndex: 0)
            command_encoder.setBuffer(timerBuffer, offset: 0, atIndex: 1)
            command_encoder.setBuffer(mousexBuffer, offset: 0, atIndex: 2)
            command_encoder.setBuffer(mouseyBuffer, offset: 0, atIndex: 3)
            update()
            let threadGroupCount = MTLSizeMake(8, 8, 1)
            let threadGroups = MTLSizeMake(drawable.texture.width / threadGroupCount.width, drawable.texture.height / threadGroupCount.height, 1)
            command_encoder.dispatchThreadgroups(threadGroups, threadsPerThreadgroup: threadGroupCount)
            command_encoder.endEncoding()
            command_buffer.presentDrawable(drawable)
            command_buffer.commit()
        
    

    func registerShaders() 
        queue = device!.newCommandQueue()
        do 
            let library = device!.newDefaultLibrary()!
            let kernel = library.newFunctionWithName("compute")!
            timerBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
            mousexBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
            mouseyBuffer = device!.newBufferWithLength(sizeof(Float), options: [])
            cps = try device!.newComputePipelineStateWithFunction(kernel)
         catch let e 
            Swift.print("\(e)")
        
    

    func update() 
        timer += 0.01
        var bufferPointer = timerBuffer.contents()
        memcpy(bufferPointer, &timer, sizeof(Float))
        bufferPointer = mousexBuffer.contents()
        memcpy(bufferPointer, &floatx, sizeof(NSPoint))
        bufferPointer = mouseyBuffer.contents()
        memcpy(bufferPointer, &floaty, sizeof(NSPoint))
    

    override public func mouseDragged(event: NSEvent) 
        pos = convertPointToLayer(convertPoint(event.locationInWindow, fromView: nil))
        let scale = layer!.contentsScale
        pos.x *= scale
        pos.y *= scale
        floatx = Float(pos.x)
        floaty = Float(pos.y)
        debugPrint("Hello",pos.x,pos.y)
    

更新 3

按照 KickimusButticus 的解决方案实施后,着色器确实编译了。但是我有另一个错误:

【问题讨论】:

【参考方案1】:

您的设置不正确(编辑:我在其他答案和此答案的先前版本中的设置也是如此。)

您可以像在 C++ 中一样使用标头 (Metal is based on C++11, after all...)。您需要的只是更多文件,我将其命名为SDF.h。该文件包含没有命名空间声明的函数原型声明。您需要在其他文件中的 using namespace metal; 声明之后 #include 它。确保头文件不是 .metal 文件,并且它不是 在您的构建阶段的Compile Sources 列表中。如果标头被视为编译源,则很可能是导致CompilerError 的原因。

SDF.h:

// SDFHeaders.metal
#ifndef SDF_HEADERS
#define SDF_HEADERS

float kk(float2 v);
float kkk(float3 v);

#endif

SDF.metal:

#include <metal_stdlib>

using namespace metal;
#include "SDF.h"

float kk(float2 v) 
    return max(v.x, v.y);


float kkk(float3 v) 
    return max(max(v.x, v.y), v.z);

Shaders.metal:

这里是你在包含SDF.h之后使用函数的地方。

// Shaders.metal

#include <metal_stdlib>

using namespace metal;
#include "SDF.h"    

float fBoxCheap(float3 p, float3 b)  //cheap box
    return kkk(abs(p) - b);


// ...

当然,在清理之后构建。祝你好运!

【讨论】:

我按照你说的做了,它确实编译了。但是我在command_encoder.setComputePipelineState(cps) - fatal error: unexpectedly found nil while unwrapping an Optional value 行有另一个运行时错误。我更新了上面的MetalView 文件。 这是你的金属文件编译的进展。哪个可选项意外地是nil?是cps吗? registerShaders() 中的 try/catch 块打印有用吗? 好的,我认为问题是当您在构建阶段的“编译源”列表中有 SDFHeaders.metal 时。尝试在那里删除它。另外,我对我的答案做了一些其他的改变。请查看并进行这些调整。 我可以注意到Metal 1是基于C++11的,如你所说,但Metal 2是基于C++14的? 需要注意的一个重要的事情,这让我很困惑的是,include 语句(与代码其余部分中的 import 语句不同)尊重您的项目目录结构。这意味着,如果您要包含的文件与执行包含的文件不在同一文件夹中,则必须为其提供该文件的路径(即#include "../shared.h",如果它位于父文件夹中)【参考方案2】:

如果您在该符号上运行demangler,您会发现编译器认为您有两个或多个签名为vmax(float2 v) 的函数定义。检查被包含的文件是否有多个这样的函数定义,或者被包含的文件和包含它的文件是否都提供了这样的定义。

【讨论】:

以上是关于Metal - `include` 或 `import` 函数的主要内容,如果未能解决你的问题,请参考以下文章

SCNAction.rotateByAngle 旋转在 El Capitan 或 Metal 中反转

何时可以安全地重写和重用 MTLBuffer 或其他 Metal 顶点缓冲区?

如何使用 SwiftUI 连续渲染软件位图?

将纯 Metal-API 与 SceneKit 或 SpriteKit 一起使用

在 Metal File 中访问 swift 结构或类

如何使用 Metal API 或 Accelerate Framework 绘制裁剪的位图?