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    NSUInteger options = 0;
39    if (@available(macOS 10.11, iOS 9.0, *)) {
40        if (fIsDynamic) {
41#ifdef SK_BUILD_FOR_MAC
42            options |= MTLResourceStorageModeManaged;
43#else
44            options |= MTLResourceStorageModeShared;
45#endif
46        } else {
47            options |= MTLResourceStorageModePrivate;
48        }
49    }
50#ifdef SK_BUILD_FOR_MAC
51    // Mac requires 4-byte alignment for copies so we need
52    // to ensure we have space for the extra data
53    size = SkAlign4(size);
54#endif
55    fMtlBuffer = size == 0 ? nil :
56            [gpu->device() newBufferWithLength: size
57                                       options: options];
58    this->registerWithCache(SkBudgeted::kYes);
59    VALIDATE();
60}
61
62GrMtlBuffer::~GrMtlBuffer() {
63    SkASSERT(fMtlBuffer == nil);
64    SkASSERT(fMappedBuffer == nil);
65    SkASSERT(fMapPtr == nullptr);
66}
67
68bool GrMtlBuffer::onUpdateData(const void* src, size_t srcInBytes) {
69    if (!fIsDynamic) {
70        if (fMtlBuffer == nil) {
71            return false;
72        }
73        if (srcInBytes > fMtlBuffer.length) {
74            return false;
75        }
76    }
77    VALIDATE();
78
79    this->internalMap(srcInBytes);
80    if (fMapPtr == nil) {
81        return false;
82    }
83    SkASSERT(fMappedBuffer);
84    if (!fIsDynamic) {
85        SkASSERT(SkAlign4(srcInBytes) == fMappedBuffer.length);
86    }
87    memcpy(fMapPtr, src, srcInBytes);
88    this->internalUnmap(srcInBytes);
89
90    VALIDATE();
91    return true;
92}
93
94inline GrMtlGpu* GrMtlBuffer::mtlGpu() const {
95    SkASSERT(!this->wasDestroyed());
96    return static_cast<GrMtlGpu*>(this->getGpu());
97}
98
99void GrMtlBuffer::onAbandon() {
100    fMtlBuffer = nil;
101    fMappedBuffer = nil;
102    fMapPtr = nullptr;
103    VALIDATE();
104    INHERITED::onAbandon();
105}
106
107void GrMtlBuffer::onRelease() {
108    if (!this->wasDestroyed()) {
109        VALIDATE();
110        fMtlBuffer = nil;
111        fMappedBuffer = nil;
112        fMapPtr = nullptr;
113        VALIDATE();
114    }
115    INHERITED::onRelease();
116}
117
118void GrMtlBuffer::internalMap(size_t sizeInBytes) {
119    if (this->wasDestroyed()) {
120        return;
121    }
122    VALIDATE();
123    SkASSERT(!this->isMapped());
124    if (fIsDynamic) {
125        fMappedBuffer = fMtlBuffer;
126        fMapPtr = static_cast<char*>(fMtlBuffer.contents) + fOffset;
127    } else {
128        SkASSERT(fMtlBuffer);
129        SkASSERT(fMappedBuffer == nil);
130        NSUInteger options = 0;
131        if (@available(macOS 10.11, iOS 9.0, *)) {
132            options |= MTLResourceStorageModeShared;
133        }
134#ifdef SK_BUILD_FOR_MAC
135        // Mac requires 4-byte alignment for copies so we pad this out
136        sizeInBytes = SkAlign4(sizeInBytes);
137#endif
138        fMappedBuffer =
139                [this->mtlGpu()->device() newBufferWithLength: sizeInBytes
140                                                      options: options];
141        fMapPtr = fMappedBuffer.contents;
142    }
143    VALIDATE();
144}
145
146void GrMtlBuffer::internalUnmap(size_t sizeInBytes) {
147    SkASSERT(fMtlBuffer);
148    if (this->wasDestroyed()) {
149        return;
150    }
151    VALIDATE();
152    SkASSERT(this->isMapped());
153    if (fMtlBuffer == nil) {
154        fMappedBuffer = nil;
155        fMapPtr = nullptr;
156        return;
157    }
158#ifdef SK_BUILD_FOR_MAC
159    // In both cases the size needs to be 4-byte aligned on Mac
160    sizeInBytes = SkAlign4(sizeInBytes);
161#endif
162    if (fIsDynamic) {
163#ifdef SK_BUILD_FOR_MAC
164        SkASSERT(0 == (fOffset & 0x3));  // should be 4-byte aligned
165        [fMtlBuffer didModifyRange: NSMakeRange(fOffset, sizeInBytes)];
166#endif
167    } else {
168        GrMtlCommandBuffer* cmdBuffer = this->mtlGpu()->commandBuffer();
169        id<MTLBlitCommandEncoder> blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
170        [blitCmdEncoder copyFromBuffer: fMappedBuffer
171                          sourceOffset: 0
172                              toBuffer: fMtlBuffer
173                     destinationOffset: 0
174                                  size: sizeInBytes];
175    }
176    fMappedBuffer = nil;
177    fMapPtr = nullptr;
178}
179
180void GrMtlBuffer::onMap() {
181    this->internalMap(this->size());
182}
183
184void GrMtlBuffer::onUnmap() {
185    this->internalUnmap(this->size());
186}
187
188#ifdef SK_DEBUG
189void GrMtlBuffer::validate() const {
190    SkASSERT(fMtlBuffer == nil ||
191             this->intendedType() == GrGpuBufferType::kVertex ||
192             this->intendedType() == GrGpuBufferType::kIndex ||
193             this->intendedType() == GrGpuBufferType::kXferCpuToGpu ||
194             this->intendedType() == GrGpuBufferType::kXferGpuToCpu);
195    SkASSERT(fMappedBuffer == nil || fMtlBuffer == nil ||
196             fMappedBuffer.length <= fMtlBuffer.length);
197}
198#endif
199