Expand the compressed_textures GM

This CL adds testing for some edge cases in compressed texture handling

Bug: skia:9680
Change-Id: I4915f46bca9e2fb5c827a51152f28dc1e9d22942
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/266201
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Robert Phillips 2020-01-28 14:22:43 -05:00 committed by Skia Commit-Bot
parent 0f4cb09320
commit 162e04b9a9
4 changed files with 85 additions and 56 deletions

View File

@ -53,7 +53,7 @@ static SkPoint gen_pt(float angle, const SkVector& scale) {
static SkPath make_gear(SkISize dimensions, int numTeeth) { static SkPath make_gear(SkISize dimensions, int numTeeth) {
SkVector outerRad{ dimensions.fWidth / 2.0f, dimensions.fHeight / 2.0f }; SkVector outerRad{ dimensions.fWidth / 2.0f, dimensions.fHeight / 2.0f };
SkVector innerRad{ dimensions.fWidth / 2.5f, dimensions.fHeight / 2.5f }; SkVector innerRad{ dimensions.fWidth / 2.5f, dimensions.fHeight / 2.5f };
const float kAnglePerTooth = SK_ScalarPI / numTeeth; const float kAnglePerTooth = 2.0f * SK_ScalarPI / (3 * numTeeth);
float angle = 0.0f; float angle = 0.0f;
@ -62,11 +62,11 @@ static SkPath make_gear(SkISize dimensions, int numTeeth) {
tmp.moveTo(gen_pt(angle, outerRad)); tmp.moveTo(gen_pt(angle, outerRad));
for (int i = 0; i < numTeeth; ++i, angle += 2*kAnglePerTooth) { for (int i = 0; i < numTeeth; ++i, angle += 3*kAnglePerTooth) {
tmp.lineTo(gen_pt(angle+kAnglePerTooth, outerRad)); tmp.lineTo(gen_pt(angle+kAnglePerTooth, outerRad));
tmp.lineTo(gen_pt(angle+kAnglePerTooth, innerRad)); tmp.lineTo(gen_pt(angle+(1.5f*kAnglePerTooth), innerRad));
tmp.lineTo(gen_pt(angle+2*kAnglePerTooth, innerRad)); tmp.lineTo(gen_pt(angle+(2.5f*kAnglePerTooth), innerRad));
tmp.lineTo(gen_pt(angle+2*kAnglePerTooth, outerRad)); tmp.lineTo(gen_pt(angle+(3.0f*kAnglePerTooth), outerRad));
} }
tmp.close(); tmp.close();
@ -164,32 +164,59 @@ static sk_sp<SkData> make_compressed_data(SkISize dimensions,
// RGBA8 | | kBC1_RGBA8_UNORM | // RGBA8 | | kBC1_RGBA8_UNORM |
// -------------------------------------- // --------------------------------------
// //
// The nonPowerOfTwo and nonMultipleOfFour cases exercise some compression edge cases.
class CompressedTexturesGM : public skiagm::GM { class CompressedTexturesGM : public skiagm::GM {
public: public:
CompressedTexturesGM() { enum class Type {
kNormal,
kNonPowerOfTwo,
kNonMultipleOfFour
};
CompressedTexturesGM(Type type) : fType(type) {
this->setBGColor(0xFFCCCCCC); this->setBGColor(0xFFCCCCCC);
switch (fType) {
case Type::kNonPowerOfTwo:
// These dimensions force the top two mip levels to be 1x3 and 1x1
fImgDimensions.set(20, 60);
break;
case Type::kNonMultipleOfFour:
// These dimensions force the top three mip levels to be 1x7, 1x3 and 1x1
fImgDimensions.set(13, 61); // prime numbers - just bc
break;
default:
fImgDimensions.set(kBaseTexWidth, kBaseTexHeight);
break;
}
} }
protected: protected:
SkString onShortName() override { SkString onShortName() override {
return SkString("compressed_textures"); SkString name("compressed_textures");
if (fType == Type::kNonPowerOfTwo) {
name.append("_npot");
} else if (fType == Type::kNonMultipleOfFour) {
name.append("_nmof");
}
return name;
} }
SkISize onISize() override { SkISize onISize() override {
return SkISize::Make(2*kCellWidth + 3*kPad, 2*kTexHeight + 3*kPad); return SkISize::Make(2*kCellWidth + 3*kPad, 2*kBaseTexHeight + 3*kPad);
} }
void onOnceBeforeDraw() override { void onOnceBeforeDraw() override {
fOpaqueETC2Data = make_compressed_data({ kTexWidth, kTexHeight }, fOpaqueETC2Data = make_compressed_data(fImgDimensions, kRGB_565_SkColorType, true,
kRGB_565_SkColorType, true,
SkImage::CompressionType::kETC2_RGB8_UNORM); SkImage::CompressionType::kETC2_RGB8_UNORM);
fOpaqueBC1Data = make_compressed_data({ kTexWidth, kTexHeight }, fOpaqueBC1Data = make_compressed_data(fImgDimensions, kRGBA_8888_SkColorType, true,
kRGBA_8888_SkColorType, true,
SkImage::CompressionType::kBC1_RGB8_UNORM); SkImage::CompressionType::kBC1_RGB8_UNORM);
fTransparentBC1Data = make_compressed_data({ kTexWidth, kTexHeight }, fTransparentBC1Data = make_compressed_data(fImgDimensions, kRGBA_8888_SkColorType, false,
kRGBA_8888_SkColorType, false,
SkImage::CompressionType::kBC1_RGBA8_UNORM); SkImage::CompressionType::kBC1_RGBA8_UNORM);
} }
@ -204,7 +231,7 @@ protected:
this->drawCell(context, canvas, fTransparentBC1Data, this->drawCell(context, canvas, fTransparentBC1Data,
SkImage::CompressionType::kBC1_RGBA8_UNORM, SkImage::CompressionType::kBC1_RGBA8_UNORM,
{ 2*kPad + kCellWidth, 2*kPad + kTexHeight }); { 2*kPad + kCellWidth, 2*kPad + kBaseTexHeight });
} }
private: private:
@ -213,19 +240,23 @@ private:
sk_sp<SkImage> image; sk_sp<SkImage> image;
if (context) { if (context) {
image = SkImage::MakeFromCompressed(context, std::move(data), kTexWidth, kTexHeight, image = SkImage::MakeFromCompressed(context, std::move(data),
fImgDimensions.width(),
fImgDimensions.height(),
compression, GrMipMapped::kYes); compression, GrMipMapped::kYes);
} else { } else {
image = SkImage::MakeRasterFromCompressed(std::move(data), kTexWidth, kTexHeight, image = SkImage::MakeRasterFromCompressed(std::move(data),
fImgDimensions.width(),
fImgDimensions.height(),
compression); compression);
} }
if (!image) { if (!image) {
return; return;
} }
SkISize dimensions{ kTexWidth, kTexHeight }; SkISize levelDimensions = fImgDimensions;
int numMipLevels = SkMipMap::ComputeLevelCount(levelDimensions.width(),
int numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1; levelDimensions.height()) + 1;
SkPaint paint; SkPaint paint;
paint.setFilterQuality(kHigh_SkFilterQuality); // to force mipmapping paint.setFilterQuality(kHigh_SkFilterQuality); // to force mipmapping
@ -245,25 +276,28 @@ private:
for (int i = 0; i < numMipLevels; ++i) { for (int i = 0; i < numMipLevels; ++i) {
SkRect r = SkRect::MakeXYWH(offset.fX, offset.fY, SkRect r = SkRect::MakeXYWH(offset.fX, offset.fY,
dimensions.width(), dimensions.height()); levelDimensions.width(), levelDimensions.height());
canvas->drawImageRect(image, r, &paint); canvas->drawImageRect(image, r, &paint);
if (i == 0) { if (i == 0) {
offset.fX += dimensions.width(); offset.fX += levelDimensions.width()+1;
} else { } else {
offset.fY += dimensions.height(); offset.fY += levelDimensions.height()+1;
} }
dimensions = {SkTMax(1, dimensions.width()/2), SkTMax(1, dimensions.height()/2)}; levelDimensions = {SkTMax(1, levelDimensions.width()/2),
SkTMax(1, levelDimensions.height()/2)};
} }
} }
static const int kPad = 8; static const int kPad = 8;
static const int kTexWidth = 64; static const int kBaseTexWidth = 64;
static const int kCellWidth = 1.5f * kTexWidth; static const int kCellWidth = 1.5f * kBaseTexWidth;
static const int kTexHeight = 64; static const int kBaseTexHeight = 64;
Type fType;
SkISize fImgDimensions;
sk_sp<SkData> fOpaqueETC2Data; sk_sp<SkData> fOpaqueETC2Data;
sk_sp<SkData> fOpaqueBC1Data; sk_sp<SkData> fOpaqueBC1Data;
sk_sp<SkData> fTransparentBC1Data; sk_sp<SkData> fTransparentBC1Data;
@ -273,6 +307,8 @@ private:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
DEF_GM(return new CompressedTexturesGM;) DEF_GM(return new CompressedTexturesGM(CompressedTexturesGM::Type::kNormal);)
DEF_GM(return new CompressedTexturesGM(CompressedTexturesGM::Type::kNonPowerOfTwo);)
DEF_GM(return new CompressedTexturesGM(CompressedTexturesGM::Type::kNonMultipleOfFour);)
#endif #endif

View File

@ -76,10 +76,15 @@ static bool decompress_bc1(SkISize dimensions, const uint8_t* srcData,
int shift = 0; int shift = 0;
int offsetX = 4 * x, offsetY = 4 * y; int offsetX = 4 * x, offsetY = 4 * y;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) { for (int j = 0; j < 4; ++j, shift += 2) {
if (offsetX + j >= dst->width() || offsetY + i >= dst->height()) {
// This can happen for the topmost levels of a mipmap and for
// non-multiple of 4 textures
continue;
}
int index = (curBlock->fIndices >> shift) & 0x3; int index = (curBlock->fIndices >> shift) & 0x3;
*dst->getAddr32(offsetX + j, offsetY + i) = colors[index]; *dst->getAddr32(offsetX + j, offsetY + i) = colors[index];
shift += 2;
} }
} }
} }

View File

@ -106,25 +106,13 @@ static void create_etc1_block(SkColor col, ETC1Block* block) {
block->fLow = SkBSwap32(low); block->fLow = SkBSwap32(low);
} }
static int num_ETC1_blocks_w(int w) { static int num_4x4_blocks(int size) {
if (w < 4) { return ((size + 3) & ~3) >> 2;
w = 1;
} else {
SkASSERT((w & 3) == 0);
w >>= 2;
}
return w;
} }
static int num_ETC1_blocks(int w, int h) { static int num_ETC1_blocks(int w, int h) {
w = num_ETC1_blocks_w(w); w = num_4x4_blocks(w);
h = num_4x4_blocks(h);
if (h < 4) {
h = 1;
} else {
SkASSERT((h & 3) == 0);
h >>= 2;
}
return w * h; return w * h;
} }
@ -165,7 +153,7 @@ size_t GrCompressedRowBytes(SkImage::CompressionType type, int width) {
case SkImage::CompressionType::kETC2_RGB8_UNORM: case SkImage::CompressionType::kETC2_RGB8_UNORM:
case SkImage::CompressionType::kBC1_RGB8_UNORM: case SkImage::CompressionType::kBC1_RGB8_UNORM:
case SkImage::CompressionType::kBC1_RGBA8_UNORM: { case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
int numBlocksWidth = num_ETC1_blocks_w(width); int numBlocksWidth = num_4x4_blocks(width);
static_assert(sizeof(ETC1Block) == sizeof(BC1Block)); static_assert(sizeof(ETC1Block) == sizeof(BC1Block));
return numBlocksWidth * sizeof(ETC1Block); return numBlocksWidth * sizeof(ETC1Block);
@ -181,8 +169,8 @@ SkISize GrCompressedDimensions(SkImage::CompressionType type, SkISize baseDimens
case SkImage::CompressionType::kETC2_RGB8_UNORM: case SkImage::CompressionType::kETC2_RGB8_UNORM:
case SkImage::CompressionType::kBC1_RGB8_UNORM: case SkImage::CompressionType::kBC1_RGB8_UNORM:
case SkImage::CompressionType::kBC1_RGBA8_UNORM: { case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
int numBlocksWidth = num_ETC1_blocks_w(baseDimensions.width()); int numBlocksWidth = num_4x4_blocks(baseDimensions.width());
int numBlocksHeight = num_ETC1_blocks_w(baseDimensions.height()); int numBlocksHeight = num_4x4_blocks(baseDimensions.height());
// Each BC1_RGB8_UNORM and ETC1 block has 16 pixels // Each BC1_RGB8_UNORM and ETC1 block has 16 pixels
return { 4 * numBlocksWidth, 4 * numBlocksHeight }; return { 4 * numBlocksWidth, 4 * numBlocksHeight };
@ -231,18 +219,19 @@ void GrTwoColorBC1Compress(const SkPixmap& pixmap, SkColor otherColor, char* dst
// black -> fColor0, otherColor -> fColor1 // black -> fColor0, otherColor -> fColor1
create_BC1_block(SK_ColorBLACK, otherColor, &block); create_BC1_block(SK_ColorBLACK, otherColor, &block);
int numXBlocks = num_ETC1_blocks_w(pixmap.width()); int numXBlocks = num_4x4_blocks(pixmap.width());
int numYBlocks = num_ETC1_blocks_w(pixmap.height()); int numYBlocks = num_4x4_blocks(pixmap.height());
for (int y = 0; y < numYBlocks; ++y) { for (int y = 0; y < numYBlocks; ++y) {
for (int x = 0; x < numXBlocks; ++x) { for (int x = 0; x < numXBlocks; ++x) {
int shift = 0; int shift = 0;
int offsetX = 4 * x, offsetY = 4 * y; int offsetX = 4 * x, offsetY = 4 * y;
block.fIndices = 0; // init all the pixels to color0 block.fIndices = 0; // init all the pixels to color0 (i.e., opaque black)
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) { for (int j = 0; j < 4; ++j, shift += 2) {
if (offsetX + j >= pixmap.width() || offsetY + i >= pixmap.height()) { if (offsetX + j >= pixmap.width() || offsetY + i >= pixmap.height()) {
// This can happen for the topmost levels of a mipmap // This can happen for the topmost levels of a mipmap and for
// non-multiple of 4 textures
continue; continue;
} }
@ -253,7 +242,6 @@ void GrTwoColorBC1Compress(const SkPixmap& pixmap, SkColor otherColor, char* dst
} else if (tmp != SK_ColorBLACK) { } else if (tmp != SK_ColorBLACK) {
block.fIndices |= 1 << shift; // color1 block.fIndices |= 1 << shift; // color1
} }
shift += 2;
} }
} }

View File

@ -297,7 +297,7 @@ sk_sp<SkImage> SkImage::MakeRasterFromCompressed(sk_sp<SkData> data,
SkAlphaType at = SkCompressionTypeIsOpaque(type) ? kOpaque_SkAlphaType SkAlphaType at = SkCompressionTypeIsOpaque(type) ? kOpaque_SkAlphaType
: kPremul_SkAlphaType; : kPremul_SkAlphaType;
SkImageInfo ii = SkImageInfo::Make({ width, height }, kRGBA_8888_SkColorType, at); SkImageInfo ii = SkImageInfo::MakeN32(width, height, at);
if (!SkImage_Raster::ValidArgs(ii, ii.minRowBytes(), nullptr)) { if (!SkImage_Raster::ValidArgs(ii, ii.minRowBytes(), nullptr)) {
return nullptr; return nullptr;