2 * Copyright (C) 2018 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "GPURenderPipeline.h"
32 #import "GPUErrorScopes.h"
34 #import "GPUPipelineMetalConvertLayout.h"
35 #import "GPURenderPipelineDescriptor.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>
51 static RetainPtr<MTLDepthStencilState> tryCreateMtlDepthStencilState(const GPUDepthStencilStateDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
53 RetainPtr<MTLDepthStencilDescriptor> mtlDescriptor;
55 BEGIN_BLOCK_OBJC_EXCEPTIONS;
57 mtlDescriptor = adoptNS([MTLDepthStencilDescriptor new]);
59 END_BLOCK_OBJC_EXCEPTIONS;
62 errorScopes.generatePrefixedError("Unable to create MTLDepthStencilDescriptor!");
66 auto mtlDepthCompare = static_cast<MTLCompareFunction>(platformCompareFunctionForGPUCompareFunction(descriptor.depthCompare));
67 [mtlDescriptor setDepthCompareFunction:mtlDepthCompare];
68 [mtlDescriptor setDepthWriteEnabled:descriptor.depthWriteEnabled];
70 // FIXME: Implement back/frontFaceStencil.
72 RetainPtr<MTLDepthStencilState> state;
74 BEGIN_BLOCK_OBJC_EXCEPTIONS;
76 state = adoptNS([device.platformDevice() newDepthStencilStateWithDescriptor:mtlDescriptor.get()]);
78 END_BLOCK_OBJC_EXCEPTIONS;
81 errorScopes.generatePrefixedError("Error creating MTLDepthStencilState!");
88 static WHLSL::VertexFormat convertVertexFormat(GPUVertexFormat vertexFormat)
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;
98 ASSERT(vertexFormat == GPUVertexFormat::Float);
99 return WHLSL::VertexFormat::FloatR32;
103 static Optional<WHLSL::TextureFormat> convertTextureFormat(GPUTextureFormat 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;
121 static MTLVertexFormat mtlVertexFormatForGPUVertexFormat(GPUVertexFormat 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;
134 ASSERT_NOT_REACHED();
137 static MTLVertexStepFunction mtlStepFunctionForGPUInputStepMode(GPUInputStepMode mode)
140 case GPUInputStepMode::Vertex:
141 return MTLVertexStepFunctionPerVertex;
142 case GPUInputStepMode::Instance:
143 return MTLVertexStepFunctionPerInstance;
146 ASSERT_NOT_REACHED();
149 // FIXME: Move this into GPULimits when that is implemented properly.
150 constexpr unsigned maxVertexAttributes = 16;
152 static bool trySetVertexInput(const GPUVertexInputDescriptor& descriptor, MTLRenderPipelineDescriptor *mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
154 const auto& buffers = descriptor.vertexBuffers;
156 if (buffers.size() > maxVertexBuffers) {
157 errorScopes.generatePrefixedError("Too many GPUVertexBufferDescriptors!");
161 auto mtlVertexDescriptor = adoptNS([MTLVertexDescriptor new]);
163 auto layoutArray = retainPtr(mtlVertexDescriptor.get().layouts);
164 auto attributeArray = retainPtr(mtlVertexDescriptor.get().attributes);
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;
170 for (size_t index = 0; index < buffers.size(); ++index) {
174 const auto& attributes = buffers[index]->attributeSet;
176 if (attributes.size() + attributeIndex > maxVertexAttributes) {
177 errorScopes.generatePrefixedError("Too many GPUVertexAttributeDescriptors!");
181 NSUInteger inputStride = 0;
182 if (!WTF::convertSafely(buffers[index]->stride, inputStride)) {
183 errorScopes.generatePrefixedError(makeString("Stride for GPUVertexBufferDescriptor ", index, " is too large!"));
187 auto convertedBufferIndex = WHLSL::Metal::calculateVertexBufferIndex(index);
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;
195 for (const auto& attribute : attributes) {
196 if (!locations.add(attribute.shaderLocation).isNewEntry) {
197 errorScopes.generatePrefixedError(makeString("Duplicate shaderLocation ", attribute.shaderLocation, " for vertex attribute!"));
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!"));
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;
215 whlslDescriptor->vertexAttributes.append({ convertVertexFormat(attribute.format), attribute.shaderLocation, attributeIndex });
221 [mtlDescriptor setVertexDescriptor:mtlVertexDescriptor.get()];
226 static MTLColorWriteMask mtlColorWriteMaskForGPUColorWriteFlags(GPUColorWriteFlags flags)
228 if (flags == static_cast<GPUColorWriteFlags>(GPUColorWrite::Flags::All))
229 return MTLColorWriteMaskAll;
231 auto options = OptionSet<GPUColorWrite::Flags>::fromRaw(flags);
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;
246 static MTLBlendOperation mtlBlendOperationForGPUBlendOperation(GPUBlendOperation 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;
261 ASSERT_NOT_REACHED();
264 static MTLBlendFactor mtlBlendFactorForGPUBlendFactor(GPUBlendFactor 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;
295 ASSERT_NOT_REACHED();
298 static bool trySetColorStates(const Vector<GPUColorStateDescriptor>& colorStates, MTLRenderPipelineColorAttachmentDescriptorArray* array, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
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!");
306 BEGIN_BLOCK_OBJC_EXCEPTIONS;
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)];
321 if (whlslDescriptor) {
322 if (auto format = convertTextureFormat(state.format))
323 whlslDescriptor->attachmentsStateDescriptor.attachmentDescriptors.append({*format, i});
325 errorScopes.generatePrefixedError(makeString("Invalid GPUTextureFormat for GPUColorStateDescriptor ", i, "!"));
331 END_BLOCK_OBJC_EXCEPTIONS;
336 static bool trySetMetalFunctions(MTLLibrary *vertexMetalLibrary, MTLLibrary *fragmentMetalLibrary, MTLRenderPipelineDescriptor *mtlDescriptor, const String& vertexEntryPointName, const String& fragmentEntryPointName, GPUErrorScopes& errorScopes)
339 BEGIN_BLOCK_OBJC_EXCEPTIONS;
341 // Metal requires a vertex shader in all render pipelines.
342 if (!vertexMetalLibrary) {
343 errorScopes.generatePrefixedError("MTLLibrary for vertex stage does not exist!");
347 auto function = adoptNS([vertexMetalLibrary newFunctionWithName:vertexEntryPointName]);
349 errorScopes.generatePrefixedError(makeString("Cannot create vertex MTLFunction '", vertexEntryPointName, "'!"));
353 [mtlDescriptor setVertexFunction:function.get()];
355 END_BLOCK_OBJC_EXCEPTIONS;
359 BEGIN_BLOCK_OBJC_EXCEPTIONS;
361 // However, fragment shaders are optional.
362 if (!fragmentMetalLibrary || fragmentEntryPointName.isNull())
365 auto function = adoptNS([fragmentMetalLibrary newFunctionWithName:fragmentEntryPointName]);
368 errorScopes.generatePrefixedError(makeString("Cannot create fragment MTLFunction '", fragmentEntryPointName, "'!"));
372 [mtlDescriptor setFragmentFunction:function.get()];
375 END_BLOCK_OBJC_EXCEPTIONS;
381 static bool trySetFunctions(const GPUProgrammableStageDescriptor& vertexStage, const Optional<GPUProgrammableStageDescriptor>& fragmentStage, const GPUDevice& device, MTLRenderPipelineDescriptor* mtlDescriptor, Optional<WHLSL::RenderPipelineDescriptor>& whlslDescriptor, GPUErrorScopes& errorScopes)
383 RetainPtr<MTLLibrary> vertexLibrary, fragmentLibrary;
384 String vertexEntryPoint, fragmentEntryPoint;
386 if (whlslDescriptor) {
387 ASSERT(vertexStage.module->whlslModule());
388 ASSERT(!fragmentStage || fragmentStage->module->whlslModule());
390 whlslDescriptor->vertexEntryPointName = vertexStage.entryPoint;
392 whlslDescriptor->fragmentEntryPointName = fragmentStage->entryPoint;
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()));
400 NSError *error = nil;
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;
412 if (!vertexLibrary && error) {
413 errorScopes.generatePrefixedError(error.localizedDescription.UTF8String);
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.
422 fragmentLibrary = vertexLibrary;
423 vertexEntryPoint = whlslCompileResult->mangledVertexEntryPointName.toString();
425 fragmentEntryPoint = whlslCompileResult->mangledFragmentEntryPointName.toString();
427 vertexLibrary = vertexStage.module->platformShaderModule();
428 vertexEntryPoint = vertexStage.entryPoint;
430 fragmentLibrary = fragmentStage->module->platformShaderModule();
431 fragmentEntryPoint = fragmentStage->entryPoint;
435 return trySetMetalFunctions(vertexLibrary.get(), fragmentLibrary.get(), mtlDescriptor, vertexEntryPoint, fragmentEntryPoint, errorScopes);
438 static RetainPtr<MTLRenderPipelineDescriptor> convertRenderPipelineDescriptor(const GPURenderPipelineDescriptor& descriptor, const GPUDevice& device, GPUErrorScopes& errorScopes)
440 RetainPtr<MTLRenderPipelineDescriptor> mtlDescriptor;
442 BEGIN_BLOCK_OBJC_EXCEPTIONS;
444 mtlDescriptor = adoptNS([MTLRenderPipelineDescriptor new]);
446 END_BLOCK_OBJC_EXCEPTIONS;
448 if (!mtlDescriptor) {
449 errorScopes.generatePrefixedError("Error creating MTLDescriptor!");
453 // Determine if shader source is in WHLSL or MSL.
454 const auto& vertexStage = descriptor.vertexStage;
455 const auto& fragmentStage = descriptor.fragmentStage;
457 if (fragmentStage && static_cast<bool>(vertexStage.module->whlslModule()) != static_cast<bool>(fragmentStage->module->whlslModule()))
459 bool isWhlsl = vertexStage.module->whlslModule();
461 // Set data for the Metal pipeline descriptor (and WHLSL's, if needed).
462 Optional<WHLSL::RenderPipelineDescriptor> whlslDescriptor;
464 whlslDescriptor = WHLSL::RenderPipelineDescriptor();
466 if (!trySetVertexInput(descriptor.vertexInput, mtlDescriptor.get(), whlslDescriptor, errorScopes))
469 if (!trySetColorStates(descriptor.colorStates, mtlDescriptor.get().colorAttachments, whlslDescriptor, errorScopes))
472 if (descriptor.layout && whlslDescriptor) {
473 if (auto layout = convertLayout(*descriptor.layout))
474 whlslDescriptor->layout = WTFMove(*layout);
476 errorScopes.generatePrefixedError("Error converting GPUPipelineLayout!");
481 if (!trySetFunctions(vertexStage, fragmentStage, device, mtlDescriptor.get(), whlslDescriptor, errorScopes))
484 return mtlDescriptor;
487 static RetainPtr<MTLRenderPipelineState> tryCreateMtlRenderPipelineState(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor, GPUErrorScopes& errorScopes)
489 if (!device.platformDevice()) {
490 errorScopes.generatePrefixedError("Invalid GPUDevice!");
494 auto mtlDescriptor = convertRenderPipelineDescriptor(descriptor, device, errorScopes);
498 RetainPtr<MTLRenderPipelineState> pipeline;
500 BEGIN_BLOCK_OBJC_EXCEPTIONS;
502 NSError *error = nil;
503 pipeline = adoptNS([device.platformDevice() newRenderPipelineStateWithDescriptor:mtlDescriptor.get() error:&error]);
505 errorScopes.generatePrefixedError(error.localizedDescription.UTF8String);
507 END_BLOCK_OBJC_EXCEPTIONS;
512 RefPtr<GPURenderPipeline> GPURenderPipeline::tryCreate(const GPUDevice& device, const GPURenderPipelineDescriptor& descriptor, GPUErrorScopes& errorScopes)
514 if (!device.platformDevice()) {
515 errorScopes.generatePrefixedError("Invalid GPUDevice!");
519 RetainPtr<MTLDepthStencilState> depthStencil;
521 if (descriptor.depthStencilState && !(depthStencil = tryCreateMtlDepthStencilState(*descriptor.depthStencilState, device, errorScopes)))
524 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=198387 depthStencilAttachmentDescriptor isn't implemented yet for WHLSL compiler.
526 auto pipeline = tryCreateMtlRenderPipelineState(device, descriptor, errorScopes);
530 ASSERT(descriptor.layout);
531 return adoptRef(new GPURenderPipeline(WTFMove(depthStencil), WTFMove(pipeline), descriptor.primitiveTopology, descriptor.vertexInput.indexFormat, *descriptor.layout, descriptor));
534 GPURenderPipeline::GPURenderPipeline(RetainPtr<MTLDepthStencilState>&& depthStencil, RetainPtr<MTLRenderPipelineState>&& pipeline, GPUPrimitiveTopology topology, Optional<GPUIndexFormat> format, GPUPipelineLayout& layout, const GPURenderPipelineDescriptorBase& renderDescriptorBase)
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)
545 GPURenderPipeline::~GPURenderPipeline() = default;
547 bool GPURenderPipeline::recompile(const GPUDevice& device, GPUProgrammableStageDescriptor&& vertexStage, Optional<GPUProgrammableStageDescriptor>&& fragmentStage)
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);
561 } // namespace WebCore
563 #endif // ENABLE(WEBGPU)