1/*
2 * Copyright 2018 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "include/private/GrTypesPriv.h"
9#include "src/gpu/GrGpuResourcePriv.h"
10#include "src/gpu/mtl/GrMtlBuffer.h"
11#include "src/gpu/mtl/GrMtlCommandBuffer.h"
12#include "src/gpu/mtl/GrMtlGpu.h"
13
14#if !__has_feature(objc_arc)
15#error This file must be compiled with Arc. Use -fobjc-arc flag
16#endif
17
18#ifdef SK_DEBUG
19#define VALIDATE() this->validate()
20#else
21#define VALIDATE() do {} while(false)
22#endif
23
24sk_sp<GrMtlBuffer> GrMtlBuffer::Make(GrMtlGpu* gpu, size_t size, GrGpuBufferType intendedType,
25                                     GrAccessPattern accessPattern, const void* data) {
26    sk_sp<GrMtlBuffer> buffer(new GrMtlBuffer(gpu, size, intendedType, accessPattern));
27    if (data && !buffer->onUpdateData(data, size)) {
28        return nullptr;
29    }
30    return buffer;
31}
32
33GrMtlBuffer::GrMtlBuffer(GrMtlGpu* gpu, size_t size, GrGpuBufferType intendedType,
34                         GrAccessPattern accessPattern)
35        : INHERITED(gpu, size, intendedType, accessPattern)
36        , fIsDynamic(accessPattern != kStatic_GrAccessPattern)
37        , fOffset(0) {
38    // In most cases, we'll allocate dynamic buffers when we map them, below.
39    if (!fIsDynamic) {
40        NSUInteger options = 0;
41        if (@available(macOS 10.11, iOS 9.0, *)) {
42            options |= MTLResourceStorageModePrivate;
43        }
44        fMtlBuffer = size == 0 ? nil :
45                [gpu->device() newBufferWithLength: size
46                                           options: options];
47    }
48    this->registerWithCache(SkBudgeted::kYes);
49    VALIDATE();
50}
51
52GrMtlBuffer::~GrMtlBuffer() {
53    SkASSERT(fMtlBuffer == nil);
54    SkASSERT(fMappedBuffer == nil);
55    SkASSERT(fMapPtr == nullptr);
56}
57
58void GrMtlBuffer::bind() {
59    SkASSERT(fIsDynamic && GrGpuBufferType::kXferGpuToCpu == this->intendedType());
60    fMtlBuffer = this->mtlGpu()->resourceProvider().getDynamicBuffer(this->size(), &fOffset);
61}
62
63bool GrMtlBuffer::onUpdateData(const void* src, size_t srcInBytes) {
64    if (!fIsDynamic) {
65        if (fMtlBuffer == nil) {
66            return false;
67        }
68        if (srcInBytes > fMtlBuffer.length) {
69            return false;
70        }
71    }
72    VALIDATE();
73
74    this->internalMap(srcInBytes);
75    if (fMapPtr == nil) {
76        return false;
77    }
78    SkASSERT(fMappedBuffer);
79    if (!fIsDynamic) {
80        SkASSERT(srcInBytes == fMappedBuffer.length);
81    }
82    memcpy(fMapPtr, src, srcInBytes);
83    this->internalUnmap(srcInBytes);
84
85    VALIDATE();
86    return true;
87}
88
89inline GrMtlGpu* GrMtlBuffer::mtlGpu() const {
90    SkASSERT(!this->wasDestroyed());
91    return static_cast<GrMtlGpu*>(this->getGpu());
92}
93
94void GrMtlBuffer::onAbandon() {
95    fMtlBuffer = nil;
96    fMappedBuffer = nil;
97    fMapPtr = nullptr;
98    VALIDATE();
99    INHERITED::onAbandon();
100}
101
102void GrMtlBuffer::onRelease() {
103    if (!this->wasDestroyed()) {
104        VALIDATE();
105        fMtlBuffer = nil;
106        fMappedBuffer = nil;
107        fMapPtr = nullptr;
108        VALIDATE();
109    }
110    INHERITED::onRelease();
111}
112
113void GrMtlBuffer::internalMap(size_t sizeInBytes) {
114    if (this->wasDestroyed()) {
115        return;
116    }
117    VALIDATE();
118    SkASSERT(!this->isMapped());
119    if (fIsDynamic) {
120        if (GrGpuBufferType::kXferGpuToCpu != this->intendedType()) {
121            fMtlBuffer = this->mtlGpu()->resourceProvider().getDynamicBuffer(sizeInBytes, &fOffset);
122        }
123        fMappedBuffer = fMtlBuffer;
124        fMapPtr = static_cast<char*>(fMtlBuffer.contents) + fOffset;
125    } else {
126        SkASSERT(fMtlBuffer);
127        SkASSERT(fMappedBuffer == nil);
128        NSUInteger options = 0;
129        if (@available(macOS 10.11, iOS 9.0, *)) {
130            options |= MTLResourceStorageModeShared;
131        }
132        fMappedBuffer =
133                [this->mtlGpu()->device() newBufferWithLength: sizeInBytes
134                                                      options: options];
135        fMapPtr = fMappedBuffer.contents;
136    }
137    VALIDATE();
138}
139
140void GrMtlBuffer::internalUnmap(size_t sizeInBytes) {
141    SkASSERT(fMtlBuffer);
142    if (this->wasDestroyed()) {
143        return;
144    }
145    VALIDATE();
146    SkASSERT(this->isMapped());
147    if (fMtlBuffer == nil) {
148        fMappedBuffer = nil;
149        fMapPtr = nullptr;
150        return;
151    }
152    if (fIsDynamic) {
153#ifdef SK_BUILD_FOR_MAC
154        // TODO: need to make sure offset and size have valid alignments.
155        [fMtlBuffer didModifyRange: NSMakeRange(fOffset, sizeInBytes)];
156#endif
157    } else {
158        GrMtlCommandBuffer* cmdBuffer = this->mtlGpu()->commandBuffer();
159        id<MTLBlitCommandEncoder> blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
160        [blitCmdEncoder copyFromBuffer: fMappedBuffer
161                          sourceOffset: 0
162                              toBuffer: fMtlBuffer
163                     destinationOffset: 0
164                                  size: sizeInBytes];
165    }
166    fMappedBuffer = nil;
167    fMapPtr = nullptr;
168}
169
170void GrMtlBuffer::onMap() {
171    this->internalMap(this->size());
172}
173
174void GrMtlBuffer::onUnmap() {
175    this->internalUnmap(this->size());
176}
177
178#ifdef SK_DEBUG
179void GrMtlBuffer::validate() const {
180    SkASSERT(fMtlBuffer == nil ||
181             this->intendedType() == GrGpuBufferType::kVertex ||
182             this->intendedType() == GrGpuBufferType::kIndex ||
183             this->intendedType() == GrGpuBufferType::kXferCpuToGpu ||
184             this->intendedType() == GrGpuBufferType::kXferGpuToCpu);
185    SkASSERT(fMappedBuffer == nil || fMtlBuffer == nil ||
186             fMappedBuffer.length <= fMtlBuffer.length);
187}
188#endif
189