fbe246079846ea2654a89da73d1bdba11c20e6f1
[WebKit-https.git] / Source / WebCore / platform / graphics / gpu / cocoa / GPURenderPipelineMetal.mm
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "GPURenderPipeline.h"
28
29 #if ENABLE(WEBGPU)
30
31 #import "GPUDevice.h"
32 #import "GPULimits.h"
33 #import "GPUUtils.h"
34 #import "Logging.h"
35 #import "WHLSLPrepare.h"
36 #import "WHLSLVertexBufferIndexCalculator.h"
37 #import <Metal/Metal.h>
38 #import <wtf/BlockObjCExceptions.h>
39 #import <wtf/OptionSet.h>
40 #import <wtf/Optional.h>
41
42 namespace WebCore {
43
44 static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const char* const functionName, const GPUDepthStencilStateDescriptor& descriptor, const GPUDevice& device)
45 {
46 #if LOG_DISABLED
47     UNUSED_PARAM(functionName);
48 #endif
49     RetainPtr<MTLDepthStencilDescriptor> mtlDescriptor;
50
51     BEGIN_BLOCK_OBJC_EXCEPTIONS;
52
53     mtlDescriptor = adoptNS([MTLDepthStencilDescriptor new]);
54
55     END_BLOCK_OBJC_EXCEPTIONS;
56
57     if (!mtlDescriptor) {
58         LOG(WebGPU, "%s: Unable to create MTLDepthStencilDescriptor!", functionName);
59         return nullptr;
60     }
61
62     auto mtlDepthCompare = static_cast<MTLCompareFunction>(platformCompareFunctionForGPUCompareFunction(descriptor.depthCompare));
63     [mtlDescriptor setDepthCompareFunction:mtlDepthCompare];
64     [mtlDescriptor setDepthWriteEnabled:descriptor.depthWriteEnabled];
65
66     // FIXME: Implement back/frontFaceStencil.
67
68     RetainPtr<MTLDepthStencilState> state;
69
70     BEGIN_BLOCK_OBJC_EXCEPTIONS;
71
72     state = adoptNS([device.platformDevice() newDepthStencilStateWithDescriptor:mtlDescriptor.get()]);
73
74     END_BLOCK_OBJC_EXCEPTIONS;
75
76     if (!state) {
77         LOG(WebGPU, "%s: Error creating MTLDepthStencilState!", functionName);
78         return nullptr;
79     }
80
81     return state;
82 }
83
84 static WHLSL::VertexFormat convertVertexFormat(GPUVertexFormat vertexFormat)
85 {
86     switch (vertexFormat) {
87     case GPUVertexFormat::Float4:
88         return WHLSL::VertexFormat::FloatR32G32B32A32;
89     case GPUVertexFormat::Float3:
90         return WHLSL::VertexFormat::FloatR32G32B32;
91     case GPUVertexFormat::Float2:
92         return WHLSL::VertexFormat::FloatR32G32;
93     default:
94         ASSERT(vertexFormat == GPUVertexFormat::Float);
95         return WHLSL::VertexFormat::FloatR32;
96     }
97 }
98
99 static OptionSet<WHLSL::ShaderStage> convertShaderStageFlags(GPUShaderStageFlags flags)
100 {
101     OptionSet<WHLSL::ShaderStage> result;
102     if (flags & GPUShaderStageBit::Flags::Vertex)
103         result.add(WHLSL::ShaderStage::Vertex);
104     if (flags & GPUShaderStageBit::Flags::Fragment)
105         result.add(WHLSL::ShaderStage::Fragment);
106     if (flags & GPUShaderStageBit::Flags::Compute)
107         result.add(WHLSL::ShaderStage::Compute);
108     return result;
109 }
110
111 static Optional<WHLSL::BindingType> convertBindingType(GPUBindingType type)
112 {
113     switch (type) {
114     case GPUBindingType::UniformBuffer:
115         return WHLSL::BindingType::UniformBuffer;
116     case GPUBindingType::Sampler:
117         return WHLSL::BindingType::Sampler;
118     case GPUBindingType::SampledTexture:
119         return WHLSL::BindingType::Texture;
120     case GPUBindingType::StorageBuffer:
121         return WHLSL::BindingType::StorageBuffer;
122     default:
123         return WTF::nullopt;
124     }
125 }
126
127 static Optional<WHLSL::TextureFormat> convertTextureFormat(GPUTextureFormat format)
128 {
129     switch (format) {
130     case GPUTextureFormat::Rgba8unorm:
131         return WHLSL::TextureFormat::RGBA8Unorm;
132     case GPUTextureFormat::Rgba8uint:
133         return WHLSL::TextureFormat::RGBA8Uint;
134     case GPUTextureFormat::Bgra8unorm:
135         return WHLSL::TextureFormat::BGRA8Unorm;
136     case GPUTextureFormat::Depth32floatStencil8:
137         return WTF::nullopt; // FIXME: Figure out what to do with this.
138     case GPUTextureFormat::Bgra8unormSRGB:
139         return WHLSL::TextureFormat::BGRA8UnormSrgb;
140     case GPUTextureFormat::Rgba16float:
141         return WHLSL::TextureFormat::RGBA16Float;
142     default:
143         return WTF::nullopt;
144     }
145 }
146
147 static Optional<WHLSL::Layout> convertLayout(const GPUPipelineLayout& layout)
148 {
149     WHLSL::Layout result;
150     if (layout.bindGroupLayouts().size() > std::numeric_limits<unsigned>::max())
151         return WTF::nullopt;
152     for (size_t i = 0; i < layout.bindGroupLayouts().size(); ++i) {
153         const auto& bindGroupLayout = layout.bindGroupLayouts()[i];
154         WHLSL::BindGroup bindGroup;
155         bindGroup.name = static_cast<unsigned>(i);
156         for (const auto& keyValuePair : bindGroupLayout->bindingsMap()) {
157             const auto& gpuBindGroupLayoutBinding = keyValuePair.value;
158             WHLSL::Binding binding;
159             binding.visibility = convertShaderStageFlags(gpuBindGroupLayoutBinding.visibility);
160             if (auto bindingType = convertBindingType(gpuBindGroupLayoutBinding.type))
161                 binding.bindingType = *bindingType;
162             else
163                 return WTF::nullopt;
164             if (gpuBindGroupLayoutBinding.binding > std::numeric_limits<unsigned>::max())
165                 return WTF::nullopt;
166             binding.name = static_cast<unsigned>(gpuBindGroupLayoutBinding.binding);
167             bindGroup.bindings.append(WTFMove(binding));
168         }
169         result.append(WTFMove(bindGroup));
170     }
171     return result;
172 }
173
174 static Optional<WHLSL::RenderPipelineDescriptor> convertRenderPipelineDescriptor(const GPURenderPipelineDescriptor& descriptor)
175 {
176     WHLSL::RenderPipelineDescriptor whlslDescriptor;
177     if (descriptor.inputState.attributes.size() > std::numeric_limits<unsigned>::max())
178         return WTF::nullopt;
179     if (descriptor.colorStates.size() > std::numeric_limits<unsigned>::max())
180         return WTF::nullopt;
181
182     for (size_t i = 0; i < descriptor.inputState.attributes.size(); ++i)
183         whlslDescriptor.vertexAttributes.append({ convertVertexFormat(descriptor.inputState.attributes[i].format), static_cast<unsigned>(i) });
184
185     for (size_t i = 0; i < descriptor.colorStates.size(); ++i) {
186         if (auto format = convertTextureFormat(descriptor.colorStates[i].format))
187             whlslDescriptor.attachmentsStateDescriptor.attachmentDescriptors.append({*format, static_cast<unsigned>(i)});
188         else
189             return WTF::nullopt;
190     }
191
192     // FIXME: depthStencilAttachmentDescriptor isn't implemented yet.
193
194     if (descriptor.layout) {
195         if (auto layout = convertLayout(*descriptor.layout))
196             whlslDescriptor.layout = WTFMove(*layout);
197         else
198             return WTF::nullopt;
199     }
200     whlslDescriptor.vertexEntryPointName = descriptor.vertexStage.entryPoint;
201     whlslDescriptor.fragmentEntryPointName = descriptor.fragmentStage.entryPoint;
202     return whlslDescriptor;
203 }
204
205 static bool trySetMetalFunctionsForPipelineDescriptor(const char* const functionName, MTLLibrary *vertexMetalLibrary, MTLLibrary *fragmentMetalLibrary, MTLRenderPipelineDescriptor *mtlDescriptor, const String& vertexEntryPointName, const String& fragmentEntryPointName)
206 {
207 #if LOG_DISABLED
208     UNUSED_PARAM(functionName);
209 #endif
210
211     {
212         BEGIN_BLOCK_OBJC_EXCEPTIONS;
213
214         // Metal requires a vertex shader in all render pipelines.
215         if (!vertexMetalLibrary) {
216             LOG(WebGPU, "%s: MTLLibrary for vertex stage does not exist!", functionName);
217             return false;
218         }
219
220         auto function = adoptNS([vertexMetalLibrary newFunctionWithName:vertexEntryPointName]);
221         if (!function) {
222             LOG(WebGPU, "%s: Cannot create vertex MTLFunction \"%s\"!", functionName, vertexEntryPointName.utf8().data());
223             return false;
224         }
225
226         [mtlDescriptor setVertexFunction:function.get()];
227
228         END_BLOCK_OBJC_EXCEPTIONS;
229     }
230
231     {
232         BEGIN_BLOCK_OBJC_EXCEPTIONS;
233
234         // However, fragment shaders are optional.
235         if (!fragmentMetalLibrary)
236             return true;
237
238         auto function = adoptNS([fragmentMetalLibrary newFunctionWithName:fragmentEntryPointName]);
239
240         if (!function) {
241             LOG(WebGPU, "%s: Cannot create fragment MTLFunction \"%s\"!", functionName, fragmentEntryPointName.utf8().data());
242             return false;
243         }
244
245         [mtlDescriptor setFragmentFunction:function.get()];
246         return true;
247
248         END_BLOCK_OBJC_EXCEPTIONS;
249     }
250
251     return false;
252 }
253
254 static bool trySetWHLSLFunctionsForPipelineDescriptor(const char* const functionName, MTLRenderPipelineDescriptor *mtlDescriptor, const GPURenderPipelineDescriptor& descriptor, String whlslSource, const GPUDevice& device)
255 {
256     auto whlslDescriptor = convertRenderPipelineDescriptor(descriptor);
257     if (!whlslDescriptor)
258         return false;
259
260     auto result = WHLSL::prepare(whlslSource, *whlslDescriptor);
261     if (!result)
262         return false;
263
264     NSError *error = nil;
265     auto library = adoptNS([device.platformDevice() newLibraryWithSource:result->metalSource options:nil error:&error]);
266     ASSERT(library);
267     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=195771 Once we zero-fill variables, there should be no warnings, so we should be able to ASSERT(!error) here.
268
269     return trySetMetalFunctionsForPipelineDescriptor(functionName, library.get(), library.get(), mtlDescriptor, result->mangledVertexEntryPointName, result->mangledFragmentEntryPointName);
270 }
271
272 static bool trySetFunctionsForPipelineDescriptor(const char* const functionName, MTLRenderPipelineDescriptor *mtlDescriptor, const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device)
273 {
274     const auto& vertexStage = descriptor.vertexStage;
275     const auto& fragmentStage = descriptor.fragmentStage;
276
277     if (vertexStage.module.ptr() == fragmentStage.module.ptr()) {
278         // FIXME: https://bugs.webkit.org/show_bug.cgi?id=195446 Allow WHLSL shaders to come from different programs.
279         const auto& whlslSource = vertexStage.module->whlslSource();
280         if (!whlslSource.isNull())
281             return trySetWHLSLFunctionsForPipelineDescriptor(functionName, mtlDescriptor, descriptor, whlslSource, device);
282     }
283
284     auto vertexLibrary = vertexStage.module->platformShaderModule();
285     MTLLibrary *fragmentLibrary = nil;
286     if (!fragmentStage.entryPoint.isNull())
287         fragmentLibrary = fragmentStage.module->platformShaderModule();
288
289     return trySetMetalFunctionsForPipelineDescriptor(functionName, vertexLibrary, fragmentLibrary, mtlDescriptor, vertexStage.entryPoint, fragmentStage.entryPoint);
290 }
291
292 static MTLVertexFormat mtlVertexFormatForGPUVertexFormat(GPUVertexFormat format)
293 {
294     switch (format) {
295     case GPUVertexFormat::Float:
296         return MTLVertexFormatFloat;
297     case GPUVertexFormat::Float2:
298         return MTLVertexFormatFloat2;
299     case GPUVertexFormat::Float3:
300         return MTLVertexFormatFloat3;
301     case GPUVertexFormat::Float4:
302         return MTLVertexFormatFloat4;
303     }
304
305     ASSERT_NOT_REACHED();
306 }
307
308 static MTLVertexStepFunction mtlStepFunctionForGPUInputStepMode(GPUInputStepMode mode)
309 {
310     switch (mode) {
311     case GPUInputStepMode::Vertex:
312         return MTLVertexStepFunctionPerVertex;
313     case GPUInputStepMode::Instance:
314         return MTLVertexStepFunctionPerInstance;
315     }
316
317     ASSERT_NOT_REACHED();
318 }
319
320 static bool trySetInputStateForPipelineDescriptor(const char* const functionName, MTLRenderPipelineDescriptor *mtlDescriptor, const GPUInputStateDescriptor& descriptor)
321 {
322 #if LOG_DISABLED
323     UNUSED_PARAM(functionName);
324 #endif
325     auto mtlVertexDescriptor = adoptNS([MTLVertexDescriptor new]);
326
327     const auto& attributes = descriptor.attributes;
328
329     auto attributeArray = retainPtr(mtlVertexDescriptor.get().attributes);
330
331     for (size_t i = 0; i < attributes.size(); ++i) {
332         auto location = attributes[i].shaderLocation;
333         // Maximum number of vertex attributes to be supported by Web GPU.
334         if (location >= 16) {
335             LOG(WebGPU, "%s: Invalid shaderLocation %u for vertex attribute!", functionName, location);
336             return false;
337         }
338         if (attributes[i].inputSlot >= maxVertexBuffers) {
339             LOG(WebGPU, "%s: Invalid inputSlot %u for vertex attribute %u!", functionName, attributes[i].inputSlot, location);
340             return false;
341         }
342
343         auto mtlAttributeDesc = retainPtr([attributeArray objectAtIndexedSubscript:location]);
344         [mtlAttributeDesc setFormat:mtlVertexFormatForGPUVertexFormat(attributes[i].format)];
345         [mtlAttributeDesc setOffset:attributes[i].offset]; // FIXME: After adding more vertex formats, ensure offset < buffer's stride + format's data size.
346         [mtlAttributeDesc setBufferIndex:WHLSL::Metal::calculateVertexBufferIndex(attributes[i].inputSlot)];
347     }
348
349     const auto& inputs = descriptor.inputs;
350
351     auto layoutArray = retainPtr(mtlVertexDescriptor.get().layouts);
352
353     for (size_t j = 0; j < inputs.size(); ++j) {
354         auto slot = inputs[j].inputSlot;
355         if (slot >= maxVertexBuffers) {
356             LOG(WebGPU, "%s: Invalid inputSlot %d for vertex buffer!", functionName, slot);
357             return false;
358         }
359
360         auto convertedSlot = WHLSL::Metal::calculateVertexBufferIndex(slot);
361         auto mtlLayoutDesc = retainPtr([layoutArray objectAtIndexedSubscript:convertedSlot]);
362         [mtlLayoutDesc setStepFunction:mtlStepFunctionForGPUInputStepMode(inputs[j].stepMode)];
363         [mtlLayoutDesc setStride:inputs[j].stride];
364     }
365
366     [mtlDescriptor setVertexDescriptor:mtlVertexDescriptor.get()];
367
368     return true;
369 }
370
371 static MTLColorWriteMask mtlColorWriteMaskForGPUColorWriteFlags(GPUColorWriteFlags flags)
372 {
373     if (flags == static_cast<GPUColorWriteFlags>(GPUColorWriteBits::Flags::All))
374         return MTLColorWriteMaskAll;
375
376     auto options = OptionSet<GPUColorWriteBits::Flags>::fromRaw(flags);
377
378     MTLColorWriteMask mask = MTLColorWriteMaskNone;
379     if (options & GPUColorWriteBits::Flags::Red)
380         mask |= MTLColorWriteMaskRed;
381     if (options & GPUColorWriteBits::Flags::Green)
382         mask |= MTLColorWriteMaskGreen;
383     if (options & GPUColorWriteBits::Flags::Blue)
384         mask |= MTLColorWriteMaskBlue;
385     if (options & GPUColorWriteBits::Flags::Alpha)
386         mask |= MTLColorWriteMaskAlpha;
387
388     return mask;
389 }
390
391 static MTLBlendOperation mtlBlendOperationForGPUBlendOperation(GPUBlendOperation op)
392 {
393     switch (op) {
394     case GPUBlendOperation::Add:
395         return MTLBlendOperationAdd;
396     case GPUBlendOperation::Subtract:
397         return MTLBlendOperationSubtract;
398     case GPUBlendOperation::ReverseSubtract:
399         return MTLBlendOperationReverseSubtract;
400     case GPUBlendOperation::Min:
401         return MTLBlendOperationMin;
402     case GPUBlendOperation::Max:
403         return MTLBlendOperationMax;
404     }
405
406     ASSERT_NOT_REACHED();
407 }
408
409 static MTLBlendFactor mtlBlendFactorForGPUBlendFactor(GPUBlendFactor factor)
410 {
411     switch (factor) {
412     case GPUBlendFactor::Zero:
413         return MTLBlendFactorZero;
414     case GPUBlendFactor::One:
415         return MTLBlendFactorOne;
416     case GPUBlendFactor::SrcColor:
417         return MTLBlendFactorSourceColor;
418     case GPUBlendFactor::OneMinusSrcColor:
419         return MTLBlendFactorOneMinusSourceColor;
420     case GPUBlendFactor::SrcAlpha:
421         return MTLBlendFactorSourceAlpha;
422     case GPUBlendFactor::OneMinusSrcAlpha:
423         return MTLBlendFactorOneMinusSourceAlpha;
424     case GPUBlendFactor::DstColor:
425         return MTLBlendFactorDestinationColor;
426     case GPUBlendFactor::OneMinusDstColor:
427         return MTLBlendFactorOneMinusDestinationColor;
428     case GPUBlendFactor::DstAlpha:
429         return MTLBlendFactorDestinationAlpha;
430     case GPUBlendFactor::OneMinusDstAlpha:
431         return MTLBlendFactorOneMinusDestinationAlpha;
432     case GPUBlendFactor::SrcAlphaSaturated:
433         return MTLBlendFactorSourceAlpha;
434     case GPUBlendFactor::BlendColor:
435         return MTLBlendFactorBlendColor;
436     case GPUBlendFactor::OneMinusBlendColor:
437         return MTLBlendFactorOneMinusBlendColor;
438     }
439
440     ASSERT_NOT_REACHED();
441 }
442
443 static void setColorStatesForColorAttachmentArray(MTLRenderPipelineColorAttachmentDescriptorArray* array, const Vector<GPUColorStateDescriptor>& colorStates)
444 {
445     for (unsigned i = 0; i < colorStates.size(); ++i) {
446         auto& state = colorStates[i];
447         auto descriptor = retainPtr([array objectAtIndexedSubscript:i]);
448         [descriptor setPixelFormat:static_cast<MTLPixelFormat>(platformTextureFormatForGPUTextureFormat(state.format))];
449         [descriptor setWriteMask:mtlColorWriteMaskForGPUColorWriteFlags(state.writeMask)];
450         [descriptor setBlendingEnabled:YES];
451         [descriptor setAlphaBlendOperation:mtlBlendOperationForGPUBlendOperation(state.alphaBlend.operation)];
452         [descriptor setRgbBlendOperation:mtlBlendOperationForGPUBlendOperation(state.colorBlend.operation)];
453         [descriptor setDestinationAlphaBlendFactor:mtlBlendFactorForGPUBlendFactor(state.alphaBlend.dstFactor)];
454         [descriptor setDestinationRGBBlendFactor:mtlBlendFactorForGPUBlendFactor(state.colorBlend.dstFactor)];
455         [descriptor setSourceAlphaBlendFactor:mtlBlendFactorForGPUBlendFactor(state.alphaBlend.srcFactor)];
456         [descriptor setSourceRGBBlendFactor:mtlBlendFactorForGPUBlendFactor(state.colorBlend.srcFactor)];
457     }
458 }
459
460 static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const char* const functionName, const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device)
461 {
462     RetainPtr<MTLRenderPipelineDescriptor> mtlDescriptor;
463
464     BEGIN_BLOCK_OBJC_EXCEPTIONS;
465
466     mtlDescriptor = adoptNS([MTLRenderPipelineDescriptor new]);
467
468     END_BLOCK_OBJC_EXCEPTIONS;
469
470     if (!mtlDescriptor) {
471         LOG(WebGPU, "%s: Error creating MTLDescriptor!", functionName);
472         return nullptr;
473     }
474
475     bool didSetFunctions = false, didSetInputState = false;
476
477     BEGIN_BLOCK_OBJC_EXCEPTIONS;
478
479     didSetFunctions = trySetFunctionsForPipelineDescriptor(functionName, mtlDescriptor.get(), descriptor, device);
480     didSetInputState = trySetInputStateForPipelineDescriptor(functionName, mtlDescriptor.get(), descriptor.inputState);
481     setColorStatesForColorAttachmentArray(mtlDescriptor.get().colorAttachments, descriptor.colorStates);
482
483     END_BLOCK_OBJC_EXCEPTIONS;
484
485     if (!didSetFunctions || !didSetInputState)
486         return nullptr;
487
488     RetainPtr<MTLRenderPipelineState> pipeline;
489
490     BEGIN_BLOCK_OBJC_EXCEPTIONS;
491
492     NSError *error = [NSError errorWithDomain:@"com.apple.WebKit.GPU" code:1 userInfo:nil];
493     pipeline = adoptNS([device.platformDevice() newRenderPipelineStateWithDescriptor:mtlDescriptor.get() error:&error]);
494     if (!pipeline)
495         LOG(WebGPU, "%s: %s!", functionName, error.localizedDescription.UTF8String);
496
497     END_BLOCK_OBJC_EXCEPTIONS;
498
499     return pipeline;
500 }
501
502 RefPtr<GPURenderPipeline> GPURenderPipeline::tryCreate(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor)
503 {
504     const char* const functionName = "GPURenderPipeline::create()";
505
506     if (!device.platformDevice()) {
507         LOG(WebGPU, "%s: Invalid GPUDevice!", functionName);
508         return nullptr;
509     }
510
511     RetainPtr<MTLDepthStencilState> depthStencil;
512
513     if (descriptor.depthStencilState && !(depthStencil = tryCreateMtlDepthStencilState(functionName, *descriptor.depthStencilState, device)))
514         return nullptr;
515
516     auto pipeline = tryCreateMtlRenderPipelineState(functionName, descriptor, device);
517     if (!pipeline)
518         return nullptr;
519
520     return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology));
521 }
522
523 GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology)
524     : m_depthStencilState(WTFMove(depthStencil))
525     , m_platformRenderPipeline(WTFMove(pipeline))
526     , m_primitiveTopology(topology)
527 {
528 }
529
530 } // namespace WebCore
531
532 #endif // ENABLE(WEBGPU)