Home | History | Annotate | Download | only in ccpr
      1 /*
      2  * Copyright 2017 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 "GrCCPRAtlas.h"
      9 
     10 #include "GrOnFlushResourceProvider.h"
     11 #include "GrClip.h"
     12 #include "GrRectanizer_skyline.h"
     13 #include "GrTextureProxy.h"
     14 #include "GrRenderTargetContext.h"
     15 #include "SkMakeUnique.h"
     16 #include "SkMathPriv.h"
     17 #include "ccpr/GrCCPRCoverageProcessor.h"
     18 #include "ops/GrDrawOp.h"
     19 
     20 class GrCCPRAtlas::Node {
     21 public:
     22     Node(std::unique_ptr<Node> previous, int l, int t, int r, int b)
     23             : fPrevious(std::move(previous))
     24             , fX(l), fY(t)
     25             , fRectanizer(r - l, b - t) {}
     26 
     27     Node* previous() const { return fPrevious.get(); }
     28 
     29     bool addRect(int w, int h, SkIPoint16* loc) {
     30         static constexpr int kPad = 1;
     31 
     32         if (!fRectanizer.addRect(w + kPad, h + kPad, loc)) {
     33             return false;
     34         }
     35         loc->fX += fX;
     36         loc->fY += fY;
     37         return true;
     38     }
     39 
     40 private:
     41     const std::unique_ptr<Node>   fPrevious;
     42     const int                     fX, fY;
     43     GrRectanizerSkyline           fRectanizer;
     44 };
     45 
     46 GrCCPRAtlas::GrCCPRAtlas(const GrCaps& caps, int minWidth, int minHeight)
     47         : fMaxAtlasSize(caps.maxRenderTargetSize())
     48         , fDrawBounds{0, 0} {
     49     SkASSERT(fMaxAtlasSize <= caps.maxTextureSize());
     50     SkASSERT(SkTMax(minWidth, minHeight) <= fMaxAtlasSize);
     51     int initialSize = GrNextPow2(SkTMax(minWidth, minHeight));
     52     initialSize = SkTMax(int(kMinSize), initialSize);
     53     initialSize = SkTMin(initialSize, fMaxAtlasSize);
     54     fHeight = fWidth = initialSize;
     55     fTopNode = skstd::make_unique<Node>(nullptr, 0, 0, initialSize, initialSize);
     56 }
     57 
     58 GrCCPRAtlas::~GrCCPRAtlas() {
     59 }
     60 
     61 bool GrCCPRAtlas::addRect(int w, int h, SkIPoint16* loc) {
     62     // This can't be called anymore once finalize() has been called.
     63     SkASSERT(!fTextureProxy);
     64 
     65     if (!this->internalPlaceRect(w, h, loc)) {
     66         return false;
     67     }
     68 
     69     fDrawBounds.fWidth = SkTMax(fDrawBounds.width(), loc->x() + w);
     70     fDrawBounds.fHeight = SkTMax(fDrawBounds.height(), loc->y() + h);
     71     return true;
     72 }
     73 
     74 bool GrCCPRAtlas::internalPlaceRect(int w, int h, SkIPoint16* loc) {
     75     SkASSERT(SkTMax(w, h) < fMaxAtlasSize);
     76 
     77     for (Node* node = fTopNode.get(); node; node = node->previous()) {
     78         if (node->addRect(w, h, loc)) {
     79             return true;
     80         }
     81     }
     82 
     83     // The rect didn't fit. Grow the atlas and try again.
     84     do {
     85         SkASSERT(SkTMax(fWidth, fHeight) <= fMaxAtlasSize);
     86         if (fWidth == fMaxAtlasSize && fHeight == fMaxAtlasSize) {
     87             return false;
     88         }
     89         if (fHeight <= fWidth) {
     90             int top = fHeight;
     91             fHeight = SkTMin(fHeight * 2, fMaxAtlasSize);
     92             fTopNode = skstd::make_unique<Node>(std::move(fTopNode), 0, top, fWidth, fHeight);
     93         } else {
     94             int left = fWidth;
     95             fWidth = SkTMin(fWidth * 2, fMaxAtlasSize);
     96             fTopNode = skstd::make_unique<Node>(std::move(fTopNode), left, 0, fWidth, fHeight);
     97         }
     98     } while (!fTopNode->addRect(w, h, loc));
     99 
    100     return true;
    101 }
    102 
    103 sk_sp<GrRenderTargetContext> GrCCPRAtlas::finalize(GrOnFlushResourceProvider* onFlushRP,
    104                                                      std::unique_ptr<GrDrawOp> atlasOp) {
    105     SkASSERT(!fTextureProxy);
    106 
    107     GrSurfaceDesc desc;
    108     desc.fOrigin = GrCCPRCoverageProcessor::kAtlasOrigin;
    109     desc.fWidth = fWidth;
    110     desc.fHeight = fHeight;
    111     desc.fConfig = kAlpha_half_GrPixelConfig;
    112     sk_sp<GrRenderTargetContext> rtc = onFlushRP->makeRenderTargetContext(desc, nullptr, nullptr);
    113     if (!rtc) {
    114         SkDebugf("WARNING: failed to allocate a %ix%i atlas. Some paths will not be drawn.\n",
    115                  fWidth, fHeight);
    116         return nullptr;
    117     }
    118 
    119     SkIRect clearRect = SkIRect::MakeSize(fDrawBounds);
    120     rtc->clear(&clearRect, 0, true);
    121     rtc->addDrawOp(GrNoClip(), std::move(atlasOp));
    122 
    123     fTextureProxy = sk_ref_sp(rtc->asTextureProxy());
    124     return rtc;
    125 }
    126