C++程序  |  350行  |  12.72 KB

/*
 * 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 "GrDrawingManager.h"

#include "GrContext.h"
#include "GrRenderTargetContext.h"
#include "GrPathRenderingRenderTargetContext.h"
#include "GrRenderTargetProxy.h"
#include "GrResourceProvider.h"
#include "GrSoftwarePathRenderer.h"
#include "GrSurfacePriv.h"
#include "GrTextureContext.h"
#include "GrTextureOpList.h"
#include "SkSurface_Gpu.h"
#include "SkTTopoSort.h"

#include "text/GrAtlasTextContext.h"
#include "text/GrStencilAndCoverTextContext.h"

void GrDrawingManager::cleanup() {
    for (int i = 0; i < fOpLists.count(); ++i) {
        fOpLists[i]->makeClosed();  // no opList should receive a new command after this
        fOpLists[i]->clearTarget();

        // We shouldn't need to do this, but it turns out some clients still hold onto opLists
        // after a cleanup
        fOpLists[i]->reset();
        fOpLists[i]->unref();
    }

    fOpLists.reset();

    delete fPathRendererChain;
    fPathRendererChain = nullptr;
    SkSafeSetNull(fSoftwarePathRenderer);
}

GrDrawingManager::~GrDrawingManager() {
    this->cleanup();
}

void GrDrawingManager::abandon() {
    fAbandoned = true;
    for (int i = 0; i < fOpLists.count(); ++i) {
        fOpLists[i]->abandonGpuResources();
    }
    this->cleanup();
}

void GrDrawingManager::freeGpuResources() {
    // a path renderer may be holding onto resources
    delete fPathRendererChain;
    fPathRendererChain = nullptr;
    SkSafeSetNull(fSoftwarePathRenderer);
    for (int i = 0; i < fOpLists.count(); ++i) {
        fOpLists[i]->freeGpuResources();
    }
}

void GrDrawingManager::reset() {
    for (int i = 0; i < fOpLists.count(); ++i) {
        fOpLists[i]->reset();
    }
    fFlushState.reset();
}

void GrDrawingManager::internalFlush(GrResourceCache::FlushType type) {
    if (fFlushing || this->wasAbandoned()) {
        return;
    }
    fFlushing = true;
    bool flushed = false;

    for (int i = 0; i < fOpLists.count(); ++i) {
        // Semi-usually the GrOpLists are already closed at this point, but sometimes Ganesh
        // needs to flush mid-draw. In that case, the SkGpuDevice's GrOpLists won't be closed
        // but need to be flushed anyway. Closing such GrOpLists here will mean new
        // GrOpLists will be created to replace them if the SkGpuDevice(s) write to them again.
        fOpLists[i]->makeClosed();
    }

    SkDEBUGCODE(bool result =)
                        SkTTopoSort<GrOpList, GrOpList::TopoSortTraits>(&fOpLists);
    SkASSERT(result);

    GrPreFlushResourceProvider preFlushProvider(this);

    if (fPreFlushCBObjects.count()) {
        // MDB TODO: pre-MDB '1' is the correct pre-allocated size. Post-MDB it will need
        // to be larger.
        SkAutoSTArray<1, uint32_t> opListIds(fOpLists.count());
        for (int i = 0; i < fOpLists.count(); ++i) {
            opListIds[i] = fOpLists[i]->uniqueID();
        }

        SkSTArray<1, sk_sp<GrRenderTargetContext>> renderTargetContexts;
        for (int i = 0; i < fPreFlushCBObjects.count(); ++i) {
            fPreFlushCBObjects[i]->preFlush(&preFlushProvider,
                                            opListIds.get(), opListIds.count(),
                                            &renderTargetContexts);
            if (!renderTargetContexts.count()) {
                continue;       // This is fine. No atlases of this type are required for this flush
            }

            for (int j = 0; j < renderTargetContexts.count(); ++j) {
                GrRenderTargetOpList* opList = renderTargetContexts[j]->getOpList();
                if (!opList) {
                    continue;   // Odd - but not a big deal
                }
                SkDEBUGCODE(opList->validateTargetsSingleRenderTarget());
                opList->prepareOps(&fFlushState);
                if (!opList->executeOps(&fFlushState)) {
                    continue;         // This is bad
                }
            }
            renderTargetContexts.reset();
        }
    }

    for (int i = 0; i < fOpLists.count(); ++i) {
        fOpLists[i]->prepareOps(&fFlushState);
    }

#if 0
    // Enable this to print out verbose GrOp information
    for (int i = 0; i < fOpLists.count(); ++i) {
        SkDEBUGCODE(fOpLists[i]->dump();)
    }
#endif

    // Upload all data to the GPU
    fFlushState.preIssueDraws();

    for (int i = 0; i < fOpLists.count(); ++i) {
        if (fOpLists[i]->executeOps(&fFlushState)) {
            flushed = true;
        }
    }

    SkASSERT(fFlushState.nextDrawToken() == fFlushState.nextTokenToFlush());

    for (int i = 0; i < fOpLists.count(); ++i) {
        fOpLists[i]->reset();
#ifdef ENABLE_MDB
        fOpLists[i]->unref();
#endif
    }

#ifndef ENABLE_MDB
    // When MDB is disabled we keep reusing the same GrOpList
    if (fOpLists.count()) {
        SkASSERT(fOpLists.count() == 1);
        // Clear out this flag so the topological sort's SkTTopoSort_CheckAllUnmarked check
        // won't bark
        fOpLists[0]->resetFlag(GrOpList::kWasOutput_Flag);
    }
#else
    fOpLists.reset();
#endif

    fFlushState.reset();
    // We always have to notify the cache when it requested a flush so it can reset its state.
    if (flushed || type == GrResourceCache::FlushType::kCacheRequested) {
        fContext->getResourceCache()->notifyFlushOccurred(type);
    }
    fFlushing = false;
}

void GrDrawingManager::prepareSurfaceForExternalIO(GrSurface* surface) {
    if (this->wasAbandoned()) {
        return;
    }
    SkASSERT(surface);
    SkASSERT(surface->getContext() == fContext);

    if (surface->surfacePriv().hasPendingIO()) {
        this->flush();
    }

    GrRenderTarget* rt = surface->asRenderTarget();
    if (fContext->getGpu() && rt) {
        fContext->getGpu()->resolveRenderTarget(rt);
    }
}

void GrDrawingManager::addPreFlushCallbackObject(sk_sp<GrPreFlushCallbackObject> preFlushCBObject) {
    fPreFlushCBObjects.push_back(preFlushCBObject);
}

GrRenderTargetOpList* GrDrawingManager::newOpList(GrRenderTargetProxy* rtp) {
    SkASSERT(fContext);

#ifndef ENABLE_MDB
    // When MDB is disabled we always just return the single GrOpList
    if (fOpLists.count()) {
        SkASSERT(fOpLists.count() == 1);
        // In the non-MDB-world the same GrOpList gets reused for multiple render targets.
        // Update this pointer so all the asserts are happy
        rtp->setLastOpList(fOpLists[0]);
        // DrawingManager gets the creation ref - this ref is for the caller

        // TODO: although this is true right now it isn't cool
        return SkRef((GrRenderTargetOpList*) fOpLists[0]);
    }
#endif

    GrRenderTargetOpList* opList = new GrRenderTargetOpList(rtp,
                                                            fContext->getGpu(),
                                                            fContext->resourceProvider(),
                                                            fContext->getAuditTrail(),
                                                            fOptionsForOpLists);

    *fOpLists.append() = opList;

    // DrawingManager gets the creation ref - this ref is for the caller
    return SkRef(opList);
}

GrTextureOpList* GrDrawingManager::newOpList(GrTextureProxy* textureProxy) {
    SkASSERT(fContext);

    GrTextureOpList* opList = new GrTextureOpList(textureProxy, fContext->getGpu(),
                                                  fContext->getAuditTrail());

#ifndef ENABLE_MDB
    // When MDB is disabled we still create a new GrOpList, but don't store or ref it - we rely
    // on the caller to immediately execute and free it.
    return opList;
#else
    *fOpLists.append() = opList;

    // Drawing manager gets the creation ref - this ref is for the caller
    return SkRef(opList);
#endif
}

GrAtlasTextContext* GrDrawingManager::getAtlasTextContext() {
    if (!fAtlasTextContext) {
        fAtlasTextContext.reset(GrAtlasTextContext::Create());
    }

    return fAtlasTextContext.get();
}

/*
 * This method finds a path renderer that can draw the specified path on
 * the provided target.
 * Due to its expense, the software path renderer has split out so it can
 * can be individually allowed/disallowed via the "allowSW" boolean.
 */
GrPathRenderer* GrDrawingManager::getPathRenderer(const GrPathRenderer::CanDrawPathArgs& args,
                                                  bool allowSW,
                                                  GrPathRendererChain::DrawType drawType,
                                                  GrPathRenderer::StencilSupport* stencilSupport) {

    if (!fPathRendererChain) {
        fPathRendererChain = new GrPathRendererChain(fContext, fOptionsForPathRendererChain);
    }

    GrPathRenderer* pr = fPathRendererChain->getPathRenderer(args, drawType, stencilSupport);
    if (!pr && allowSW) {
        if (!fSoftwarePathRenderer) {
            fSoftwarePathRenderer =
                    new GrSoftwarePathRenderer(fContext->resourceProvider(),
                                               fOptionsForPathRendererChain.fAllowPathMaskCaching);
        }
        if (fSoftwarePathRenderer->canDrawPath(args)) {
            pr = fSoftwarePathRenderer;
        }
    }

    return pr;
}

sk_sp<GrRenderTargetContext> GrDrawingManager::makeRenderTargetContext(
                                                            sk_sp<GrSurfaceProxy> sProxy,
                                                            sk_sp<SkColorSpace> colorSpace,
                                                            const SkSurfaceProps* surfaceProps) {
    if (this->wasAbandoned() || !sProxy->asRenderTargetProxy()) {
        return nullptr;
    }

    // SkSurface catches bad color space usage at creation. This check handles anything that slips
    // by, including internal usage. We allow a null color space here, for read/write pixels and
    // other special code paths. If a color space is provided, though, enforce all other rules.
    if (colorSpace && !SkSurface_Gpu::Valid(fContext, sProxy->config(), colorSpace.get())) {
        SkDEBUGFAIL("Invalid config and colorspace combination");
        return nullptr;
    }

    sk_sp<GrRenderTargetProxy> rtp(sk_ref_sp(sProxy->asRenderTargetProxy()));

    bool useDIF = false;
    if (surfaceProps) {
        useDIF = surfaceProps->isUseDeviceIndependentFonts();
    }

    if (useDIF && fContext->caps()->shaderCaps()->pathRenderingSupport() &&
        rtp->isStencilBufferMultisampled()) {
        // TODO: defer stencil buffer attachment for PathRenderingDrawContext
        sk_sp<GrRenderTarget> rt(sk_ref_sp(rtp->instantiate(fContext->resourceProvider())));
        if (!rt) {
            return nullptr;
        }
        GrStencilAttachment* sb = fContext->resourceProvider()->attachStencilAttachment(rt.get());
        if (sb) {
            return sk_sp<GrRenderTargetContext>(new GrPathRenderingRenderTargetContext(
                                                        fContext, this, std::move(rtp),
                                                        std::move(colorSpace), surfaceProps,
                                                        fContext->getAuditTrail(), fSingleOwner));
        }
    }

    return sk_sp<GrRenderTargetContext>(new GrRenderTargetContext(fContext, this, std::move(rtp),
                                                                  std::move(colorSpace),
                                                                  surfaceProps,
                                                                  fContext->getAuditTrail(),
                                                                  fSingleOwner));
}

sk_sp<GrTextureContext> GrDrawingManager::makeTextureContext(sk_sp<GrSurfaceProxy> sProxy,
                                                             sk_sp<SkColorSpace> colorSpace) {
    if (this->wasAbandoned() || !sProxy->asTextureProxy()) {
        return nullptr;
    }

    // SkSurface catches bad color space usage at creation. This check handles anything that slips
    // by, including internal usage. We allow a null color space here, for read/write pixels and
    // other special code paths. If a color space is provided, though, enforce all other rules.
    if (colorSpace && !SkSurface_Gpu::Valid(fContext, sProxy->config(), colorSpace.get())) {
        SkDEBUGFAIL("Invalid config and colorspace combination");
        return nullptr;
    }

    // GrTextureRenderTargets should always be using GrRenderTargetContext
    SkASSERT(!sProxy->asRenderTargetProxy());

    sk_sp<GrTextureProxy> textureProxy(sk_ref_sp(sProxy->asTextureProxy()));

    return sk_sp<GrTextureContext>(new GrTextureContext(fContext, this, std::move(textureProxy),
                                                        std::move(colorSpace),
                                                        fContext->getAuditTrail(),
                                                        fSingleOwner));
}