1/*
2 * Copyright 2015 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 "GrResourceProvider.h"
9
10#include "GrBackendSemaphore.h"
11#include "GrBuffer.h"
12#include "GrCaps.h"
13#include "GrContext.h"
14#include "GrContextPriv.h"
15#include "GrGpu.h"
16#include "GrPath.h"
17#include "GrPathRendering.h"
18#include "GrProxyProvider.h"
19#include "GrRenderTargetPriv.h"
20#include "GrResourceCache.h"
21#include "GrResourceKey.h"
22#include "GrSemaphore.h"
23#include "GrStencilAttachment.h"
24#include "GrTexturePriv.h"
25#include "../private/GrSingleOwner.h"
26#include "SkGr.h"
27#include "SkMathPriv.h"
28
29GR_DECLARE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
30
31const uint32_t GrResourceProvider::kMinScratchTextureSize = 16;
32
33#define ASSERT_SINGLE_OWNER \
34    SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(fSingleOwner);)
35
36GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* owner)
37        : fCache(cache)
38        , fGpu(gpu)
39#ifdef SK_DEBUG
40        , fSingleOwner(owner)
41#endif
42        {
43    fCaps = sk_ref_sp(fGpu->caps());
44
45    GR_DEFINE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey);
46    fQuadIndexBufferKey = gQuadIndexBufferKey;
47}
48
49bool validate_desc(const GrSurfaceDesc& desc, const GrCaps& caps, int levelCount = 0) {
50    if (desc.fSampleCnt < 1) {
51        return false;
52    }
53
54    if (desc.fWidth <= 0 || desc.fHeight <= 0) {
55        return false;
56    }
57    if (!caps.isConfigTexturable(desc.fConfig)) {
58        return false;
59    }
60    if (desc.fFlags & kRenderTarget_GrSurfaceFlag) {
61        if (!caps.isConfigRenderable(desc.fConfig, desc.fSampleCnt > 1)) {
62            return false;
63        }
64    } else {
65        if (desc.fSampleCnt > 1) {
66            return false;
67        }
68    }
69    if (levelCount > 1 && (GrPixelConfigIsSint(desc.fConfig) || !caps.mipMapSupport())) {
70        return false;
71    }
72    return true;
73}
74
75sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
76                                                   const GrMipLevel texels[], int mipLevelCount,
77                                                   SkDestinationSurfaceColorMode mipColorMode) {
78    ASSERT_SINGLE_OWNER
79
80    SkASSERT(mipLevelCount > 0);
81
82    if (this->isAbandoned()) {
83        return nullptr;
84    }
85
86    if (!validate_desc(desc, *fCaps, mipLevelCount)) {
87        return nullptr;
88    }
89
90    sk_sp<GrTexture> tex(fGpu->createTexture(desc, budgeted, texels, mipLevelCount));
91    if (tex) {
92        tex->texturePriv().setMipColorMode(mipColorMode);
93    }
94
95    return tex;
96}
97
98sk_sp<GrTexture> GrResourceProvider::getExactScratch(const GrSurfaceDesc& desc,
99                                                     SkBudgeted budgeted, uint32_t flags) {
100    sk_sp<GrTexture> tex(this->refScratchTexture(desc, flags));
101    if (tex && SkBudgeted::kNo == budgeted) {
102        tex->resourcePriv().makeUnbudgeted();
103    }
104
105    return tex;
106}
107
108static bool make_info(int w, int h, GrPixelConfig config, SkImageInfo* ii) {
109    SkColorType colorType;
110    if (!GrPixelConfigToColorType(config, &colorType)) {
111        return false;
112    }
113
114    *ii = SkImageInfo::Make(w, h, colorType, kUnknown_SkAlphaType, nullptr);
115    return true;
116}
117
118sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc,
119                                                   SkBudgeted budgeted,
120                                                   const GrMipLevel& mipLevel) {
121    ASSERT_SINGLE_OWNER
122
123    if (this->isAbandoned()) {
124        return nullptr;
125    }
126
127    if (!mipLevel.fPixels) {
128        return nullptr;
129    }
130
131    if (!validate_desc(desc, *fCaps)) {
132        return nullptr;
133    }
134
135    GrContext* context = fGpu->getContext();
136    GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
137
138    SkImageInfo srcInfo;
139
140    if (make_info(desc.fWidth, desc.fHeight, desc.fConfig, &srcInfo)) {
141        // DDL TODO: remove this use of createInstantiatedProxy and convert it to a testing-only
142        // method.
143        sk_sp<GrTextureProxy> proxy = proxyProvider->createInstantiatedProxy(desc,
144                                                                             SkBackingFit::kExact,
145                                                                             budgeted);
146        if (proxy) {
147            sk_sp<GrSurfaceContext> sContext = context->contextPriv().makeWrappedSurfaceContext(
148                                                                                std::move(proxy));
149            if (sContext) {
150                if (sContext->writePixels(srcInfo, mipLevel.fPixels, mipLevel.fRowBytes, 0, 0)) {
151                    return sk_ref_sp(sContext->asTextureProxy()->priv().peekTexture());
152                }
153            }
154        }
155    }
156
157    return fGpu->createTexture(desc, budgeted, &mipLevel, 1);
158}
159
160sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
161                                                   uint32_t flags) {
162    ASSERT_SINGLE_OWNER
163    if (this->isAbandoned()) {
164        return nullptr;
165    }
166
167    if (!validate_desc(desc, *fCaps)) {
168        return nullptr;
169    }
170
171    sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, flags);
172    if (tex) {
173        return tex;
174    }
175
176    return fGpu->createTexture(desc, budgeted);
177}
178
179sk_sp<GrTexture> GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc,
180                                                         uint32_t flags) {
181    ASSERT_SINGLE_OWNER
182    SkASSERT(0 == flags || kNoPendingIO_Flag == flags);
183
184    if (this->isAbandoned()) {
185        return nullptr;
186    }
187
188    if (!validate_desc(desc, *fCaps)) {
189        return nullptr;
190    }
191
192    if (auto tex = this->refScratchTexture(desc, flags)) {
193        return tex;
194    }
195
196    SkTCopyOnFirstWrite<GrSurfaceDesc> copyDesc(desc);
197
198    // bin by pow2 with a reasonable min
199    if (!SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
200        (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag))) {
201        GrSurfaceDesc* wdesc = copyDesc.writable();
202        wdesc->fWidth  = SkTMax(kMinScratchTextureSize, GrNextPow2(desc.fWidth));
203        wdesc->fHeight = SkTMax(kMinScratchTextureSize, GrNextPow2(desc.fHeight));
204    }
205
206    if (auto tex = this->refScratchTexture(*copyDesc, flags)) {
207        return tex;
208    }
209
210    return fGpu->createTexture(*copyDesc, SkBudgeted::kYes);
211}
212
213sk_sp<GrTexture> GrResourceProvider::refScratchTexture(const GrSurfaceDesc& desc,
214                                                       uint32_t flags) {
215    ASSERT_SINGLE_OWNER
216    SkASSERT(!this->isAbandoned());
217    SkASSERT(validate_desc(desc, *fCaps));
218
219    // We could make initial clears work with scratch textures but it is a rare case so we just opt
220    // to fall back to making a new texture.
221    if (!SkToBool(desc.fFlags & kPerformInitialClear_GrSurfaceFlag) &&
222        (fGpu->caps()->reuseScratchTextures() || (desc.fFlags & kRenderTarget_GrSurfaceFlag))) {
223
224        GrScratchKey key;
225        GrTexturePriv::ComputeScratchKey(desc, &key);
226        uint32_t scratchFlags = 0;
227        if (kNoPendingIO_Flag & flags) {
228            scratchFlags = GrResourceCache::kRequireNoPendingIO_ScratchFlag;
229        } else  if (!(desc.fFlags & kRenderTarget_GrSurfaceFlag)) {
230            // If it is not a render target then it will most likely be populated by
231            // writePixels() which will trigger a flush if the texture has pending IO.
232            scratchFlags = GrResourceCache::kPreferNoPendingIO_ScratchFlag;
233        }
234        GrGpuResource* resource = fCache->findAndRefScratchResource(key,
235                                                                    GrSurface::WorstCaseSize(desc),
236                                                                    scratchFlags);
237        if (resource) {
238            GrSurface* surface = static_cast<GrSurface*>(resource);
239            return sk_sp<GrTexture>(surface->asTexture());
240        }
241    }
242
243    return nullptr;
244}
245
246sk_sp<GrTexture> GrResourceProvider::wrapBackendTexture(const GrBackendTexture& tex,
247                                                        GrWrapOwnership ownership) {
248    ASSERT_SINGLE_OWNER
249    if (this->isAbandoned()) {
250        return nullptr;
251    }
252    return fGpu->wrapBackendTexture(tex, ownership);
253}
254
255sk_sp<GrTexture> GrResourceProvider::wrapRenderableBackendTexture(const GrBackendTexture& tex,
256                                                                  int sampleCnt,
257                                                                  GrWrapOwnership ownership) {
258    ASSERT_SINGLE_OWNER
259    if (this->isAbandoned()) {
260        return nullptr;
261    }
262    return fGpu->wrapRenderableBackendTexture(tex, sampleCnt, ownership);
263}
264
265sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendRenderTarget(
266        const GrBackendRenderTarget& backendRT)
267{
268    ASSERT_SINGLE_OWNER
269    return this->isAbandoned() ? nullptr : fGpu->wrapBackendRenderTarget(backendRT);
270}
271
272void GrResourceProvider::assignUniqueKeyToResource(const GrUniqueKey& key,
273                                                   GrGpuResource* resource) {
274    ASSERT_SINGLE_OWNER
275    if (this->isAbandoned() || !resource) {
276        return;
277    }
278    resource->resourcePriv().setUniqueKey(key);
279}
280
281sk_sp<GrGpuResource> GrResourceProvider::findResourceByUniqueKey(const GrUniqueKey& key) {
282    ASSERT_SINGLE_OWNER
283    return this->isAbandoned() ? nullptr
284                               : sk_sp<GrGpuResource>(fCache->findAndRefUniqueResource(key));
285}
286
287sk_sp<const GrBuffer> GrResourceProvider::findOrMakeStaticBuffer(GrBufferType intendedType,
288                                                                 size_t size,
289                                                                 const void* data,
290                                                                 const GrUniqueKey& key) {
291    if (auto buffer = this->findByUniqueKey<GrBuffer>(key)) {
292        return buffer;
293    }
294    if (auto buffer = this->createBuffer(size, intendedType, kStatic_GrAccessPattern, 0,
295                                         data)) {
296        // We shouldn't bin and/or cachestatic buffers.
297        SkASSERT(buffer->sizeInBytes() == size);
298        SkASSERT(!buffer->resourcePriv().getScratchKey().isValid());
299        SkASSERT(!buffer->resourcePriv().hasPendingIO_debugOnly());
300        buffer->resourcePriv().setUniqueKey(key);
301        return sk_sp<const GrBuffer>(buffer);
302    }
303    return nullptr;
304}
305
306sk_sp<const GrBuffer> GrResourceProvider::createPatternedIndexBuffer(const uint16_t* pattern,
307                                                                     int patternSize,
308                                                                     int reps,
309                                                                     int vertCount,
310                                                                     const GrUniqueKey& key) {
311    size_t bufferSize = patternSize * reps * sizeof(uint16_t);
312
313    // This is typically used in GrMeshDrawOps, so we assume kNoPendingIO.
314    sk_sp<GrBuffer> buffer(this->createBuffer(bufferSize, kIndex_GrBufferType,
315                                              kStatic_GrAccessPattern, kNoPendingIO_Flag));
316    if (!buffer) {
317        return nullptr;
318    }
319    uint16_t* data = (uint16_t*) buffer->map();
320    SkAutoTArray<uint16_t> temp;
321    if (!data) {
322        temp.reset(reps * patternSize);
323        data = temp.get();
324    }
325    for (int i = 0; i < reps; ++i) {
326        int baseIdx = i * patternSize;
327        uint16_t baseVert = (uint16_t)(i * vertCount);
328        for (int j = 0; j < patternSize; ++j) {
329            data[baseIdx+j] = baseVert + pattern[j];
330        }
331    }
332    if (temp.get()) {
333        if (!buffer->updateData(data, bufferSize)) {
334            return nullptr;
335        }
336    } else {
337        buffer->unmap();
338    }
339    this->assignUniqueKeyToResource(key, buffer.get());
340    return std::move(buffer);
341}
342
343static constexpr int kMaxQuads = 1 << 12;  // max possible: (1 << 14) - 1;
344
345sk_sp<const GrBuffer> GrResourceProvider::createQuadIndexBuffer() {
346    GR_STATIC_ASSERT(4 * kMaxQuads <= 65535);
347    static const uint16_t kPattern[] = { 0, 1, 2, 2, 1, 3 };
348    return this->createPatternedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey);
349}
350
351int GrResourceProvider::QuadCountOfQuadBuffer() { return kMaxQuads; }
352
353sk_sp<GrPath> GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) {
354    if (this->isAbandoned()) {
355        return nullptr;
356    }
357
358    SkASSERT(this->gpu()->pathRendering());
359    return this->gpu()->pathRendering()->createPath(path, style);
360}
361
362sk_sp<GrPathRange> GrResourceProvider::createPathRange(GrPathRange::PathGenerator* gen,
363                                                       const GrStyle& style) {
364    if (this->isAbandoned()) {
365        return nullptr;
366    }
367
368    SkASSERT(this->gpu()->pathRendering());
369    return this->gpu()->pathRendering()->createPathRange(gen, style);
370}
371
372sk_sp<GrPathRange> GrResourceProvider::createGlyphs(const SkTypeface* tf,
373                                                    const SkScalerContextEffects& effects,
374                                                    const SkDescriptor* desc,
375                                                    const GrStyle& style) {
376
377    SkASSERT(this->gpu()->pathRendering());
378    return this->gpu()->pathRendering()->createGlyphs(tf, effects, desc, style);
379}
380
381GrBuffer* GrResourceProvider::createBuffer(size_t size, GrBufferType intendedType,
382                                           GrAccessPattern accessPattern, uint32_t flags,
383                                           const void* data) {
384    if (this->isAbandoned()) {
385        return nullptr;
386    }
387    if (kDynamic_GrAccessPattern != accessPattern) {
388        return this->gpu()->createBuffer(size, intendedType, accessPattern, data);
389    }
390    if (!(flags & kRequireGpuMemory_Flag) &&
391        this->gpu()->caps()->preferClientSideDynamicBuffers() &&
392        GrBufferTypeIsVertexOrIndex(intendedType) &&
393        kDynamic_GrAccessPattern == accessPattern) {
394        return GrBuffer::CreateCPUBacked(this->gpu(), size, intendedType, data);
395    }
396
397    // bin by pow2 with a reasonable min
398    static const size_t MIN_SIZE = 1 << 12;
399    size_t allocSize = SkTMax(MIN_SIZE, GrNextSizePow2(size));
400
401    GrScratchKey key;
402    GrBuffer::ComputeScratchKeyForDynamicVBO(allocSize, intendedType, &key);
403    uint32_t scratchFlags = 0;
404    if (flags & kNoPendingIO_Flag) {
405        scratchFlags = GrResourceCache::kRequireNoPendingIO_ScratchFlag;
406    } else {
407        scratchFlags = GrResourceCache::kPreferNoPendingIO_ScratchFlag;
408    }
409    GrBuffer* buffer = static_cast<GrBuffer*>(
410        this->cache()->findAndRefScratchResource(key, allocSize, scratchFlags));
411    if (!buffer) {
412        buffer = this->gpu()->createBuffer(allocSize, intendedType, kDynamic_GrAccessPattern);
413        if (!buffer) {
414            return nullptr;
415        }
416    }
417    if (data) {
418        buffer->updateData(data, size);
419    }
420    SkASSERT(!buffer->isCPUBacked()); // We should only cache real VBOs.
421    return buffer;
422}
423
424bool GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt) {
425    SkASSERT(rt);
426    if (rt->renderTargetPriv().getStencilAttachment()) {
427        return true;
428    }
429
430    if (!rt->wasDestroyed() && rt->canAttemptStencilAttachment()) {
431        GrUniqueKey sbKey;
432
433        int width = rt->width();
434        int height = rt->height();
435#if 0
436        if (this->caps()->oversizedStencilSupport()) {
437            width  = SkNextPow2(width);
438            height = SkNextPow2(height);
439        }
440#endif
441        SkDEBUGCODE(bool newStencil = false;)
442        GrStencilAttachment::ComputeSharedStencilAttachmentKey(width, height,
443                                                               rt->numStencilSamples(), &sbKey);
444        auto stencil = this->findByUniqueKey<GrStencilAttachment>(sbKey);
445        if (!stencil) {
446            // Need to try and create a new stencil
447            stencil.reset(this->gpu()->createStencilAttachmentForRenderTarget(rt, width, height));
448            if (stencil) {
449                this->assignUniqueKeyToResource(sbKey, stencil.get());
450                SkDEBUGCODE(newStencil = true;)
451            }
452        }
453        if (rt->renderTargetPriv().attachStencilAttachment(std::move(stencil))) {
454#ifdef SK_DEBUG
455            // Fill the SB with an inappropriate value. opLists that use the
456            // SB should clear it properly.
457            if (newStencil) {
458                SkASSERT(rt->renderTargetPriv().getStencilAttachment()->isDirty());
459                this->gpu()->clearStencil(rt, 0xFFFF);
460                SkASSERT(rt->renderTargetPriv().getStencilAttachment()->isDirty());
461            }
462#endif
463        }
464    }
465    return SkToBool(rt->renderTargetPriv().getStencilAttachment());
466}
467
468sk_sp<GrRenderTarget> GrResourceProvider::wrapBackendTextureAsRenderTarget(
469        const GrBackendTexture& tex, int sampleCnt)
470{
471    if (this->isAbandoned()) {
472        return nullptr;
473    }
474    return fGpu->wrapBackendTextureAsRenderTarget(tex, sampleCnt);
475}
476
477sk_sp<GrSemaphore> SK_WARN_UNUSED_RESULT GrResourceProvider::makeSemaphore(bool isOwned) {
478    return fGpu->makeSemaphore(isOwned);
479}
480
481sk_sp<GrSemaphore> GrResourceProvider::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
482                                                            SemaphoreWrapType wrapType,
483                                                            GrWrapOwnership ownership) {
484    ASSERT_SINGLE_OWNER
485    return this->isAbandoned() ? nullptr : fGpu->wrapBackendSemaphore(semaphore,
486                                                                      wrapType,
487                                                                      ownership);
488}
489
490void GrResourceProvider::takeOwnershipOfSemaphore(sk_sp<GrSemaphore> semaphore) {
491    semaphore->resetGpu(fGpu);
492}
493
494void GrResourceProvider::releaseOwnershipOfSemaphore(sk_sp<GrSemaphore> semaphore) {
495    semaphore->resetGpu(nullptr);
496}
497