// // Copyright 2019 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // mtl_resources.h: // Declares wrapper classes for Metal's MTLTexture and MTLBuffer. // #ifndef LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_ #define LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_ #import #include #include #include "common/FastVector.h" #include "common/MemoryBuffer.h" #include "common/angleutils.h" #include "libANGLE/Error.h" #include "libANGLE/angletypes.h" #include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/renderer/metal/mtl_format_utils.h" namespace rx { class ContextMtl; namespace mtl { class ContextDevice; class CommandQueue; class BlitCommandEncoder; class Resource; class Texture; class Buffer; using ResourceRef = std::shared_ptr; using TextureRef = std::shared_ptr; using TextureWeakRef = std::weak_ptr; using BufferRef = std::shared_ptr; using BufferWeakRef = std::weak_ptr; class Resource : angle::NonCopyable { public: virtual ~Resource() {} // Check whether the resource still being used by GPU including the pending (uncommitted) // command buffer. bool isBeingUsedByGPU(Context *context) const; // Checks whether the last command buffer that uses the given resource has been committed or not bool hasPendingWorks(Context *context) const; void setUsedByCommandBufferWithQueueSerial(uint64_t serial, bool writing); void setWrittenToByRenderEncoder(uint64_t serial); uint64_t getCommandBufferQueueSerial() const { return mUsageRef->cmdBufferQueueSerial; } // Flag indicate whether we should synchronize the content to CPU after GPU changed this // resource's content. bool isCPUReadMemNeedSync() const { return mUsageRef->cpuReadMemNeedSync; } void resetCPUReadMemNeedSync() { mUsageRef->cpuReadMemNeedSync = false; } bool isCPUReadMemSyncPending() const { return mUsageRef->cpuReadMemSyncPending; } void setCPUReadMemSyncPending(bool value) const { mUsageRef->cpuReadMemSyncPending = value; } void resetCPUReadMemSyncPending() { mUsageRef->cpuReadMemSyncPending = false; } bool isCPUReadMemDirty() const { return mUsageRef->cpuReadMemDirty; } void resetCPUReadMemDirty() { mUsageRef->cpuReadMemDirty = false; } bool getLastWritingRenderEncoderSerial() const { return mUsageRef->lastWritingRenderEncoderSerial; } void setLastWritingRenderEncoderSerial(uint64_t serial) const { mUsageRef->lastWritingRenderEncoderSerial = serial; } virtual size_t estimatedByteSize() const = 0; virtual id getID() const = 0; protected: Resource(); // Share the GPU usage ref with other resource Resource(Resource *other); void reset(); private: struct UsageRef { // The id of the last command buffer that is using this resource. uint64_t cmdBufferQueueSerial = 0; // This flag means the resource was issued to be modified by GPU, if CPU wants to read // its content, explicit synchronization call must be invoked. bool cpuReadMemNeedSync = false; // This flag is set when synchronization for the resource has been // encoded on the GPU, and a map operation must wait // until it's completed. bool cpuReadMemSyncPending = false; // This flag is useful for BufferMtl to know whether it should update the shadow copy bool cpuReadMemDirty = false; // The id of the last render encoder to write to this resource uint64_t lastWritingRenderEncoderSerial = 0; }; // One resource object might just be a view of another resource. For example, a texture 2d // object might be a view of one face of a cube texture object. Another example is one texture // object of size 2x2 might be a mipmap view of a texture object size 4x4. Thus, if one object // is being used by a command buffer, it means the other object is being used also. In this // case, the two objects must share the same UsageRef property. std::shared_ptr mUsageRef; }; class Texture final : public Resource, public WrappedObject>, public std::enable_shared_from_this { public: static angle::Result Make2DTexture(ContextMtl *context, const Format &format, uint32_t width, uint32_t height, uint32_t mips /** use zero to create full mipmaps chain */, bool renderTargetOnly, bool allowFormatView, TextureRef *refOut); // On macOS, memory will still be allocated for this texture. static angle::Result MakeMemoryLess2DTexture(ContextMtl *context, const Format &format, uint32_t width, uint32_t height, TextureRef *refOut); static angle::Result MakeCubeTexture(ContextMtl *context, const Format &format, uint32_t size, uint32_t mips /** use zero to create full mipmaps chain */, bool renderTargetOnly, bool allowFormatView, TextureRef *refOut); static angle::Result Make2DMSTexture(ContextMtl *context, const Format &format, uint32_t width, uint32_t height, uint32_t samples, bool renderTargetOnly, bool allowFormatView, TextureRef *refOut); static angle::Result Make2DArrayTexture(ContextMtl *context, const Format &format, uint32_t width, uint32_t height, uint32_t mips, uint32_t arrayLength, bool renderTargetOnly, bool allowFormatView, TextureRef *refOut); static angle::Result Make3DTexture(ContextMtl *context, const Format &format, uint32_t width, uint32_t height, uint32_t depth, uint32_t mips, bool renderTargetOnly, bool allowFormatView, TextureRef *refOut); static TextureRef MakeFromMetal(id metalTexture); // Allow CPU to read & write data directly to this texture? bool isCPUAccessible() const; // Allow shaders to read/sample this texture? // Texture created with renderTargetOnly flag won't be readable bool isShaderReadable() const; // Allow shaders to write this texture? bool isShaderWritable() const; bool supportFormatView() const; void replace2DRegion(ContextMtl *context, const MTLRegion ®ion, const MipmapNativeLevel &mipmapLevel, uint32_t slice, const uint8_t *data, size_t bytesPerRow); void replaceRegion(ContextMtl *context, const MTLRegion ®ion, const MipmapNativeLevel &mipmapLevel, uint32_t slice, const uint8_t *data, size_t bytesPerRow, size_t bytesPer2DImage); void getBytes(ContextMtl *context, size_t bytesPerRow, size_t bytesPer2DInage, const MTLRegion ®ion, const MipmapNativeLevel &mipmapLevel, uint32_t slice, uint8_t *dataOut); // Create 2d view of a cube face which full range of mip levels. TextureRef createCubeFaceView(uint32_t face); // Create a view of one slice at a level. TextureRef createSliceMipView(uint32_t slice, const MipmapNativeLevel &level); // Create a view of a level. TextureRef createMipView(const MipmapNativeLevel &level); // Create a view with different format TextureRef createViewWithDifferentFormat(MTLPixelFormat format); // Create a view for a shader image binding. TextureRef createShaderImageView(const MipmapNativeLevel &level, int layer, MTLPixelFormat format); // Same as above but the target format must be compatible, for example sRGB to linear. In this // case texture doesn't need format view usage flag. TextureRef createViewWithCompatibleFormat(MTLPixelFormat format); // Create a swizzled view TextureRef createSwizzleView(MTLPixelFormat format, const TextureSwizzleChannels &swizzle); MTLTextureType textureType() const; MTLPixelFormat pixelFormat() const; uint32_t mipmapLevels() const; uint32_t arrayLength() const; uint32_t cubeFacesOrArrayLength() const; uint32_t width(const MipmapNativeLevel &level) const; uint32_t height(const MipmapNativeLevel &level) const; uint32_t depth(const MipmapNativeLevel &level) const; gl::Extents size(const MipmapNativeLevel &level) const; gl::Extents size(const ImageNativeIndex &index) const; uint32_t widthAt0() const { return width(kZeroNativeMipLevel); } uint32_t heightAt0() const { return height(kZeroNativeMipLevel); } uint32_t depthAt0() const { return depth(kZeroNativeMipLevel); } gl::Extents sizeAt0() const { return size(kZeroNativeMipLevel); } uint32_t samples() const; bool hasIOSurface() const; bool sameTypeAndDimemsionsAs(const TextureRef &other) const; angle::Result resize(ContextMtl *context, uint32_t width, uint32_t height); // For render target MTLColorWriteMask getColorWritableMask() const { return *mColorWritableMask; } void setColorWritableMask(MTLColorWriteMask mask) { *mColorWritableMask = mask; } // Get reading copy. Used for reading non-readable texture or reading stencil value from // packed depth & stencil texture. // NOTE: this only copies 1 depth slice of the 3D texture. // The texels will be copied to region(0, 0, 0, areaToCopy.size) of the returned texture. // The returned pointer will be retained by the original texture object. // Calling getReadableCopy() will overwrite previously returned texture. TextureRef getReadableCopy(ContextMtl *context, mtl::BlitCommandEncoder *encoder, const uint32_t levelToCopy, const uint32_t sliceToCopy, const MTLRegion &areaToCopy); void releaseReadableCopy(); // Get stencil view TextureRef getStencilView(); // Get linear color TextureRef getLinearColorView(); // Change the wrapped metal object. Special case for swapchain image void set(id metalTexture); // Explicitly sync content between CPU and GPU void syncContent(ContextMtl *context, mtl::BlitCommandEncoder *encoder); void setEstimatedByteSize(size_t bytes) { mEstimatedByteSize = bytes; } size_t estimatedByteSize() const override { return mEstimatedByteSize; } id getID() const override { return get(); } private: using ParentClass = WrappedObject>; static angle::Result MakeTexture(ContextMtl *context, const Format &mtlFormat, MTLTextureDescriptor *desc, uint32_t mips, bool renderTargetOnly, bool allowFormatView, TextureRef *refOut); static angle::Result MakeTexture(ContextMtl *context, const Format &mtlFormat, MTLTextureDescriptor *desc, uint32_t mips, bool renderTargetOnly, bool allowFormatView, bool memoryLess, TextureRef *refOut); static angle::Result MakeTexture(ContextMtl *context, const Format &mtlFormat, MTLTextureDescriptor *desc, IOSurfaceRef surfaceRef, NSUInteger slice, bool renderTargetOnly, TextureRef *refOut); Texture(id metalTexture); Texture(ContextMtl *context, MTLTextureDescriptor *desc, uint32_t mips, bool renderTargetOnly, bool allowFormatView); Texture(ContextMtl *context, MTLTextureDescriptor *desc, uint32_t mips, bool renderTargetOnly, bool allowFormatView, bool memoryLess); Texture(ContextMtl *context, MTLTextureDescriptor *desc, IOSurfaceRef iosurface, NSUInteger plane, bool renderTargetOnly); // Create a texture view Texture(Texture *original, MTLPixelFormat format); Texture(Texture *original, MTLTextureType type, NSRange mipmapLevelRange, NSRange slices); Texture(Texture *original, MTLPixelFormat format, const TextureSwizzleChannels &swizzle); // Creates a view for a shader image binding. Texture(Texture *original, MTLTextureType type, const MipmapNativeLevel &level, int layer, MTLPixelFormat pixelFormat); void syncContentIfNeeded(ContextMtl *context); AutoObjCObj mCreationDesc; // This property is shared between this object and its views: std::shared_ptr mColorWritableMask; // Linear view of sRGB texture TextureRef mLinearColorView; TextureRef mStencilView; // Readable copy of texture TextureRef mReadCopy; size_t mEstimatedByteSize = 0; }; class Buffer final : public Resource, public WrappedObject> { public: static MTLStorageMode getStorageModeForSharedBuffer(ContextMtl *contextMtl); using Usage = gl::BufferUsage; static MTLStorageMode getStorageModeForUsage(ContextMtl *context, Usage usage); static angle::Result MakeBuffer(ContextMtl *context, size_t size, const uint8_t *data, BufferRef *bufferOut); static angle::Result MakeBufferWithStorageMode(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data, BufferRef *bufferOut); angle::Result reset(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data); const uint8_t *mapReadOnly(ContextMtl *context); uint8_t *map(ContextMtl *context); uint8_t *mapWithOpt(ContextMtl *context, bool readonly, bool noSync); void unmap(ContextMtl *context); // Same as unmap but do not do implicit flush() void unmapNoFlush(ContextMtl *context); void unmapAndFlushSubset(ContextMtl *context, size_t offsetWritten, size_t sizeWritten); void flush(ContextMtl *context, size_t offsetWritten, size_t sizeWritten); size_t size() const; MTLStorageMode storageMode() const; // Explicitly sync content between CPU and GPU void syncContent(ContextMtl *context, mtl::BlitCommandEncoder *encoder); size_t estimatedByteSize() const override { return size(); } id getID() const override { return get(); } size_t getNumContextSwitchesAtLastUse() { return mContextSwitchesAtLastUse; } void setNumContextSwitchesAtLastUse(size_t num) { mContextSwitchesAtLastUse = num; } size_t getNumCommandBufferCommitsAtLastUse() { return mCommandBufferCommitsAtLastUse; } void setNumCommandBufferCommitsAtLastUse(size_t num) { mCommandBufferCommitsAtLastUse = num; } private: Buffer(ContextMtl *context, MTLStorageMode storageMode, size_t size, const uint8_t *data); bool mMapReadOnly = true; // For garbage collecting shadow buffers in BufferManager. size_t mContextSwitchesAtLastUse = 0; size_t mCommandBufferCommitsAtLastUse = 0; }; class NativeTexLevelArray { public: TextureRef &at(const MipmapNativeLevel &level) { return mTexLevels.at(level.get()); } const TextureRef &at(const MipmapNativeLevel &level) const { return mTexLevels.at(level.get()); } TextureRef &operator[](const MipmapNativeLevel &level) { return at(level); } const TextureRef &operator[](const MipmapNativeLevel &level) const { return at(level); } gl::TexLevelArray::iterator begin() { return mTexLevels.begin(); } gl::TexLevelArray::const_iterator begin() const { return mTexLevels.begin(); } gl::TexLevelArray::iterator end() { return mTexLevels.end(); } gl::TexLevelArray::const_iterator end() const { return mTexLevels.end(); } private: gl::TexLevelArray mTexLevels; }; } // namespace mtl } // namespace rx #endif /* LIBANGLE_RENDERER_METAL_MTL_RESOURCES_H_ */