a6821d052a886f01ebf7d0f7dab063de9ef801f6
[WebKit-https.git] / Source / WebCore / platform / graphics / gpu / cocoa / GPURenderPassEncoderMetal.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 "GPURenderPassEncoder.h"
28
29 #if ENABLE(WEBGPU)
30
31 #import "GPUBuffer.h"
32 #import "GPUColor.h"
33 #import "GPUCommandBuffer.h"
34 #import "GPURenderPassDescriptor.h"
35 #import "GPURenderPipeline.h"
36 #import "Logging.h"
37 #import "WHLSLVertexBufferIndexCalculator.h"
38 #import <Foundation/Foundation.h>
39 #import <Metal/Metal.h>
40 #import <wtf/BlockObjCExceptions.h>
41
42 namespace WebCore {
43
44 static MTLLoadAction loadActionForGPULoadOp(GPULoadOp op)
45 {
46     switch (op) {
47     case GPULoadOp::Clear:
48         return MTLLoadActionClear;
49     case GPULoadOp::Load:
50         return MTLLoadActionLoad;
51     }
52
53     ASSERT_NOT_REACHED();
54 }
55
56 static MTLStoreAction storeActionForGPUStoreOp(GPUStoreOp op)
57 {
58     switch (op) {
59     case GPUStoreOp::Store:
60         return MTLStoreActionStore;
61     }
62
63     ASSERT_NOT_REACHED();
64 }
65
66 static bool populateMtlColorAttachmentsArray(MTLRenderPassColorAttachmentDescriptorArray *array, const Vector<GPURenderPassColorAttachmentDescriptor>& descriptors, const char* const functionName)
67 {
68 #if LOG_DISABLED
69     UNUSED_PARAM(functionName);
70 #endif
71
72     for (unsigned i = 0; i < descriptors.size(); ++i) {
73         const auto& descriptor = descriptors[i];
74         if (!descriptor.attachment->platformTexture()) {
75             LOG(WebGPU, "%s: Invalid MTLTexture for color attachment %u!", functionName, i);
76             return false;
77         }
78         const auto& color = descriptor.clearColor;
79
80         BEGIN_BLOCK_OBJC_EXCEPTIONS;
81
82         auto mtlAttachment = retainPtr([array objectAtIndexedSubscript:i]);
83         [mtlAttachment setTexture:descriptor.attachment->platformTexture()];
84         [mtlAttachment setClearColor:MTLClearColorMake(color.r, color.g, color.b, color.a)];
85         [mtlAttachment setLoadAction:loadActionForGPULoadOp(descriptor.loadOp)];
86         [mtlAttachment setStoreAction:storeActionForGPUStoreOp(descriptor.storeOp)];
87
88         END_BLOCK_OBJC_EXCEPTIONS;
89     }
90
91     return true;
92 }
93
94 static bool populateMtlDepthStencilAttachment(MTLRenderPassDepthAttachmentDescriptor *mtlAttachment, const GPURenderPassDepthStencilAttachmentDescriptor& descriptor, const char* const functionName)
95 {
96 #if LOG_DISABLED
97     UNUSED_PARAM(functionName);
98 #endif
99
100     if (!descriptor.attachment->platformTexture()) {
101         LOG(WebGPU, "%s: Invalid MTLTexture for depth attachment!", functionName);
102         return false;
103     }
104
105     BEGIN_BLOCK_OBJC_EXCEPTIONS;
106
107     [mtlAttachment setTexture:descriptor.attachment->platformTexture()];
108     [mtlAttachment setClearDepth:descriptor.clearDepth];
109     [mtlAttachment setLoadAction:loadActionForGPULoadOp(descriptor.depthLoadOp)];
110     [mtlAttachment setStoreAction:storeActionForGPUStoreOp(descriptor.depthStoreOp)];
111
112     END_BLOCK_OBJC_EXCEPTIONS;
113
114     return true;
115 }
116
117 static void useAttachments(GPUCommandBuffer& buffer, GPURenderPassDescriptor&& descriptor)
118 {
119     for (auto& colorAttachment : descriptor.colorAttachments)
120         buffer.useTexture(WTFMove(colorAttachment.attachment));
121     if (descriptor.depthStencilAttachment)
122         buffer.useTexture(WTFMove((*descriptor.depthStencilAttachment).attachment));
123 }
124
125 RefPtr<GPURenderPassEncoder> GPURenderPassEncoder::tryCreate(Ref<GPUCommandBuffer>&& buffer, GPURenderPassDescriptor&& descriptor)
126 {
127     const char* const functionName = "GPURenderPassEncoder::tryCreate()";
128
129     // Only one command encoder may be active at a time.
130     if (buffer->isEncodingPass()) {
131         LOG(WebGPU, "%s: Existing pass encoder must be ended first!");
132         return nullptr;
133     }
134
135     RetainPtr<MTLRenderPassDescriptor> mtlDescriptor;
136
137     BEGIN_BLOCK_OBJC_EXCEPTIONS;
138
139     mtlDescriptor = adoptNS([MTLRenderPassDescriptor new]);
140
141     END_BLOCK_OBJC_EXCEPTIONS;
142
143     if (!mtlDescriptor) {
144         LOG(WebGPU, "%s: Unable to create MTLRenderPassDescriptor!", functionName);
145         return nullptr;
146     }
147
148     if (!populateMtlColorAttachmentsArray(mtlDescriptor.get().colorAttachments, descriptor.colorAttachments, functionName))
149         return nullptr;
150
151     if (descriptor.depthStencilAttachment
152         && !populateMtlDepthStencilAttachment(mtlDescriptor.get().depthAttachment, *descriptor.depthStencilAttachment, functionName))
153         return nullptr;
154
155     buffer->endBlitEncoding();
156
157     RetainPtr<MTLRenderCommandEncoder> mtlEncoder;
158
159     BEGIN_BLOCK_OBJC_EXCEPTIONS;
160
161     mtlEncoder = [buffer->platformCommandBuffer() renderCommandEncoderWithDescriptor:mtlDescriptor.get()];
162
163     END_BLOCK_OBJC_EXCEPTIONS;
164
165     if (!mtlEncoder) {
166         LOG(WebGPU, "%s: Unable to create MTLRenderCommandEncoder!", functionName);
167         return nullptr;
168     }
169
170     // All is well; ensure GPUCommandBuffer is aware of new attachments.
171     useAttachments(buffer, WTFMove(descriptor));
172     
173     return adoptRef(new GPURenderPassEncoder(WTFMove(buffer), WTFMove(mtlEncoder)));
174 }
175
176 GPURenderPassEncoder::GPURenderPassEncoder(Ref<GPUCommandBuffer>&& commandBuffer, RetainPtr<MTLRenderCommandEncoder>&& encoder)
177     : GPUProgrammablePassEncoder(WTFMove(commandBuffer))
178     , m_platformRenderPassEncoder(WTFMove(encoder))
179 {
180 }
181
182 const MTLCommandEncoder *GPURenderPassEncoder::platformPassEncoder() const
183 {
184     return m_platformRenderPassEncoder.get();
185 }
186
187 void GPURenderPassEncoder::setPipeline(Ref<const GPURenderPipeline>&& pipeline)
188 {
189     if (!m_platformRenderPassEncoder) {
190         LOG(WebGPU, "GPURenderPassEncoder::setPipeline(): Invalid operation: Encoding is ended!");
191         return;
192     }
193
194     // FIXME: Metal throws an error if the MTLPipelineState's attachment formats do not match the MTLCommandEncoder's attachment formats.
195
196     BEGIN_BLOCK_OBJC_EXCEPTIONS;
197
198     if (pipeline->depthStencilState())
199         [m_platformRenderPassEncoder setDepthStencilState:pipeline->depthStencilState()];
200
201     [m_platformRenderPassEncoder setRenderPipelineState:pipeline->platformRenderPipeline()];
202
203     END_BLOCK_OBJC_EXCEPTIONS;
204
205     m_pipeline = WTFMove(pipeline);
206 }
207
208 void GPURenderPassEncoder::setBlendColor(const GPUColor& color)
209 {
210     if (!m_platformRenderPassEncoder) {
211         LOG(WebGPU, "GPURenderPassEncoder::setBlendColor(): Invalid operation: Encoding is ended!");
212         return;
213     }
214
215     BEGIN_BLOCK_OBJC_EXCEPTIONS;
216     [m_platformRenderPassEncoder setBlendColorRed:color.r green:color.g blue:color.b alpha:color.a];
217     END_BLOCK_OBJC_EXCEPTIONS;
218 }
219
220 void GPURenderPassEncoder::setViewport(float x, float y, float width, float height, float minDepth, float maxDepth)
221 {
222     if (!m_platformRenderPassEncoder) {
223         LOG(WebGPU, "GPURenderPassEncoder::setViewport(): Invalid operation: Encoding is ended!");
224         return;
225     }
226
227     BEGIN_BLOCK_OBJC_EXCEPTIONS;
228     [m_platformRenderPassEncoder setViewport: { x, y, width, height, minDepth, maxDepth }];
229     END_BLOCK_OBJC_EXCEPTIONS;
230 }
231
232 void GPURenderPassEncoder::setScissorRect(unsigned x, unsigned y, unsigned width, unsigned height)
233 {
234     if (!m_platformRenderPassEncoder) {
235         LOG(WebGPU, "GPURenderPassEncoder::setScissorRect(): Invalid operation: Encoding is ended!");
236         return;
237     }
238
239     BEGIN_BLOCK_OBJC_EXCEPTIONS;
240     [m_platformRenderPassEncoder setScissorRect: { x, y, width, height }];
241     END_BLOCK_OBJC_EXCEPTIONS;
242 }
243
244 void GPURenderPassEncoder::setVertexBuffers(unsigned index, Vector<Ref<GPUBuffer>>&& buffers, Vector<uint64_t>&& offsets)
245 {
246     if (!m_platformRenderPassEncoder) {
247         LOG(WebGPU, "GPURenderPassEncoder::setVertexBuffers(): Invalid operation: Encoding is ended!");
248         return;
249     }
250
251     ASSERT(buffers.size() && offsets.size() == buffers.size());
252
253     BEGIN_BLOCK_OBJC_EXCEPTIONS;
254
255     auto mtlBuffers = buffers.map([this] (auto& buffer) {
256         commandBuffer().useBuffer(buffer.copyRef());
257         return buffer->platformBuffer();
258     });
259
260     auto indexRanges = NSMakeRange(WHLSL::Metal::calculateVertexBufferIndex(index), buffers.size());
261
262     [m_platformRenderPassEncoder setVertexBuffers:mtlBuffers.data() offsets:(const NSUInteger *)offsets.data() withRange:indexRanges];
263
264     END_BLOCK_OBJC_EXCEPTIONS;
265 }
266
267 static MTLPrimitiveType primitiveTypeForGPUPrimitiveTopology(GPUPrimitiveTopology type)
268 {
269     switch (type) {
270     case GPUPrimitiveTopology::PointList:
271         return MTLPrimitiveTypePoint;
272     case GPUPrimitiveTopology::LineList:
273         return MTLPrimitiveTypeLine;
274     case GPUPrimitiveTopology::LineStrip:
275         return MTLPrimitiveTypeLineStrip;
276     case GPUPrimitiveTopology::TriangleList:
277         return MTLPrimitiveTypeTriangle;
278     case GPUPrimitiveTopology::TriangleStrip:
279         return MTLPrimitiveTypeTriangleStrip;
280     }
281
282     ASSERT_NOT_REACHED();
283 }
284
285 void GPURenderPassEncoder::draw(unsigned vertexCount, unsigned instanceCount, unsigned firstVertex, unsigned firstInstance)
286 {
287     if (!m_platformRenderPassEncoder) {
288         LOG(WebGPU, "GPURenderPassEncoder::draw(): Invalid operation: Encoding is ended!");
289         return;
290     }
291
292     if (!m_pipeline) {
293         LOG(WebGPU, "GPURenderPassEncoder::draw(): No valid GPURenderPipeline found!");
294         return;
295     }
296
297     BEGIN_BLOCK_OBJC_EXCEPTIONS;
298     [m_platformRenderPassEncoder 
299         drawPrimitives:primitiveTypeForGPUPrimitiveTopology(m_pipeline->primitiveTopology())
300         vertexStart:firstVertex
301         vertexCount:vertexCount
302         instanceCount:instanceCount
303         baseInstance:firstInstance];
304     END_BLOCK_OBJC_EXCEPTIONS;
305 }
306
307 #if USE(METAL)
308
309 void GPURenderPassEncoder::useResource(const MTLResource *resource, unsigned usage)
310 {
311     ASSERT(m_platformRenderPassEncoder);
312
313     BEGIN_BLOCK_OBJC_EXCEPTIONS;
314     [m_platformRenderPassEncoder useResource:resource usage:usage];
315     END_BLOCK_OBJC_EXCEPTIONS;
316 }
317
318 void GPURenderPassEncoder::setVertexBuffer(const MTLBuffer *buffer, unsigned offset, unsigned index)
319 {
320     ASSERT(m_platformRenderPassEncoder);
321
322     BEGIN_BLOCK_OBJC_EXCEPTIONS;
323     [m_platformRenderPassEncoder setVertexBuffer:buffer offset:offset atIndex:index];
324     END_BLOCK_OBJC_EXCEPTIONS;
325 }
326
327 void GPURenderPassEncoder::setFragmentBuffer(const MTLBuffer *buffer, unsigned offset, unsigned index)
328 {
329     ASSERT(m_platformRenderPassEncoder);
330
331     BEGIN_BLOCK_OBJC_EXCEPTIONS;
332     [m_platformRenderPassEncoder setFragmentBuffer:buffer offset:offset atIndex:index];
333     END_BLOCK_OBJC_EXCEPTIONS;
334 }
335
336 #endif // USE(METAL)
337
338 } // namespace WebCore
339
340 #endif // ENABLE(WEBGPU)