9f4d073a439221d1cb440a9ec4627c92f7d8e56f
[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 "GPUErrorScopes.h"
33 #import "GPULimits.h"
34 #import "GPUPipelineMetalConvertLayout.h"
35 #import "GPURenderPipelineDescriptor.h"
36 #import "GPUUtils.h"
37 #import "WHLSLPrepare.h"
38 #import "WHLSLVertexBufferIndexCalculator.h"
39 #import <Metal/Metal.h>
40 #import <wtf/BlockObjCExceptions.h>
41 #import <wtf/CheckedArithmetic.h>
42 #import <wtf/DataLog.h>
43 #import <wtf/HashSet.h>
44 #import <wtf/MonotonicTime.h>
45 #import <wtf/OptionSet.h>
46 #import <wtf/Optional.h>
47 #import <wtf/text/StringConcatenate.h>
48
49 namespace WebCore {
50
51 static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const GPUDepthStencilStateDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
52 {
53     RetainPtr<MTLDepthStencilDescriptor> mtlDescriptor;
54
55     BEGIN_BLOCK_OBJC_EXCEPTIONS;
56
57     mtlDescriptor = adoptNS([MTLDepthStencilDescriptor new]);
58
59     END_BLOCK_OBJC_EXCEPTIONS;
60
61     if (!mtlDescriptor) {
62         errorScopes.generatePrefixedError("Unable to create MTLDepthStencilDescriptor!");
63         return nullptr;
64     }
65
66     auto mtlDepthCompare = static_cast<MTLCompareFunction>(platformCompareFunctionForGPUCompareFunction(descriptor.depthCompare));
67     [mtlDescriptor setDepthCompareFunction:mtlDepthCompare];
68     [mtlDescriptor setDepthWriteEnabled:descriptor.depthWriteEnabled];
69
70     // FIXME: Implement back/frontFaceStencil.
71
72     RetainPtr<MTLDepthStencilState> state;
73
74     BEGIN_BLOCK_OBJC_EXCEPTIONS;
75
76     state = adoptNS([device.platformDevice() newDepthStencilStateWithDescriptor:mtlDescriptor.get()]);
77
78     END_BLOCK_OBJC_EXCEPTIONS;
79
80     if (!state) {
81         errorScopes.generatePrefixedError("Error creating MTLDepthStencilState!");
82         return nullptr;
83     }
84
85     return state;
86 }
87
88 static WHLSL::VertexFormat convertVertexFormat(GPUVertexFormat vertexFormat)
89 {
90     switch (vertexFormat) {
91     case GPUVertexFormat::Float4:
92         return WHLSL::VertexFormat::FloatR32G32B32A32;
93     case GPUVertexFormat::Float3:
94         return WHLSL::VertexFormat::FloatR32G32B32;
95     case GPUVertexFormat::Float2:
96         return WHLSL::VertexFormat::FloatR32G32;
97     default:
98         ASSERT(vertexFormat == GPUVertexFormat::Float);
99         return WHLSL::VertexFormat::FloatR32;
100     }
101 }
102
103 static Optional<WHLSL::TextureFormat> convertTextureFormat(GPUTextureFormat format)
104 {
105     switch (format) {
106     case GPUTextureFormat::Rgba8unorm:
107         return WHLSL::TextureFormat::RGBA8Unorm;
108     case GPUTextureFormat::Bgra8unorm:
109         return WHLSL::TextureFormat::BGRA8Unorm;
110     case GPUTextureFormat::Depth32floatStencil8:
111         return WTF::nullopt; // FIXME: Figure out what to do with this.
112     case GPUTextureFormat::Bgra8unormSRGB:
113         return WHLSL::TextureFormat::BGRA8UnormSrgb;
114     case GPUTextureFormat::Rgba16float:
115         return WHLSL::TextureFormat::RGBA16Float;
116     default:
117         return WTF::nullopt;
118     }
119 }
120
121 static MTLVertexFormat mtlVertexFormatForGPUVertexFormat(GPUVertexFormat format)
122 {
123     switch (format) {
124     case GPUVertexFormat::Float:
125         return MTLVertexFormatFloat;
126     case GPUVertexFormat::Float2:
127         return MTLVertexFormatFloat2;
128     case GPUVertexFormat::Float3:
129         return MTLVertexFormatFloat3;
130     case GPUVertexFormat::Float4:
131         return MTLVertexFormatFloat4;
132     }
133
134     ASSERT_NOT_REACHED();
135 }
136
137 static MTLVertexStepFunction mtlStepFunctionForGPUInputStepMode(GPUInputStepMode mode)
138 {
139     switch (mode) {
140     case GPUInputStepMode::Vertex:
141         return MTLVertexStepFunctionPerVertex;
142     case GPUInputStepMode::Instance:
143         return MTLVertexStepFunctionPerInstance;
144     }
145
146     ASSERT_NOT_REACHED();
147 }
148
149 // FIXME: Move this into GPULimits when that is implemented properly.
150 constexpr unsigned maxVertexAttributes = 16;
151
152 static bool trySetVertexInput(const GPUVertexInputDescriptor& descriptor, MTLRenderPipelineDescriptor *mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
153 {
154     const auto& buffers = descriptor.vertexBuffers;
155
156     if (buffers.size() > maxVertexBuffers) {
157         errorScopes.generatePrefixedError("Too many GPUVertexBufferDescriptors!");
158         return false;
159     }
160
161     auto mtlVertexDescriptor = adoptNS([MTLVertexDescriptor new]);
162
163     auto layoutArray = retainPtr(mtlVertexDescriptor.get().layouts);
164     auto attributeArray = retainPtr(mtlVertexDescriptor.get().attributes);
165
166     // Attribute shaderLocations must be uniquely flat-mapped to [0, {max number of vertex attributes}].
167     unsigned attributeIndex = 0;
168     HashSet<unsigned, IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> locations;
169
170     for (size_t index = 0; index < buffers.size(); ++index) {
171         if (!buffers[index])
172             continue;
173
174         const auto& attributes = buffers[index]->attributeSet;
175
176         if (attributes.size() + attributeIndex > maxVertexAttributes) {
177             errorScopes.generatePrefixedError("Too many GPUVertexAttributeDescriptors!");
178             return false;
179         }
180
181         NSUInteger inputStride = 0;
182         if (!WTF::convertSafely(buffers[index]->stride, inputStride)) {
183             errorScopes.generatePrefixedError(makeString("Stride for GPUVertexBufferDescriptor ", index, " is too large!"));
184             return false;
185         }
186
187         auto convertedBufferIndex = WHLSL::Metal::calculateVertexBufferIndex(index);
188
189         BEGIN_BLOCK_OBJC_EXCEPTIONS;
190         auto mtlLayoutDesc = retainPtr([layoutArray objectAtIndexedSubscript:convertedBufferIndex]);
191         [mtlLayoutDesc setStepFunction:mtlStepFunctionForGPUInputStepMode(buffers[index]->stepMode)];
192         [mtlLayoutDesc setStride:inputStride];
193         END_BLOCK_OBJC_EXCEPTIONS;
194
195         for (const auto& attribute : attributes) {
196             if (!locations.add(attribute.shaderLocation).isNewEntry) {
197                 errorScopes.generatePrefixedError(makeString("Duplicate shaderLocation ", attribute.shaderLocation, " for vertex attribute!"));
198                 return false;
199             }
200
201             NSUInteger offset = 0;
202             if (!WTF::convertSafely(attribute.offset, offset)) {
203                 errorScopes.generatePrefixedError(makeString("Buffer offset for vertex attribute ", attribute.shaderLocation, " is too large!"));
204                 return false;
205             }
206
207             BEGIN_BLOCK_OBJC_EXCEPTIONS;
208             auto mtlAttributeDesc = retainPtr([attributeArray objectAtIndexedSubscript:attributeIndex]);
209             [mtlAttributeDesc setFormat:mtlVertexFormatForGPUVertexFormat(attribute.format)];
210             [mtlAttributeDesc setOffset:offset];
211             [mtlAttributeDesc setBufferIndex:convertedBufferIndex];
212             END_BLOCK_OBJC_EXCEPTIONS;
213
214             if (whlslDescriptor)
215                 whlslDescriptor->vertexAttributes.append({ convertVertexFormat(attribute.format), attribute.shaderLocation, attributeIndex });
216
217             ++attributeIndex;
218         }
219     }
220
221     [mtlDescriptor setVertexDescriptor:mtlVertexDescriptor.get()];
222
223     return true;
224 }
225
226 static MTLColorWriteMask mtlColorWriteMaskForGPUColorWriteFlags(GPUColorWriteFlags flags)
227 {
228     if (flags == static_cast<GPUColorWriteFlags>(GPUColorWrite::Flags::All))
229         return MTLColorWriteMaskAll;
230
231     auto options = OptionSet<GPUColorWrite::Flags>::fromRaw(flags);
232
233     MTLColorWriteMask mask = MTLColorWriteMaskNone;
234     if (options & GPUColorWrite::Flags::Red)
235         mask |= MTLColorWriteMaskRed;
236     if (options & GPUColorWrite::Flags::Green)
237         mask |= MTLColorWriteMaskGreen;
238     if (options & GPUColorWrite::Flags::Blue)
239         mask |= MTLColorWriteMaskBlue;
240     if (options & GPUColorWrite::Flags::Alpha)
241         mask |= MTLColorWriteMaskAlpha;
242
243     return mask;
244 }
245
246 static MTLBlendOperation mtlBlendOperationForGPUBlendOperation(GPUBlendOperation op)
247 {
248     switch (op) {
249     case GPUBlendOperation::Add:
250         return MTLBlendOperationAdd;
251     case GPUBlendOperation::Subtract:
252         return MTLBlendOperationSubtract;
253     case GPUBlendOperation::ReverseSubtract:
254         return MTLBlendOperationReverseSubtract;
255     case GPUBlendOperation::Min:
256         return MTLBlendOperationMin;
257     case GPUBlendOperation::Max:
258         return MTLBlendOperationMax;
259     }
260
261     ASSERT_NOT_REACHED();
262 }
263
264 static MTLBlendFactor mtlBlendFactorForGPUBlendFactor(GPUBlendFactor factor)
265 {
266     switch (factor) {
267     case GPUBlendFactor::Zero:
268         return MTLBlendFactorZero;
269     case GPUBlendFactor::One:
270         return MTLBlendFactorOne;
271     case GPUBlendFactor::SrcColor:
272         return MTLBlendFactorSourceColor;
273     case GPUBlendFactor::OneMinusSrcColor:
274         return MTLBlendFactorOneMinusSourceColor;
275     case GPUBlendFactor::SrcAlpha:
276         return MTLBlendFactorSourceAlpha;
277     case GPUBlendFactor::OneMinusSrcAlpha:
278         return MTLBlendFactorOneMinusSourceAlpha;
279     case GPUBlendFactor::DstColor:
280         return MTLBlendFactorDestinationColor;
281     case GPUBlendFactor::OneMinusDstColor:
282         return MTLBlendFactorOneMinusDestinationColor;
283     case GPUBlendFactor::DstAlpha:
284         return MTLBlendFactorDestinationAlpha;
285     case GPUBlendFactor::OneMinusDstAlpha:
286         return MTLBlendFactorOneMinusDestinationAlpha;
287     case GPUBlendFactor::SrcAlphaSaturated:
288         return MTLBlendFactorSourceAlpha;
289     case GPUBlendFactor::BlendColor:
290         return MTLBlendFactorBlendColor;
291     case GPUBlendFactor::OneMinusBlendColor:
292         return MTLBlendFactorOneMinusBlendColor;
293     }
294
295     ASSERT_NOT_REACHED();
296 }
297
298 static bool trySetColorStates(const Vector<GPUColorStateDescriptor>& colorStates, MTLRenderPipelineColorAttachmentDescriptorArray* array, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
299 {
300     // FIXME: Replace with maximum number of color attachments per render pass from GPULimits.
301     if (colorStates.size() > 4) {
302         errorScopes.generatePrefixedError("Too many GPUColorStateDescriptors!");
303         return false;
304     }
305
306     BEGIN_BLOCK_OBJC_EXCEPTIONS;
307
308     for (unsigned i = 0; i < colorStates.size(); ++i) {
309         auto& state = colorStates[i];
310         auto descriptor = retainPtr([array objectAtIndexedSubscript:i]);
311         [descriptor setPixelFormat:static_cast<MTLPixelFormat>(platformTextureFormatForGPUTextureFormat(state.format))];
312         [descriptor setWriteMask:mtlColorWriteMaskForGPUColorWriteFlags(state.writeMask)];
313         [descriptor setBlendingEnabled:YES];
314         [descriptor setAlphaBlendOperation:mtlBlendOperationForGPUBlendOperation(state.alphaBlend.operation)];
315         [descriptor setRgbBlendOperation:mtlBlendOperationForGPUBlendOperation(state.colorBlend.operation)];
316         [descriptor setDestinationAlphaBlendFactor:mtlBlendFactorForGPUBlendFactor(state.alphaBlend.dstFactor)];
317         [descriptor setDestinationRGBBlendFactor:mtlBlendFactorForGPUBlendFactor(state.colorBlend.dstFactor)];
318         [descriptor setSourceAlphaBlendFactor:mtlBlendFactorForGPUBlendFactor(state.alphaBlend.srcFactor)];
319         [descriptor setSourceRGBBlendFactor:mtlBlendFactorForGPUBlendFactor(state.colorBlend.srcFactor)];
320
321         if (whlslDescriptor) {
322             if (auto format = convertTextureFormat(state.format))
323                 whlslDescriptor->attachmentsStateDescriptor.attachmentDescriptors.append({*format, i});
324             else {
325                 errorScopes.generatePrefixedError(makeString("Invalid GPUTextureFormat for GPUColorStateDescriptor ", i, "!"));
326                 return false;
327             }
328         }
329     }
330
331     END_BLOCK_OBJC_EXCEPTIONS;
332
333     return true;
334 }
335
336 static bool trySetMetalFunctions(MTLLibrary *vertexMetalLibrary, MTLLibrary *fragmentMetalLibrary, MTLRenderPipelineDescriptor *mtlDescriptor, const String& vertexEntryPointName, const String& fragmentEntryPointName, GPUErrorScopes& errorScopes)
337 {
338     {
339         BEGIN_BLOCK_OBJC_EXCEPTIONS;
340
341         // Metal requires a vertex shader in all render pipelines.
342         if (!vertexMetalLibrary) {
343             errorScopes.generatePrefixedError("MTLLibrary for vertex stage does not exist!");
344             return false;
345         }
346
347         auto function = adoptNS([vertexMetalLibrary newFunctionWithName:vertexEntryPointName]);
348         if (!function) {
349             errorScopes.generatePrefixedError(makeString("Cannot create vertex MTLFunction '", vertexEntryPointName, "'!"));
350             return false;
351         }
352
353         [mtlDescriptor setVertexFunction:function.get()];
354
355         END_BLOCK_OBJC_EXCEPTIONS;
356     }
357
358     {
359         BEGIN_BLOCK_OBJC_EXCEPTIONS;
360
361         // However, fragment shaders are optional.
362         if (!fragmentMetalLibrary || fragmentEntryPointName.isNull())
363             return true;
364
365         auto function = adoptNS([fragmentMetalLibrary newFunctionWithName:fragmentEntryPointName]);
366
367         if (!function) {
368             errorScopes.generatePrefixedError(makeString("Cannot create fragment MTLFunction '", fragmentEntryPointName, "'!"));
369             return false;
370         }
371
372         [mtlDescriptor setFragmentFunction:function.get()];
373         return true;
374
375         END_BLOCK_OBJC_EXCEPTIONS;
376     }
377
378     return false;
379 }
380
381 static bool trySetFunctions(const GPUProgrammableStageDescriptor& vertexStage, const Optional<GPUProgrammableStageDescriptor>& fragmentStage, const GPUDevice& device, MTLRenderPipelineDescriptor* mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
382 {
383     RetainPtr<MTLLibrary> vertexLibrary, fragmentLibrary;
384     String vertexEntryPoint, fragmentEntryPoint;
385
386     if (whlslDescriptor) {
387         ASSERT(vertexStage.module->whlslModule());
388         ASSERT(!fragmentStage || fragmentStage->module->whlslModule());
389
390         whlslDescriptor->vertexEntryPointName = vertexStage.entryPoint;
391         if (fragmentStage)
392             whlslDescriptor->fragmentEntryPointName = fragmentStage->entryPoint;
393
394         auto whlslCompileResult = WHLSL::prepare(*vertexStage.module->whlslModule(), fragmentStage ? fragmentStage->module->whlslModule() : nullptr, *whlslDescriptor);
395         if (!whlslCompileResult) {
396             errorScopes.generatePrefixedError(makeString("WHLSL compile error: ", whlslCompileResult.error()));
397             return false;
398         }
399
400         NSError *error = nil;
401
402         BEGIN_BLOCK_OBJC_EXCEPTIONS;
403         MonotonicTime startTime;
404         if (WHLSL::dumpMetalCompileTimes)
405             startTime = MonotonicTime::now();
406         // FIXME: https://webkit.org/b/200474 Add direct StringBuilder -> NSString conversion to avoid extra copy into a WTF::String
407         vertexLibrary = adoptNS([device.platformDevice() newLibraryWithSource:whlslCompileResult->metalSource.toString() options:nil error:&error]);
408         if (WHLSL::dumpMetalCompileTimes)
409             dataLogLn("Metal compile times: ", (MonotonicTime::now() - startTime).milliseconds(), " ms");
410         END_BLOCK_OBJC_EXCEPTIONS;
411
412         if (!vertexLibrary && error) {
413             errorScopes.generatePrefixedError(error.localizedDescription.UTF8String);
414 #ifndef NDEBUG
415             NSLog(@"%@", error);
416 #endif
417         }
418
419         ASSERT(vertexLibrary);
420         // 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.
421
422         fragmentLibrary = vertexLibrary;
423         vertexEntryPoint = whlslCompileResult->mangledVertexEntryPointName.toString();
424         if (fragmentStage)
425             fragmentEntryPoint = whlslCompileResult->mangledFragmentEntryPointName.toString();
426     } else {
427         vertexLibrary = vertexStage.module->platformShaderModule();
428         vertexEntryPoint = vertexStage.entryPoint;
429         if (fragmentStage) {
430             fragmentLibrary = fragmentStage->module->platformShaderModule();
431             fragmentEntryPoint = fragmentStage->entryPoint;
432         }
433     }
434
435     return trySetMetalFunctions(vertexLibrary.get(), fragmentLibrary.get(), mtlDescriptor, vertexEntryPoint, fragmentEntryPoint, errorScopes);
436 }
437
438 static RetainPtr<MTLRenderPipelineDescriptor> convertRenderPipelineDescriptor(const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
439 {
440     RetainPtr<MTLRenderPipelineDescriptor> mtlDescriptor;
441
442     BEGIN_BLOCK_OBJC_EXCEPTIONS;
443
444     mtlDescriptor = adoptNS([MTLRenderPipelineDescriptor new]);
445
446     END_BLOCK_OBJC_EXCEPTIONS;
447
448     if (!mtlDescriptor) {
449         errorScopes.generatePrefixedError("Error creating MTLDescriptor!");
450         return nullptr;
451     }
452
453     // Determine if shader source is in WHLSL or MSL.
454     const auto& vertexStage = descriptor.vertexStage;
455     const auto& fragmentStage = descriptor.fragmentStage;
456
457     if (fragmentStage && static_cast<bool>(vertexStage.module->whlslModule()) != static_cast<bool>(fragmentStage->module->whlslModule()))
458         return nullptr;
459     bool isWhlsl = vertexStage.module->whlslModule();
460
461     // Set data for the Metal pipeline descriptor (and WHLSL's, if needed).
462     Optional<WHLSL::RenderPipelineDescriptor> whlslDescriptor;
463     if (isWhlsl)
464         whlslDescriptor = WHLSL::RenderPipelineDescriptor();
465
466     if (!trySetVertexInput(descriptor.vertexInput, mtlDescriptor.get(), whlslDescriptor, errorScopes))
467         return nullptr;
468
469     if (!trySetColorStates(descriptor.colorStates, mtlDescriptor.get().colorAttachments, whlslDescriptor, errorScopes))
470         return nullptr;
471
472     if (descriptor.layout && whlslDescriptor) {
473         if (auto layout = convertLayout(*descriptor.layout))
474             whlslDescriptor->layout = WTFMove(*layout);
475         else {
476             errorScopes.generatePrefixedError("Error converting GPUPipelineLayout!");
477             return nullptr;
478         }
479     }
480
481     if (!trySetFunctions(vertexStage, fragmentStage, device, mtlDescriptor.get(), whlslDescriptor, errorScopes))
482         return nullptr;
483
484     return mtlDescriptor;
485 }
486
487 static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor, GPUErrorScopes& errorScopes)
488 {
489     if (!device.platformDevice()) {
490         errorScopes.generatePrefixedError("Invalid GPUDevice!");
491         return nullptr;
492     }
493
494     auto mtlDescriptor = convertRenderPipelineDescriptor(descriptor, device, errorScopes);
495     if (!mtlDescriptor)
496         return nullptr;
497
498     RetainPtr<MTLRenderPipelineState> pipeline;
499
500     BEGIN_BLOCK_OBJC_EXCEPTIONS;
501
502     NSError *error = nil;
503     pipeline = adoptNS([device.platformDevice() newRenderPipelineStateWithDescriptor:mtlDescriptor.get() error:&error]);
504     if (!pipeline)
505         errorScopes.generatePrefixedError(error.localizedDescription.UTF8String);
506
507     END_BLOCK_OBJC_EXCEPTIONS;
508
509     return pipeline;
510 }
511
512 RefPtr<GPURenderPipeline> GPURenderPipeline::tryCreate(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor, GPUErrorScopes& errorScopes)
513 {
514     if (!device.platformDevice()) {
515         errorScopes.generatePrefixedError("Invalid GPUDevice!");
516         return nullptr;
517     }
518
519     RetainPtr<MTLDepthStencilState> depthStencil;
520
521     if (descriptor.depthStencilState && !(depthStencil = tryCreateMtlDepthStencilState(*descriptor.depthStencilState, device, errorScopes)))
522         return nullptr;
523
524     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198387 depthStencilAttachmentDescriptor isn't implemented yet for WHLSL compiler.
525
526     auto pipeline = tryCreateMtlRenderPipelineState(device, descriptor, errorScopes);
527     if (!pipeline)
528         return nullptr;
529
530     ASSERT(descriptor.layout);
531     return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology, descriptor.vertexInput.indexFormat, *descriptor.layout, descriptor));
532 }
533
534 GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology, Optional<GPUIndexFormat> format, GPUPipelineLayout& layout, const GPURenderPipelineDescriptorBase& renderDescriptorBase)
535     : GPUPipeline()
536     , m_depthStencilState(WTFMove(depthStencil))
537     , m_platformRenderPipeline(WTFMove(pipeline))
538     , m_primitiveTopology(topology)
539     , m_indexFormat(format)
540     , m_layout(makeRef(layout))
541     , m_renderDescriptorBase(renderDescriptorBase)
542 {
543 }
544
545 GPURenderPipeline::~GPURenderPipeline() = default;
546
547 bool GPURenderPipeline::recompile(const GPUDevice& device, GPUProgrammableStageDescriptor&& vertexStage, Optional<GPUProgrammableStageDescriptor>&& fragmentStage)
548 {
549     if (m_layout) {
550         GPURenderPipelineDescriptor descriptor(makeRef(*m_layout), WTFMove(vertexStage), WTFMove(fragmentStage), m_renderDescriptorBase);
551         auto errorScopes = GPUErrorScopes::create([] (GPUError&&) { });
552         if (auto pipeline = tryCreateMtlRenderPipelineState(device, descriptor, errorScopes)) {
553             m_platformRenderPipeline = WTFMove(pipeline);
554             return true;
555         }
556     }
557
558     return false;
559 }
560
561 } // namespace WebCore
562
563 #endif // ENABLE(WEBGPU)