| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "GrMtlBuffer.h" |
| #include "GrMtlGpu.h" |
| #include "GrGpuResourcePriv.h" |
| #include "GrTypesPriv.h" |
| |
| #ifdef SK_DEBUG |
| #define VALIDATE() this->validate() |
| #else |
| #define VALIDATE() do {} while(false) |
| #endif |
| |
| sk_sp<GrMtlBuffer> GrMtlBuffer::Make(GrMtlGpu* gpu, size_t size, GrGpuBufferType intendedType, |
| GrAccessPattern accessPattern, const void* data) { |
| sk_sp<GrMtlBuffer> buffer(new GrMtlBuffer(gpu, size, intendedType, accessPattern)); |
| if (data && !buffer->onUpdateData(data, size)) { |
| return nullptr; |
| } |
| return buffer; |
| } |
| |
| GrMtlBuffer::GrMtlBuffer(GrMtlGpu* gpu, size_t size, GrGpuBufferType intendedType, |
| GrAccessPattern accessPattern) |
| : INHERITED(gpu, size, intendedType, accessPattern) |
| , fIsDynamic(accessPattern == kDynamic_GrAccessPattern) { |
| // TODO: We are treating all buffers as static access since we don't have an implementation to |
| // synchronize gpu and cpu access of a resource yet. See comments in GrMtlBuffer::internalMap() |
| // and interalUnmap() for more details. |
| fIsDynamic = false; |
| |
| // The managed resource mode is only available for macOS. iOS should use shared. |
| fMtlBuffer = size == 0 ? nil : |
| [gpu->device() newBufferWithLength: size |
| options: !fIsDynamic ? MTLResourceStorageModePrivate |
| #ifdef SK_BUILD_FOR_MAC |
| : MTLResourceStorageModeManaged]; |
| #else |
| : MTLResourceStorageModeShared]; |
| #endif |
| this->registerWithCache(SkBudgeted::kYes); |
| VALIDATE(); |
| } |
| |
| GrMtlBuffer::~GrMtlBuffer() { |
| SkASSERT(fMtlBuffer == nil); |
| SkASSERT(fMappedBuffer == nil); |
| SkASSERT(fMapPtr == nullptr); |
| } |
| |
| bool GrMtlBuffer::onUpdateData(const void* src, size_t srcInBytes) { |
| if (fMtlBuffer == nil) { |
| return false; |
| } |
| if (srcInBytes > fMtlBuffer.length) { |
| return false; |
| } |
| VALIDATE(); |
| |
| this->internalMap(srcInBytes); |
| if (fMapPtr == nil) { |
| return false; |
| } |
| SkASSERT(fMappedBuffer); |
| SkASSERT(srcInBytes == fMappedBuffer.length); |
| memcpy(fMapPtr, src, srcInBytes); |
| this->internalUnmap(srcInBytes); |
| |
| VALIDATE(); |
| return true; |
| } |
| |
| inline GrMtlGpu* GrMtlBuffer::mtlGpu() const { |
| SkASSERT(!this->wasDestroyed()); |
| return static_cast<GrMtlGpu*>(this->getGpu()); |
| } |
| |
| void GrMtlBuffer::onAbandon() { |
| fMtlBuffer = nil; |
| fMappedBuffer = nil; |
| fMapPtr = nullptr; |
| VALIDATE(); |
| INHERITED::onAbandon(); |
| } |
| |
| void GrMtlBuffer::onRelease() { |
| if (!this->wasDestroyed()) { |
| VALIDATE(); |
| fMtlBuffer = nil; |
| fMappedBuffer = nil; |
| fMapPtr = nullptr; |
| VALIDATE(); |
| } |
| INHERITED::onRelease(); |
| } |
| |
| void GrMtlBuffer::internalMap(size_t sizeInBytes) { |
| SkASSERT(fMtlBuffer); |
| if (this->wasDestroyed()) { |
| return; |
| } |
| VALIDATE(); |
| SkASSERT(!this->isMapped()); |
| if (fIsDynamic) { |
| // TODO: We will want to decide if we need to create a new buffer here in order to avoid |
| // possibly invalidating a buffer which is being used by the gpu. |
| fMappedBuffer = fMtlBuffer; |
| fMapPtr = fMappedBuffer.contents; |
| } else { |
| SK_BEGIN_AUTORELEASE_BLOCK |
| // TODO: We can't ensure that map will only be called once on static access buffers until |
| // we actually enable dynamic access. |
| // SkASSERT(fMappedBuffer == nil); |
| fMappedBuffer = |
| [this->mtlGpu()->device() newBufferWithLength: sizeInBytes |
| #ifdef SK_BUILD_FOR_MAC |
| options: MTLResourceStorageModeManaged]; |
| #else |
| options: MTLResourceStorageModeShared]; |
| #endif |
| fMapPtr = fMappedBuffer.contents; |
| SK_END_AUTORELEASE_BLOCK |
| } |
| VALIDATE(); |
| } |
| |
| void GrMtlBuffer::internalUnmap(size_t sizeInBytes) { |
| SkASSERT(fMtlBuffer); |
| if (this->wasDestroyed()) { |
| return; |
| } |
| VALIDATE(); |
| SkASSERT(this->isMapped()); |
| if (fMtlBuffer == nil) { |
| fMappedBuffer = nil; |
| fMapPtr = nullptr; |
| return; |
| } |
| #ifdef SK_BUILD_FOR_MAC |
| // TODO: by calling didModifyRange here we invalidate the buffer. This will cause problems for |
| // dynamic access buffers if they are being used by the gpu. |
| [fMappedBuffer didModifyRange: NSMakeRange(0, sizeInBytes)]; |
| #endif |
| if (!fIsDynamic) { |
| SK_BEGIN_AUTORELEASE_BLOCK |
| id<MTLBlitCommandEncoder> blitCmdEncoder = |
| [this->mtlGpu()->commandBuffer() blitCommandEncoder]; |
| [blitCmdEncoder copyFromBuffer: fMappedBuffer |
| sourceOffset: 0 |
| toBuffer: fMtlBuffer |
| destinationOffset: 0 |
| size: sizeInBytes]; |
| [blitCmdEncoder endEncoding]; |
| SK_END_AUTORELEASE_BLOCK |
| } |
| fMappedBuffer = nil; |
| fMapPtr = nullptr; |
| } |
| |
| void GrMtlBuffer::onMap() { |
| this->internalMap(fMtlBuffer.length); |
| } |
| |
| void GrMtlBuffer::onUnmap() { |
| this->internalUnmap(fMappedBuffer.length); |
| } |
| |
| #ifdef SK_DEBUG |
| void GrMtlBuffer::validate() const { |
| SkASSERT(fMtlBuffer == nil || |
| this->intendedType() == GrGpuBufferType::kVertex || |
| this->intendedType() == GrGpuBufferType::kIndex || |
| this->intendedType() == GrGpuBufferType::kXferCpuToGpu || |
| this->intendedType() == GrGpuBufferType::kXferGpuToCpu); |
| SkASSERT(fMappedBuffer == nil || fMtlBuffer == nil || |
| fMappedBuffer.length <= fMtlBuffer.length); |
| SkASSERT(fIsDynamic == false); // TODO: implement synchronization to allow dynamic access. |
| } |
| #endif |