• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "experimental/graphite/src/mtl/MtlGraphicsPipeline.h"
9
10#include "experimental/graphite/src/GraphicsPipelineDesc.h"
11#include "experimental/graphite/src/mtl/MtlGpu.h"
12#include "experimental/graphite/src/mtl/MtlUtils.h"
13#include "include/private/SkSLString.h"
14
15namespace skgpu::mtl {
16
17static const char* kTestingOnlyShaders[] = {
18    // clear viewport to blue
19    "#include <metal_stdlib>\n"
20    "#include <simd/simd.h>\n"
21    "using namespace metal;\n"
22    "\n"
23    "typedef struct {\n"
24    "    float4 position [[position]];\n"
25    "} VertexOutput;\n"
26    "\n"
27    "vertex VertexOutput vertexMain(uint vertexID [[vertex_id]]) {\n"
28    "    VertexOutput out;\n"
29    "    float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
30    "    out.position.xy = position * 2 - 1;\n"
31    "    out.position.zw = float2(0.0, 1.0);\n"
32    "    return out;\n"
33    "}\n"
34    "\n"
35    "fragment float4 fragmentMain(VertexOutput in [[stage_in]]) {\n"
36    "    return float4(0.0, 0.0, 1.0, 1.0);\n"
37    "}",
38
39    // clear subarea to given color, using uniform buffer
40    "#include <metal_stdlib>\n"
41    "#include <simd/simd.h>\n"
42    "using namespace metal;\n"
43    "\n"
44    "typedef struct {\n"
45    "    float4 position [[position]];\n"
46    "} VertexOutput;\n"
47    "\n"
48    "typedef struct {\n"
49    "    float4 uPosXform;\n"
50    "    float4 uColor;\n"
51    "} UniformData;\n"
52    "\n"
53    "vertex VertexOutput vertexMain(constant UniformData& uniforms [[buffer(0)]],\n"
54    "                               uint vertexID [[vertex_id]]) {\n"
55    "    VertexOutput out;\n"
56    "    float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
57    "    out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
58    "    out.position.zw = float2(0.0, 1.0);\n"
59    "    return out;\n"
60    "}\n"
61    "\n"
62    "fragment float4 fragmentMain(constant UniformData& uniforms [[buffer(0)]],\n"
63    "                             VertexOutput in [[stage_in]]) {\n"
64    "    return uniforms.uColor;\n"
65    "}",
66
67    // draw triangles with given color, using uniform buffer and vertex data
68    "#include <metal_stdlib>\n"
69    "#include <simd/simd.h>\n"
70    "using namespace metal;\n"
71    "\n"
72    "typedef struct {\n"
73    "    float2 position [[attribute(0)]];\n"
74    "} VertexInput;\n"
75    "\n"
76    "typedef struct {\n"
77    "    float4 position [[position]];\n"
78    "} VertexOutput;\n"
79    "\n"
80    "typedef struct {\n"
81    "    float4 uPosXform;\n"
82    "    float4 uColor;\n"
83    "} UniformData;\n"
84    "\n"
85    "vertex VertexOutput vertexMain(VertexInput in [[stage_in]],\n"
86    "                               constant UniformData& uniforms [[buffer(0)]],\n"
87    "                               uint vertexID [[vertex_id]]) {\n"
88    "    VertexOutput out;\n"
89    "    float2 position = in.position;\n"
90    "    out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
91    "    out.position.zw = float2(0.0, 1.0);\n"
92    "    return out;\n"
93    "}\n"
94    "\n"
95    "fragment float4 fragmentMain(constant UniformData& uniforms [[buffer(0)]],\n"
96    "                             VertexOutput in [[stage_in]]) {\n"
97    "    return uniforms.uColor;\n"
98    "}",
99
100    // draw triangles with vertex ID and instance buffer
101    "#include <metal_stdlib>\n"
102    "#include <simd/simd.h>\n"
103    "using namespace metal;\n"
104    "\n"
105    "typedef struct {\n"
106    "    float2 position [[attribute(0)]];\n"
107    "    float2 dims [[attribute(1)]];\n"
108    "    float4 color [[attribute(2)]];\n"
109    "} InstanceInput;\n"
110    "\n"
111    "typedef struct {\n"
112    "    float4 position [[position]];\n"
113    "    float4 color;\n"
114    "} VertexOutput;\n"
115    "\n"
116    "vertex VertexOutput vertexMain(InstanceInput in [[stage_in]],\n"
117    "                               uint vertexID [[vertex_id]]) {\n"
118    "    VertexOutput out;\n"
119    "    float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
120    "    out.position.xy = position * in.dims + in.position;\n"
121    "    out.position.zw = float2(0.0, 1.0);\n"
122    "    out.color = in.color;"
123    "    return out;\n"
124    "}\n"
125    "\n"
126    "fragment float4 fragmentMain(VertexOutput in [[stage_in]]) {\n"
127    "    return in.color;\n"
128    "}",
129};
130
131static constexpr NSString* kTestingOnlyShaderLabels[]  = {
132    @"Clear viewport to blue",
133    @"Clear rect with uniforms",
134    @"Draw triangles with uniform color",
135    @"Draw triangles with instance buffer"
136};
137
138static inline MTLVertexFormat attribute_type_to_mtlformat(VertexAttribType type) {
139    switch (type) {
140        case VertexAttribType::kFloat:
141            return MTLVertexFormatFloat;
142        case VertexAttribType::kFloat2:
143            return MTLVertexFormatFloat2;
144        case VertexAttribType::kFloat3:
145            return MTLVertexFormatFloat3;
146        case VertexAttribType::kFloat4:
147            return MTLVertexFormatFloat4;
148        case VertexAttribType::kHalf:
149            if (@available(macOS 10.13, iOS 11.0, *)) {
150                return MTLVertexFormatHalf;
151            } else {
152                return MTLVertexFormatInvalid;
153            }
154        case VertexAttribType::kHalf2:
155            return MTLVertexFormatHalf2;
156        case VertexAttribType::kHalf4:
157            return MTLVertexFormatHalf4;
158        case VertexAttribType::kInt2:
159            return MTLVertexFormatInt2;
160        case VertexAttribType::kInt3:
161            return MTLVertexFormatInt3;
162        case VertexAttribType::kInt4:
163            return MTLVertexFormatInt4;
164        case VertexAttribType::kByte:
165            if (@available(macOS 10.13, iOS 11.0, *)) {
166                return MTLVertexFormatChar;
167            } else {
168                return MTLVertexFormatInvalid;
169            }
170        case VertexAttribType::kByte2:
171            return MTLVertexFormatChar2;
172        case VertexAttribType::kByte4:
173            return MTLVertexFormatChar4;
174        case VertexAttribType::kUByte:
175            if (@available(macOS 10.13, iOS 11.0, *)) {
176                return MTLVertexFormatUChar;
177            } else {
178                return MTLVertexFormatInvalid;
179            }
180        case VertexAttribType::kUByte2:
181            return MTLVertexFormatUChar2;
182        case VertexAttribType::kUByte4:
183            return MTLVertexFormatUChar4;
184        case VertexAttribType::kUByte_norm:
185            if (@available(macOS 10.13, iOS 11.0, *)) {
186                return MTLVertexFormatUCharNormalized;
187            } else {
188                return MTLVertexFormatInvalid;
189            }
190        case VertexAttribType::kUByte4_norm:
191            return MTLVertexFormatUChar4Normalized;
192        case VertexAttribType::kShort2:
193            return MTLVertexFormatShort2;
194        case VertexAttribType::kShort4:
195            return MTLVertexFormatShort4;
196        case VertexAttribType::kUShort2:
197            return MTLVertexFormatUShort2;
198        case VertexAttribType::kUShort2_norm:
199            return MTLVertexFormatUShort2Normalized;
200        case VertexAttribType::kInt:
201            return MTLVertexFormatInt;
202        case VertexAttribType::kUInt:
203            return MTLVertexFormatUInt;
204        case VertexAttribType::kUShort_norm:
205            if (@available(macOS 10.13, iOS 11.0, *)) {
206                return MTLVertexFormatUShortNormalized;
207            } else {
208                return MTLVertexFormatInvalid;
209            }
210        case VertexAttribType::kUShort4_norm:
211            return MTLVertexFormatUShort4Normalized;
212    }
213    SK_ABORT("Unknown vertex attribute type");
214}
215
216static MTLVertexDescriptor* create_vertex_descriptor(const GraphicsPipelineDesc& desc) {
217    auto vertexDescriptor = [[MTLVertexDescriptor alloc] init];
218    int attributeIndex = 0;
219
220    int vertexAttributeCount = desc.numVertexAttributes();
221    size_t vertexAttributeOffset = 0;
222    for (const auto& attribute : desc.vertexAttributes()) {
223        MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex];
224        MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType());
225        SkASSERT(MTLVertexFormatInvalid != format);
226        mtlAttribute.format = format;
227        mtlAttribute.offset = vertexAttributeOffset;
228        mtlAttribute.bufferIndex = GraphicsPipeline::kVertexBufferIndex;
229
230        vertexAttributeOffset += attribute.sizeAlign4();
231        attributeIndex++;
232    }
233    SkASSERT(vertexAttributeOffset == desc.vertexStride());
234
235    if (vertexAttributeCount) {
236        MTLVertexBufferLayoutDescriptor* vertexBufferLayout =
237                vertexDescriptor.layouts[GraphicsPipeline::kVertexBufferIndex];
238        vertexBufferLayout.stepFunction = MTLVertexStepFunctionPerVertex;
239        vertexBufferLayout.stepRate = 1;
240        vertexBufferLayout.stride = vertexAttributeOffset;
241    }
242
243    int instanceAttributeCount = desc.numInstanceAttributes();
244    size_t instanceAttributeOffset = 0;
245    for (const auto& attribute : desc.instanceAttributes()) {
246        MTLVertexAttributeDescriptor* mtlAttribute = vertexDescriptor.attributes[attributeIndex];
247        MTLVertexFormat format = attribute_type_to_mtlformat(attribute.cpuType());
248        SkASSERT(MTLVertexFormatInvalid != format);
249        mtlAttribute.format = format;
250        mtlAttribute.offset = instanceAttributeOffset;
251        mtlAttribute.bufferIndex = GraphicsPipeline::kInstanceBufferIndex;
252
253        instanceAttributeOffset += attribute.sizeAlign4();
254        attributeIndex++;
255    }
256    SkASSERT(instanceAttributeOffset == desc.instanceStride());
257
258    if (instanceAttributeCount) {
259        MTLVertexBufferLayoutDescriptor* instanceBufferLayout =
260                vertexDescriptor.layouts[GraphicsPipeline::kInstanceBufferIndex];
261        instanceBufferLayout.stepFunction = MTLVertexStepFunctionPerInstance;
262        instanceBufferLayout.stepRate = 1;
263        instanceBufferLayout.stride = instanceAttributeOffset;
264    }
265    return vertexDescriptor;
266}
267
268sk_sp<GraphicsPipeline> GraphicsPipeline::Make(const Gpu* gpu,
269                                               const skgpu::GraphicsPipelineDesc& desc) {
270    sk_cfp<MTLRenderPipelineDescriptor*> psoDescriptor([[MTLRenderPipelineDescriptor alloc] init]);
271
272    // Temp pipeline for now that just fills the viewport with blue
273    int shaderIndex = desc.testingOnlyShaderIndex();
274    SkSL::String shaderText;
275    shaderText.append(kTestingOnlyShaders[shaderIndex]);
276
277    auto metallib = CompileShaderLibrary(gpu, shaderText);
278    if (!metallib) {
279        return nullptr;
280    }
281
282    (*psoDescriptor).label = kTestingOnlyShaderLabels[shaderIndex];
283
284    (*psoDescriptor).vertexFunction =
285            [*metallib newFunctionWithName: @"vertexMain"];
286    (*psoDescriptor).fragmentFunction =
287            [*metallib newFunctionWithName: @"fragmentMain"];
288
289    // TODO: I *think* this gets cleaned up by the pipelineDescriptor?
290    (*psoDescriptor).vertexDescriptor = create_vertex_descriptor(desc);
291
292    // TODO: I *think* this gets cleaned up by the pipelineDescriptor as well?
293    auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init];
294
295    mtlColorAttachment.pixelFormat = MTLPixelFormatRGBA8Unorm;
296    mtlColorAttachment.blendingEnabled = FALSE;
297    mtlColorAttachment.writeMask = MTLColorWriteMaskAll;
298
299    (*psoDescriptor).colorAttachments[0] = mtlColorAttachment;
300    (*psoDescriptor).sampleCount = 1;
301
302    NSError* error;
303    sk_cfp<id<MTLRenderPipelineState>> pso(
304            [gpu->device() newRenderPipelineStateWithDescriptor:psoDescriptor.get()
305                                                          error:&error]);
306    if (!pso) {
307        SkDebugf("Pipeline creation failure\n");
308        SkDebugf("Errors:\n%s", error.debugDescription.UTF8String);
309        return nullptr;
310    }
311    return sk_sp<GraphicsPipeline>(new GraphicsPipeline(std::move(pso), desc.vertexStride(),
312                                                        desc.instanceStride()));
313}
314
315} // namespace skgpu::mtl
316