/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrResourceProvider.h" #include "GrBuffer.h" #include "GrCaps.h" #include "GrGpu.h" #include "GrPathRendering.h" #include "GrRenderTarget.h" #include "GrRenderTargetPriv.h" #include "GrResourceCache.h" #include "GrResourceKey.h" #include "GrStencilAttachment.h" #include "SkMathPriv.h" GR_DECLARE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey); GrResourceProvider::GrResourceProvider(GrGpu* gpu, GrResourceCache* cache, GrSingleOwner* owner) : INHERITED(gpu, cache, owner) { GR_DEFINE_STATIC_UNIQUE_KEY(gQuadIndexBufferKey); fQuadIndexBufferKey = gQuadIndexBufferKey; } const GrBuffer* GrResourceProvider::createInstancedIndexBuffer(const uint16_t* pattern, int patternSize, int reps, int vertCount, const GrUniqueKey& key) { size_t bufferSize = patternSize * reps * sizeof(uint16_t); // This is typically used in GrMeshDrawOps, so we assume kNoPendingIO. GrBuffer* buffer = this->createBuffer(bufferSize, kIndex_GrBufferType, kStatic_GrAccessPattern, kNoPendingIO_Flag); if (!buffer) { return nullptr; } uint16_t* data = (uint16_t*) buffer->map(); bool useTempData = (nullptr == data); if (useTempData) { data = new uint16_t[reps * patternSize]; } for (int i = 0; i < reps; ++i) { int baseIdx = i * patternSize; uint16_t baseVert = (uint16_t)(i * vertCount); for (int j = 0; j < patternSize; ++j) { data[baseIdx+j] = baseVert + pattern[j]; } } if (useTempData) { if (!buffer->updateData(data, bufferSize)) { buffer->unref(); return nullptr; } delete[] data; } else { buffer->unmap(); } this->assignUniqueKeyToResource(key, buffer); return buffer; } const GrBuffer* GrResourceProvider::createQuadIndexBuffer() { static const int kMaxQuads = 1 << 12; // max possible: (1 << 14) - 1; GR_STATIC_ASSERT(4 * kMaxQuads <= 65535); static const uint16_t kPattern[] = { 0, 1, 2, 0, 2, 3 }; return this->createInstancedIndexBuffer(kPattern, 6, kMaxQuads, 4, fQuadIndexBufferKey); } GrPath* GrResourceProvider::createPath(const SkPath& path, const GrStyle& style) { SkASSERT(this->gpu()->pathRendering()); return this->gpu()->pathRendering()->createPath(path, style); } GrPathRange* GrResourceProvider::createPathRange(GrPathRange::PathGenerator* gen, const GrStyle& style) { SkASSERT(this->gpu()->pathRendering()); return this->gpu()->pathRendering()->createPathRange(gen, style); } GrPathRange* GrResourceProvider::createGlyphs(const SkTypeface* tf, const SkScalerContextEffects& effects, const SkDescriptor* desc, const GrStyle& style) { SkASSERT(this->gpu()->pathRendering()); return this->gpu()->pathRendering()->createGlyphs(tf, effects, desc, style); } GrBuffer* GrResourceProvider::createBuffer(size_t size, GrBufferType intendedType, GrAccessPattern accessPattern, uint32_t flags, const void* data) { if (this->isAbandoned()) { return nullptr; } if (kDynamic_GrAccessPattern != accessPattern) { return this->gpu()->createBuffer(size, intendedType, accessPattern, data); } if (!(flags & kRequireGpuMemory_Flag) && this->gpu()->caps()->preferClientSideDynamicBuffers() && GrBufferTypeIsVertexOrIndex(intendedType) && kDynamic_GrAccessPattern == accessPattern) { return GrBuffer::CreateCPUBacked(this->gpu(), size, intendedType, data); } // bin by pow2 with a reasonable min static const size_t MIN_SIZE = 1 << 12; size_t allocSize = SkTMax(MIN_SIZE, GrNextSizePow2(size)); GrScratchKey key; GrBuffer::ComputeScratchKeyForDynamicVBO(allocSize, intendedType, &key); uint32_t scratchFlags = 0; if (flags & kNoPendingIO_Flag) { scratchFlags = GrResourceCache::kRequireNoPendingIO_ScratchFlag; } else { scratchFlags = GrResourceCache::kPreferNoPendingIO_ScratchFlag; } GrBuffer* buffer = static_cast( this->cache()->findAndRefScratchResource(key, allocSize, scratchFlags)); if (!buffer) { buffer = this->gpu()->createBuffer(allocSize, intendedType, kDynamic_GrAccessPattern); if (!buffer) { return nullptr; } } if (data) { buffer->updateData(data, size); } SkASSERT(!buffer->isCPUBacked()); // We should only cache real VBOs. return buffer; } std::unique_ptr GrResourceProvider::makeAtlas(GrContext* context, GrPixelConfig config, int width, int height, int numPlotsX, int numPlotsY, GrDrawOpAtlas::EvictionFunc func, void* data) { GrSurfaceDesc desc; desc.fFlags = kNone_GrSurfaceFlags; desc.fWidth = width; desc.fHeight = height; desc.fConfig = config; // We don't want to flush the context so we claim we're in the middle of flushing so as to // guarantee we do not recieve a texture with pending IO // TODO: Determine how to avoid having to do this. (https://bug.skia.org/4156) static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag; sk_sp texture(this->createApproxTexture(desc, kFlags)); if (!texture) { return nullptr; } // MDB TODO: for now, wrap an instantiated texture. Having the deferred instantiation // possess the correct properties (e.g., no pendingIO) should fall out of the system but // should receive special attention. // Note: When switching over to the deferred proxy, use the kExact flag to create // the atlas. sk_sp proxy = GrSurfaceProxy::MakeWrapped(std::move(texture)); if (!proxy) { return nullptr; } std::unique_ptr atlas( new GrDrawOpAtlas(context, std::move(proxy), numPlotsX, numPlotsY)); atlas->registerEvictionCallback(func, data); return atlas; } GrStencilAttachment* GrResourceProvider::attachStencilAttachment(GrRenderTarget* rt) { SkASSERT(rt); if (rt->renderTargetPriv().getStencilAttachment()) { return rt->renderTargetPriv().getStencilAttachment(); } if (!rt->wasDestroyed() && rt->canAttemptStencilAttachment()) { GrUniqueKey sbKey; int width = rt->width(); int height = rt->height(); #if 0 if (this->caps()->oversizedStencilSupport()) { width = SkNextPow2(width); height = SkNextPow2(height); } #endif bool newStencil = false; GrStencilAttachment::ComputeSharedStencilAttachmentKey(width, height, rt->numStencilSamples(), &sbKey); GrStencilAttachment* stencil = static_cast( this->findAndRefResourceByUniqueKey(sbKey)); if (!stencil) { // Need to try and create a new stencil stencil = this->gpu()->createStencilAttachmentForRenderTarget(rt, width, height); if (stencil) { stencil->resourcePriv().setUniqueKey(sbKey); newStencil = true; } } if (rt->renderTargetPriv().attachStencilAttachment(stencil)) { if (newStencil) { // Right now we're clearing the stencil attachment here after it is // attached to a RT for the first time. When we start matching // stencil buffers with smaller color targets this will no longer // be correct because it won't be guaranteed to clear the entire // sb. // We used to clear down in the GL subclass using a special purpose // FBO. But iOS doesn't allow a stencil-only FBO. It reports unsupported // FBO status. this->gpu()->clearStencil(rt); } } } return rt->renderTargetPriv().getStencilAttachment(); } sk_sp GrResourceProvider::wrapBackendTextureAsRenderTarget( const GrBackendTextureDesc& desc) { if (this->isAbandoned()) { return nullptr; } return this->gpu()->wrapBackendTextureAsRenderTarget(desc); }