您可以通过向 NativeScript 项目添加 Metal 着色器来直接访问 GPU,让我们看看如何操作。
Metal 通过提供低开销 API、丰富的着色语言、图形和计算之间的紧密集成以及无与伦比的 GPU 分析和调试工具,为 Apple 平台提供硬件加速图形。
使用 Metal,应用程序可以利用 GPU 快速渲染复杂的场景并在并行运行计算任务。例如,以下类别的应用程序使用 Metal 来最大化其性能
- 渲染复杂的 2D 或 3D 环境的游戏
- 视频处理应用程序,例如 Final Cut Pro
- 分析和处理大型数据集的科学研究应用程序
- 完全沉浸式的 visionOS 应用程序
着色器是用 MSL(Metal Shading Language)编写的,MSL 是一种为 GPU 编程设计的 C++ 变体。有很多很棒的示例可以供您尝试,并且可以与 C++ 程序员合作创建它们。让我们尝试一个示例来了解如何在 NativeScript 项目中使用它们。
这些文件写入 .metal
文件中,该文件与任何其他 iOS 应用程序资源一样。
App_Resources/iOS/src/sample.metal
#include <metal_stdlib>
using namespace metal;
// Lines
float hash( float n ) {
return fract(sin(n)*753.5453123);
}
// Slight modification of iq's noise function.
float noise(vector_float2 x )
{
vector_float2 p = floor(x);
vector_float2 f = fract(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*157.0;
return mix(
mix( hash(n+ 0.0), hash(n+ 1.0),f.x),
mix( hash(n+157.0), hash(n+158.0),f.x),
f.y);
}
float fbm(vector_float2 p, vector_float3 a)
{
float v = 0.0;
v += noise(p*a.x)*0.50 ;
v += noise(p*a.y)*1.50 ;
v += noise(p*a.z)*0.125 * 0.1; // variable
return v;
}
vector_float3 drawLines(
vector_float2 uv,
vector_float3 fbmOffset,
vector_float3 color1,
vector_float3 colorSet[4],
float secs
)
{
float timeVal = secs * 0.1;
vector_float3 finalColor = vector_float3( 0.0 );
vector_float3 colorSets[4] = {
vector_float3(0.7, 0.05, 1.0),
vector_float3(1.0, 0.19, 0.0),
vector_float3(0.0, 1.0, 0.3),
vector_float3(0.0, 0.38, 1.0)
};
for( int i=0; i < 4; ++i )
{
float indexAsFloat = float(i);
float amp = 80.0 + (indexAsFloat*0.0);
float period = 2.0 + (indexAsFloat+2.0);
float thickness = mix( 0.4, 0.2, noise(uv*2.0) );
float t = abs( 1. /(sin(uv.y + fbm( uv + timeVal * period, fbmOffset )) * amp) * thickness );
finalColor += t * colorSets[i];
}
for( int i=0; i < 4; ++i )
{
float indexAsFloat = float(i);
float amp = 40.0 + (indexAsFloat*5.0);
float period = 9.0 + (indexAsFloat+2.0);
float thickness = mix( 0.1, 0.1, noise(uv * 12.0) );
float t = abs( 1. /(sin(uv.y + fbm( uv + timeVal * period, fbmOffset )) * amp) * thickness );
finalColor += t * colorSets[i] * color1;
}
return finalColor;
}
[[ stitchable ]] half4 timeLines(
float2 position,
half4 color,
float4 bounds,
float secs,
float tapValue
) {
vector_float2 uv = ( position / bounds.w ) * 1.0 - 1.0;
uv *= 1.0 + 0.5;
vector_float3 lineColor1 = vector_float3( 1.0, 0.0, 0.5 );
vector_float3 lineColor2 = vector_float3( 0.3, 0.5, 1.5 );
float spread = abs(tapValue);
vector_float3 finalColor = vector_float3(0);
vector_float3 colorSet[4] = {
vector_float3(0.7, 0.05, 1.0),
vector_float3(1.0, 0.19, 0.0),
vector_float3(0.0, 1.0, 0.3),
vector_float3(0.0, 0.38, 1.0)
};
float t = sin( secs ) * 0.5 + 0.5;
float pulse = mix( 0.05, 0.20, t);
finalColor = drawLines(uv, vector_float3( 65.2, 40.0, 4.0), lineColor1, colorSet, secs * 0.1) * pulse;
finalColor += drawLines( uv, vector_float3( 5.0 * spread/2, 2.1 * spread, 1.0), lineColor2, colorSet, secs );
return half4(half3(finalColor), 1.0);
}
通过将文件包含到 App_Resources/iOS/src
中,我们允许 NativeScript CLI 自动将它们包含到我们的 Xcode 项目中,这使得它们可以在项目中的任何位置使用。
我们可以通过 View 扩展修饰符启用该着色器的使用,该修饰符可以通过多种方式连接。在本例中,我们只需插入一个 SwiftUI 视图来进行尝试。
App_Resources/iOS/src/Sample.swift
import SwiftUI
struct MetalSample: View {
@State var start = Date()
@State var tapCount: CGFloat = 0
var body: some View {
ZStack {
TimelineView(.animation) { context in
Rectangle()
.foregroundStyle(.white)
.timeLines(
seconds: context.date.timeIntervalSince1970 - self.start.timeIntervalSince1970,
tapValue: tapCount
)
}
Button(action: {
self.tapCount += 1
}) {
Text("Metal is Dope")
}
}
}
}
@objc
class MetalSampleProvider: UIViewController, SwiftUIProvider {
private var swiftUI: MetalSample?
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
required public init() {
super.init(nibName: nil, bundle: nil)
}
public override func viewDidLoad() {
super.viewDidLoad()
swiftUI = MetalSample()
setupSwiftUIView(content: swiftUI)
}
/// Receive data from NativeScript
func updateData(data: NSDictionary) {}
/// Allow sending of data to NativeScript
var onEvent: ((NSDictionary) -> ())?
}
extension View {
func timeLines(seconds: Double, tapValue: CGFloat ) -> some View {
self
.colorEffect(
ShaderLibrary.default.timeLines(
.boundingRect,
.float(seconds),
.float(tapValue))
)
}
}
现在,我们可以初始化我们的提供程序以在任何布局中使用它,方法是使用 SwiftUI 插件
npm install @nativescript/swift-ui
在我们的 main.ts
(或 app.ts
)引导文件(*或根组件*)中,我们可以执行以下操作
import { registerElement } from '@nativescript/angular';
import { registerSwiftUI, SwiftUI, UIDataDriver } from '@nativescript/swift-ui';
// we could also run `ns typings ios` to include our custom types if desired
declare var MetalSampleProvider: any;
registerSwiftUI('metalSample', view => new UIDataDriver(MetalSampleProvider.alloc().init(), view));
registerElement('SwiftUI', () => SwiftUI);
现在可以在任何地方使用它,在此处了解更多信息
<GridLayout class="bg-black">
<SwiftUI swiftId="metalSample" class="w-full h-full"></SwiftUI>
</GridLayout>
注意:根据您计划使用的功能,您可能需要通过在 App_Resources/iOS/build.xcconfig
文件中包含以下行来提高您的目标 iOS 版本
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
您还可以通过包含 Metal
TypeScript 声明此处到您的 references.d.ts
中来执行很多操作,您可以在此处了解更多信息。这将允许您更深入地了解此处讨论的库引用
https://developer.apple.com/documentation/metal/performing_calculations_on_a_gpu