da21d24ec72369a3b18553b092e00b9f0007295a
[WebKit-https.git] / Source / WebCore / platform / graphics / gpu / cocoa / GPUCommandBufferMetal.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 "GPUCommandBuffer.h"
28
29 #if ENABLE(WEBGPU)
30
31 #import "GPUDevice.h"
32 #import "GPUExtent3D.h"
33 #import "GPUQueue.h"
34 #import "Logging.h"
35
36 #import <Metal/Metal.h>
37 #import <wtf/BlockObjCExceptions.h>
38 #import <wtf/CheckedArithmetic.h>
39
40 namespace WebCore {
41
42 RefPtr<GPUCommandBuffer> GPUCommandBuffer::tryCreate(const GPUDevice& device)
43 {
44     if (!device.platformDevice()) {
45         LOG(WebGPU, "GPUCommandBuffer::create(): Invalid GPUDevice!");
46         return nullptr;
47     }
48
49     auto gpuCommandQueue = device.tryGetQueue();
50     if (!gpuCommandQueue)
51         return nullptr;
52
53     auto mtlQueue = gpuCommandQueue->platformQueue();
54
55     RetainPtr<MTLCommandBuffer> buffer;
56
57     BEGIN_BLOCK_OBJC_EXCEPTIONS;
58
59     buffer = [mtlQueue commandBuffer];
60
61     END_BLOCK_OBJC_EXCEPTIONS;
62
63     if (!buffer) {
64         LOG(WebGPU, "GPUCommandBuffer::create(): Unable to create MTLCommandBuffer!");
65         return nullptr;
66     }
67
68     return adoptRef(new GPUCommandBuffer(WTFMove(buffer)));
69 }
70
71 GPUCommandBuffer::GPUCommandBuffer(RetainPtr<MTLCommandBuffer>&& buffer)
72     : m_platformCommandBuffer(WTFMove(buffer))
73 {
74 }
75
76 GPUCommandBuffer::~GPUCommandBuffer()
77 {
78     endBlitEncoding();
79 }
80
81 MTLBlitCommandEncoder *GPUCommandBuffer::blitEncoder() const
82 {
83     return m_blitEncoder ? m_blitEncoder.get() : (m_blitEncoder = [m_platformCommandBuffer blitCommandEncoder]).get();
84 }
85
86 void GPUCommandBuffer::endBlitEncoding()
87 {
88     if (!m_blitEncoder)
89         return;
90     BEGIN_BLOCK_OBJC_EXCEPTIONS;
91     [m_blitEncoder endEncoding];
92     END_BLOCK_OBJC_EXCEPTIONS;
93     m_blitEncoder = nullptr;
94 }
95
96 void GPUCommandBuffer::copyBufferToBuffer(Ref<GPUBuffer>&& src, uint64_t srcOffset, Ref<GPUBuffer>&& dst, uint64_t dstOffset, uint64_t size)
97 {
98     if (!src->isTransferSource() || !dst->isTransferDestination()) {
99         LOG(WebGPU, "GPUCommandBuffer::copyBufferToBuffer(): Invalid operation!");
100         return;
101     }
102
103 #if PLATFORM(MAC)
104     if (size % 4 || srcOffset % 4 || dstOffset % 4) {
105         LOG(WebGPU, "GPUCommandBuffer::copyBufferToBuffer(): Copy must be aligned to a multiple of 4 bytes!");
106         return;
107     }
108 #endif
109
110     auto srcLength = checkedSum<uint64_t>(size, srcOffset);
111     auto dstLength = checkedSum<uint64_t>(size, dstOffset);
112     if (srcLength.hasOverflowed() || dstLength.hasOverflowed()
113         || srcLength.unsafeGet() > src->byteLength() || dstLength.unsafeGet() > dst->byteLength()) {
114         LOG(WebGPU, "GPUCommandBuffer::copyBufferToBuffer(): Invalid offset or copy size!");
115         return;
116     }
117
118     BEGIN_BLOCK_OBJC_EXCEPTIONS;
119
120     [blitEncoder()
121         copyFromBuffer:src->platformBuffer()
122         sourceOffset:srcOffset
123         toBuffer:dst->platformBuffer()
124         destinationOffset:dstOffset
125         size:size];
126
127     END_BLOCK_OBJC_EXCEPTIONS;
128
129     useBuffer(WTFMove(src));
130     useBuffer(WTFMove(dst));
131 }
132
133 void GPUCommandBuffer::copyBufferToTexture(GPUBufferCopyView&& srcBuffer, GPUTextureCopyView&& dstTexture, const GPUExtent3D& size)
134 {
135     if (!srcBuffer.buffer->isTransferSource() || !dstTexture.texture->isTransferDestination()) {
136         LOG(WebGPU, "GPUComandBuffer::copyBufferToTexture(): Invalid operation!");
137         return;
138     }
139
140     // FIXME: Add Metal validation.
141
142     // GPUBufferCopyView::offset: The location must be aligned to the size of the destination texture's pixel format. The value must be a multiple of the destination texture's pixel size, in bytes.
143
144     // GPUBufferCopyView::rowPitch: The value must be a multiple of the destination texture's pixel size, in bytes. The value must be less than or equal to 32,767 multiplied by the destination texture’s pixel size.
145
146     // GPUBufferCopyView::imageHeight: The value must be a multiple of the destination texture's pixel size, in bytes.
147
148     // GPUExtent3D: When you copy to a 1D texture, height and depth must be 1. When you copy to a 2D texture, depth must be 1.
149
150     // GPUTextureCopyView::texture: The value must not be a framebufferOnly texture and must not have a PVRTC pixel format.
151
152     BEGIN_BLOCK_OBJC_EXCEPTIONS;
153
154     [blitEncoder()
155         copyFromBuffer:srcBuffer.buffer->platformBuffer()
156         sourceOffset:srcBuffer.offset
157         sourceBytesPerRow:srcBuffer.rowPitch
158         sourceBytesPerImage:srcBuffer.imageHeight
159         sourceSize:MTLSizeMake(size.width, size.height, size.depth)
160         toTexture:dstTexture.texture->platformTexture()
161         destinationSlice:dstTexture.arrayLayer
162         destinationLevel:dstTexture.mipLevel
163         destinationOrigin:MTLOriginMake(dstTexture.origin.x, dstTexture.origin.y, dstTexture.origin.z)];
164
165     END_BLOCK_OBJC_EXCEPTIONS;
166
167     useBuffer(WTFMove(srcBuffer.buffer));
168     useTexture(WTFMove(dstTexture.texture));
169 }
170
171 void GPUCommandBuffer::copyTextureToBuffer(GPUTextureCopyView&& srcTexture, GPUBufferCopyView&& dstBuffer, const GPUExtent3D& size)
172 {
173     if (!srcTexture.texture->isTransferSource() || !dstBuffer.buffer->isTransferDestination()) {
174         LOG(WebGPU, "GPUCommandBuffer::copyTextureToBuffer(): Invalid operation!");
175         return;
176     }
177
178     // FIXME: Add Metal validation?
179
180     BEGIN_BLOCK_OBJC_EXCEPTIONS;
181
182     [blitEncoder()
183         copyFromTexture:srcTexture.texture->platformTexture()
184         sourceSlice:srcTexture.arrayLayer
185         sourceLevel:srcTexture.mipLevel
186         sourceOrigin:MTLOriginMake(srcTexture.origin.x, srcTexture.origin.y, srcTexture.origin.z)
187         sourceSize:MTLSizeMake(size.width, size.height, size.depth)
188         toBuffer:dstBuffer.buffer->platformBuffer()
189         destinationOffset:dstBuffer.offset
190         destinationBytesPerRow:dstBuffer.rowPitch
191         destinationBytesPerImage:dstBuffer.imageHeight];
192
193     END_BLOCK_OBJC_EXCEPTIONS;
194
195     useTexture(WTFMove(srcTexture.texture));
196     useBuffer(WTFMove(dstBuffer.buffer));
197 }
198
199 void GPUCommandBuffer::copyTextureToTexture(GPUTextureCopyView&& src, GPUTextureCopyView&& dst, const GPUExtent3D& size)
200 {
201     if (!src.texture->isTransferSource() || !dst.texture->isTransferDestination()) {
202         LOG(WebGPU, "GPUCommandBuffer::copyTextureToTexture(): Invalid operation!");
203         return;
204     }
205
206     // FIXME: Add Metal validation?
207
208     BEGIN_BLOCK_OBJC_EXCEPTIONS;
209
210     [blitEncoder()
211         copyFromTexture:src.texture->platformTexture()
212         sourceSlice:src.arrayLayer
213         sourceLevel:src.mipLevel
214         sourceOrigin:MTLOriginMake(src.origin.x, src.origin.y, src.origin.z)
215         sourceSize:MTLSizeMake(size.width, size.height, size.depth)
216         toTexture:dst.texture->platformTexture()
217         destinationSlice:src.arrayLayer
218         destinationLevel:src.mipLevel
219         destinationOrigin:MTLOriginMake(dst.origin.x, dst.origin.y, dst.origin.z)];
220
221     END_BLOCK_OBJC_EXCEPTIONS;
222
223     useTexture(WTFMove(src.texture));
224     useTexture(WTFMove(dst.texture));
225 }
226
227 } // namespace WebCore
228
229 #endif // ENABLE(WEBGPU)