Reland GrRectanizerPow2

This brings back the pow2 rectanizer for use with various ongoing
atlas experiments. If we can further optimize the skyline rectanizer,
then pow2 will be good to have around as a baseline comparison. And
if skyline gets fast enough, then we can delete pow2 again.

Change-Id: I79088c53fba7ba0d120534af99bee7840c135e42
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290810
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2020-05-19 11:57:53 -06:00 committed by Skia Commit-Bot
parent d8fd0bf574
commit e5a141d0f6
10 changed files with 290 additions and 34 deletions

View File

@ -10,7 +10,7 @@
#include "include/private/SkTDArray.h"
#include "include/utils/SkRandom.h"
#include "src/core/SkMathPriv.h"
#include "src/gpu/GrRectanizerPow2.h"
#include "src/gpu/GrRectanizerSkyline.h"
/**
@ -29,6 +29,7 @@ public:
static const int kHeight = 1024;
enum RectanizerType {
kPow2_RectanizerType,
kSkyline_RectanizerType,
};
@ -40,9 +41,15 @@ public:
RectanizerBench(RectanizerType rectanizerType, RectType rectType)
: fName("rectanizer_")
, fRectanizerType(rectanizerType)
, fRectType(rectType) {
fName.append("skyline_");
if (kPow2_RectanizerType == fRectanizerType) {
fName.append("pow2_");
} else {
SkASSERT(kSkyline_RectanizerType == fRectanizerType);
fName.append("skyline_");
}
if (kRand_RectType == fRectType) {
fName.append("rand");
@ -66,7 +73,12 @@ protected:
void onDelayedSetup() override {
SkASSERT(nullptr == fRectanizer.get());
fRectanizer.reset(new GrRectanizerSkyline(kWidth, kHeight));
if (kPow2_RectanizerType == fRectanizerType) {
fRectanizer.reset(new GrRectanizerPow2(kWidth, kHeight));
} else {
SkASSERT(kSkyline_RectanizerType == fRectanizerType);
fRectanizer.reset(new GrRectanizerSkyline(kWidth, kHeight));
}
}
void onDraw(int loops, SkCanvas* canvas) override {
@ -99,14 +111,21 @@ protected:
private:
SkString fName;
RectanizerType fRectanizerType;
RectType fRectType;
std::unique_ptr<GrRectanizerSkyline> fRectanizer;
std::unique_ptr<GrRectanizer> fRectanizer;
typedef Benchmark INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_BENCH(return new RectanizerBench(RectanizerBench::kPow2_RectanizerType,
RectanizerBench::kRand_RectType);)
DEF_BENCH(return new RectanizerBench(RectanizerBench::kPow2_RectanizerType,
RectanizerBench::kRandPow2_RectType);)
DEF_BENCH(return new RectanizerBench(RectanizerBench::kPow2_RectanizerType,
RectanizerBench::kSmallPow2_RectType);)
DEF_BENCH(return new RectanizerBench(RectanizerBench::kSkyline_RectanizerType,
RectanizerBench::kRand_RectType);)
DEF_BENCH(return new RectanizerBench(RectanizerBench::kSkyline_RectanizerType,

View File

@ -151,6 +151,9 @@ skia_gpu_sources = [
"$_src/gpu/GrProxyProvider.h",
"$_src/gpu/GrRecordingContext.cpp",
"$_src/gpu/GrRecordingContextPriv.h",
"$_src/gpu/GrRectanizer.h",
"$_src/gpu/GrRectanizerPow2.cpp",
"$_src/gpu/GrRectanizerPow2.h",
"$_src/gpu/GrRectanizerSkyline.cpp",
"$_src/gpu/GrRectanizerSkyline.h",
"$_src/gpu/GrReducedClip.cpp",

View File

@ -10,20 +10,21 @@
#include "include/core/SkPaint.h"
#include "include/utils/SkRandom.h"
#include "samplecode/Sample.h"
#include "src/core/SkMathPriv.h"
#include "src/utils/SkUTF.h"
#if SK_SUPPORT_GPU
#include "src/gpu/GrRectanizerPow2.h"
#include "src/gpu/GrRectanizerSkyline.h"
// This slide visualizes the various GrRectanizer-derived classes behavior
// for various input sets
// 'j' will cycle through the various rectanizers
// Pow2 -> GrRectanizerPow2
// Skyline -> GrRectanizerSkyline
// 'h' will cycle through the various rect sets
// Rand -> random rects from 2-256
// Pow2Rand -> random power of 2 sized rects from 2-256
// SmallPow2 -> 128x128 rects
class RectanizerView : public Sample {
static constexpr int kWidth = 1024;
static constexpr int kHeight = 1024;
public:
RectanizerView()
: fCurRandRect(0)
@ -46,7 +47,10 @@ public:
fCurRects = &fRects[0];
fRectanizers.emplace_back(kWidth, kHeight);
fRectanizers.push_back(
std::unique_ptr<GrRectanizer>(new GrRectanizerPow2(kWidth, kHeight)));
fRectanizers.push_back(
std::unique_ptr<GrRectanizer>(new GrRectanizerSkyline(kWidth, kHeight)));
}
protected:
@ -58,6 +62,9 @@ protected:
// Only consider events for single char keys
if (1 == size) {
switch (utf8[0]) {
case kCycleRectanizerKey:
this->cycleRectanizer();
return true;
case kCycleRectsKey:
this->cycleRects();
return true;
@ -70,9 +77,9 @@ protected:
void onDrawContent(SkCanvas* canvas) override {
if (fCurRandRect < kNumRandRects) {
if (fRectanizers[fCurRectanizer].addRect((*fCurRects)[fCurRandRect].fWidth,
(*fCurRects)[fCurRandRect].fHeight,
&fRectLocations[fCurRandRect])) {
if (fRectanizers[fCurRectanizer]->addRect((*fCurRects)[fCurRandRect].fWidth,
(*fCurRects)[fCurRandRect].fHeight,
&fRectLocations[fCurRandRect])) {
++fCurRandRect;
}
}
@ -102,34 +109,52 @@ protected:
SkString str;
str.printf("%s-%s: tot Area: %ld (%.2f) numTextures: %d/%d",
str.printf("%s-%s: tot Area: %ld %%full: %.2f (%.2f) numTextures: %d/%d",
this->getRectanizerName(),
this->getRectsName(),
totArea,
100.0f * fRectanizers[fCurRectanizer]->percentFull(),
100.0f * totArea / ((float)kWidth*kHeight),
fCurRandRect,
kNumRandRects);
canvas->drawString(str, 50, kHeight + 50, blackBigFont, SkPaint());
str.printf("Press \'j\' to toggle rectanizer");
canvas->drawString(str, 50, kHeight + 100, blackBigFont, SkPaint());
str.printf("Press \'h\' to toggle rects");
canvas->drawString(str, 50, kHeight + 150, blackBigFont, SkPaint());
}
private:
static const int kWidth = 1024;
static const int kHeight = 1024;
static const int kNumRandRects = 200;
static const char kCycleRectanizerKey = 'j';
static const char kCycleRectsKey = 'h';
static const int kMinRectSize = 2;
static const int kMaxRectSize = 256;
int fCurRandRect;
SkTDArray<SkISize> fRects[3];
SkTDArray<SkISize>* fCurRects;
SkTDArray<SkIPoint16> fRectLocations;
SkTArray<GrRectanizerSkyline> fRectanizers;
int fCurRectanizer;
int fCurRandRect;
SkTDArray<SkISize> fRects[3];
SkTDArray<SkISize>* fCurRects;
SkTDArray<SkIPoint16> fRectLocations;
SkTArray<std::unique_ptr<GrRectanizer>> fRectanizers;
int fCurRectanizer;
const char* getRectanizerName() const {
return "Skyline";
if (!fCurRectanizer) {
return "Pow2";
} else {
return "Skyline";
}
}
void cycleRectanizer() {
fCurRectanizer = (fCurRectanizer + 1) % fRectanizers.count();
fRectanizers[fCurRectanizer]->reset();
fCurRandRect = 0;
}
const char* getRectsName() const {
@ -151,7 +176,7 @@ private:
fCurRects = &fRects[0];
}
fRectanizers[fCurRectanizer].reset();
fRectanizers[fCurRectanizer]->reset();
fCurRandRect = 0;
}

View File

@ -7,6 +7,7 @@
#include "src/gpu/GrDynamicAtlas.h"
#include "src/core/SkIPoint16.h"
#include "src/gpu/GrOnFlushResourceProvider.h"
#include "src/gpu/GrProxyProvider.h"
#include "src/gpu/GrRectanizerSkyline.h"

44
src/gpu/GrRectanizer.h Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrRectanizer_DEFINED
#define GrRectanizer_DEFINED
#include "include/gpu/GrTypes.h"
struct SkIPoint16;
class GrRectanizer {
public:
GrRectanizer(int width, int height) : fWidth(width), fHeight(height) {
SkASSERT(width >= 0);
SkASSERT(height >= 0);
}
virtual ~GrRectanizer() {}
virtual void reset() = 0;
int width() const { return fWidth; }
int height() const { return fHeight; }
// Attempt to add a rect. Return true on success; false on failure. If
// successful the position in the atlas is returned in 'loc'.
virtual bool addRect(int width, int height, SkIPoint16* loc) = 0;
virtual float percentFull() const = 0;
/**
* Our factory, which returns the subclass du jour
*/
static GrRectanizer* Factory(int width, int height);
private:
const int fWidth;
const int fHeight;
};
#endif

View File

@ -0,0 +1,59 @@
/*
* Copyright 2010 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/GrRectanizerPow2.h"
bool GrRectanizerPow2::addRect(int width, int height, SkIPoint16* loc) {
if ((unsigned)width > (unsigned)this->width() ||
(unsigned)height > (unsigned)this->height()) {
return false;
}
int32_t area = width * height; // computed here since height will be modified
height = GrNextPow2(height);
if (height < kMIN_HEIGHT_POW2) {
height = kMIN_HEIGHT_POW2;
}
Row* row = &fRows[HeightToRowIndex(height)];
SkASSERT(row->fRowHeight == 0 || row->fRowHeight == height);
if (0 == row->fRowHeight) {
if (!this->canAddStrip(height)) {
return false;
}
this->initRow(row, height);
} else {
if (!row->canAddWidth(width, this->width())) {
if (!this->canAddStrip(height)) {
return false;
}
// that row is now "full", so retarget our Row record for
// another one
this->initRow(row, height);
}
}
SkASSERT(row->fRowHeight == height);
SkASSERT(row->canAddWidth(width, this->width()));
*loc = row->fLoc;
row->fLoc.fX += width;
SkASSERT(row->fLoc.fX <= this->width());
SkASSERT(row->fLoc.fY <= this->height());
SkASSERT(fNextStripY <= this->height());
fAreaSoFar += area;
return true;
}
///////////////////////////////////////////////////////////////////////////////
// factory is now in GrRectanizer_skyline.cpp
//GrRectanizer* GrRectanizer::Factory(int width, int height) {
// return new GrRectanizerPow2 (width, height);
//}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrRectanizerPow2_DEFINED
#define GrRectanizerPow2_DEFINED
#include "include/private/SkMalloc.h"
#include "src/core/SkIPoint16.h"
#include "src/core/SkMathPriv.h"
#include "src/gpu/GrRectanizer.h"
// This Rectanizer quantizes the incoming rects to powers of 2. Each power
// of two can have, at most, one active row/shelf. Once a row/shelf for
// a particular power of two gets full its fRows entry is recycled to point
// to a new row.
// The skyline algorithm almost always provides a better packing.
//
// Mark this class final in an effort to avoid the vtable when this subclass is used explicitly.
class GrRectanizerPow2 final : public GrRectanizer {
public:
GrRectanizerPow2(int w, int h) : INHERITED(w, h) {
this->reset();
}
~GrRectanizerPow2() final {}
void reset() final {
fNextStripY = 0;
fAreaSoFar = 0;
sk_bzero(fRows, sizeof(fRows));
}
bool addRect(int w, int h, SkIPoint16* loc) final;
float percentFull() const final {
return fAreaSoFar / ((float)this->width() * this->height());
}
private:
static const int kMIN_HEIGHT_POW2 = 2;
static const int kMaxExponent = 16;
struct Row {
SkIPoint16 fLoc;
// fRowHeight is actually known by this struct's position in fRows
// but it is used to signal if there exists an open row of this height
int fRowHeight;
bool canAddWidth(int width, int containerWidth) const {
return fLoc.fX + width <= containerWidth;
}
};
Row fRows[kMaxExponent]; // 0-th entry will be unused
int fNextStripY;
int32_t fAreaSoFar;
static int HeightToRowIndex(int height) {
SkASSERT(height >= kMIN_HEIGHT_POW2);
int index = 32 - SkCLZ(height - 1);
SkASSERT(index < kMaxExponent);
return index;
}
bool canAddStrip(int height) const {
return fNextStripY + height <= this->height();
}
void initRow(Row* row, int rowHeight) {
row->fLoc.set(0, fNextStripY);
row->fRowHeight = rowHeight;
fNextStripY += rowHeight;
}
typedef GrRectanizer INHERITED;
};
#endif

View File

@ -116,3 +116,8 @@ void GrRectanizerSkyline::addSkylineLevel(int skylineIndex, int x, int y, int wi
}
}
///////////////////////////////////////////////////////////////////////////////
GrRectanizer* GrRectanizer::Factory(int width, int height) {
return new GrRectanizerSkyline(width, height);
}

View File

@ -9,17 +9,21 @@
#define GrRectanizerSkyline_DEFINED
#include "include/private/SkTDArray.h"
#include "src/core/SkIPoint16.h"
#include "src/gpu/GrRectanizer.h"
// Pack rectangles and track the current silhouette
// Based, in part, on Jukka Jylanki's work at http://clb.demon.fi
class GrRectanizerSkyline {
//
// Mark this class final in an effort to avoid the vtable when this subclass is used explicitly.
class GrRectanizerSkyline final : public GrRectanizer {
public:
GrRectanizerSkyline(int w, int h) : fWidth{w}, fHeight{h} {
GrRectanizerSkyline(int w, int h) : INHERITED(w, h) {
this->reset();
}
void reset() {
~GrRectanizerSkyline() final { }
void reset() final {
fAreaSoFar = 0;
fSkyline.reset();
SkylineSegment* seg = fSkyline.append(1);
@ -28,10 +32,11 @@ public:
seg->fWidth = this->width();
}
bool addRect(int w, int h, SkIPoint16* loc);
bool addRect(int w, int h, SkIPoint16* loc) final;
int width() const { return fWidth; }
int height() const { return fHeight; }
float percentFull() const final {
return fAreaSoFar / ((float)this->width() * this->height());
}
private:
struct SkylineSegment {
@ -40,6 +45,10 @@ private:
int fWidth;
};
SkTDArray<SkylineSegment> fSkyline;
int32_t fAreaSoFar;
// Can a width x height rectangle fit in the free space represented by
// the skyline segments >= 'skylineIndex'? If so, return true and fill in
// 'y' with the y-location at which it fits (the x location is pulled from
@ -49,10 +58,7 @@ private:
// at x,y.
void addSkylineLevel(int skylineIndex, int x, int y, int width, int height);
const int fWidth;
const int fHeight;
SkTDArray<SkylineSegment> fSkyline;
int32_t fAreaSoFar;
typedef GrRectanizer INHERITED;
};
#endif // GrRectanizerSkyline_DEFINED
#endif

View File

@ -8,6 +8,7 @@
#include "include/core/SkSize.h"
#include "include/private/SkTDArray.h"
#include "include/utils/SkRandom.h"
#include "src/gpu/GrRectanizerPow2.h"
#include "src/gpu/GrRectanizerSkyline.h"
#include "tests/Test.h"
@ -15,18 +16,20 @@ static const int kWidth = 1024;
static const int kHeight = 1024;
// Basic test of a GrRectanizer-derived class' functionality
static void test_rectanizer_basic(skiatest::Reporter* reporter, GrRectanizerSkyline* rectanizer) {
static void test_rectanizer_basic(skiatest::Reporter* reporter, GrRectanizer* rectanizer) {
REPORTER_ASSERT(reporter, kWidth == rectanizer->width());
REPORTER_ASSERT(reporter, kHeight == rectanizer->height());
SkIPoint16 loc;
REPORTER_ASSERT(reporter, rectanizer->addRect(50, 50, &loc));
REPORTER_ASSERT(reporter, rectanizer->percentFull() > 0.0f);
rectanizer->reset();
REPORTER_ASSERT(reporter, rectanizer->percentFull() == 0.0f);
}
static void test_rectanizer_inserts(skiatest::Reporter*,
GrRectanizerSkyline* rectanizer,
GrRectanizer* rectanizer,
const SkTDArray<SkISize>& rects) {
int i;
for (i = 0; i < rects.count(); ++i) {
@ -46,6 +49,13 @@ static void test_skyline(skiatest::Reporter* reporter, const SkTDArray<SkISize>&
test_rectanizer_inserts(reporter, &skylineRectanizer, rects);
}
static void test_pow2(skiatest::Reporter* reporter, const SkTDArray<SkISize>& rects) {
GrRectanizerPow2 pow2Rectanizer(kWidth, kHeight);
test_rectanizer_basic(reporter, &pow2Rectanizer);
test_rectanizer_inserts(reporter, &pow2Rectanizer, rects);
}
DEF_GPUTEST(GpuRectanizer, reporter, factory) {
SkTDArray<SkISize> rects;
SkRandom rand;
@ -56,4 +66,5 @@ DEF_GPUTEST(GpuRectanizer, reporter, factory) {
}
test_skyline(reporter, rects);
test_pow2(reporter, rects);
}