1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // mtl_resources.h:
7 //    Declares wrapper classes for Metal's MTLTexture and MTLBuffer.
8 //
9 
10 #ifndef LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_
11 #define LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_
12 
13 #import <Metal/Metal.h>
14 
15 #include <atomic>
16 #include <memory>
17 
18 #include "common/FastVector.h"
19 #include "common/MemoryBuffer.h"
20 #include "common/angleutils.h"
21 #include "libANGLE/Error.h"
22 #include "libANGLE/angletypes.h"
23 #include "libANGLE/renderer/metal/mtl_common.h"
24 #include "libANGLE/renderer/metal/mtl_format_utils.h"
25 
26 namespace rx
27 {
28 
29 class ContextMtl;
30 
31 namespace mtl
32 {
33 
34 class CommandQueue;
35 class BlitCommandEncoder;
36 class Resource;
37 class Texture;
38 class Buffer;
39 
40 using ResourceRef    = std::shared_ptr<Resource>;
41 using TextureRef     = std::shared_ptr<Texture>;
42 using TextureWeakRef = std::weak_ptr<Texture>;
43 using BufferRef      = std::shared_ptr<Buffer>;
44 using BufferWeakRef  = std::weak_ptr<Buffer>;
45 
46 class Resource : angle::NonCopyable
47 {
48   public:
~Resource()49     virtual ~Resource() {}
50 
51     // Check whether the resource still being used by GPU including the pending (uncommitted)
52     // command buffer.
53     bool isBeingUsedByGPU(Context *context) const;
54     // Checks whether the last command buffer that uses the given resource has been committed or not
55     bool hasPendingWorks(Context *context) const;
56 
57     void setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing);
58 
getCommandBufferQueueSerial()59     uint64_t getCommandBufferQueueSerial() const { return mUsageRef->cmdBufferQueueSerial; }
60 
61     // Flag indicate whether we should synchronize the content to CPU after GPU changed this
62     // resource's content.
isCPUReadMemNeedSync()63     bool isCPUReadMemNeedSync() const { return mUsageRef->cpuReadMemNeedSync; }
resetCPUReadMemNeedSync()64     void resetCPUReadMemNeedSync() { mUsageRef->cpuReadMemNeedSync = false; }
65 
isCPUReadMemDirty()66     bool isCPUReadMemDirty() const { return mUsageRef->cpuReadMemDirty; }
resetCPUReadMemDirty()67     void resetCPUReadMemDirty() { mUsageRef->cpuReadMemDirty = false; }
68 
69   protected:
70     Resource();
71     // Share the GPU usage ref with other resource
72     Resource(Resource *other);
73 
74     void reset();
75 
76   private:
77     struct UsageRef
78     {
79         // The id of the last command buffer that is using this resource.
80         uint64_t cmdBufferQueueSerial = 0;
81 
82         // This flag means the resource was issued to be modified by GPU, if CPU wants to read
83         // its content, explicit synchronization call must be invoked.
84         bool cpuReadMemNeedSync = false;
85 
86         // This flag is useful for BufferMtl to know whether it should update the shadow copy
87         bool cpuReadMemDirty = false;
88     };
89 
90     // One resource object might just be a view of another resource. For example, a texture 2d
91     // object might be a view of one face of a cube texture object. Another example is one texture
92     // object of size 2x2 might be a mipmap view of a texture object size 4x4. Thus, if one object
93     // is being used by a command buffer, it means the other object is being used also. In this
94     // case, the two objects must share the same UsageRef property.
95     std::shared_ptr<UsageRef> mUsageRef;
96 };
97 
98 class Texture final : public Resource,
99                       public WrappedObject<id<MTLTexture>>,
100                       public std::enable_shared_from_this<Texture>
101 {
102   public:
103     static angle::Result Make2DTexture(ContextMtl *context,
104                                        const Format &format,
105                                        uint32_t width,
106                                        uint32_t height,
107                                        uint32_t mips /** use zero to create full mipmaps chain */,
108                                        bool renderTargetOnly,
109                                        bool allowFormatView,
110                                        TextureRef *refOut);
111 
112     // On macOS, memory will still be allocated for this texture.
113     static angle::Result MakeMemoryLess2DTexture(ContextMtl *context,
114                                                  const Format &format,
115                                                  uint32_t width,
116                                                  uint32_t height,
117                                                  TextureRef *refOut);
118 
119     static angle::Result MakeCubeTexture(ContextMtl *context,
120                                          const Format &format,
121                                          uint32_t size,
122                                          uint32_t mips /** use zero to create full mipmaps chain */,
123                                          bool renderTargetOnly,
124                                          bool allowFormatView,
125                                          TextureRef *refOut);
126 
127     static angle::Result Make2DMSTexture(ContextMtl *context,
128                                          const Format &format,
129                                          uint32_t width,
130                                          uint32_t height,
131                                          uint32_t samples,
132                                          bool renderTargetOnly,
133                                          bool allowFormatView,
134                                          TextureRef *refOut);
135 
136     static angle::Result Make2DArrayTexture(ContextMtl *context,
137                                             const Format &format,
138                                             uint32_t width,
139                                             uint32_t height,
140                                             uint32_t mips,
141                                             uint32_t arrayLength,
142                                             bool renderTargetOnly,
143                                             bool allowFormatView,
144                                             TextureRef *refOut);
145 
146     static angle::Result Make3DTexture(ContextMtl *context,
147                                        const Format &format,
148                                        uint32_t width,
149                                        uint32_t height,
150                                        uint32_t depth,
151                                        uint32_t mips,
152                                        bool renderTargetOnly,
153                                        bool allowFormatView,
154                                        TextureRef *refOut);
155 
156     static angle::Result MakeIOSurfaceTexture(ContextMtl *context,
157                                               const Format &format,
158                                               uint32_t width,
159                                               uint32_t height,
160                                               IOSurfaceRef ref,
161                                               uint32_t plane,
162                                               TextureRef *refOut);
163 
164     static TextureRef MakeFromMetal(id<MTLTexture> metalTexture);
165 
166     // Allow CPU to read & write data directly to this texture?
167     bool isCPUAccessible() const;
168     // Allow shaders to read/sample this texture?
169     // Texture created with renderTargetOnly flag won't be readable
170     bool isShaderReadable() const;
171 
172     bool supportFormatView() const;
173 
174     void replace2DRegion(ContextMtl *context,
175                          const MTLRegion &region,
176                          const MipmapNativeLevel &mipmapLevel,
177                          uint32_t slice,
178                          const uint8_t *data,
179                          size_t bytesPerRow);
180 
181     void replaceRegion(ContextMtl *context,
182                        const MTLRegion &region,
183                        const MipmapNativeLevel &mipmapLevel,
184                        uint32_t slice,
185                        const uint8_t *data,
186                        size_t bytesPerRow,
187                        size_t bytesPer2DImage);
188 
189     void getBytes(ContextMtl *context,
190                   size_t bytesPerRow,
191                   size_t bytesPer2DInage,
192                   const MTLRegion &region,
193                   const MipmapNativeLevel &mipmapLevel,
194                   uint32_t slice,
195                   uint8_t *dataOut);
196 
197     // Create 2d view of a cube face which full range of mip levels.
198     TextureRef createCubeFaceView(uint32_t face);
199     // Create a view of one slice at a level.
200     TextureRef createSliceMipView(uint32_t slice, const MipmapNativeLevel &level);
201     // Create a view of a level.
202     TextureRef createMipView(const MipmapNativeLevel &level);
203     // Create a view with different format
204     TextureRef createViewWithDifferentFormat(MTLPixelFormat format);
205     // Same as above but the target format must be compatible, for example sRGB to linear. In this
206     // case texture doesn't need format view usage flag.
207     TextureRef createViewWithCompatibleFormat(MTLPixelFormat format);
208     // Create a swizzled view
209     TextureRef createSwizzleView(const TextureSwizzleChannels &swizzle);
210 
211     MTLTextureType textureType() const;
212     MTLPixelFormat pixelFormat() const;
213 
214     uint32_t mipmapLevels() const;
215     uint32_t arrayLength() const;
216     uint32_t cubeFacesOrArrayLength() const;
217 
218     uint32_t width(const MipmapNativeLevel &level) const;
219     uint32_t height(const MipmapNativeLevel &level) const;
220     uint32_t depth(const MipmapNativeLevel &level) const;
221 
222     gl::Extents size(const MipmapNativeLevel &level) const;
223     gl::Extents size(const ImageNativeIndex &index) const;
224 
widthAt0()225     uint32_t widthAt0() const { return width(kZeroNativeMipLevel); }
heightAt0()226     uint32_t heightAt0() const { return height(kZeroNativeMipLevel); }
depthAt0()227     uint32_t depthAt0() const { return depth(kZeroNativeMipLevel); }
sizeAt0()228     gl::Extents sizeAt0() const { return size(kZeroNativeMipLevel); }
229 
230     uint32_t samples() const;
231 
232     angle::Result resize(ContextMtl *context, uint32_t width, uint32_t height);
233 
234     // For render target
getColorWritableMask()235     MTLColorWriteMask getColorWritableMask() const { return *mColorWritableMask; }
setColorWritableMask(MTLColorWriteMask mask)236     void setColorWritableMask(MTLColorWriteMask mask) { *mColorWritableMask = mask; }
237 
238     // Get reading copy. Used for reading non-readable texture or reading stencil value from
239     // packed depth & stencil texture.
240     // NOTE: this only copies 1 depth slice of the 3D texture.
241     // The texels will be copied to region(0, 0, 0, areaToCopy.size) of the returned texture.
242     // The returned pointer will be retained by the original texture object.
243     // Calling getReadableCopy() will overwrite previously returned texture.
244     TextureRef getReadableCopy(ContextMtl *context,
245                                mtl::BlitCommandEncoder *encoder,
246                                const uint32_t levelToCopy,
247                                const uint32_t sliceToCopy,
248                                const MTLRegion &areaToCopy);
249 
250     void releaseReadableCopy();
251 
252     // Get stencil view
253     TextureRef getStencilView();
254     // Get linear color
255     TextureRef getLinearColorView();
256 
257     // Change the wrapped metal object. Special case for swapchain image
258     void set(id<MTLTexture> metalTexture);
259 
260     // Explicitly sync content between CPU and GPU
261     void syncContent(ContextMtl *context, mtl::BlitCommandEncoder *encoder);
262 
263   private:
264     using ParentClass = WrappedObject<id<MTLTexture>>;
265 
266     static angle::Result MakeTexture(ContextMtl *context,
267                                      const Format &mtlFormat,
268                                      MTLTextureDescriptor *desc,
269                                      uint32_t mips,
270                                      bool renderTargetOnly,
271                                      bool allowFormatView,
272                                      TextureRef *refOut);
273 
274     static angle::Result MakeTexture(ContextMtl *context,
275                                      const Format &mtlFormat,
276                                      MTLTextureDescriptor *desc,
277                                      uint32_t mips,
278                                      bool renderTargetOnly,
279                                      bool allowFormatView,
280                                      bool memoryLess,
281                                      TextureRef *refOut);
282 
283     static angle::Result MakeTexture(ContextMtl *context,
284                                      const Format &mtlFormat,
285                                      MTLTextureDescriptor *desc,
286                                      IOSurfaceRef surfaceRef,
287                                      NSUInteger slice,
288                                      bool renderTargetOnly,
289                                      TextureRef *refOut);
290 
291     Texture(id<MTLTexture> metalTexture);
292     Texture(ContextMtl *context,
293             MTLTextureDescriptor *desc,
294             uint32_t mips,
295             bool renderTargetOnly,
296             bool allowFormatView);
297     Texture(ContextMtl *context,
298             MTLTextureDescriptor *desc,
299             uint32_t mips,
300             bool renderTargetOnly,
301             bool allowFormatView,
302             bool memoryLess);
303 
304     Texture(ContextMtl *context,
305             MTLTextureDescriptor *desc,
306             IOSurfaceRef iosurface,
307             NSUInteger plane,
308             bool renderTargetOnly);
309 
310     // Create a texture view
311     Texture(Texture *original, MTLPixelFormat format);
312     Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, NSRange slices);
313     Texture(Texture *original, const TextureSwizzleChannels &swizzle);
314 
315     void syncContentIfNeeded(ContextMtl *context);
316 
317     AutoObjCObj<MTLTextureDescriptor> mCreationDesc;
318 
319     // This property is shared between this object and its views:
320     std::shared_ptr<MTLColorWriteMask> mColorWritableMask;
321 
322     // Linear view of sRGB texture
323     TextureRef mLinearColorView;
324 
325     TextureRef mStencilView;
326     // Readable copy of texture
327     TextureRef mReadCopy;
328 };
329 
330 class Buffer final : public Resource, public WrappedObject<id<MTLBuffer>>
331 {
332   public:
333     static angle::Result MakeBuffer(ContextMtl *context,
334                                     size_t size,
335                                     const uint8_t *data,
336                                     BufferRef *bufferOut);
337 
338     static angle::Result MakeBufferWithSharedMemOpt(ContextMtl *context,
339                                                     bool forceUseSharedMem,
340                                                     size_t size,
341                                                     const uint8_t *data,
342                                                     BufferRef *bufferOut);
343 
344     static angle::Result MakeBufferWithResOpt(ContextMtl *context,
345                                               MTLResourceOptions resourceOptions,
346                                               size_t size,
347                                               const uint8_t *data,
348                                               BufferRef *bufferOut);
349 
350     angle::Result reset(ContextMtl *context, size_t size, const uint8_t *data);
351     angle::Result resetWithSharedMemOpt(ContextMtl *context,
352                                         bool forceUseSharedMem,
353                                         size_t size,
354                                         const uint8_t *data);
355     angle::Result resetWithResOpt(ContextMtl *context,
356                                   MTLResourceOptions resourceOptions,
357                                   size_t size,
358                                   const uint8_t *data);
359 
360     const uint8_t *mapReadOnly(ContextMtl *context);
361     uint8_t *map(ContextMtl *context);
362     uint8_t *mapWithOpt(ContextMtl *context, bool readonly, bool noSync);
363 
364     void unmap(ContextMtl *context);
365     // Same as unmap but do not do implicit flush()
366     void unmapNoFlush(ContextMtl *context);
367     void unmapAndFlushSubset(ContextMtl *context, size_t offsetWritten, size_t sizeWritten);
368     void flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten);
369 
370     size_t size() const;
371     bool useSharedMem() const;
372 
373     // Explicitly sync content between CPU and GPU
374     void syncContent(ContextMtl *context, mtl::BlitCommandEncoder *encoder);
375 
376   private:
377     Buffer(ContextMtl *context, bool forceUseSharedMem, size_t size, const uint8_t *data);
378     Buffer(ContextMtl *context,
379            MTLResourceOptions resourceOptions,
380            size_t size,
381            const uint8_t *data);
382 
383     bool mMapReadOnly = true;
384 };
385 
386 class NativeTexLevelArray
387 {
388   public:
at(const MipmapNativeLevel & level)389     TextureRef &at(const MipmapNativeLevel &level) { return mTexLevels.at(level.get()); }
at(const MipmapNativeLevel & level)390     const TextureRef &at(const MipmapNativeLevel &level) const
391     {
392         return mTexLevels.at(level.get());
393     }
394 
395     TextureRef &operator[](const MipmapNativeLevel &level) { return at(level); }
396     const TextureRef &operator[](const MipmapNativeLevel &level) const { return at(level); }
397 
begin()398     gl::TexLevelArray<TextureRef>::iterator begin() { return mTexLevels.begin(); }
begin()399     gl::TexLevelArray<TextureRef>::const_iterator begin() const { return mTexLevels.begin(); }
end()400     gl::TexLevelArray<TextureRef>::iterator end() { return mTexLevels.end(); }
end()401     gl::TexLevelArray<TextureRef>::const_iterator end() const { return mTexLevels.end(); }
402 
403   private:
404     gl::TexLevelArray<TextureRef> mTexLevels;
405 };
406 
407 }  // namespace mtl
408 }  // namespace rx
409 
410 #endif /* LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_ */
411