Simplified Skyline
Preparing for glyph zero-padding optimization. This is the main reason for this change: the current data structures do not support the optimization. After I put this change under supportBilerpFromGlyphAtlas flag I could not even compare the results with and without this change (the flag increases glyph sizes). But from my previous measurement it gives saves about 1% of the memory (or 1 plot eviction) on the test. Change-Id: Ibb66821d220a98487acba88f0ce27ece23e5a669 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/550617 Reviewed-by: Jim Van Verth <jvanverth@google.com> Commit-Queue: Julia Lavrova <jlavrova@google.com>
This commit is contained in:
parent
7d8ec28bd5
commit
7d1636010a
@ -789,6 +789,8 @@ skia_shared_gpu_sources = [
|
|||||||
"$_src/gpu/BufferWriter.h",
|
"$_src/gpu/BufferWriter.h",
|
||||||
"$_src/gpu/KeyBuilder.h",
|
"$_src/gpu/KeyBuilder.h",
|
||||||
"$_src/gpu/Rectanizer.h",
|
"$_src/gpu/Rectanizer.h",
|
||||||
|
"$_src/gpu/RectanizerOptimized.cpp",
|
||||||
|
"$_src/gpu/RectanizerOptimized.h",
|
||||||
"$_src/gpu/RectanizerPow2.cpp",
|
"$_src/gpu/RectanizerPow2.cpp",
|
||||||
"$_src/gpu/RectanizerPow2.h",
|
"$_src/gpu/RectanizerPow2.h",
|
||||||
"$_src/gpu/RectanizerSkyline.cpp",
|
"$_src/gpu/RectanizerSkyline.cpp",
|
||||||
|
@ -791,6 +791,8 @@ BASE_SRCS_ALL = [
|
|||||||
"src/gpu/GrRectanizer.h",
|
"src/gpu/GrRectanizer.h",
|
||||||
"src/gpu/KeyBuilder.h",
|
"src/gpu/KeyBuilder.h",
|
||||||
"src/gpu/Rectanizer.h",
|
"src/gpu/Rectanizer.h",
|
||||||
|
"src/gpu/RectanizerOptimized.cpp",
|
||||||
|
"src/gpu/RectanizerOptimized.h",
|
||||||
"src/gpu/RectanizerPow2.cpp",
|
"src/gpu/RectanizerPow2.cpp",
|
||||||
"src/gpu/RectanizerPow2.h",
|
"src/gpu/RectanizerPow2.h",
|
||||||
"src/gpu/RectanizerSkyline.cpp",
|
"src/gpu/RectanizerSkyline.cpp",
|
||||||
|
@ -5,15 +5,16 @@
|
|||||||
* found in the LICENSE file.
|
* found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "src/gpu/AtlasTypes.h"
|
|
||||||
|
|
||||||
#include "include/private/SkMalloc.h"
|
#include "include/private/SkMalloc.h"
|
||||||
#include "src/core/SkOpts.h"
|
#include "src/core/SkOpts.h"
|
||||||
|
#include "src/gpu/AtlasTypes.h"
|
||||||
|
#include "src/gpu/RectanizerOptimized.h"
|
||||||
|
|
||||||
namespace skgpu {
|
namespace skgpu {
|
||||||
|
|
||||||
Plot::Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCounter,
|
Plot::Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCounter,
|
||||||
int offX, int offY, int width, int height, SkColorType colorType, size_t bpp)
|
int offX, int offY, int width, int height, SkColorType colorType, size_t bpp,
|
||||||
|
PadAllGlyphs padAllGLyphs)
|
||||||
: fLastUpload(DrawToken::AlreadyFlushedToken())
|
: fLastUpload(DrawToken::AlreadyFlushedToken())
|
||||||
, fLastUse(DrawToken::AlreadyFlushedToken())
|
, fLastUse(DrawToken::AlreadyFlushedToken())
|
||||||
, fFlushesSinceLastUse(0)
|
, fFlushesSinceLastUse(0)
|
||||||
@ -27,7 +28,6 @@ Plot::Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCount
|
|||||||
, fHeight(height)
|
, fHeight(height)
|
||||||
, fX(offX)
|
, fX(offX)
|
||||||
, fY(offY)
|
, fY(offY)
|
||||||
, fRectanizer(width, height)
|
|
||||||
, fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
|
, fOffset(SkIPoint16::Make(fX * fWidth, fY * fHeight))
|
||||||
, fColorType(colorType)
|
, fColorType(colorType)
|
||||||
, fBytesPerPixel(bpp)
|
, fBytesPerPixel(bpp)
|
||||||
@ -35,6 +35,11 @@ Plot::Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCount
|
|||||||
, fDirty(false)
|
, fDirty(false)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
if (padAllGLyphs == PadAllGlyphs::kYes) {
|
||||||
|
fRectanizer = std::make_unique<RectanizerOptimized>(width, height);
|
||||||
|
} else {
|
||||||
|
fRectanizer = std::make_unique<RectanizerSkyline>(width, height);
|
||||||
|
}
|
||||||
// We expect the allocated dimensions to be a multiple of 4 bytes
|
// We expect the allocated dimensions to be a multiple of 4 bytes
|
||||||
SkASSERT(((width*fBytesPerPixel) & 0x3) == 0);
|
SkASSERT(((width*fBytesPerPixel) & 0x3) == 0);
|
||||||
// The padding for faster uploads only works for 1, 2 and 4 byte texels
|
// The padding for faster uploads only works for 1, 2 and 4 byte texels
|
||||||
@ -46,11 +51,15 @@ Plot::~Plot() {
|
|||||||
sk_free(fData);
|
sk_free(fData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PadAllGlyphs Plot::padAllGlyphs() const {
|
||||||
|
return this->fRectanizer->padAllGlyphs();
|
||||||
|
}
|
||||||
|
|
||||||
bool Plot::addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator) {
|
bool Plot::addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator) {
|
||||||
SkASSERT(width <= fWidth && height <= fHeight);
|
SkASSERT(width <= fWidth && height <= fHeight);
|
||||||
|
|
||||||
SkIPoint16 loc;
|
SkIPoint16 loc;
|
||||||
if (!fRectanizer.addRect(width, height, &loc)) {
|
if (!fRectanizer->addRect(width, height, &loc)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +123,7 @@ std::pair<const void*, SkIRect> Plot::prepareForUpload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Plot::resetRects() {
|
void Plot::resetRects() {
|
||||||
fRectanizer.reset();
|
fRectanizer->reset();
|
||||||
|
|
||||||
fGenID = fGenerationCounter->next();
|
fGenID = fGenerationCounter->next();
|
||||||
fPlotLocator = PlotLocator(fPageIndex, fPlotIndex, fGenID);
|
fPlotLocator = PlotLocator(fPageIndex, fPlotIndex, fGenID);
|
||||||
|
@ -18,11 +18,12 @@
|
|||||||
#include "include/private/SkTo.h"
|
#include "include/private/SkTo.h"
|
||||||
#include "src/core/SkIPoint16.h"
|
#include "src/core/SkIPoint16.h"
|
||||||
#include "src/core/SkTInternalLList.h"
|
#include "src/core/SkTInternalLList.h"
|
||||||
#include "src/gpu/RectanizerSkyline.h"
|
|
||||||
|
|
||||||
class GrOpFlushState;
|
class GrOpFlushState;
|
||||||
class TestingUploadTarget;
|
class TestingUploadTarget;
|
||||||
namespace skgpu::graphite { class AtlasManager; }
|
namespace skgpu::graphite { class AtlasManager; }
|
||||||
|
namespace skgpu { class Rectanizer; }
|
||||||
|
namespace skgpu { enum class PadAllGlyphs : bool; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file includes internal types that are used by all of our gpu backends for atlases.
|
* This file includes internal types that are used by all of our gpu backends for atlases.
|
||||||
@ -300,6 +301,10 @@ public:
|
|||||||
return {fUVs[0] & 0x1FFF, fUVs[1]};
|
return {fUVs[0] & 0x1FFF, fUVs[1]};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkIPoint bottomRight() const {
|
||||||
|
return {fUVs[2] & 0x1FFF, fUVs[3]};
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t width() const {
|
uint16_t width() const {
|
||||||
return fUVs[2] - fUVs[0];
|
return fUVs[2] - fUVs[0];
|
||||||
}
|
}
|
||||||
@ -421,7 +426,8 @@ class Plot : public SkRefCnt {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCounter,
|
Plot(int pageIndex, int plotIndex, AtlasGenerationCounter* generationCounter,
|
||||||
int offX, int offY, int width, int height, SkColorType colorType, size_t bpp);
|
int offX, int offY, int width, int height, SkColorType colorType, size_t bpp,
|
||||||
|
PadAllGlyphs padAllGlyphs);
|
||||||
|
|
||||||
uint32_t pageIndex() const { return fPageIndex; }
|
uint32_t pageIndex() const { return fPageIndex; }
|
||||||
|
|
||||||
@ -438,7 +444,10 @@ public:
|
|||||||
}
|
}
|
||||||
SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
|
SkDEBUGCODE(size_t bpp() const { return fBytesPerPixel; })
|
||||||
|
|
||||||
bool addSubImage(int width, int height, const void* image, AtlasLocator* atlasLocator);
|
bool addSubImage(int width,
|
||||||
|
int height,
|
||||||
|
const void* image,
|
||||||
|
AtlasLocator* atlasLocator);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To manage the lifetime of a plot, we use two tokens. We use the last upload token to
|
* To manage the lifetime of a plot, we use two tokens. We use the last upload token to
|
||||||
@ -467,9 +476,11 @@ public:
|
|||||||
sk_sp<Plot> clone() const {
|
sk_sp<Plot> clone() const {
|
||||||
return sk_sp<Plot>(new Plot(
|
return sk_sp<Plot>(new Plot(
|
||||||
fPageIndex, fPlotIndex, fGenerationCounter, fX, fY, fWidth, fHeight, fColorType,
|
fPageIndex, fPlotIndex, fGenerationCounter, fX, fY, fWidth, fHeight, fColorType,
|
||||||
fBytesPerPixel));
|
fBytesPerPixel, this->padAllGlyphs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PadAllGlyphs padAllGlyphs() const;
|
||||||
|
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
void resetListPtrs() {
|
void resetListPtrs() {
|
||||||
fPrev = fNext = nullptr;
|
fPrev = fNext = nullptr;
|
||||||
@ -478,6 +489,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class PlotTestingPeer;
|
||||||
~Plot() override;
|
~Plot() override;
|
||||||
|
|
||||||
skgpu::DrawToken fLastUpload;
|
skgpu::DrawToken fLastUpload;
|
||||||
@ -496,7 +508,7 @@ private:
|
|||||||
const int fHeight;
|
const int fHeight;
|
||||||
const int fX;
|
const int fX;
|
||||||
const int fY;
|
const int fY;
|
||||||
skgpu::RectanizerSkyline fRectanizer;
|
std::unique_ptr<skgpu::Rectanizer> fRectanizer;
|
||||||
const SkIPoint16 fOffset; // the offset of the plot in the backing texture
|
const SkIPoint16 fOffset; // the offset of the plot in the backing texture
|
||||||
const SkColorType fColorType;
|
const SkColorType fColorType;
|
||||||
const size_t fBytesPerPixel;
|
const size_t fBytesPerPixel;
|
||||||
|
@ -16,6 +16,8 @@ CORE_FILES = [
|
|||||||
"Rectanizer.h",
|
"Rectanizer.h",
|
||||||
"RectanizerPow2.cpp",
|
"RectanizerPow2.cpp",
|
||||||
"RectanizerPow2.h",
|
"RectanizerPow2.h",
|
||||||
|
"RectanizerOptimized.cpp",
|
||||||
|
"RectanizerOptimized.h",
|
||||||
"RectanizerSkyline.cpp",
|
"RectanizerSkyline.cpp",
|
||||||
"RectanizerSkyline.h",
|
"RectanizerSkyline.h",
|
||||||
"RefCntedCallback.h",
|
"RefCntedCallback.h",
|
||||||
|
@ -14,6 +14,11 @@ struct SkIPoint16;
|
|||||||
|
|
||||||
namespace skgpu {
|
namespace skgpu {
|
||||||
|
|
||||||
|
enum class PadAllGlyphs : bool {
|
||||||
|
kNo = false,
|
||||||
|
kYes = true
|
||||||
|
};
|
||||||
|
|
||||||
class Rectanizer {
|
class Rectanizer {
|
||||||
public:
|
public:
|
||||||
Rectanizer(int width, int height) : fWidth(width), fHeight(height) {
|
Rectanizer(int width, int height) : fWidth(width), fHeight(height) {
|
||||||
@ -23,24 +28,30 @@ public:
|
|||||||
|
|
||||||
virtual ~Rectanizer() {}
|
virtual ~Rectanizer() {}
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() {
|
||||||
|
fAreaSoFar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int width() const { return fWidth; }
|
int width() const { return fWidth; }
|
||||||
int height() const { return fHeight; }
|
int height() const { return fHeight; }
|
||||||
|
float percentFull() const {
|
||||||
|
return fAreaSoFar / ((float)this->width() * this->height());
|
||||||
|
}
|
||||||
|
|
||||||
// Attempt to add a rect. Return true on success; false on failure. If
|
// Attempt to add a rect. Return true on success; false on failure. If
|
||||||
// successful the position in the atlas is returned in 'loc'.
|
// successful the position in the atlas is returned in 'loc'.
|
||||||
virtual bool addRect(int width, int height, SkIPoint16* loc) = 0;
|
virtual bool addRect(int width, int height, SkIPoint16* loc) = 0;
|
||||||
virtual float percentFull() const = 0;
|
virtual PadAllGlyphs padAllGlyphs() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our factory, which returns the subclass du jour
|
* Our factory, which returns the subclass du jour
|
||||||
*/
|
*/
|
||||||
static Rectanizer* Factory(int width, int height);
|
static Rectanizer* Factory(int width, int height);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
friend class RectanizerSkylineTestingPeer;
|
||||||
const int fWidth;
|
const int fWidth;
|
||||||
const int fHeight;
|
const int fHeight;
|
||||||
|
int32_t fAreaSoFar;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace skgpu
|
} // End of namespace skgpu
|
||||||
|
86
src/gpu/RectanizerOptimized.cpp
Normal file
86
src/gpu/RectanizerOptimized.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "src/core/SkIPoint16.h"
|
||||||
|
#include "src/gpu/RectanizerOptimized.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace skgpu {
|
||||||
|
|
||||||
|
// This method walks across the entire profile trying to find the area big enough to place a
|
||||||
|
// rectangle width * hight.
|
||||||
|
// It returns the area with mininum possible y coordinage,
|
||||||
|
// and if there are few like this, minumum x.
|
||||||
|
bool RectanizerOptimized::addRect(int width, int height, SkIPoint16* loc) {
|
||||||
|
|
||||||
|
SkASSERT(width > 0 && height > 0);
|
||||||
|
if (width > this->width() || height > this->height()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkIPoint16 bestLoc = SkIPoint16::Make(this->width(), this->height());
|
||||||
|
SkIPoint16 currLoc = SkIPoint16::Make(0, 0);
|
||||||
|
bool found = false;
|
||||||
|
int16_t x = 0;
|
||||||
|
// We are looking for a rectangle [currLoc.fX:x) * [0:currLoc.fY)
|
||||||
|
while (x < this->width() && currLoc.fX <= this->width() - width) {
|
||||||
|
|
||||||
|
if (currLoc.fX + width == x) {
|
||||||
|
// We found the area: [currLoc.fX:x) * [0:currLoc.fY)
|
||||||
|
found = true;
|
||||||
|
// Let's see if it's better (we do not take in account optimized flag)
|
||||||
|
if (currLoc.fY < bestLoc.fY) {
|
||||||
|
bestLoc = currLoc;
|
||||||
|
}
|
||||||
|
// Let's see if we can find better y starting over from the currLoc.fX+1.
|
||||||
|
x = (++currLoc.fX);
|
||||||
|
currLoc.fY = fProfile[x];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Still looking
|
||||||
|
currLoc.fY = std::max(currLoc.fY, fProfile[x]);
|
||||||
|
++x;
|
||||||
|
// If this column does not fit vertically start all over from the next one
|
||||||
|
if (this->height() < currLoc.fY + height) {
|
||||||
|
// We start over from the next x (nothing before will be good enough)
|
||||||
|
currLoc.fY = fProfile[x];
|
||||||
|
currLoc.fX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
// Mark the area as allocated
|
||||||
|
fAreaSoFar += width*height;
|
||||||
|
this->markAreaOccupied(bestLoc, width, height);
|
||||||
|
*loc = bestLoc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
loc->fX = 0;
|
||||||
|
loc->fY = 0;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RectanizerOptimized::markAreaOccupied(SkIPoint16 loc,
|
||||||
|
int width,
|
||||||
|
int height) {
|
||||||
|
auto y = loc.fY + SkToS16(height);
|
||||||
|
for (int16_t x = loc.fX; x < loc.fX + SkToS16(width); ++x) {
|
||||||
|
fProfile[x] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Rectanizer* Rectanizer::Factory(int width, int height) {
|
||||||
|
return new RectanizerOptimized(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End of namespace skgpu
|
75
src/gpu/RectanizerOptimized.h
Normal file
75
src/gpu/RectanizerOptimized.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef skgpu_RectanizerOptimized_DEFINED
|
||||||
|
#define skgpu_RectanizerOptimized_DEFINED
|
||||||
|
|
||||||
|
#include "include/private/SkBitmaskEnum.h"
|
||||||
|
#include "include/private/SkTDArray.h"
|
||||||
|
#include "src/gpu/RectanizerSkyline.h"
|
||||||
|
#include "src/utils/SkBitSet.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace skgpu {
|
||||||
|
enum class OptFlags {
|
||||||
|
None = 0x00,
|
||||||
|
T = 0x01,
|
||||||
|
L = 0x02,
|
||||||
|
R = 0x04,
|
||||||
|
TL = 0x03,
|
||||||
|
TR = 0x05,
|
||||||
|
LR = 0x06,
|
||||||
|
TLR = 0x07,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sknonstd {
|
||||||
|
template <> struct is_bitmask_enum<skgpu::OptFlags> : std::true_type {};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace skgpu {
|
||||||
|
// This class tracks the entire profile line without breaking it into ranges.
|
||||||
|
// Each column of a plot has an integer indicating the hight of an allocated area in this column.
|
||||||
|
// It works slightly better that RectanizerSkyline because it always selects a free area with
|
||||||
|
// the minimum y, minimum x. RectanizerSkyline selects minimin y, minimum width of a range. It
|
||||||
|
// gives RectanizerOptimized slight advantage (~1% of saved Atlas memory and 1 less plot eviction
|
||||||
|
// for a very big test).
|
||||||
|
// That advantage alone would never justify changing already working algorithm, but it makes much
|
||||||
|
// easier the upcoming zero padding glyphs optimization.
|
||||||
|
// (It also makes easier changing the skyline somewhat, but again that is not the point)
|
||||||
|
// Mark this class final in an effort to avoid the vtable when this subclass is used explicitly.
|
||||||
|
class RectanizerOptimized final : public Rectanizer {
|
||||||
|
public:
|
||||||
|
RectanizerOptimized(int w, int h) : Rectanizer(w, h) {
|
||||||
|
this->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
~RectanizerOptimized() final { }
|
||||||
|
|
||||||
|
void reset() final {
|
||||||
|
Rectanizer::reset();
|
||||||
|
fProfile.resize(this->width() + 1); // One extra element to avoid extra checking
|
||||||
|
std::fill(fProfile.begin(), fProfile.end(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool addRect(int w, int h, SkIPoint16* loc) override;
|
||||||
|
|
||||||
|
PadAllGlyphs padAllGlyphs() const override { return PadAllGlyphs::kYes; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Update the skyline structure to include a width x height rect located
|
||||||
|
// at x,y and the information about the pixels
|
||||||
|
void markAreaOccupied(SkIPoint16 loc, int width, int height);
|
||||||
|
|
||||||
|
// The distance from the top edge which is already occupied
|
||||||
|
std::vector<int16_t> fProfile;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // End of namespace skgpu
|
||||||
|
|
||||||
|
#endif
|
@ -31,16 +31,13 @@ public:
|
|||||||
~RectanizerPow2() final {}
|
~RectanizerPow2() final {}
|
||||||
|
|
||||||
void reset() final {
|
void reset() final {
|
||||||
|
Rectanizer::reset();
|
||||||
fNextStripY = 0;
|
fNextStripY = 0;
|
||||||
fAreaSoFar = 0;
|
|
||||||
sk_bzero(fRows, sizeof(fRows));
|
sk_bzero(fRows, sizeof(fRows));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addRect(int w, int h, SkIPoint16* loc) final;
|
bool addRect(int w, int h, SkIPoint16* loc) final;
|
||||||
|
PadAllGlyphs padAllGlyphs() const override { return PadAllGlyphs::kNo; }
|
||||||
float percentFull() const final {
|
|
||||||
return fAreaSoFar / ((float)this->width() * this->height());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const int kMIN_HEIGHT_POW2 = 2;
|
static const int kMIN_HEIGHT_POW2 = 2;
|
||||||
@ -60,7 +57,6 @@ private:
|
|||||||
Row fRows[kMaxExponent]; // 0-th entry will be unused
|
Row fRows[kMaxExponent]; // 0-th entry will be unused
|
||||||
|
|
||||||
int fNextStripY;
|
int fNextStripY;
|
||||||
int32_t fAreaSoFar;
|
|
||||||
|
|
||||||
static int HeightToRowIndex(int height) {
|
static int HeightToRowIndex(int height) {
|
||||||
SkASSERT(height >= kMIN_HEIGHT_POW2);
|
SkASSERT(height >= kMIN_HEIGHT_POW2);
|
||||||
|
@ -118,10 +118,4 @@ void RectanizerSkyline::addSkylineLevel(int skylineIndex, int x, int y, int widt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
Rectanizer* Rectanizer::Factory(int width, int height) {
|
|
||||||
return new RectanizerSkyline(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // End of namespace skgpu
|
} // End of namespace skgpu
|
||||||
|
@ -23,10 +23,10 @@ public:
|
|||||||
this->reset();
|
this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
~RectanizerSkyline() final { }
|
~RectanizerSkyline() override { }
|
||||||
|
|
||||||
void reset() final {
|
void reset() override {
|
||||||
fAreaSoFar = 0;
|
Rectanizer::reset();
|
||||||
fSkyline.reset();
|
fSkyline.reset();
|
||||||
SkylineSegment* seg = fSkyline.append(1);
|
SkylineSegment* seg = fSkyline.append(1);
|
||||||
seg->fX = 0;
|
seg->fX = 0;
|
||||||
@ -34,31 +34,26 @@ public:
|
|||||||
seg->fWidth = this->width();
|
seg->fWidth = this->width();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool addRect(int w, int h, SkIPoint16* loc) final;
|
bool addRect(int w, int h, SkIPoint16* loc) override;
|
||||||
|
PadAllGlyphs padAllGlyphs() const override { return PadAllGlyphs::kNo; }
|
||||||
float percentFull() const final {
|
|
||||||
return fAreaSoFar / ((float)this->width() * this->height());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SkylineSegment {
|
// Update the skyline structure to include a width x height rect located
|
||||||
int fX;
|
// at x,y.
|
||||||
int fY;
|
void addSkylineLevel(int skylineIndex, int x, int y, int width, int height);
|
||||||
int fWidth;
|
|
||||||
};
|
|
||||||
|
|
||||||
SkTDArray<SkylineSegment> fSkyline;
|
|
||||||
|
|
||||||
int32_t fAreaSoFar;
|
|
||||||
|
|
||||||
// Can a width x height rectangle fit in the free space represented by
|
// Can a width x height rectangle fit in the free space represented by
|
||||||
// the skyline segments >= 'skylineIndex'? If so, return true and fill in
|
// 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
|
// 'y' with the y-location at which it fits (the x location is pulled from
|
||||||
// 'skylineIndex's segment.
|
// 'skylineIndex's segment.
|
||||||
bool rectangleFits(int skylineIndex, int width, int height, int* y) const;
|
bool rectangleFits(int skylineIndex, int width, int height, int* y) const;
|
||||||
// Update the skyline structure to include a width x height rect located
|
|
||||||
// at x,y.
|
struct SkylineSegment {
|
||||||
void addSkylineLevel(int skylineIndex, int x, int y, int width, int height);
|
int fX;
|
||||||
|
int fY;
|
||||||
|
int fWidth;
|
||||||
|
};
|
||||||
|
SkTDArray<SkylineSegment> fSkyline;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // End of namespace skgpu
|
} // End of namespace skgpu
|
||||||
|
@ -262,13 +262,19 @@ bool GrDirectContext::init() {
|
|||||||
} else {
|
} else {
|
||||||
allowMultitexturing = GrDrawOpAtlas::AllowMultitexturing::kYes;
|
allowMultitexturing = GrDrawOpAtlas::AllowMultitexturing::kYes;
|
||||||
}
|
}
|
||||||
|
skgpu::PadAllGlyphs padAllGlyphs;
|
||||||
|
if (this->options().fSupportBilerpFromGlyphAtlas) {
|
||||||
|
padAllGlyphs = skgpu::PadAllGlyphs::kYes;
|
||||||
|
} else {
|
||||||
|
padAllGlyphs = skgpu::PadAllGlyphs::kNo;
|
||||||
|
}
|
||||||
|
|
||||||
GrProxyProvider* proxyProvider = this->priv().proxyProvider();
|
GrProxyProvider* proxyProvider = this->priv().proxyProvider();
|
||||||
|
|
||||||
fAtlasManager = std::make_unique<GrAtlasManager>(proxyProvider,
|
fAtlasManager = std::make_unique<GrAtlasManager>(proxyProvider,
|
||||||
this->options().fGlyphCacheTextureMaximumBytes,
|
this->options().fGlyphCacheTextureMaximumBytes,
|
||||||
allowMultitexturing,
|
allowMultitexturing,
|
||||||
this->options().fSupportBilerpFromGlyphAtlas);
|
padAllGlyphs);
|
||||||
this->priv().addOnFlushCallbackObject(fAtlasManager.get());
|
this->priv().addOnFlushCallbackObject(fAtlasManager.get());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -72,7 +72,8 @@ std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvide
|
|||||||
GenerationCounter* generationCounter,
|
GenerationCounter* generationCounter,
|
||||||
AllowMultitexturing allowMultitexturing,
|
AllowMultitexturing allowMultitexturing,
|
||||||
EvictionCallback* evictor,
|
EvictionCallback* evictor,
|
||||||
std::string_view label) {
|
std::string_view label,
|
||||||
|
skgpu::PadAllGlyphs padAllGlyphs) {
|
||||||
if (!format.isValid()) {
|
if (!format.isValid()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -80,7 +81,8 @@ std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvide
|
|||||||
std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, format, colorType, bpp,
|
std::unique_ptr<GrDrawOpAtlas> atlas(new GrDrawOpAtlas(proxyProvider, format, colorType, bpp,
|
||||||
width, height, plotWidth, plotHeight,
|
width, height, plotWidth, plotHeight,
|
||||||
generationCounter,
|
generationCounter,
|
||||||
allowMultitexturing, label));
|
allowMultitexturing, label,
|
||||||
|
padAllGlyphs));
|
||||||
if (!atlas->getViews()[0].proxy()) {
|
if (!atlas->getViews()[0].proxy()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -96,7 +98,8 @@ std::unique_ptr<GrDrawOpAtlas> GrDrawOpAtlas::Make(GrProxyProvider* proxyProvide
|
|||||||
GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider, const GrBackendFormat& format,
|
GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider, const GrBackendFormat& format,
|
||||||
SkColorType colorType, size_t bpp, int width, int height,
|
SkColorType colorType, size_t bpp, int width, int height,
|
||||||
int plotWidth, int plotHeight, GenerationCounter* generationCounter,
|
int plotWidth, int plotHeight, GenerationCounter* generationCounter,
|
||||||
AllowMultitexturing allowMultitexturing, std::string_view label)
|
AllowMultitexturing allowMultitexturing, std::string_view label,
|
||||||
|
skgpu::PadAllGlyphs padAllGlyphs)
|
||||||
: fFormat(format)
|
: fFormat(format)
|
||||||
, fColorType(colorType)
|
, fColorType(colorType)
|
||||||
, fBytesPerPixel(bpp)
|
, fBytesPerPixel(bpp)
|
||||||
@ -111,7 +114,8 @@ GrDrawOpAtlas::GrDrawOpAtlas(GrProxyProvider* proxyProvider, const GrBackendForm
|
|||||||
, fFlushesSinceLastUse(0)
|
, fFlushesSinceLastUse(0)
|
||||||
, fMaxPages(AllowMultitexturing::kYes == allowMultitexturing ?
|
, fMaxPages(AllowMultitexturing::kYes == allowMultitexturing ?
|
||||||
PlotLocator::kMaxMultitexturePages : 1)
|
PlotLocator::kMaxMultitexturePages : 1)
|
||||||
, fNumActivePages(0) {
|
, fNumActivePages(0)
|
||||||
|
, fPadAllGlyphs(padAllGlyphs) {
|
||||||
int numPlotsX = width/plotWidth;
|
int numPlotsX = width/plotWidth;
|
||||||
int numPlotsY = height/plotHeight;
|
int numPlotsY = height/plotHeight;
|
||||||
SkASSERT(numPlotsX * numPlotsY <= PlotLocator::kMaxPlots);
|
SkASSERT(numPlotsX * numPlotsY <= PlotLocator::kMaxPlots);
|
||||||
@ -495,9 +499,16 @@ bool GrDrawOpAtlas::createPages(
|
|||||||
for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
|
for (int y = numPlotsY - 1, r = 0; y >= 0; --y, ++r) {
|
||||||
for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
|
for (int x = numPlotsX - 1, c = 0; x >= 0; --x, ++c) {
|
||||||
uint32_t plotIndex = r * numPlotsX + c;
|
uint32_t plotIndex = r * numPlotsX + c;
|
||||||
currPlot->reset(new Plot(
|
currPlot->reset(new Plot(i,
|
||||||
i, plotIndex, generationCounter, x, y, fPlotWidth, fPlotHeight, fColorType,
|
plotIndex,
|
||||||
fBytesPerPixel));
|
generationCounter,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
fPlotWidth,
|
||||||
|
fPlotHeight,
|
||||||
|
fColorType,
|
||||||
|
fBytesPerPixel,
|
||||||
|
fPadAllGlyphs));
|
||||||
|
|
||||||
// build LRU list
|
// build LRU list
|
||||||
fPages[i].fPlotList.addToHead(currPlot->get());
|
fPages[i].fPlotList.addToHead(currPlot->get());
|
||||||
|
@ -82,7 +82,8 @@ public:
|
|||||||
skgpu::AtlasGenerationCounter* generationCounter,
|
skgpu::AtlasGenerationCounter* generationCounter,
|
||||||
AllowMultitexturing allowMultitexturing,
|
AllowMultitexturing allowMultitexturing,
|
||||||
skgpu::PlotEvictionCallback* evictor,
|
skgpu::PlotEvictionCallback* evictor,
|
||||||
std::string_view label);
|
std::string_view label,
|
||||||
|
skgpu::PadAllGlyphs padAllGlyphs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a width x height subimage to the atlas. Upon success it returns 'kSucceeded' and returns
|
* Adds a width x height subimage to the atlas. Upon success it returns 'kSucceeded' and returns
|
||||||
@ -164,12 +165,19 @@ public:
|
|||||||
|
|
||||||
int numAllocated_TestingOnly() const;
|
int numAllocated_TestingOnly() const;
|
||||||
void setMaxPages_TestingOnly(uint32_t maxPages);
|
void setMaxPages_TestingOnly(uint32_t maxPages);
|
||||||
|
skgpu::Plot* getPlot_testingOnly(int pageIdx, int plotIdx) {
|
||||||
|
return fPages[pageIdx].fPlotArray[plotIdx].get();
|
||||||
|
}
|
||||||
|
void checkEvictedPlot_testingOnly(skgpu::PlotEvictionCallback* checker) {
|
||||||
|
this->fEvictionCallbacks.emplace_back(checker);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GrDrawOpAtlas(GrProxyProvider*, const GrBackendFormat& format, SkColorType, size_t bpp,
|
GrDrawOpAtlas(GrProxyProvider*, const GrBackendFormat& format, SkColorType, size_t bpp,
|
||||||
int width, int height, int plotWidth, int plotHeight,
|
int width, int height, int plotWidth, int plotHeight,
|
||||||
skgpu::AtlasGenerationCounter* generationCounter,
|
skgpu::AtlasGenerationCounter* generationCounter,
|
||||||
AllowMultitexturing allowMultitexturing, std::string_view label);
|
AllowMultitexturing allowMultitexturing, std::string_view label,
|
||||||
|
skgpu::PadAllGlyphs padAllGlyphs);
|
||||||
|
|
||||||
inline bool updatePlot(GrDeferredUploadTarget*, skgpu::AtlasLocator*, skgpu::Plot*);
|
inline bool updatePlot(GrDeferredUploadTarget*, skgpu::AtlasLocator*, skgpu::Plot*);
|
||||||
|
|
||||||
@ -241,6 +249,8 @@ private:
|
|||||||
|
|
||||||
uint32_t fNumActivePages;
|
uint32_t fNumActivePages;
|
||||||
|
|
||||||
|
skgpu::PadAllGlyphs fPadAllGlyphs;
|
||||||
|
|
||||||
SkDEBUGCODE(void validate(const skgpu::AtlasLocator& atlasLocator) const;)
|
SkDEBUGCODE(void validate(const skgpu::AtlasLocator& atlasLocator) const;)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,7 +66,11 @@ bool SmallPathAtlasMgr::initAtlas(GrProxyProvider* proxyProvider, const GrCaps*
|
|||||||
kPlotWidth, kPlotHeight, this,
|
kPlotWidth, kPlotHeight, this,
|
||||||
GrDrawOpAtlas::AllowMultitexturing::kYes,
|
GrDrawOpAtlas::AllowMultitexturing::kYes,
|
||||||
this,
|
this,
|
||||||
/*label=*/"SmallPathAtlas");
|
/*label=*/"SmallPathAtlas",
|
||||||
|
// TODO: Investigate if this is reall the right choice. The truly small paths are uploaded
|
||||||
|
// as bitmaps and rendered with Nearest sampling. It's a question how many are falling
|
||||||
|
// back to SDF at this point, as opposed to using one of Chris's path renderers.
|
||||||
|
skgpu::PadAllGlyphs::kYes);
|
||||||
|
|
||||||
return SkToBool(fAtlas);
|
return SkToBool(fAtlas);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ using MaskFormat = skgpu::MaskFormat;
|
|||||||
GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider,
|
GrAtlasManager::GrAtlasManager(GrProxyProvider* proxyProvider,
|
||||||
size_t maxTextureBytes,
|
size_t maxTextureBytes,
|
||||||
GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,
|
GrDrawOpAtlas::AllowMultitexturing allowMultitexturing,
|
||||||
bool supportBilerpAtlas)
|
skgpu::PadAllGlyphs padAllGlyphs)
|
||||||
: fAllowMultitexturing{allowMultitexturing}
|
: fAllowMultitexturing{allowMultitexturing}
|
||||||
, fSupportBilerpAtlas{supportBilerpAtlas}
|
, fPadAllGlyphs{padAllGlyphs}
|
||||||
, fProxyProvider{proxyProvider}
|
, fProxyProvider{proxyProvider}
|
||||||
, fCaps{fProxyProvider->refCaps()}
|
, fCaps{fProxyProvider->refCaps()}
|
||||||
, fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { }
|
, fAtlasConfig{fCaps->maxTextureSize(), maxTextureBytes} { }
|
||||||
@ -158,7 +158,7 @@ GrDrawOpAtlas::ErrorCode GrAtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
|
|||||||
case 0:
|
case 0:
|
||||||
// The direct mask/image case.
|
// The direct mask/image case.
|
||||||
padding = 0;
|
padding = 0;
|
||||||
if (fSupportBilerpAtlas) {
|
if (fPadAllGlyphs == skgpu::PadAllGlyphs::kYes) {
|
||||||
// Force direct masks (glyph with no padding) to have padding.
|
// Force direct masks (glyph with no padding) to have padding.
|
||||||
padding = 1;
|
padding = 1;
|
||||||
srcPadding = 1;
|
srcPadding = 1;
|
||||||
@ -343,7 +343,8 @@ bool GrAtlasManager::initAtlas(MaskFormat format) {
|
|||||||
this,
|
this,
|
||||||
fAllowMultitexturing,
|
fAllowMultitexturing,
|
||||||
nullptr,
|
nullptr,
|
||||||
/*label=*/"TextAtlas");
|
/*label=*/"TextAtlas",
|
||||||
|
fPadAllGlyphs);
|
||||||
if (!fAtlases[index]) {
|
if (!fAtlases[index]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ public:
|
|||||||
GrAtlasManager(GrProxyProvider*,
|
GrAtlasManager(GrProxyProvider*,
|
||||||
size_t maxTextureBytes,
|
size_t maxTextureBytes,
|
||||||
GrDrawOpAtlas::AllowMultitexturing,
|
GrDrawOpAtlas::AllowMultitexturing,
|
||||||
bool supportBilerpAtlas);
|
skgpu::PadAllGlyphs padAllGlyphs);
|
||||||
~GrAtlasManager() override;
|
~GrAtlasManager() override;
|
||||||
|
|
||||||
// if getViews returns nullptr, the client must not try to use other functions on the
|
// if getViews returns nullptr, the client must not try to use other functions on the
|
||||||
@ -122,6 +122,12 @@ public:
|
|||||||
|
|
||||||
void setAtlasDimensionsToMinimum_ForTesting();
|
void setAtlasDimensionsToMinimum_ForTesting();
|
||||||
void setMaxPages_TestingOnly(uint32_t maxPages);
|
void setMaxPages_TestingOnly(uint32_t maxPages);
|
||||||
|
GrDrawOpAtlas* getAtlas_TestingOnly(skgpu::MaskFormat format) {
|
||||||
|
format = this->resolveMaskFormat(format);
|
||||||
|
int atlasIndex = MaskFormatToAtlasIndex(format);
|
||||||
|
SkASSERT(fAtlases[atlasIndex]);
|
||||||
|
return fAtlases[atlasIndex].get();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initAtlas(skgpu::MaskFormat);
|
bool initAtlas(skgpu::MaskFormat);
|
||||||
@ -155,7 +161,7 @@ private:
|
|||||||
GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing;
|
GrDrawOpAtlas::AllowMultitexturing fAllowMultitexturing;
|
||||||
std::unique_ptr<GrDrawOpAtlas> fAtlases[skgpu::kMaskFormatCount];
|
std::unique_ptr<GrDrawOpAtlas> fAtlases[skgpu::kMaskFormatCount];
|
||||||
static_assert(skgpu::kMaskFormatCount == 3);
|
static_assert(skgpu::kMaskFormatCount == 3);
|
||||||
bool fSupportBilerpAtlas;
|
skgpu::PadAllGlyphs fPadAllGlyphs;
|
||||||
GrProxyProvider* fProxyProvider;
|
GrProxyProvider* fProxyProvider;
|
||||||
sk_sp<const GrCaps> fCaps;
|
sk_sp<const GrCaps> fCaps;
|
||||||
GrDrawOpAtlasConfig fAtlasConfig;
|
GrDrawOpAtlasConfig fAtlasConfig;
|
||||||
|
@ -383,7 +383,7 @@ bool DrawAtlas::createPages(AtlasGenerationCounter* generationCounter) {
|
|||||||
uint32_t plotIndex = r * numPlotsX + c;
|
uint32_t plotIndex = r * numPlotsX + c;
|
||||||
currPlot->reset(new Plot(
|
currPlot->reset(new Plot(
|
||||||
i, plotIndex, generationCounter, x, y, fPlotWidth, fPlotHeight, fColorType,
|
i, plotIndex, generationCounter, x, y, fPlotWidth, fPlotHeight, fColorType,
|
||||||
fBytesPerPixel));
|
fBytesPerPixel, skgpu::PadAllGlyphs::kNo));
|
||||||
|
|
||||||
// build LRU list
|
// build LRU list
|
||||||
fPages[i].fPlotList.addToHead(currPlot->get());
|
fPages[i].fPlotList.addToHead(currPlot->get());
|
||||||
|
@ -26,7 +26,6 @@ namespace skgpu::graphite {
|
|||||||
|
|
||||||
AtlasManager::AtlasManager(Recorder* recorder)
|
AtlasManager::AtlasManager(Recorder* recorder)
|
||||||
: fRecorder(recorder)
|
: fRecorder(recorder)
|
||||||
, fSupportBilerpAtlas{recorder->priv().caps()->supportBilerpFromGlyphAtlas()}
|
|
||||||
, fAtlasConfig{recorder->priv().caps()->maxTextureSize(),
|
, fAtlasConfig{recorder->priv().caps()->maxTextureSize(),
|
||||||
recorder->priv().caps()->glyphCacheTextureMaximumBytes()} {
|
recorder->priv().caps()->glyphCacheTextureMaximumBytes()} {
|
||||||
if (!recorder->priv().caps()->allowMultipleGlyphCacheTextures() ||
|
if (!recorder->priv().caps()->allowMultipleGlyphCacheTextures() ||
|
||||||
@ -37,6 +36,11 @@ AtlasManager::AtlasManager(Recorder* recorder)
|
|||||||
} else {
|
} else {
|
||||||
fAllowMultitexturing = DrawAtlas::AllowMultitexturing::kYes;
|
fAllowMultitexturing = DrawAtlas::AllowMultitexturing::kYes;
|
||||||
}
|
}
|
||||||
|
if (recorder->priv().caps()->supportBilerpFromGlyphAtlas()) {
|
||||||
|
fPadAllGlyphs = skgpu::PadAllGlyphs::kYes;
|
||||||
|
} else {
|
||||||
|
fPadAllGlyphs = skgpu::PadAllGlyphs::kNo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AtlasManager::~AtlasManager() = default;
|
AtlasManager::~AtlasManager() = default;
|
||||||
@ -177,7 +181,7 @@ DrawAtlas::ErrorCode AtlasManager::addGlyphToAtlas(const SkGlyph& skGlyph,
|
|||||||
case 0:
|
case 0:
|
||||||
// The direct mask/image case.
|
// The direct mask/image case.
|
||||||
padding = 0;
|
padding = 0;
|
||||||
if (fSupportBilerpAtlas) {
|
if (fPadAllGlyphs == PadAllGlyphs::kYes) {
|
||||||
// Force direct masks (glyph with no padding) to have padding.
|
// Force direct masks (glyph with no padding) to have padding.
|
||||||
padding = 1;
|
padding = 1;
|
||||||
srcPadding = 1;
|
srcPadding = 1;
|
||||||
|
@ -106,7 +106,7 @@ private:
|
|||||||
DrawAtlas::AllowMultitexturing fAllowMultitexturing;
|
DrawAtlas::AllowMultitexturing fAllowMultitexturing;
|
||||||
std::unique_ptr<DrawAtlas> fAtlases[kMaskFormatCount];
|
std::unique_ptr<DrawAtlas> fAtlases[kMaskFormatCount];
|
||||||
static_assert(kMaskFormatCount == 3);
|
static_assert(kMaskFormatCount == 3);
|
||||||
bool fSupportBilerpAtlas;
|
PadAllGlyphs fPadAllGlyphs;
|
||||||
DrawAtlasConfig fAtlasConfig;
|
DrawAtlasConfig fAtlasConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,15 +35,25 @@
|
|||||||
#include "src/gpu/ganesh/ops/GrOp.h"
|
#include "src/gpu/ganesh/ops/GrOp.h"
|
||||||
#include "src/gpu/ganesh/text/GrAtlasManager.h"
|
#include "src/gpu/ganesh/text/GrAtlasManager.h"
|
||||||
#include "tests/Test.h"
|
#include "tests/Test.h"
|
||||||
|
#include "tools/flags/CommandLineFlags.h"
|
||||||
#include "tools/gpu/GrContextFactory.h"
|
#include "tools/gpu/GrContextFactory.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include "include/core/SkTextBlob.h"
|
||||||
|
|
||||||
|
#include "src/core/SkStrikeCache.h"
|
||||||
|
#include "src/text/gpu/Glyph.h"
|
||||||
|
|
||||||
|
using Glyph = sktext::gpu::Glyph;
|
||||||
|
|
||||||
using MaskFormat = skgpu::MaskFormat;
|
using MaskFormat = skgpu::MaskFormat;
|
||||||
|
|
||||||
class GrResourceProvider;
|
class GrResourceProvider;
|
||||||
|
|
||||||
|
DEFINE_bool(verboseSkyline, false, "Skyline will be very verbose.");
|
||||||
|
|
||||||
static const int kNumPlots = 2;
|
static const int kNumPlots = 2;
|
||||||
static const int kPlotSize = 32;
|
static const int kPlotSize = 32;
|
||||||
static const int kAtlasSize = kNumPlots * kPlotSize;
|
static const int kAtlasSize = kNumPlots * kPlotSize;
|
||||||
@ -116,15 +126,20 @@ static bool fill_plot(GrDrawOpAtlas* atlas,
|
|||||||
GrResourceProvider* resourceProvider,
|
GrResourceProvider* resourceProvider,
|
||||||
GrDeferredUploadTarget* target,
|
GrDeferredUploadTarget* target,
|
||||||
skgpu::AtlasLocator* atlasLocator,
|
skgpu::AtlasLocator* atlasLocator,
|
||||||
int alpha) {
|
int alpha,
|
||||||
SkImageInfo ii = SkImageInfo::MakeA8(kPlotSize, kPlotSize);
|
int plotSize = kPlotSize,
|
||||||
|
void** plot = nullptr) {
|
||||||
|
SkImageInfo ii = SkImageInfo::MakeA8(plotSize, plotSize);
|
||||||
|
|
||||||
SkBitmap data;
|
SkBitmap data;
|
||||||
data.allocPixels(ii);
|
data.allocPixels(ii);
|
||||||
data.eraseARGB(alpha, 0, 0, 0);
|
data.eraseARGB(alpha, 0, 0, 0);
|
||||||
|
if (plot != nullptr) {
|
||||||
|
*plot = data.getAddr(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
GrDrawOpAtlas::ErrorCode code;
|
GrDrawOpAtlas::ErrorCode code;
|
||||||
code = atlas->addToAtlas(resourceProvider, target, kPlotSize, kPlotSize,
|
code = atlas->addToAtlas(resourceProvider, target, plotSize, plotSize,
|
||||||
data.getAddr(0, 0), atlasLocator);
|
data.getAddr(0, 0), atlasLocator);
|
||||||
return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
|
return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
|
||||||
}
|
}
|
||||||
@ -159,7 +174,8 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(BasicDrawOpAtlas, reporter, ctxInfo) {
|
|||||||
&counter,
|
&counter,
|
||||||
GrDrawOpAtlas::AllowMultitexturing::kYes,
|
GrDrawOpAtlas::AllowMultitexturing::kYes,
|
||||||
&evictor,
|
&evictor,
|
||||||
/*label=*/"BasicDrawOpAtlasTest");
|
/*label=*/"BasicDrawOpAtlasTest",
|
||||||
|
skgpu::PadAllGlyphs::kNo);
|
||||||
check(reporter, atlas.get(), 0, 4, 0);
|
check(reporter, atlas.get(), 0, 4, 0);
|
||||||
|
|
||||||
// Fill up the first level
|
// Fill up the first level
|
||||||
@ -316,3 +332,280 @@ DEF_GPUTEST(GrDrawOpAtlasConfig_Basic, reporter, options) {
|
|||||||
test_atlas_config(reporter, 65536, 0, MaskFormat::kA8,
|
test_atlas_config(reporter, 65536, 0, MaskFormat::kA8,
|
||||||
{ 512, 512 }, { 256, 256 });
|
{ 512, 512 }, { 256, 256 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void supportBilerpFromGlyphAtlas(GrContextOptions* options) {
|
||||||
|
options->fSupportBilerpFromGlyphAtlas = true;
|
||||||
|
}
|
||||||
|
static void doNotSupportBilerpFromGlyphAtlas(GrContextOptions* options) {
|
||||||
|
options->fSupportBilerpFromGlyphAtlas = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace skgpu {
|
||||||
|
class RectanizerSkylineTestingPeer {
|
||||||
|
public:
|
||||||
|
static int allocatedBytes(skgpu::Rectanizer* rectanizer) { return rectanizer->fAreaSoFar; }
|
||||||
|
|
||||||
|
static int allBytes(skgpu::Rectanizer* rectanizer) {
|
||||||
|
return rectanizer->fWidth * rectanizer->fHeight;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PlotTestingPeer {
|
||||||
|
public:
|
||||||
|
static int allocatedBytes(skgpu::Plot* plot) {
|
||||||
|
return RectanizerSkylineTestingPeer::allocatedBytes(plot->fRectanizer.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static int allBytes(skgpu::Plot* plot) {
|
||||||
|
return RectanizerSkylineTestingPeer::allBytes(plot->fRectanizer.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static SkIRect getRect(skgpu::Plot* plot) {
|
||||||
|
return SkIRect::MakeXYWH(plot->fOffset.fX, plot->fOffset.fY, plot->fWidth, plot->fHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char* data(skgpu::Plot* plot) { return plot->fData; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Data : public skgpu::PlotEvictionCallback
|
||||||
|
, public skgpu::RectanizerSkylineTestingPeer
|
||||||
|
, public skgpu::PlotTestingPeer {
|
||||||
|
public:
|
||||||
|
SkArenaAlloc alloc;
|
||||||
|
TestingUploadTarget uploadTarget;
|
||||||
|
GrResourceProvider* resourceProvider;
|
||||||
|
std::vector<SkGlyph> skGlyphs;
|
||||||
|
std::vector<Glyph> grGlyphs;
|
||||||
|
std::vector<int> srcPaddings;
|
||||||
|
std::vector<bool> checked; // To avoid double checked glyphs from evicted plots
|
||||||
|
GrAtlasManager* atlasManager;
|
||||||
|
skiatest::Reporter* reporter;
|
||||||
|
sk_sp<SkTypeface> typeface;
|
||||||
|
SkString text;
|
||||||
|
|
||||||
|
int allocatedBytes = 0;
|
||||||
|
int optimizedBytes = 0;
|
||||||
|
int allBytes = 0;
|
||||||
|
int evicted = 0;
|
||||||
|
int evictedGlyphs = 0;
|
||||||
|
int evictedArea = 0;
|
||||||
|
|
||||||
|
Data(const sk_gpu_test::ContextInfo& ctxInfo,
|
||||||
|
skiatest::Reporter* reporter,
|
||||||
|
const SkString& text)
|
||||||
|
: alloc(1 << 12)
|
||||||
|
, reporter(reporter)
|
||||||
|
, text(text) {
|
||||||
|
auto dContext = ctxInfo.directContext();
|
||||||
|
resourceProvider = dContext->priv().resourceProvider();
|
||||||
|
atlasManager = dContext->priv().getAtlasManager();
|
||||||
|
atlasManager->setAtlasDimensionsToMinimum_ForTesting();
|
||||||
|
atlasManager->freeAll();
|
||||||
|
unsigned int numProxies;
|
||||||
|
atlasManager->getViews(MaskFormat::kA8, &numProxies);
|
||||||
|
atlasManager->setMaxPages_TestingOnly(1);
|
||||||
|
GrDrawOpAtlas* atlas = atlasManager->getAtlas_TestingOnly(MaskFormat::kA8);
|
||||||
|
atlas->checkEvictedPlot_testingOnly(this);
|
||||||
|
|
||||||
|
typeface = SkTypeface::MakeFromName("Segoe UI", SkFontStyle());
|
||||||
|
this->reset();
|
||||||
|
|
||||||
|
}
|
||||||
|
void evict(skgpu::PlotLocator plotLocator) override {
|
||||||
|
++evicted;
|
||||||
|
auto genID = plotLocator.genID();
|
||||||
|
bool once = true;
|
||||||
|
auto glyphs = 0;
|
||||||
|
for (auto i = 0ul; i < skGlyphs.size(); ++i) {
|
||||||
|
auto& grGlyph = grGlyphs[i];
|
||||||
|
auto pageIndex = grGlyph.fAtlasLocator.pageIndex();
|
||||||
|
auto plotIndex = grGlyph.fAtlasLocator.plotIndex();
|
||||||
|
skgpu::Plot* plot = atlasManager->getAtlas_TestingOnly(MaskFormat::kA8)->
|
||||||
|
getPlot_testingOnly(pageIndex, plotIndex);
|
||||||
|
if (genID == plot->genID()) {
|
||||||
|
if (checkPadding(i)) {
|
||||||
|
++glyphs;
|
||||||
|
}
|
||||||
|
if (once) {
|
||||||
|
once = false;
|
||||||
|
int allocatedBytes1 = PlotTestingPeer::allocatedBytes(plot);
|
||||||
|
int allBytes1 = PlotTestingPeer::allBytes(plot);
|
||||||
|
allBytes += allBytes1;
|
||||||
|
allocatedBytes += allocatedBytes1;
|
||||||
|
evictedArea += allocatedBytes1;
|
||||||
|
if (FLAGS_verboseSkyline) {
|
||||||
|
SkDebugf(
|
||||||
|
"Plot #%u: allocated=%d (%.2f)",
|
||||||
|
plotIndex,
|
||||||
|
allocatedBytes1,
|
||||||
|
allocatedBytes1 * 100.0f / allBytes1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (FLAGS_verboseSkyline) {
|
||||||
|
SkDebugf(", %d glyphs\n", glyphs);
|
||||||
|
}
|
||||||
|
evictedGlyphs += glyphs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
skGlyphs.clear();
|
||||||
|
grGlyphs.clear();
|
||||||
|
srcPaddings.clear();
|
||||||
|
checked.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(SkGlyph skGlyph, Glyph grGlyph, int srcPadding) {
|
||||||
|
skGlyphs.emplace_back(skGlyph);
|
||||||
|
grGlyphs.emplace_back(grGlyph);
|
||||||
|
srcPaddings.emplace_back(srcPadding);
|
||||||
|
checked.emplace_back(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkPadding(int i) {
|
||||||
|
if (checked[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto& skGlyph = skGlyphs[i];
|
||||||
|
auto& grGlyph = grGlyphs[i];
|
||||||
|
// Check sizes
|
||||||
|
// Check if grGlyph is surrounded with zero padding
|
||||||
|
auto pageIndex = grGlyph.fAtlasLocator.pageIndex();
|
||||||
|
auto plotIndex = grGlyph.fAtlasLocator.plotIndex();
|
||||||
|
skgpu::Plot* plot = atlasManager->getAtlas_TestingOnly(MaskFormat::kA8)->
|
||||||
|
getPlot_testingOnly(pageIndex, plotIndex);
|
||||||
|
auto plotRect = PlotTestingPeer::getRect(plot);
|
||||||
|
auto loc = grGlyph.fAtlasLocator.getUVs();
|
||||||
|
SkRect glyphRect = SkRect::MakeLTRB(loc[0], loc[1], loc[2], loc[3]);
|
||||||
|
if (srcPaddings[i] == 1) {
|
||||||
|
REPORTER_ASSERT(reporter, skGlyph.width() == glyphRect.width());
|
||||||
|
REPORTER_ASSERT(reporter, skGlyph.height() == glyphRect.height());
|
||||||
|
glyphRect.offset(-plotRect.fLeft, -plotRect.fTop);
|
||||||
|
auto data = PlotTestingPeer::data(plot);
|
||||||
|
// Check if there is a zero padding around each glyph
|
||||||
|
REPORTER_ASSERT(reporter, glyphRect.fTop > 0);
|
||||||
|
REPORTER_ASSERT(reporter, glyphRect.fBottom + 1 <= plotRect.height());
|
||||||
|
int y0 = glyphRect.fTop - 1;
|
||||||
|
int y1 = glyphRect.fBottom;
|
||||||
|
for (int x = glyphRect.fLeft; x < glyphRect.fRight; ++x) {
|
||||||
|
auto byte0 = data + y0 * plotRect.width() + x;
|
||||||
|
REPORTER_ASSERT(reporter, *byte0 == 0);
|
||||||
|
auto byte1 = data + y1 * plotRect.width() + x;
|
||||||
|
REPORTER_ASSERT(reporter, *byte1 == 0);
|
||||||
|
}
|
||||||
|
for (int x = glyphRect.fLeft; x < glyphRect.fRight; ++x) {
|
||||||
|
}
|
||||||
|
} else if (srcPaddings[i] == 0) {
|
||||||
|
REPORTER_ASSERT(reporter, skGlyph.width() == glyphRect.width());
|
||||||
|
REPORTER_ASSERT(reporter, skGlyph.height() == glyphRect.height());
|
||||||
|
} else {
|
||||||
|
REPORTER_ASSERT(reporter, skGlyph.width() - 4 == glyphRect.width());
|
||||||
|
REPORTER_ASSERT(reporter, skGlyph.height() - 4 == glyphRect.height());
|
||||||
|
// TODO: Check that there are no zeros at all?
|
||||||
|
}
|
||||||
|
checked[i] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawText(int srcPadding, SkScalar fontSize) {
|
||||||
|
|
||||||
|
SkFont defaultFont(typeface, fontSize);
|
||||||
|
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeWithNoDevice(defaultFont);
|
||||||
|
sk_sp<SkStrike> strike = strikeSpec.findOrCreateStrike();
|
||||||
|
|
||||||
|
for (auto i = 0ul; i < text.size(); ++i) {
|
||||||
|
char c = text[i];
|
||||||
|
SkPackedGlyphID id(defaultFont.unicharToGlyph(c));
|
||||||
|
SkGlyph skGlyph = strike->getScalerContext()->makeGlyph(id, &alloc);
|
||||||
|
SkTArray<unsigned char> ones;
|
||||||
|
ones.push_back_n(skGlyph.imageSize(), 0xff);
|
||||||
|
skGlyph.setImage(&alloc, ones.data());
|
||||||
|
SkPackedGlyphID glyphID;
|
||||||
|
Glyph grGlyph(glyphID);
|
||||||
|
auto errorCode = atlasManager->addGlyphToAtlas(
|
||||||
|
skGlyph, &grGlyph, srcPadding, resourceProvider, &uploadTarget);
|
||||||
|
REPORTER_ASSERT(reporter, errorCode == GrDrawOpAtlas::ErrorCode::kSucceeded);
|
||||||
|
add(std::move(skGlyph), std::move(grGlyph), std::move(srcPadding));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Testing that every glyph has a zero-pixel padding
|
||||||
|
// (0, 1, or 2 without slug or 1, 2 with slug)
|
||||||
|
// Testing happen on plot eviction event to make sure all plots are checked
|
||||||
|
// (the active plots will be manually evicted, too)
|
||||||
|
void testPlots(skiatest::Reporter* reporter,
|
||||||
|
const sk_gpu_test::ContextInfo& ctxInfo,
|
||||||
|
SkString text) {
|
||||||
|
Data data(ctxInfo, reporter, text);
|
||||||
|
auto repeat = 1;
|
||||||
|
// Draw glyphs and tests them on plot eviction
|
||||||
|
for (; repeat > 0; --repeat) {
|
||||||
|
for (auto i = 0ul; i < text.size(); ++i) {
|
||||||
|
auto srcPadding = i % 2;
|
||||||
|
auto fontSize = i + 10.0f;
|
||||||
|
data.drawText(srcPadding, fontSize);
|
||||||
|
data.drawText(srcPadding, text.size() - i + 10.0f);
|
||||||
|
|
||||||
|
auto oldText = data.text;
|
||||||
|
data.text = SkString(&text[i], 1);
|
||||||
|
data.drawText(2, 180);
|
||||||
|
data.text = oldText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_verboseSkyline) {
|
||||||
|
// Print all the plots that are not evicted
|
||||||
|
SkDebugf("Summary: optimized=%.2f%% / %.2f%%, allocated=%.2f%% sum=%d "
|
||||||
|
"evicted plots=%d evicted glyphs/plot=%.2f evicted area/plot=%.2f\n",
|
||||||
|
data.optimizedBytes*100.0f/data.allocatedBytes,
|
||||||
|
data.optimizedBytes*100.0f/data.allBytes,
|
||||||
|
data.allocatedBytes*100.0f/data.allBytes,
|
||||||
|
data.optimizedBytes + data.allocatedBytes,
|
||||||
|
data.evicted,
|
||||||
|
(float)data.evictedGlyphs/data.evicted,
|
||||||
|
(float)data.evictedArea/data.evicted);
|
||||||
|
SkDebugf("Current plots:\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manually call evict to test all the glyphs that were not evicted
|
||||||
|
GrDrawOpAtlas* atlas = data.atlasManager->getAtlas_TestingOnly(MaskFormat::kA8);
|
||||||
|
for (auto page = 0ul; page < atlas->numActivePages(); ++page) {
|
||||||
|
for (auto plot = 0; plot < 4; ++plot) {
|
||||||
|
auto p = atlas->getPlot_testingOnly(page, plot);
|
||||||
|
data.evict(p->plotLocator());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAGS_verboseSkyline) {
|
||||||
|
SkDebugf("Summary: optimized=%.2f%% / %.2f%%, allocated=%.2f%% sum=%d "
|
||||||
|
"used plots=%d average glyphs/plot=%.2f average area/plot=%.2f\n",
|
||||||
|
data.optimizedBytes*100.0f/data.allocatedBytes,
|
||||||
|
data.optimizedBytes*100.0f/data.allBytes,
|
||||||
|
data.allocatedBytes*100.0f/data.allBytes,
|
||||||
|
data.optimizedBytes + data.allocatedBytes,
|
||||||
|
data.evicted,
|
||||||
|
(float)data.evictedGlyphs/data.evicted,
|
||||||
|
(float)data.evictedArea/data.evicted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* TEXT = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||||
|
|
||||||
|
DEF_GPUTEST_FOR_CONTEXTS(GrAtlasManager_withOpt,
|
||||||
|
sk_gpu_test::GrContextFactory::IsRenderingContext,
|
||||||
|
reporter,
|
||||||
|
ctxInfo,
|
||||||
|
supportBilerpFromGlyphAtlas) {
|
||||||
|
testPlots(reporter, ctxInfo, SkString(TEXT));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEF_GPUTEST_FOR_CONTEXTS(GrAtlasManager_withoutOpt,
|
||||||
|
sk_gpu_test::GrContextFactory::IsRenderingContext,
|
||||||
|
reporter,
|
||||||
|
ctxInfo,
|
||||||
|
doNotSupportBilerpFromGlyphAtlas) {
|
||||||
|
testPlots(reporter, ctxInfo, SkString(TEXT));
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user