// // 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. // // BufferMtl.h: // Defines the class interface for BufferMtl, implementing BufferImpl. // #ifndef LIBANGLE_RENDERER_METAL_BUFFERMTL_H_ #define LIBANGLE_RENDERER_METAL_BUFFERMTL_H_ #import #include #include #include "common/span.h" #include "libANGLE/Buffer.h" #include "libANGLE/Observer.h" #include "libANGLE/angletypes.h" #include "libANGLE/renderer/BufferImpl.h" #include "libANGLE/renderer/Format.h" #include "libANGLE/renderer/metal/mtl_buffer_pool.h" #include "libANGLE/renderer/metal/mtl_resources.h" namespace rx { struct DrawCommandRange { uint32_t count; size_t offset; }; // Inclusive range of consecutive primitive restart value indexes. struct IndexRange { IndexRange(size_t begin, size_t end) : restartBegin(begin), restartEnd(end) {} size_t restartBegin; size_t restartEnd; }; // Conversion buffers hold translated index and vertex data. struct ConversionBufferMtl { ConversionBufferMtl(ContextMtl *context, size_t initialSize, size_t alignment); ~ConversionBufferMtl(); // One state value determines if we need to re-stream vertex data. bool dirty; // The conversion is stored in a dynamic buffer. mtl::BufferPool data; // These properties are to be filled by user of this buffer conversion mtl::BufferRef convertedBuffer; size_t convertedOffset; }; struct VertexConversionBufferMtl : public ConversionBufferMtl { VertexConversionBufferMtl(ContextMtl *context, angle::FormatID formatIDIn, GLuint strideIn, size_t offsetIn); // The conversion is identified by the triple of {format, stride, offset}. angle::FormatID formatID; GLuint stride; size_t offset; }; struct IndexConversionBufferMtl : public ConversionBufferMtl { IndexConversionBufferMtl(ContextMtl *context, gl::DrawElementsType elemType, bool primitiveRestartEnabled, size_t offsetIn); const gl::DrawElementsType elemType; const size_t offset; bool primitiveRestartEnabled; IndexRange getRangeForConvertedBuffer(size_t count); }; struct UniformConversionBufferMtl : public ConversionBufferMtl { UniformConversionBufferMtl(ContextMtl *context, std::pair offsetIn, size_t blockSize); size_t initialSrcOffset() { return offset.second; } const size_t uniformBufferBlockSize; const std::pair offset; }; class BufferHolderMtl { public: virtual ~BufferHolderMtl() = default; // Due to the complication of synchronizing accesses between CPU and GPU, // a mtl::Buffer might be under used by GPU but CPU wants to modify its content through // map() method, this could lead to GPU stalling. The more efficient method is maintain // a queue of mtl::Buffer and only let CPU modifies a free mtl::Buffer. // So, in order to let GPU use the most recent modified content, one must call this method // right before the draw call to retrieved the most up-to-date mtl::Buffer. mtl::BufferRef getCurrentBuffer() const { return mIsWeak ? mBufferWeakRef.lock() : mBuffer; } protected: mtl::BufferRef mBuffer; mtl::BufferWeakRef mBufferWeakRef; bool mIsWeak = false; }; class BufferMtl : public BufferImpl, public BufferHolderMtl { public: BufferMtl(const gl::BufferState &state); ~BufferMtl() override; void destroy(const gl::Context *context) override; angle::Result setData(const gl::Context *context, gl::BufferBinding target, const void *data, size_t size, gl::BufferUsage usage, BufferFeedback *feedback) override; angle::Result setSubData(const gl::Context *context, gl::BufferBinding target, const void *data, size_t size, size_t offset, BufferFeedback *feedback) override; angle::Result copySubData(const gl::Context *context, BufferImpl *source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size, BufferFeedback *feedback) override; angle::Result map(const gl::Context *context, GLenum access, void **mapPtr, BufferFeedback *feedback) override; angle::Result mapRange(const gl::Context *context, size_t offset, size_t length, GLbitfield access, void **mapPtr, BufferFeedback *feedback) override; angle::Result unmap(const gl::Context *context, GLboolean *result, BufferFeedback *feedback) override; angle::Result getIndexRange(const gl::Context *context, gl::DrawElementsType type, size_t offset, size_t count, bool primitiveRestartEnabled, gl::IndexRange *outRange) override; void onDataChanged() override; angle::Result getFirstLastIndices(ContextMtl *contextMtl, gl::DrawElementsType type, size_t offset, size_t count, std::pair *outIndices); angle::Span getBufferDataReadOnly(ContextMtl *contextMtl, size_t offset); bool isSafeToReadFromBufferViaBlit(ContextMtl *contextMtl); ConversionBufferMtl *getVertexConversionBuffer(ContextMtl *context, angle::FormatID formatID, GLuint stride, size_t offset); IndexConversionBufferMtl *getIndexConversionBuffer(ContextMtl *context, gl::DrawElementsType elemType, bool primitiveRestartEnabled, size_t offset); ConversionBufferMtl *getUniformConversionBuffer(ContextMtl *context, std::pair offset, size_t blockSize); size_t size() const { return static_cast(mState.getSize()); } const std::vector &getRestartIndices(ContextMtl *ctx, gl::DrawElementsType indexType); static const std::vector getRestartIndicesFromClientData( ContextMtl *ctx, gl::DrawElementsType indexType, const mtl::BufferRef clientBuffer); private: angle::Result allocateNewMetalBuffer(ContextMtl *contextMtl, MTLStorageMode storageMode, size_t size, bool returnOldBufferImmediately, BufferFeedback *feedback); angle::Result setDataImpl(const gl::Context *context, gl::BufferBinding target, size_t size, gl::BufferUsage usage, BufferFeedback *feedback); angle::Result setSubDataImpl(const gl::Context *context, angle::Span data, size_t offset, BufferFeedback *feedback); angle::Result commitShadowCopy(ContextMtl *contextMtl, BufferFeedback *feedback); angle::Result commitShadowCopy(ContextMtl *contextMtl, size_t size, BufferFeedback *feedback); void markConversionBuffersDirty(); void clearConversionBuffers(); angle::Result putDataInNewBufferAndStartUsingNewBuffer(ContextMtl *contextMtl, angle::Span data, size_t offset, BufferFeedback *feedback); angle::Result updateExistingBufferViaBlitFromStagingBuffer(ContextMtl *contextMtl, angle::Span data, size_t offset); angle::Result copyDataToExistingBufferViaCPU(ContextMtl *contextMtl, angle::Span data, size_t offset); angle::Result updateShadowCopyThenCopyShadowToNewBuffer(ContextMtl *contextMtl, angle::Span data, size_t offset, BufferFeedback *feedback); bool clientShadowCopyDataNeedSync(ContextMtl *contextMtl); void ensureShadowCopySyncedFromGPU(ContextMtl *contextMtl); angle::Span syncAndObtainShadowCopy(ContextMtl *contextMtl, size_t offset); angle::Span syncAndObtainShadowCopy(ContextMtl *contextMtl, size_t offset, size_t length); // Optional client side shadow buffer angle::MemoryBuffer mShadowCopy; // A cache of converted vertex data. std::vector mVertexConversionBuffers; std::vector mIndexConversionBuffers; std::vector mUniformConversionBuffers; struct RestartRangeCache { RestartRangeCache(std::vector &&ranges_, gl::DrawElementsType indexType_) : ranges(ranges_), indexType(indexType_) {} const std::vector ranges; const gl::DrawElementsType indexType; }; std::optional mRestartRangeCache; std::vector mRestartIndices; size_t mGLSize = 0; // size GL asked for (vs size we actually allocated) size_t mRevisionCount = 0; // for generating labels only gl::BufferUsage mUsage; }; inline angle::Span BufferMtl::syncAndObtainShadowCopy(ContextMtl *contextMtl, size_t offset, size_t length) { return syncAndObtainShadowCopy(contextMtl, offset).first(length); } class SimpleWeakBufferHolderMtl : public BufferHolderMtl { public: SimpleWeakBufferHolderMtl(); void set(const mtl::BufferRef &buffer) { mBufferWeakRef = buffer; } }; } // namespace rx #endif /* LIBANGLE_RENDERER_METAL_BUFFERMTL_H_ */