783c0130ba69b24387cd817eee2c85cc306389a7
[WebKit-https.git] / Source / WebCore / platform / graphics / gpu / cocoa / GPUTextureMetal.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 "GPUTexture.h"
28
29 #if ENABLE(WEBGPU)
30
31 #import "GPUDevice.h"
32 #import "GPUTextureDescriptor.h"
33 #import "GPUUtils.h"
34 #import "Logging.h"
35 #import <Metal/Metal.h>
36 #import <wtf/BlockObjCExceptions.h>
37 #import <wtf/Optional.h>
38
39 namespace WebCore {
40
41 static MTLTextureType mtlTextureTypeForGPUTextureDescriptor(const GPUTextureDescriptor& descriptor)
42 {
43     switch (descriptor.dimension) {
44     case GPUTextureDimension::_1d:
45         return (descriptor.arrayLayerCount == 1) ? MTLTextureType1D : MTLTextureType1DArray;
46     case GPUTextureDimension::_2d: {
47         if (descriptor.arrayLayerCount == 1)
48             return (descriptor.sampleCount == 1) ? MTLTextureType2D : MTLTextureType2DMultisample;
49
50         return MTLTextureType2DArray;
51     }
52     case GPUTextureDimension::_3d:
53         return MTLTextureType3D;
54     }
55 }
56
57 static Optional<MTLTextureUsage> mtlTextureUsageForGPUTextureUsageFlags(OptionSet<GPUTextureUsage::Flags> flags, const char* const functionName)
58 {
59 #if LOG_DISABLED
60     UNUSED_PARAM(functionName);
61 #endif
62     if (flags.containsAny({ GPUTextureUsage::Flags::TransferSource, GPUTextureUsage::Flags::Sampled }) && (flags & GPUTextureUsage::Flags::Storage)) {
63         LOG(WebGPU, "%s: Texture cannot have both STORAGE and a read-only usage!", functionName);
64         return WTF::nullopt;
65     }
66
67     if (flags & GPUTextureUsage::Flags::OutputAttachment) {
68         if (flags.containsAny({ GPUTextureUsage::Flags::Storage, GPUTextureUsage::Flags::Sampled })) {
69             LOG(WebGPU, "%s: Texture cannot have OUTPUT_ATTACHMENT usage with STORAGE or SAMPLED usages!", functionName);
70             return WTF::nullopt;
71         }
72
73         return MTLTextureUsageRenderTarget | MTLTextureUsagePixelFormatView;
74     }
75
76     if (flags & GPUTextureUsage::Flags::Storage)
77         return MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView;
78
79     if (flags & GPUTextureUsage::Flags::Sampled)
80         return MTLTextureUsageShaderRead | MTLTextureUsagePixelFormatView;
81
82     return MTLTextureUsageUnknown;
83 }
84
85 #if !PLATFORM(MAC)
86 static MTLStorageMode storageModeForPixelFormatAndSampleCount(MTLPixelFormat format, unsigned samples)
87 {
88     // Depth, Stencil, DepthStencil, and Multisample textures must be allocated with the MTLStorageModePrivate resource option.
89     if (format == MTLPixelFormatDepth32Float_Stencil8 || samples > 1)
90         return MTLStorageModePrivate;
91
92     return MTLStorageModeShared;
93 }
94 #endif
95
96 static RetainPtr<MTLTextureDescriptor> tryCreateMtlTextureDescriptor(const char* const functionName, const GPUTextureDescriptor& descriptor, OptionSet<GPUTextureUsage::Flags> usage)
97 {
98     RetainPtr<MTLTextureDescriptor> mtlDescriptor;
99
100     BEGIN_BLOCK_OBJC_EXCEPTIONS;
101
102     mtlDescriptor = adoptNS([MTLTextureDescriptor new]);
103
104     END_BLOCK_OBJC_EXCEPTIONS;
105
106     if (!mtlDescriptor) {
107         LOG(WebGPU, "%s: Unable to create new MTLTextureDescriptor!", functionName);
108         return nullptr;
109     }
110
111     // FIXME: Add more validation as constraints are added to spec.
112     auto pixelFormat = static_cast<MTLPixelFormat>(platformTextureFormatForGPUTextureFormat(descriptor.format));
113
114     auto mtlUsage = mtlTextureUsageForGPUTextureUsageFlags(usage, functionName);
115     if (!mtlUsage)
116         return nullptr;
117
118 #if PLATFORM(MAC)
119     auto storageMode = MTLStorageModePrivate;
120 #else
121     auto storageMode = storageModeForPixelFormatAndSampleCount(pixelFormat, descriptor.sampleCount);
122 #endif
123
124     BEGIN_BLOCK_OBJC_EXCEPTIONS;
125
126     [mtlDescriptor setWidth:descriptor.size.width];
127     [mtlDescriptor setHeight:descriptor.size.height];
128     [mtlDescriptor setDepth:descriptor.size.depth];
129     [mtlDescriptor setArrayLength:descriptor.arrayLayerCount];
130     [mtlDescriptor setMipmapLevelCount:descriptor.mipLevelCount];
131     [mtlDescriptor setSampleCount:descriptor.sampleCount];
132     [mtlDescriptor setTextureType:mtlTextureTypeForGPUTextureDescriptor(descriptor)];
133     [mtlDescriptor setPixelFormat:pixelFormat];
134     [mtlDescriptor setUsage:*mtlUsage];
135
136     [mtlDescriptor setStorageMode:storageMode];
137
138     END_BLOCK_OBJC_EXCEPTIONS;
139
140     return mtlDescriptor;
141 }
142
143 RefPtr<GPUTexture> GPUTexture::tryCreate(const GPUDevice& device, const GPUTextureDescriptor& descriptor)
144 {
145     const char* const functionName = "GPUTexture::tryCreate()";
146
147     if (!device.platformDevice()) {
148         LOG(WebGPU, "%s: Invalid GPUDevice!", functionName);
149         return nullptr;
150     }
151
152     auto usage = OptionSet<GPUTextureUsage::Flags>::fromRaw(descriptor.usage);
153     auto mtlDescriptor = tryCreateMtlTextureDescriptor(functionName, descriptor, usage);
154     if (!mtlDescriptor)
155         return nullptr;
156
157     RetainPtr<MTLTexture> mtlTexture;
158
159     BEGIN_BLOCK_OBJC_EXCEPTIONS;
160
161     mtlTexture = adoptNS([device.platformDevice() newTextureWithDescriptor:mtlDescriptor.get()]);
162
163     END_BLOCK_OBJC_EXCEPTIONS;
164
165     if (!mtlTexture) {
166         LOG(WebGPU, "%s: Unable to create MTLTexture!", functionName);
167         return nullptr;
168     }
169
170     return adoptRef(new GPUTexture(WTFMove(mtlTexture), usage));
171 }
172
173 Ref<GPUTexture> GPUTexture::create(RetainPtr<MTLTexture>&& texture, OptionSet<GPUTextureUsage::Flags> usage)
174 {
175     return adoptRef(*new GPUTexture(WTFMove(texture), usage));
176 }
177
178 GPUTexture::GPUTexture(RetainPtr<MTLTexture>&& texture, OptionSet<GPUTextureUsage::Flags> usage)
179     : m_platformTexture(WTFMove(texture))
180     , m_usage(usage)
181 {
182 }
183
184 RefPtr<GPUTexture> GPUTexture::tryCreateDefaultTextureView()
185 {
186     RetainPtr<MTLTexture> texture;
187
188     BEGIN_BLOCK_OBJC_EXCEPTIONS;
189
190     texture = adoptNS([m_platformTexture newTextureViewWithPixelFormat:m_platformTexture.get().pixelFormat]);
191
192     END_BLOCK_OBJC_EXCEPTIONS;
193
194     if (!texture) {
195         LOG(WebGPU, "GPUTexture::createDefaultTextureView(): Unable to create MTLTexture view!");
196         return nullptr;
197     }
198
199     return GPUTexture::create(WTFMove(texture), m_usage);
200 }
201
202 } // namespace WebCore
203
204 #endif // ENABLE(WEBGPU)