Skip to content

Commit 95a73b4

Browse files
committed
feat: add metal graphics example
1 parent 06e4d33 commit 95a73b4

File tree

1 file changed

+209
-10
lines changed

1 file changed

+209
-10
lines changed

examples/metal_graphics.js

Lines changed: 209 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
// @ts-check
22

3+
// https://developer.apple.com/documentation/metal/using_a_render_pipeline_to_render_primitives?language=objc
4+
35
import "objc";
46

7+
objc.import("Metal");
8+
objc.import("MetalKit");
9+
510
export class ApplicationDelegate extends NSObject {
611
static ObjCProtocols = [NSApplicationDelegate, NSWindowDelegate];
712

@@ -30,19 +35,11 @@ export class ApplicationDelegate extends NSObject {
3035
window.title = "NativeScript for macOS";
3136
window.delegate = this;
3237
window.styleMask = NSWindowStyleMask.Titled | NSWindowStyleMask.Closable |
33-
NSWindowStyleMask.Miniaturizable | NSWindowStyleMask.Resizable |
34-
NSWindowStyleMask.FullSizeContentView;
38+
NSWindowStyleMask.Miniaturizable | NSWindowStyleMask.Resizable;
3539

3640
window.titlebarAppearsTransparent = true;
3741
window.titleVisibility = NSWindowTitleVisibility.Hidden;
3842

39-
window.backgroundColor = NSColor.colorWithSRGBRedGreenBlueAlpha(
40-
118 / 255,
41-
171 / 255,
42-
235 / 255,
43-
1,
44-
);
45-
4643
window.makeKeyAndOrderFront(this);
4744

4845
NSApp.activateIgnoringOtherApps(false);
@@ -56,17 +53,219 @@ export class ApplicationDelegate extends NSObject {
5653
}
5754
}
5855

56+
// deno-fmt-ignore
57+
const vertices = new Float32Array([
58+
250, -250, 0, 0, 1, 0, 0, 1,
59+
-250, -250, 0, 0, 0, 1, 0, 1,
60+
0, 250, 0, 0, 0, 0, 1, 1,
61+
]);
62+
63+
/**
64+
* @implements {MTKViewDelegate}
65+
*/
66+
export class Renderer extends NSObject {
67+
static ObjCProtocols = [MTKViewDelegate];
68+
69+
static {
70+
NativeClass(this);
71+
}
72+
73+
/** @type {MTLDevice} */
74+
device;
75+
/** @type {MTLRenderPipelineState} */
76+
pipelineState;
77+
/** @type {MTLCommandQueue} */
78+
commandQueue;
79+
/** @type {Uint32Array} */
80+
viewportSize;
81+
82+
/**
83+
* @param {MTKView} mtkView
84+
*/
85+
initWithMtkView(mtkView) {
86+
this.device = mtkView.device;
87+
88+
const error = new interop.Reference();
89+
const library = this.device.newLibraryWithSourceOptionsError(
90+
`
91+
#ifndef AAPLShaderTypes_h
92+
#define AAPLShaderTypes_h
93+
94+
#include <simd/simd.h>
95+
96+
typedef enum AAPLVertexInputIndex
97+
{
98+
AAPLVertexInputIndexVertices = 0,
99+
AAPLVertexInputIndexViewportSize = 1,
100+
} AAPLVertexInputIndex;
101+
102+
typedef struct
103+
{
104+
vector_float2 position;
105+
vector_float4 color;
106+
} AAPLVertex;
107+
108+
#endif /* AAPLShaderTypes_h */
109+
110+
#include <metal_stdlib>
111+
112+
using namespace metal;
113+
114+
struct RasterizerData
115+
{
116+
float4 position [[position]];
117+
float4 color;
118+
};
119+
120+
vertex RasterizerData
121+
vertexShader(uint vertexID [[vertex_id]],
122+
constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
123+
constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])
124+
{
125+
RasterizerData out;
126+
127+
float2 pixelSpacePosition = vertices[vertexID].position.xy;
128+
129+
vector_float2 viewportSize = vector_float2(*viewportSizePointer);
130+
131+
out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
132+
out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
133+
134+
out.color = vertices[vertexID].color;
135+
136+
return out;
137+
}
138+
139+
fragment float4 fragmentShader(RasterizerData in [[stage_in]])
140+
{
141+
return in.color;
142+
}
143+
144+
`,
145+
null,
146+
error,
147+
);
148+
149+
if (!library) {
150+
console.log(error.value);
151+
throw new Error("Failed to create library");
152+
}
153+
154+
const vertexFunction = library.newFunctionWithName("vertexShader");
155+
const fragmentFunction = library.newFunctionWithName("fragmentShader");
156+
157+
const descriptor = MTLRenderPipelineDescriptor.new();
158+
descriptor.vertexFunction = vertexFunction;
159+
descriptor.fragmentFunction = fragmentFunction;
160+
descriptor.colorAttachments.objectAtIndexedSubscript(0).pixelFormat =
161+
mtkView.colorPixelFormat;
162+
163+
this.pipelineState = this.device.newRenderPipelineStateWithDescriptorError(
164+
descriptor,
165+
error,
166+
);
167+
168+
if (!this.pipelineState) {
169+
console.log(error.value);
170+
throw new Error("Failed to create pipeline state");
171+
}
172+
173+
this.commandQueue = this.device.newCommandQueue();
174+
175+
this.viewportSize = new Uint32Array([
176+
mtkView.drawableSize.width,
177+
mtkView.drawableSize.height,
178+
]);
179+
180+
return this;
181+
}
182+
183+
/**
184+
* @param {MTKView} _view
185+
* @param {CGSize} size
186+
*/
187+
mtkViewDrawableSizeWillChange(_view, size) {
188+
this.viewportSize[0] = size.width;
189+
this.viewportSize[1] = size.height;
190+
}
191+
192+
/**
193+
* @param {MTKView} view
194+
*/
195+
drawInMTKView(view) {
196+
const commandBuffer = this.commandQueue.commandBuffer();
197+
commandBuffer.label = "MyCommand";
198+
199+
const renderPassDescriptor = view.currentRenderPassDescriptor;
200+
201+
if (renderPassDescriptor !== null) {
202+
const renderEncoder = commandBuffer.renderCommandEncoderWithDescriptor(
203+
renderPassDescriptor,
204+
);
205+
renderEncoder.label = "MyRenderEncoder";
206+
207+
renderEncoder.setViewport({
208+
originX: 0,
209+
originY: 0,
210+
width: this.viewportSize[0],
211+
height: this.viewportSize[1],
212+
znear: 0,
213+
zfar: 1.,
214+
});
215+
216+
renderEncoder.setRenderPipelineState(this.pipelineState);
217+
218+
renderEncoder.setVertexBytesLengthAtIndex(
219+
vertices,
220+
vertices.byteLength,
221+
0,
222+
);
223+
224+
renderEncoder.setVertexBytesLengthAtIndex(
225+
this.viewportSize,
226+
this.viewportSize.byteLength,
227+
1,
228+
);
229+
230+
renderEncoder.drawPrimitivesVertexStartVertexCount(
231+
MTLPrimitiveType.Triangle,
232+
0,
233+
3,
234+
);
235+
236+
renderEncoder.endEncoding();
237+
238+
commandBuffer.presentDrawable(view.currentDrawable);
239+
}
240+
241+
commandBuffer.commit();
242+
}
243+
}
244+
59245
export class ViewController extends NSViewController {
60246
static {
61247
NativeClass(this);
62248
}
63249

250+
/** @type {MTKView} */
251+
mtkView;
252+
64253
loadView() {
65-
this.view = NSView.new();
254+
this.mtkView = this.view = MTKView.alloc().initWithFrameDevice(
255+
{
256+
origin: { x: 0, y: 0 },
257+
size: { width: 480, height: 480 },
258+
},
259+
MTLCreateSystemDefaultDevice(),
260+
);
66261
}
67262

68263
viewDidLoad() {
69264
super.viewDidLoad();
265+
266+
this.renderer = Renderer.new().initWithMtkView(this.mtkView);
267+
268+
this.mtkView.delegate = this.renderer;
70269
}
71270
}
72271

0 commit comments

Comments
 (0)