1 module hello_triangle;
2 import metal;
3 
4 void print(string str)
5 {
6     NSLog(str.ns);
7 }
8 
9 const float[] vertexData = [
10     0.0, 1.0, 0.0,
11     -1.0, -1.0, 0.0,
12     1.0, -1.0, 0.0
13 ];
14 
15 
16 enum metalVertexShader = q{
17 
18 vertex float4 basic_vertex(
19     const device packed_float3* vertex_array [[buffer(0)]],
20     unsigned int vid [[vertex_id]]
21 )
22 {
23     return float4(vertex_array[vid], 1.0);
24 }
25 
26 };
27 
28 enum metalFragmentShader = q{
29 
30 fragment half4 basic_fragment()
31 {
32     return half4(1.0);
33 }
34 
35 };
36 
37 
38 import metal;
39 
40 __gshared MTLBuffer vertexBuffer;
41 __gshared MTLRenderPipelineDescriptor pipelineDescriptor;
42 __gshared MTLCommandQueue commandQueue;
43 __gshared MTLRenderPipelineState state;
44 
45 enum DefaultPixelFormat = MTLPixelFormat.BGRA8Unorm_sRGB;
46 
47 extern(C) void initMetal(MTLDevice device)
48 {
49     import std.conv:to;
50     print("Initializing Metal!");
51     vertexBuffer = device.newBuffer(vertexData.ptr, float.sizeof*vertexData.length, MTLResourceOptions.DefaultCache);
52 
53     NSError err;
54     MTLLibrary defaultLibrary = device.newLibraryWithSource((metalVertexShader ~ metalFragmentShader).ns, null, &err);
55     if(err !is null || defaultLibrary is null)
56     {
57         NSLog("Error compiling shader %@".ns, err);
58         return;
59     }
60 
61     print("Getting vertex and fragment shaders");
62     MTLFunction fragmentProgram = defaultLibrary.newFunctionWithName("basic_fragment".ns);
63     MTLFunction vertexProgram = defaultLibrary.newFunctionWithName("basic_vertex".ns);
64 
65     if(fragmentProgram is null)
66         print("Could not get fragment shader");
67     if(vertexProgram is null)
68         print("Coult not get vertex shader");
69 
70     print("Creating VertexFormat");
71 
72     MTLVertexDescriptor descriptor = MTLVertexDescriptor.vertexDescriptor;
73 
74     enum POSITION = 0;
75     enum BufferIndexMeshPositions = 0;
76     descriptor.attributes[POSITION].format = MTLVertexFormat.float3;
77     descriptor.attributes[POSITION].offset = 0;
78     descriptor.attributes[POSITION].bufferIndex = BufferIndexMeshPositions;
79 
80     descriptor.layouts[POSITION].stepRate = 1;
81     descriptor.layouts[POSITION].stepFunction = MTLVertexStepFunction.PerVertex;
82     descriptor.layouts[POSITION].stride = float.sizeof*3;
83 
84 
85     print("Creating PipelineDescriptor");
86     pipelineDescriptor = MTLRenderPipelineDescriptor.alloc.initialize;
87     pipelineDescriptor.label = "TestProgram".ns;
88     pipelineDescriptor.vertexFunction = vertexProgram;
89     pipelineDescriptor.fragmentFunction = fragmentProgram;
90     pipelineDescriptor.vertexDescriptor = descriptor;
91     pipelineDescriptor.colorAttachments[0].pixelFormat = DefaultPixelFormat;
92     pipelineDescriptor.depthAttachmentPixelFormat = MTLPixelFormat.Depth32Float_Stencil8;
93     pipelineDescriptor.stencilAttachmentPixelFormat = MTLPixelFormat.Depth32Float_Stencil8;
94 
95     state = device.newRenderPipelineStateWithDescriptor(pipelineDescriptor, &err);
96 
97     if(state is null || err !is null)
98     {
99         NSLog("Failed to create RenderPipelineDescriptor %@".ns, err);
100         return;
101     }
102 
103 
104     commandQueue = device.newCommandQueue();
105 
106 }
107 
108 extern(C) void renderMetal(MTKView view)
109 {
110     MTLRenderPassDescriptor desc = view.currentRenderPassDescriptor;
111     if(desc is null) return;
112 
113     MTLCommandBuffer cmdBuffer = commandQueue.commandBuffer();
114     cmdBuffer.label = "MyCommand".ns;
115 
116 
117     MTLRenderCommandEncoder encoder = cmdBuffer.renderCommandEncoderWithDescriptor(desc);
118 
119     encoder.setRenderPipelineState(state);
120     encoder.setVertexBuffer(vertexBuffer, 0, 0);
121 
122     encoder.drawPrimitives(MTLPrimitiveType.Triangle, 0, 9);
123 
124     encoder.endEncoding();
125     cmdBuffer.presentDrawable(view.currentDrawable);
126     cmdBuffer.commit();
127 
128     // desc.colorAttachments[0].texture = 
129 }