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:
parent
d8fd0bf574
commit
e5a141d0f6
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
44
src/gpu/GrRectanizer.h
Normal 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
|
59
src/gpu/GrRectanizerPow2.cpp
Normal file
59
src/gpu/GrRectanizerPow2.cpp
Normal 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);
|
||||
//}
|
83
src/gpu/GrRectanizerPow2.h
Normal file
83
src/gpu/GrRectanizerPow2.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user