Revert "Remove compressed (ETC1) texture support from Ganesh"

This reverts commit ee26363aaa.

Reason for revert: Failing Google 3 roll.

Original change's description:
> Remove compressed (ETC1) texture support from Ganesh
> 
> Change-Id: If4cf286df87ea87338aba47001d90a5fcc4f2667
> Reviewed-on: https://skia-review.googlesource.com/17456
> Commit-Queue: Robert Phillips <robertphillips@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>
> 

TBR=bsalomon@google.com,robertphillips@google.com
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Change-Id: Ie1a57187287e03600a69e374501478e93c41415c
Reviewed-on: https://skia-review.googlesource.com/17527
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2017-05-22 20:14:41 +00:00 committed by Skia Commit-Bot
parent bcd8637772
commit bc262e110a
31 changed files with 1672 additions and 68 deletions

View File

@ -136,6 +136,7 @@ config("skia_private") {
"src/utils",
"src/utils/win",
"src/xml",
"third_party/etc1",
"third_party/gif",
]
@ -631,6 +632,7 @@ component("skia") {
"src/sfnt/SkOTTable_name.cpp",
"src/sfnt/SkOTUtils.cpp",
"src/utils/mac/SkStream_mac.cpp",
"third_party/etc1/etc1.cpp",
"third_party/gif/SkGifImageReader.cpp",
]

118
gm/etc1.cpp Normal file
View File

@ -0,0 +1,118 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm.h"
#include "sk_tool_utils.h"
#include "SkRandom.h"
#if SK_SUPPORT_GPU
#include "etc1.h"
#include "GrContext.h"
#include "GrRenderTargetContext.h"
#include "GrRenderTargetContextPriv.h"
#include "GrTextureProxy.h"
#include "effects/GrSimpleTextureEffect.h"
#include "ops/GrNonAAFillRectOp.h"
// Basic test of Ganesh's ETC1 support
class ETC1GM : public skiagm::GM {
public:
ETC1GM() {
this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
}
protected:
SkString onShortName() override {
return SkString("etc1");
}
SkISize onISize() override {
return SkISize::Make(kTexWidth + 2*kPad, kTexHeight + 2*kPad);
}
void onOnceBeforeDraw() override {
SkBitmap bm;
SkImageInfo ii = SkImageInfo::Make(kTexWidth, kTexHeight, kRGB_565_SkColorType,
kOpaque_SkAlphaType);
bm.allocPixels(ii);
bm.erase(SK_ColorBLUE, SkIRect::MakeWH(kTexWidth, kTexHeight));
for (int y = 0; y < kTexHeight; y += 4) {
for (int x = 0; x < kTexWidth; x += 4) {
bm.erase((x+y) % 8 ? SK_ColorRED : SK_ColorGREEN, SkIRect::MakeXYWH(x, y, 4, 4));
}
}
int size = etc1_get_encoded_data_size(bm.width(), bm.height());
fETC1Data.reset(size);
unsigned char* pixels = (unsigned char*) fETC1Data.get();
if (etc1_encode_image((unsigned char*) bm.getAddr16(0, 0),
bm.width(), bm.height(), 2, bm.rowBytes(), pixels)) {
fETC1Data.reset();
}
}
void onDraw(SkCanvas* canvas) override {
GrRenderTargetContext* renderTargetContext =
canvas->internal_private_accessTopLayerRenderTargetContext();
if (!renderTargetContext) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
return;
}
GrContext* context = canvas->getGrContext();
if (!context) {
return;
}
GrSurfaceDesc desc;
desc.fConfig = kETC1_GrPixelConfig;
desc.fWidth = kTexWidth;
desc.fHeight = kTexHeight;
sk_sp<GrTextureProxy> proxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
desc, SkBudgeted::kYes,
fETC1Data.get(), 0);
if (!proxy) {
return;
}
const SkMatrix trans = SkMatrix::MakeTrans(-kPad, -kPad);
sk_sp<GrFragmentProcessor> fp = GrSimpleTextureEffect::Make(context->resourceProvider(),
std::move(proxy),
nullptr, trans);
GrPaint grPaint;
grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
grPaint.addColorFragmentProcessor(std::move(fp));
SkRect rect = SkRect::MakeXYWH(kPad, kPad, kTexWidth, kTexHeight);
renderTargetContext->priv().testingOnly_addDrawOp(GrNonAAFillRectOp::Make(
std::move(grPaint), SkMatrix::I(), rect, nullptr, nullptr, GrAAType::kNone));
}
private:
static const int kPad = 8;
static const int kTexWidth = 16;
static const int kTexHeight = 20;
SkAutoTMalloc<char> fETC1Data;
typedef GM INHERITED;
};
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new ETC1GM;)
#endif

View File

@ -117,6 +117,7 @@ gm_sources = [
"$_gm/encode-alpha-jpeg.cpp",
"$_gm/encode-platform.cpp",
"$_gm/encode-srgb.cpp",
"$_gm/etc1.cpp",
"$_gm/extractbitmap.cpp",
"$_gm/fadefilter.cpp",
"$_gm/fatpathfill.cpp",

View File

@ -45,6 +45,7 @@ public:
bool srgbWriteControl() const { return fSRGBWriteControl; }
bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; }
bool gpuTracingSupport() const { return fGpuTracingSupport; }
bool compressedTexSubImageSupport() const { return fCompressedTexSubImageSupport; }
bool oversizedStencilSupport() const { return fOversizedStencilSupport; }
bool textureBarrierSupport() const { return fTextureBarrierSupport; }
bool sampleLocationsSupport() const { return fSampleLocationsSupport; }
@ -211,6 +212,7 @@ protected:
bool fReuseScratchTextures : 1;
bool fReuseScratchBuffers : 1;
bool fGpuTracingSupport : 1;
bool fCompressedTexSubImageSupport : 1;
bool fOversizedStencilSupport : 1;
bool fTextureBarrierSupport : 1;
bool fSampleLocationsSupport : 1;

View File

@ -298,7 +298,10 @@ enum GrPixelConfig {
* 8 bit signed integers per-channel. Byte order is b,g,r,a.
*/
kRGBA_8888_sint_GrPixelConfig,
/**
* ETC1 Compressed Data
*/
kETC1_GrPixelConfig,
/**
* Byte order is r, g, b, a. This color format is 32 bits per channel
*/
@ -334,6 +337,58 @@ static const int kGrPixelConfigCnt = kLast_GrPixelConfig + 1;
#error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format."
#endif
// Returns true if the pixel config is a GPU-specific compressed format
// representation.
static inline bool GrPixelConfigIsCompressed(GrPixelConfig config) {
switch (config) {
case kETC1_GrPixelConfig:
return true;
case kUnknown_GrPixelConfig:
case kAlpha_8_GrPixelConfig:
case kGray_8_GrPixelConfig:
case kRGB_565_GrPixelConfig:
case kRGBA_4444_GrPixelConfig:
case kRGBA_8888_GrPixelConfig:
case kBGRA_8888_GrPixelConfig:
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
case kRGBA_half_GrPixelConfig:
return false;
}
SkFAIL("Invalid pixel config");
return false;
}
/** If the pixel config is compressed, return an equivalent uncompressed format. */
static inline GrPixelConfig GrMakePixelConfigUncompressed(GrPixelConfig config) {
switch (config) {
case kETC1_GrPixelConfig:
return kRGBA_8888_GrPixelConfig;
case kUnknown_GrPixelConfig:
case kAlpha_8_GrPixelConfig:
case kGray_8_GrPixelConfig:
case kRGB_565_GrPixelConfig:
case kRGBA_4444_GrPixelConfig:
case kRGBA_8888_GrPixelConfig:
case kBGRA_8888_GrPixelConfig:
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
case kRGBA_half_GrPixelConfig:
SkASSERT(!GrPixelConfigIsCompressed(config));
return config;
}
SkFAIL("Invalid pixel config");
return config;
}
// Returns true if the pixel config is 32 bits per pixel
static inline bool GrPixelConfigIs8888Unorm(GrPixelConfig config) {
switch (config) {
@ -348,6 +403,7 @@ static inline bool GrPixelConfigIs8888Unorm(GrPixelConfig config) {
case kRGB_565_GrPixelConfig:
case kRGBA_4444_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kETC1_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
@ -373,6 +429,7 @@ static inline bool GrPixelConfigIsSRGB(GrPixelConfig config) {
case kRGBA_8888_GrPixelConfig:
case kBGRA_8888_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kETC1_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
@ -401,6 +458,7 @@ static inline GrPixelConfig GrPixelConfigSwapRAndB(GrPixelConfig config) {
case kRGB_565_GrPixelConfig:
case kRGBA_4444_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kETC1_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
@ -412,6 +470,7 @@ static inline GrPixelConfig GrPixelConfigSwapRAndB(GrPixelConfig config) {
}
static inline size_t GrBytesPerPixel(GrPixelConfig config) {
SkASSERT(!GrPixelConfigIsCompressed(config));
switch (config) {
case kAlpha_8_GrPixelConfig:
case kGray_8_GrPixelConfig:
@ -433,6 +492,7 @@ static inline size_t GrBytesPerPixel(GrPixelConfig config) {
case kRG_float_GrPixelConfig:
return 8;
case kUnknown_GrPixelConfig:
case kETC1_GrPixelConfig:
return 0;
}
SkFAIL("Invalid pixel config");
@ -441,6 +501,7 @@ static inline size_t GrBytesPerPixel(GrPixelConfig config) {
static inline bool GrPixelConfigIsOpaque(GrPixelConfig config) {
switch (config) {
case kETC1_GrPixelConfig:
case kRGB_565_GrPixelConfig:
case kGray_8_GrPixelConfig:
return true;
@ -476,6 +537,7 @@ static inline bool GrPixelConfigIsAlphaOnly(GrPixelConfig config) {
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kETC1_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kRGBA_half_GrPixelConfig:
@ -502,6 +564,7 @@ static inline bool GrPixelConfigIsFloatingPoint(GrPixelConfig config) {
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kETC1_GrPixelConfig:
return false;
}
SkFAIL("Invalid pixel config");
@ -716,6 +779,41 @@ enum GrGLBackendState {
kALL_GrGLBackendState = 0xffff
};
/**
* Returns the data size for the given compressed pixel config
*/
static inline size_t GrCompressedFormatDataSize(GrPixelConfig config,
int width, int height) {
SkASSERT(GrPixelConfigIsCompressed(config));
switch (config) {
case kETC1_GrPixelConfig:
SkASSERT((width & 3) == 0);
SkASSERT((height & 3) == 0);
return (width >> 2) * (height >> 2) * 8;
case kUnknown_GrPixelConfig:
case kAlpha_8_GrPixelConfig:
case kGray_8_GrPixelConfig:
case kRGB_565_GrPixelConfig:
case kRGBA_4444_GrPixelConfig:
case kRGBA_8888_GrPixelConfig:
case kBGRA_8888_GrPixelConfig:
case kSRGBA_8888_GrPixelConfig:
case kSBGRA_8888_GrPixelConfig:
case kRGBA_8888_sint_GrPixelConfig:
case kRGBA_float_GrPixelConfig:
case kRG_float_GrPixelConfig:
case kAlpha_half_GrPixelConfig:
case kRGBA_half_GrPixelConfig:
SkFAIL("Unknown compressed pixel config");
return 4 * width * height;
}
SkFAIL("Invalid pixel config");
return 4 * width * height;
}
/**
* This value translates to reseting all the context state for any backend.
*/

View File

@ -21,6 +21,7 @@ static const char* pixel_config_name(GrPixelConfig config) {
case kSRGBA_8888_GrPixelConfig: return "SRGBA8888";
case kSBGRA_8888_GrPixelConfig: return "SBGRA8888";
case kRGBA_8888_sint_GrPixelConfig: return "RGBA8888_sint";
case kETC1_GrPixelConfig: return "ETC1";
case kRGBA_float_GrPixelConfig: return "RGBAFloat";
case kRG_float_GrPixelConfig: return "RGFloat";
case kAlpha_half_GrPixelConfig: return "AlphaHalf";
@ -39,6 +40,7 @@ GrCaps::GrCaps(const GrContextOptions& options) {
fReuseScratchTextures = true;
fReuseScratchBuffers = true;
fGpuTracingSupport = false;
fCompressedTexSubImageSupport = false;
fOversizedStencilSupport = false;
fTextureBarrierSupport = false;
fSampleLocationsSupport = false;
@ -128,6 +130,7 @@ SkString GrCaps::dump() const {
r.appendf("Reuse Scratch Textures : %s\n", gNY[fReuseScratchTextures]);
r.appendf("Reuse Scratch Buffers : %s\n", gNY[fReuseScratchBuffers]);
r.appendf("Gpu Tracing Support : %s\n", gNY[fGpuTracingSupport]);
r.appendf("Compressed Update Support : %s\n", gNY[fCompressedTexSubImageSupport]);
r.appendf("Oversized Stencil Support : %s\n", gNY[fOversizedStencilSupport]);
r.appendf("Texture Barrier Support : %s\n", gNY[fTextureBarrierSupport]);
r.appendf("Sample Locations Support : %s\n", gNY[fSampleLocationsSupport]);

View File

@ -172,6 +172,9 @@ GrDrawOpAtlas::GrDrawOpAtlas(GrContext* context, sk_sp<GrTextureProxy> proxy,
SkDEBUGCODE(fNumPlots = numPlotsX * numPlotsY;)
// We currently do not support compressed atlases...
SkASSERT(!GrPixelConfigIsCompressed(fProxy->config()));
// set up allocated plots
fPlotArray.reset(new sk_sp<Plot>[ numPlotsX * numPlotsY ]);

View File

@ -143,8 +143,24 @@ GrTexture* GrGpu::createTexture(const GrSurfaceDesc& origDesc, SkBudgeted budget
desc.fOrigin = resolve_origin(desc.fOrigin, isRT);
this->handleDirtyContext();
GrTexture* tex = this->onCreateTexture(desc, budgeted, texels);
GrTexture* tex = nullptr;
if (GrPixelConfigIsCompressed(desc.fConfig)) {
// We shouldn't be rendering into this
SkASSERT(!isRT);
SkASSERT(0 == desc.fSampleCnt);
if (!caps->npotTextureTileSupport() &&
(!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
return nullptr;
}
this->handleDirtyContext();
tex = this->onCreateCompressedTexture(desc, budgeted, texels);
} else {
this->handleDirtyContext();
tex = this->onCreateTexture(desc, budgeted, texels);
}
if (tex) {
if (!caps->reuseScratchTextures() && !isRT) {
tex->resourcePriv().removeScratchKey();
@ -245,6 +261,9 @@ bool GrGpu::copySurface(GrSurface* dst,
if (GrPixelConfigIsSint(dst->config()) != GrPixelConfigIsSint(src->config())) {
return false;
}
if (GrPixelConfigIsCompressed(dst->config())) {
return false;
}
return this->onCopySurface(dst, src, srcRect, dstPoint);
}
@ -256,6 +275,11 @@ bool GrGpu::getReadPixelsInfo(GrSurface* srcSurface, int width, int height, size
SkASSERT(srcSurface);
SkASSERT(kGpuPrefersDraw_DrawPreference != *drawPreference);
// We currently do not support reading into a compressed buffer
if (GrPixelConfigIsCompressed(readConfig)) {
return false;
}
// We currently do not support reading into the packed formats 565 or 4444 as they are not
// required to have read back support on all devices and backends.
if (kRGB_565_GrPixelConfig == readConfig || kRGBA_4444_GrPixelConfig == readConfig) {
@ -287,6 +311,10 @@ bool GrGpu::getWritePixelsInfo(GrSurface* dstSurface, int width, int height,
SkASSERT(dstSurface);
SkASSERT(kGpuPrefersDraw_DrawPreference != *drawPreference);
if (GrPixelConfigIsCompressed(dstSurface->config()) && dstSurface->config() != srcConfig) {
return false;
}
if (SkToBool(dstSurface->asRenderTarget())) {
if (this->caps()->useDrawInsteadOfAllRenderTargetWrites()) {
ElevateDrawPreference(drawPreference, kRequireDraw_DrawPreference);
@ -325,6 +353,11 @@ bool GrGpu::readPixels(GrSurface* surface,
return false;
}
// We cannot read pixels into a compressed buffer
if (GrPixelConfigIsCompressed(config)) {
return false;
}
size_t bpp = GrBytesPerPixel(config);
if (!GrSurfacePriv::AdjustReadPixelParams(surface->width(), surface->height(), bpp,
&left, &top, &width, &height,

View File

@ -100,7 +100,8 @@ public:
* @param budgeted does this texture count against the resource cache budget?
* @param texels array of mipmap levels containing texel data to load.
* Each level begins with full-size palette data for paletted textures.
* It contains width*height texels. If there is only one
* For compressed formats the level contains the compressed pixel data.
* Otherwise, it contains width*height texels. If there is only one
* element and it contains nullptr fPixels, texture data is
* uninitialized.
* @return The texture object if successful, otherwise nullptr.
@ -545,10 +546,13 @@ private:
// overridden by backend-specific derived class to create objects.
// Texture size and sample size will have already been validated in base class before
// onCreateTexture is called.
// onCreateTexture/CompressedTexture are called.
virtual GrTexture* onCreateTexture(const GrSurfaceDesc& desc,
SkBudgeted budgeted,
const SkTArray<GrMipLevel>& texels) = 0;
virtual GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc,
SkBudgeted budgeted,
const SkTArray<GrMipLevel>& texels) = 0;
virtual sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
GrSurfaceOrigin,

View File

@ -157,15 +157,17 @@ sk_sp<GrTextureProxy> GrResourceProvider::createTextureProxy(const GrSurfaceDesc
GrContext* context = fGpu->getContext();
SkImageInfo srcInfo;
if (!GrPixelConfigIsCompressed(desc.fConfig)) {
SkImageInfo srcInfo;
if (make_info(desc.fWidth, desc.fHeight, desc.fConfig, &srcInfo)) {
sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, 0);
sk_sp<GrSurfaceContext> sContext =
context->contextPriv().makeWrappedSurfaceContext(std::move(tex));
if (sContext) {
if (sContext->writePixels(srcInfo, mipLevel.fPixels, mipLevel.fRowBytes, 0, 0)) {
return sContext->asTextureProxyRef();
if (make_info(desc.fWidth, desc.fHeight, desc.fConfig, &srcInfo)) {
sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, 0);
sk_sp<GrSurfaceContext> sContext =
context->contextPriv().makeWrappedSurfaceContext(std::move(tex));
if (sContext) {
if (sContext->writePixels(srcInfo, mipLevel.fPixels, mipLevel.fRowBytes, 0, 0)) {
return sContext->asTextureProxyRef();
}
}
}
}
@ -190,12 +192,14 @@ sk_sp<GrTexture> GrResourceProvider::createTexture(const GrSurfaceDesc& desc, Sk
return nullptr;
}
sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, flags);
if (tex) {
return tex;
if (!GrPixelConfigIsCompressed(desc.fConfig)) {
sk_sp<GrTexture> tex = this->getExactScratch(desc, budgeted, flags);
if (tex) {
return tex;
}
}
tex.reset(fGpu->createTexture(desc, budgeted));
sk_sp<GrTexture> tex(fGpu->createTexture(desc, budgeted));
return tex;
}
@ -207,6 +211,11 @@ GrTexture* GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc, ui
return nullptr;
}
// Currently we don't recycle compressed textures as scratch.
if (GrPixelConfigIsCompressed(desc.fConfig)) {
return nullptr;
}
if (!validate_desc(desc, *fCaps)) {
return nullptr;
}
@ -217,6 +226,7 @@ GrTexture* GrResourceProvider::createApproxTexture(const GrSurfaceDesc& desc, ui
GrTexture* GrResourceProvider::refScratchTexture(const GrSurfaceDesc& inDesc, uint32_t flags) {
ASSERT_SINGLE_OWNER
SkASSERT(!this->isAbandoned());
SkASSERT(!GrPixelConfigIsCompressed(inDesc.fConfig));
SkASSERT(validate_desc(inDesc, *fCaps));
SkTCopyOnFirstWrite<GrSurfaceDesc> desc(inDesc);

View File

@ -213,12 +213,13 @@ void GrShaderCaps::initSamplerPrecisionTable() {
table[kSRGBA_8888_GrPixelConfig] = lowp;
table[kSBGRA_8888_GrPixelConfig] = lowp;
table[kRGBA_8888_sint_GrPixelConfig] = lowp;
table[kETC1_GrPixelConfig] = lowp;
table[kRGBA_float_GrPixelConfig] = kHigh_GrSLPrecision;
table[kRG_float_GrPixelConfig] = kHigh_GrSLPrecision;
table[kAlpha_half_GrPixelConfig] = mediump;
table[kRGBA_half_GrPixelConfig] = mediump;
GR_STATIC_ASSERT(14 == kGrPixelConfigCnt);
GR_STATIC_ASSERT(15 == kGrPixelConfigCnt);
}
}

View File

@ -29,6 +29,7 @@ size_t GrSurface::WorstCaseSize(const GrSurfaceDesc& desc, bool useNextPow2) {
colorValuesPerPixel += 1;
}
SkASSERT(kUnknown_GrPixelConfig != desc.fConfig);
SkASSERT(!GrPixelConfigIsCompressed(desc.fConfig));
size_t colorBytes = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
// This would be a nice assert to have (i.e., we aren't creating 0 width/height surfaces).
@ -38,7 +39,11 @@ size_t GrSurface::WorstCaseSize(const GrSurfaceDesc& desc, bool useNextPow2) {
size = colorValuesPerPixel * colorBytes;
size += colorBytes/3; // in case we have to mipmap
} else {
size = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
if (GrPixelConfigIsCompressed(desc.fConfig)) {
size = GrCompressedFormatDataSize(desc.fConfig, width, height);
} else {
size = (size_t) width * height * GrBytesPerPixel(desc.fConfig);
}
size += size/3; // in case we have to mipmap
}
@ -52,11 +57,17 @@ size_t GrSurface::ComputeSize(GrPixelConfig config,
int colorSamplesPerPixel,
bool hasMIPMaps,
bool useNextPow2) {
size_t colorSize;
width = useNextPow2 ? GrNextPow2(width) : width;
height = useNextPow2 ? GrNextPow2(height) : height;
SkASSERT(kUnknown_GrPixelConfig != config);
size_t colorSize = (size_t)width * height * GrBytesPerPixel(config);
if (GrPixelConfigIsCompressed(config)) {
colorSize = GrCompressedFormatDataSize(config, width, height);
} else {
colorSize = (size_t)width * height * GrBytesPerPixel(config);
}
SkASSERT(colorSize > 0);
size_t finalSize = colorSamplesPerPixel * colorSize;

View File

@ -136,6 +136,18 @@ sk_sp<GrTextureProxy> GrSurfaceProxy::MakeDeferred(GrResourceProvider* resourceP
// TODO: move this logic into GrResourceProvider!
// TODO: share this testing code with check_texture_creation_params
if (GrPixelConfigIsCompressed(desc.fConfig)) {
if (SkBackingFit::kApprox == fit || kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
// We don't allow scratch compressed textures and, apparently can't Y-flip compressed
// textures
return nullptr;
}
if (!caps->npotTextureTileSupport() && (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
return nullptr;
}
}
if (!caps->isConfigTexturable(desc.fConfig)) {
return nullptr;
}

View File

@ -76,14 +76,16 @@ GrTexture::GrTexture(GrGpu* gpu, const GrSurfaceDesc& desc, GrSLType samplerType
}
void GrTexture::computeScratchKey(GrScratchKey* key) const {
const GrRenderTarget* rt = this->asRenderTarget();
int sampleCount = 0;
if (rt) {
sampleCount = rt->numStencilSamples();
if (!GrPixelConfigIsCompressed(this->config())) {
const GrRenderTarget* rt = this->asRenderTarget();
int sampleCount = 0;
if (rt) {
sampleCount = rt->numStencilSamples();
}
GrTexturePriv::ComputeScratchKey(this->config(), this->width(), this->height(),
this->origin(), SkToBool(rt), sampleCount,
this->texturePriv().hasMipMaps(), key);
}
GrTexturePriv::ComputeScratchKey(this->config(), this->width(), this->height(),
this->origin(), SkToBool(rt), sampleCount,
this->texturePriv().hasMipMaps(), key);
}
void GrTexturePriv::ComputeScratchKey(GrPixelConfig config, int width, int height,

View File

@ -23,10 +23,12 @@ sk_sp<GrTextureProxy> GrTextureProducer::CopyOnGpu(GrContext* context,
SkASSERT(!subset || !subset->isEmpty());
SkASSERT(context);
GrPixelConfig config = GrMakePixelConfigUncompressed(inputProxy->config());
const SkRect dstRect = SkRect::MakeIWH(copyParams.fWidth, copyParams.fHeight);
sk_sp<GrRenderTargetContext> copyRTC = context->makeDeferredRenderTargetContextWithFallback(
SkBackingFit::kExact, dstRect.width(), dstRect.height(), inputProxy->config(), nullptr);
SkBackingFit::kExact, dstRect.width(), dstRect.height(), config, nullptr);
if (!copyRTC) {
return nullptr;
}

View File

@ -209,7 +209,7 @@ sk_sp<GrTextureProxy> GrRefCachedBitmapTextureProxy(GrContext*,
/**
* Creates a new texture for the bitmap. Does not concern itself with cache keys or texture params.
* The bitmap must have CPU-accessible pixels. Attempts to take advantage of faster paths for
* yuv planes.
* compressed textures and yuv planes.
*/
sk_sp<GrTextureProxy> GrUploadBitmapToTextureProxy(GrResourceProvider*, const SkBitmap&,
SkColorSpace* dstColorSpace);

View File

@ -1344,6 +1344,15 @@ bool GrGLCaps::getTexImageFormats(GrPixelConfig surfaceConfig, GrPixelConfig ext
return true;
}
bool GrGLCaps::getCompressedTexImageFormats(GrPixelConfig surfaceConfig,
GrGLenum* internalFormat) const {
if (!GrPixelConfigIsCompressed(surfaceConfig)) {
return false;
}
*internalFormat = fConfigTable[surfaceConfig].fFormats.fInternalFormatTexImage;
return true;
}
bool GrGLCaps::getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig,
GrGLenum* externalFormat, GrGLenum* externalType) const {
if (!this->getExternalFormat(surfaceConfig, externalConfig, kOther_ExternalFormatUsage,
@ -1354,6 +1363,9 @@ bool GrGLCaps::getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig ex
}
bool GrGLCaps::getRenderbufferFormat(GrPixelConfig config, GrGLenum* internalFormat) const {
if (GrPixelConfigIsCompressed(config)) {
return false;
}
*internalFormat = fConfigTable[config].fFormats.fInternalFormatRenderbuffer;
return true;
}
@ -1362,6 +1374,9 @@ bool GrGLCaps::getExternalFormat(GrPixelConfig surfaceConfig, GrPixelConfig memo
ExternalFormatUsage usage, GrGLenum* externalFormat,
GrGLenum* externalType) const {
SkASSERT(externalFormat && externalType);
if (GrPixelConfigIsCompressed(memoryConfig)) {
return false;
}
bool surfaceIsAlphaOnly = GrPixelConfigIsAlphaOnly(surfaceConfig);
bool memoryIsAlphaOnly = GrPixelConfigIsAlphaOnly(memoryConfig);
@ -1911,6 +1926,39 @@ void GrGLCaps::initConfigTable(const GrContextOptions& contextOptions,
}
fConfigTable[kRGBA_half_GrPixelConfig].fSwizzle = GrSwizzle::RGBA();
// Compressed texture support
// glCompressedTexImage2D is available on all OpenGL ES devices. It is available on standard
// OpenGL after version 1.3. We'll assume at least that level of OpenGL support.
// TODO: Fix command buffer bindings and remove this.
fCompressedTexSubImageSupport = SkToBool(gli->fFunctions.fCompressedTexSubImage2D);
// No sized/unsized internal format distinction for compressed formats, no external format.
// Below we set the external formats and types to 0.
{
fConfigTable[kETC1_GrPixelConfig].fFormats.fBaseInternalFormat = GR_GL_COMPRESSED_ETC1_RGB8;
fConfigTable[kETC1_GrPixelConfig].fFormats.fSizedInternalFormat =
GR_GL_COMPRESSED_ETC1_RGB8;
fConfigTable[kETC1_GrPixelConfig].fFormats.fExternalFormat[kOther_ExternalFormatUsage] = 0;
fConfigTable[kETC1_GrPixelConfig].fFormats.fExternalType = 0;
fConfigTable[kETC1_GrPixelConfig].fFormatType = kNormalizedFixedPoint_FormatType;
if (kGL_GrGLStandard == standard) {
if (version >= GR_GL_VER(4, 3) || ctxInfo.hasExtension("GL_ARB_ES3_compatibility")) {
fConfigTable[kETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
}
} else {
if (version >= GR_GL_VER(3, 0) ||
ctxInfo.hasExtension("GL_OES_compressed_ETC1_RGB8_texture") ||
// ETC2 is a superset of ETC1, so we can just check for that, too.
(ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGB8_texture") &&
ctxInfo.hasExtension("GL_OES_compressed_ETC2_RGBA8_texture"))) {
fConfigTable[kETC1_GrPixelConfig].fFlags = ConfigInfo::kTextureable_Flag;
}
}
fConfigTable[kETC1_GrPixelConfig].fSwizzle = GrSwizzle::RGBA();
}
// Bulk populate the texture internal/external formats here and then deal with exceptions below.
// ES 2.0 requires that the internal/external formats match.

View File

@ -153,6 +153,8 @@ public:
GrGLenum* internalFormat, GrGLenum* externalFormat,
GrGLenum* externalType) const;
bool getCompressedTexImageFormats(GrPixelConfig surfaceConfig, GrGLenum* internalFormat) const;
bool getReadPixelsFormat(GrPixelConfig surfaceConfig, GrPixelConfig externalConfig,
GrGLenum* externalFormat, GrGLenum* externalType) const;

View File

@ -662,6 +662,10 @@ bool GrGLGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height,
GrPixelConfig srcConfig,
DrawPreference* drawPreference,
WritePixelTempDrawInfo* tempDrawInfo) {
if (GrPixelConfigIsCompressed(dstSurface->config())) {
return false;
}
// This subclass only allows writes to textures. If the dst is not a texture we have to draw
// into it. We could use glDrawPixels on GLs that have it, but we don't today.
if (!dstSurface->asTexture()) {
@ -761,9 +765,20 @@ bool GrGLGpu::onWritePixels(GrSurface* surface,
this->setScratchTextureUnit();
GL_CALL(BindTexture(glTex->target(), glTex->textureID()));
return this->uploadTexData(glTex->config(), glTex->width(), glTex->height(),
glTex->origin(), glTex->target(), kWrite_UploadType,
left, top, width, height, config, texels);
bool success = false;
if (GrPixelConfigIsCompressed(glTex->config())) {
// We check that config == desc.fConfig in GrGLGpu::canWriteTexturePixels()
SkASSERT(config == glTex->config());
success = this->uploadCompressedTexData(glTex->config(), glTex->width(), glTex->height(),
glTex->origin(), glTex->target(), texels,
kWrite_UploadType, left, top, width, height);
} else {
success = this->uploadTexData(glTex->config(), glTex->width(), glTex->height(),
glTex->origin(), glTex->target(), kWrite_UploadType,
left, top, width, height, config, texels);
}
return success;
}
bool GrGLGpu::onTransferPixels(GrSurface* surface,
@ -776,6 +791,11 @@ bool GrGLGpu::onTransferPixels(GrSurface* surface,
return false;
}
// For the moment, can't transfer compressed data
if (GrPixelConfigIsCompressed(glTex->config())) {
return false;
}
this->setScratchTextureUnit();
GL_CALL(BindTexture(glTex->target(), glTex->textureID()));
@ -798,6 +818,7 @@ bool GrGLGpu::onTransferPixels(GrSurface* surface,
// For GL_[UN]PACK_ALIGNMENT.
static inline GrGLint config_alignment(GrPixelConfig config) {
SkASSERT(!GrPixelConfigIsCompressed(config));
switch (config) {
case kAlpha_8_GrPixelConfig:
case kGray_8_GrPixelConfig:
@ -816,6 +837,7 @@ static inline GrGLint config_alignment(GrPixelConfig config) {
case kRG_float_GrPixelConfig:
return 4;
case kUnknown_GrPixelConfig:
case kETC1_GrPixelConfig:
return 0;
}
SkFAIL("Invalid pixel config");
@ -836,16 +858,16 @@ static inline GrGLint config_alignment(GrPixelConfig config) {
* @param baseWidth The width of the texture's base mipmap level
* @param baseHeight The height of the texture's base mipmap level
*/
static bool allocate_and_populate_texture(GrPixelConfig config,
const GrGLInterface& interface,
const GrGLCaps& caps,
GrGLenum target,
GrGLenum internalFormat,
GrGLenum internalFormatForTexStorage,
GrGLenum externalFormat,
GrGLenum externalType,
const SkTArray<GrMipLevel>& texels,
int baseWidth, int baseHeight) {
static bool allocate_and_populate_uncompressed_texture(GrPixelConfig config,
const GrGLInterface& interface,
const GrGLCaps& caps,
GrGLenum target,
GrGLenum internalFormat,
GrGLenum internalFormatForTexStorage,
GrGLenum externalFormat,
GrGLenum externalType,
const SkTArray<GrMipLevel>& texels,
int baseWidth, int baseHeight) {
CLEAR_ERROR_BEFORE_ALLOC(&interface);
bool useTexStorage = caps.isConfigTexSupportEnabled(config);
@ -928,6 +950,97 @@ static bool allocate_and_populate_texture(GrPixelConfig config,
return true;
}
/**
* Creates storage space for the texture and fills it with texels.
*
* @param config Compressed pixel config of the texture.
* @param desc The surface descriptor for the texture being created.
* @param interface The GL interface in use.
* @param caps The capabilities of the GL device.
* @param internalFormat The data format used for the internal storage of the texture.
* @param texels The texel data of the texture being created.
*/
static bool allocate_and_populate_compressed_texture(GrPixelConfig config,
const GrGLInterface& interface,
const GrGLCaps& caps,
GrGLenum target, GrGLenum internalFormat,
const SkTArray<GrMipLevel>& texels,
int baseWidth, int baseHeight) {
CLEAR_ERROR_BEFORE_ALLOC(&interface);
bool useTexStorage = caps.isConfigTexSupportEnabled(config);
// We can only use TexStorage if we know we will not later change the storage requirements.
// This means if we may later want to add mipmaps, we cannot use TexStorage.
// Right now, we cannot know if we will later add mipmaps or not.
// The only time we can use TexStorage is when we already have the
// mipmaps.
useTexStorage &= texels.count() > 1;
if (useTexStorage) {
// We never resize or change formats of textures.
GL_ALLOC_CALL(&interface,
TexStorage2D(target,
texels.count(),
internalFormat,
baseWidth, baseHeight));
GrGLenum error = CHECK_ALLOC_ERROR(&interface);
if (error != GR_GL_NO_ERROR) {
return false;
} else {
for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
const void* currentMipData = texels[currentMipLevel].fPixels;
if (currentMipData == nullptr) {
continue;
}
int twoToTheMipLevel = 1 << currentMipLevel;
int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
// Make sure that the width and height that we pass to OpenGL
// is a multiple of the block size.
size_t dataSize = GrCompressedFormatDataSize(config, currentWidth, currentHeight);
GR_GL_CALL(&interface, CompressedTexSubImage2D(target,
currentMipLevel,
0, // left
0, // top
currentWidth,
currentHeight,
internalFormat,
SkToInt(dataSize),
currentMipData));
}
}
} else {
for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
int twoToTheMipLevel = 1 << currentMipLevel;
int currentWidth = SkTMax(1, baseWidth / twoToTheMipLevel);
int currentHeight = SkTMax(1, baseHeight / twoToTheMipLevel);
// Make sure that the width and height that we pass to OpenGL
// is a multiple of the block size.
size_t dataSize = GrCompressedFormatDataSize(config, baseWidth, baseHeight);
GL_ALLOC_CALL(&interface,
CompressedTexImage2D(target,
currentMipLevel,
internalFormat,
currentWidth,
currentHeight,
0, // border
SkToInt(dataSize),
texels[currentMipLevel].fPixels));
GrGLenum error = CHECK_ALLOC_ERROR(&interface);
if (error != GR_GL_NO_ERROR) {
return false;
}
}
}
return true;
}
/**
* After a texture is created, any state which was altered during its creation
* needs to be restored.
@ -952,6 +1065,9 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight
GrSurfaceOrigin texOrigin, GrGLenum target, UploadType uploadType,
int left, int top, int width, int height, GrPixelConfig dataConfig,
const SkTArray<GrMipLevel>& texels) {
// If we're uploading compressed data then we should be using uploadCompressedTexData
SkASSERT(!GrPixelConfigIsCompressed(dataConfig));
SkASSERT(this->caps()->isConfigTexturable(texConfig));
// texels is const.
@ -1106,7 +1222,7 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight
bool succeeded = true;
if (kNewTexture_UploadType == uploadType) {
if (0 == left && 0 == top && texWidth == width && texHeight == height) {
succeeded = allocate_and_populate_texture(
succeeded = allocate_and_populate_uncompressed_texture(
texConfig, *interface, caps, target, internalFormat,
internalFormatForTexStorage, externalFormat, externalType, texelsShallowCopy,
width, height);
@ -1138,6 +1254,75 @@ bool GrGLGpu::uploadTexData(GrPixelConfig texConfig, int texWidth, int texHeight
return succeeded;
}
// TODO: This function is using a lot of wonky semantics like, if width == -1
// then set width = desc.fWdith ... blah. A better way to do it might be to
// create a CompressedTexData struct that takes a desc/ptr and figures out
// the proper upload semantics. Then users can construct this function how they
// see fit if they want to go against the "standard" way to do it.
bool GrGLGpu::uploadCompressedTexData(GrPixelConfig config, int texWidth, int texHeight,
GrSurfaceOrigin texOrigin, GrGLenum target,
const SkTArray<GrMipLevel>& texels, UploadType uploadType,
int left, int top, int width, int height) {
SkASSERT(this->caps()->isConfigTexturable(config));
// No support for software flip y, yet...
SkASSERT(kBottomLeft_GrSurfaceOrigin != texOrigin);
const GrGLInterface* interface = this->glInterface();
const GrGLCaps& caps = this->glCaps();
if (-1 == width) {
width = texWidth;
}
#ifdef SK_DEBUG
else {
SkASSERT(width <= texWidth);
}
#endif
if (-1 == height) {
height = texHeight;
}
#ifdef SK_DEBUG
else {
SkASSERT(height <= texHeight);
}
#endif
// We only need the internal format for compressed 2D textures.
GrGLenum internalFormat;
if (!caps.getCompressedTexImageFormats(config, &internalFormat)) {
return false;
}
if (kNewTexture_UploadType == uploadType) {
return allocate_and_populate_compressed_texture(config, *interface, caps, target,
internalFormat, texels, width, height);
} else {
for (int currentMipLevel = 0; currentMipLevel < texels.count(); currentMipLevel++) {
SkASSERT(texels[currentMipLevel].fPixels || kTransfer_UploadType == uploadType);
int twoToTheMipLevel = 1 << currentMipLevel;
int currentWidth = SkTMax(1, width / twoToTheMipLevel);
int currentHeight = SkTMax(1, height / twoToTheMipLevel);
// Make sure that the width and height that we pass to OpenGL
// is a multiple of the block size.
size_t dataSize = GrCompressedFormatDataSize(config, currentWidth, currentHeight);
GL_CALL(CompressedTexSubImage2D(target,
currentMipLevel,
left, top,
currentWidth,
currentHeight,
internalFormat,
SkToInt(dataSize),
texels[currentMipLevel].fPixels));
}
}
return true;
}
static bool renderbuffer_storage_msaa(const GrGLContext& ctx,
int sampleCount,
GrGLenum format,
@ -1292,6 +1477,18 @@ static size_t as_size_t(int x) {
}
#endif
static GrGLTexture::IDDesc generate_gl_texture(const GrGLInterface* interface) {
GrGLTexture::IDDesc idDesc;
idDesc.fInfo.fID = 0;
GR_GL_CALL(interface, GenTextures(1, &idDesc.fInfo.fID));
idDesc.fOwnership = GrBackendObjectOwnership::kOwned;
// When we create the texture, we only
// create GL_TEXTURE_2D at the moment.
// External clients can do something different.
idDesc.fInfo.fTarget = GR_GL_TEXTURE_2D;
return idDesc;
}
static void set_initial_texture_params(const GrGLInterface* interface,
const GrGLTextureInfo& info,
GrGLTexture::TexParams* initialTexParams) {
@ -1364,6 +1561,41 @@ GrTexture* GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc,
return tex;
}
GrTexture* GrGLGpu::onCreateCompressedTexture(const GrSurfaceDesc& desc,
SkBudgeted budgeted,
const SkTArray<GrMipLevel>& texels) {
// Make sure that we're not flipping Y.
if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
return return_null_texture();
}
GrGLTexture::IDDesc idDesc = generate_gl_texture(this->glInterface());
if (!idDesc.fInfo.fID) {
return return_null_texture();
}
this->setScratchTextureUnit();
GL_CALL(BindTexture(idDesc.fInfo.fTarget, idDesc.fInfo.fID));
GrGLTexture::TexParams initialTexParams;
set_initial_texture_params(this->glInterface(), idDesc.fInfo, &initialTexParams);
if (!this->uploadCompressedTexData(desc.fConfig, desc.fWidth, desc.fHeight, desc.fOrigin,
idDesc.fInfo.fTarget, texels)) {
GL_CALL(DeleteTextures(1, &idDesc.fInfo.fID));
return return_null_texture();
}
GrGLTexture* tex;
tex = new GrGLTexture(this, budgeted, desc, idDesc);
tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
#ifdef TRACE_TEXTURE_CREATION
SkDebugf("--- new compressed texture [%d] size=(%d %d) config=%d\n",
idDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig);
#endif
return tex;
}
namespace {
const GrGLuint kUnknownBitCount = GrGLStencilAttachment::kUnknownBitCount;
@ -2795,7 +3027,7 @@ void GrGLGpu::bindTexture(int unitIdx, const GrSamplerParams& params, bool allow
GrSamplerParams::FilterMode filterMode = params.filterMode();
if (GrSamplerParams::kMipMap_FilterMode == filterMode) {
if (!this->caps()->mipMapSupport()) {
if (!this->caps()->mipMapSupport() || GrPixelConfigIsCompressed(texture->config())) {
filterMode = GrSamplerParams::kBilerp_FilterMode;
}
}
@ -2941,7 +3173,7 @@ void GrGLGpu::generateMipmaps(const GrSamplerParams& params, bool allowSRGBInput
GrSamplerParams::FilterMode filterMode = params.filterMode();
if (GrSamplerParams::kMipMap_FilterMode == filterMode) {
if (!this->caps()->mipMapSupport()) {
if (!this->caps()->mipMapSupport() || GrPixelConfigIsCompressed(texture->config())) {
filterMode = GrSamplerParams::kBilerp_FilterMode;
}
}
@ -3163,6 +3395,7 @@ static inline bool can_copy_texsubimage(const GrSurface* dst,
// Check that we could wrap the source in an FBO, that the dst is TEXTURE_2D, that no mirroring
// is required.
if (gpu->glCaps().canConfigBeFBOColorAttachment(src->config()) &&
!GrPixelConfigIsCompressed(src->config()) &&
(!srcTex || srcTex->target() == GR_GL_TEXTURE_2D) && dstTex->target() == GR_GL_TEXTURE_2D &&
dst->origin() == src->origin()) {
return true;

View File

@ -163,6 +163,9 @@ private:
GrTexture* onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
const SkTArray<GrMipLevel>& texels) override;
GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc,
SkBudgeted budgeted,
const SkTArray<GrMipLevel>& texels) override;
GrBuffer* onCreateBuffer(size_t size, GrBufferType intendedType, GrAccessPattern,
const void* data) override;
@ -363,6 +366,18 @@ private:
int top, int width, int height, GrPixelConfig dataConfig,
const SkTArray<GrMipLevel>& texels);
// helper for onCreateCompressedTexture. If width and height are
// set to -1, then this function will use desc.fWidth and desc.fHeight
// for the size of the data. The isNewTexture flag should be set to true
// whenever a new texture needs to be created. Otherwise, we assume that
// the texture is already in GPU memory and that it's going to be updated
// with new data.
bool uploadCompressedTexData(GrPixelConfig texAndDataConfig, int texWidth, int texHeight,
GrSurfaceOrigin texOrigin, GrGLenum target,
const SkTArray<GrMipLevel>& texels,
UploadType uploadType = kNewTexture_UploadType, int left = 0,
int top = 0, int width = -1, int height = -1);
bool createRenderTargetObjects(const GrSurfaceDesc&, const GrGLTextureInfo& texInfo,
GrGLRenderTarget::IDDesc*);

View File

@ -67,6 +67,9 @@ std::unique_ptr<GrOp> GrCopySurfaceOp::Make(GrResourceProvider* resourceProvider
if (GrPixelConfigIsSint(dstProxy->config()) != GrPixelConfigIsSint(srcProxy->config())) {
return nullptr;
}
if (GrPixelConfigIsCompressed(dstProxy->config())) {
return nullptr;
}
SkIRect clippedSrcRect;
SkIPoint clippedDstPoint;
// If the rect is outside the srcProxy or dstProxy then we've already succeeded.

View File

@ -31,6 +31,7 @@ GrVkCaps::GrVkCaps(const GrContextOptions& contextOptions, const GrVkInterface*
fDiscardRenderTargetSupport = true;
fReuseScratchTextures = true; //TODO: figure this out
fGpuTracingSupport = false; //TODO: figure this out
fCompressedTexSubImageSupport = false; //TODO: figure this out
fOversizedStencilSupport = false; //TODO: figure this out
fUseDrawInsteadOfClear = false;
@ -306,6 +307,11 @@ void GrVkCaps::initConfigTable(const GrVkInterface* interface, VkPhysicalDevice
fConfigTable[i].init(interface, physDev, format);
}
}
// We currently do not support compressed textures in Vulkan
const uint16_t kFlagsToRemove = ConfigInfo::kTextureable_Flag|ConfigInfo::kRenderable_Flag;
fConfigTable[kETC1_GrPixelConfig].fOptimalFlags &= ~kFlagsToRemove;
fConfigTable[kETC1_GrPixelConfig].fLinearFlags &= ~kFlagsToRemove;
}
void GrVkCaps::ConfigInfo::InitConfigFlags(VkFormatFeatureFlags vkFlags, uint16_t* flags) {

View File

@ -315,6 +315,10 @@ GrBuffer* GrVkGpu::onCreateBuffer(size_t size, GrBufferType type, GrAccessPatter
bool GrVkGpu::onGetWritePixelsInfo(GrSurface* dstSurface, int width, int height,
GrPixelConfig srcConfig, DrawPreference* drawPreference,
WritePixelTempDrawInfo* tempDrawInfo) {
if (GrPixelConfigIsCompressed(dstSurface->config())) {
return false;
}
GrRenderTarget* renderTarget = dstSurface->asRenderTarget();
// Start off assuming no swizzling
@ -375,32 +379,43 @@ bool GrVkGpu::onWritePixels(GrSurface* surface,
}
bool success = false;
bool linearTiling = vkTex->isLinearTiled();
if (linearTiling) {
if (texels.count() > 1) {
SkDebugf("Can't upload mipmap data to linear tiled texture");
return false;
}
if (VK_IMAGE_LAYOUT_PREINITIALIZED != vkTex->currentLayout()) {
// Need to change the layout to general in order to perform a host write
vkTex->setImageLayout(this,
VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT,
VK_PIPELINE_STAGE_HOST_BIT,
false);
this->submitCommandBuffer(kForce_SyncQueue);
}
success = this->uploadTexDataLinear(vkTex, left, top, width, height, config,
texels.begin()->fPixels, texels.begin()->fRowBytes);
if (GrPixelConfigIsCompressed(vkTex->config())) {
// We check that config == desc.fConfig in GrGpu::getWritePixelsInfo()
SkASSERT(config == vkTex->config());
// TODO: add compressed texture support
// delete the following two lines and uncomment the two after that when ready
vkTex->unref();
return false;
//success = this->uploadCompressedTexData(vkTex->desc(), buffer, false, left, top, width,
// height);
} else {
int newMipLevels = texels.count();
int currentMipLevels = vkTex->texturePriv().maxMipMapLevel() + 1;
if (newMipLevels > currentMipLevels) {
if (!vkTex->reallocForMipmap(this, newMipLevels)) {
bool linearTiling = vkTex->isLinearTiled();
if (linearTiling) {
if (texels.count() > 1) {
SkDebugf("Can't upload mipmap data to linear tiled texture");
return false;
}
if (VK_IMAGE_LAYOUT_PREINITIALIZED != vkTex->currentLayout()) {
// Need to change the layout to general in order to perform a host write
vkTex->setImageLayout(this,
VK_IMAGE_LAYOUT_GENERAL,
VK_ACCESS_HOST_WRITE_BIT,
VK_PIPELINE_STAGE_HOST_BIT,
false);
this->submitCommandBuffer(kForce_SyncQueue);
}
success = this->uploadTexDataLinear(vkTex, left, top, width, height, config,
texels.begin()->fPixels, texels.begin()->fRowBytes);
} else {
int newMipLevels = texels.count();
int currentMipLevels = vkTex->texturePriv().maxMipMapLevel() + 1;
if (newMipLevels > currentMipLevels) {
if (!vkTex->reallocForMipmap(this, newMipLevels)) {
return false;
}
}
success = this->uploadTexDataOptimal(vkTex, left, top, width, height, config, texels);
}
success = this->uploadTexDataOptimal(vkTex, left, top, width, height, config, texels);
}
return success;
@ -483,6 +498,9 @@ bool GrVkGpu::uploadTexDataLinear(GrVkTexture* tex,
SkASSERT(data);
SkASSERT(tex->isLinearTiled());
// If we're uploading compressed data then we should be using uploadCompressedTexData
SkASSERT(!GrPixelConfigIsCompressed(dataConfig));
size_t bpp = GrBytesPerPixel(dataConfig);
if (!GrSurfacePriv::AdjustWritePixelParams(tex->width(), tex->height(), bpp, &left, &top,
@ -551,6 +569,9 @@ bool GrVkGpu::uploadTexDataOptimal(GrVkTexture* tex,
// first.
SkASSERT(1 == texels.count() || texels.count() == (tex->texturePriv().maxMipMapLevel() + 1));
// If we're uploading compressed data then we should be using uploadCompressedTexData
SkASSERT(!GrPixelConfigIsCompressed(dataConfig));
if (width == 0 || height == 0) {
return false;
}

View File

@ -176,6 +176,9 @@ private:
GrTexture* onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
const SkTArray<GrMipLevel>&) override;
GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, SkBudgeted,
const SkTArray<GrMipLevel>&) override { return NULL; }
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
GrSurfaceOrigin,
GrBackendTextureFlags,

View File

@ -48,6 +48,10 @@ bool GrPixelConfigToVkFormat(GrPixelConfig config, VkFormat* format) {
case kGray_8_GrPixelConfig:
*format = VK_FORMAT_R8_UNORM;
return true;
case kETC1_GrPixelConfig:
// converting to ETC2 which is a superset of ETC1
*format = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
return true;
case kRGBA_float_GrPixelConfig:
*format = VK_FORMAT_R32G32B32A32_SFLOAT;
return true;
@ -86,6 +90,8 @@ GrPixelConfig GrVkFormatToPixelConfig(VkFormat format) {
return kRGBA_4444_GrPixelConfig;
case VK_FORMAT_R8_UNORM:
return kAlpha_8_GrPixelConfig;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
return kETC1_GrPixelConfig; // this conversion seems a bit sketchy
case VK_FORMAT_R32G32B32A32_SFLOAT:
return kRGBA_float_GrPixelConfig;
case VK_FORMAT_R32G32_SFLOAT:

View File

@ -118,7 +118,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredProxyTest, reporter, ctxInfo) {
for (auto origin : { kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin }) {
for (auto widthHeight : { 100, 128, 1048576 }) {
for (auto config : { kAlpha_8_GrPixelConfig, kRGB_565_GrPixelConfig,
kRGBA_8888_GrPixelConfig }) {
kETC1_GrPixelConfig, kRGBA_8888_GrPixelConfig }) {
for (auto fit : { SkBackingFit::kExact, SkBackingFit::kApprox }) {
for (auto budgeted : { SkBudgeted::kYes, SkBudgeted::kNo }) {
for (auto numSamples : { 0, 4, 16, 128 }) {

161
third_party/etc1/LICENSE vendored Normal file
View File

@ -0,0 +1,161 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the
copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other
entities that control, are controlled by, or are under common control with
that entity. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such
entity, whether by contract or otherwise, or (ii) ownership of fifty
percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled
object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object
form, made available under the License, as indicated by a copyright
notice that is included in or attached to the work (an example is
provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original
version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor
for inclusion in the Work by the copyright owner or by an individual or
Legal Entity authorized to submit on behalf of the copyright owner. For
the purposes of this definition, "submitted" means any form of electronic,
verbal, or written communication sent to the Licensor or its
representatives, including but not limited to communication on electronic
mailing lists, source code control systems, and issue tracking systems that
are managed by, or on behalf of, the Licensor for the purpose of discussing
and improving the Work, but excluding communication that is conspicuously
marked or otherwise designated in writing by the copyright owner as "Not
a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
reproduce, prepare Derivative Works of, publicly display, publicly perform,
sublicense, and distribute the Work and such Derivative Works in Source or
Object form.
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in
this section) patent license to make, have made, use, offer to sell, sell,
import, and otherwise transfer the Work, where such license applies only to
those patent claims licensable by such Contributor that are necessarily
infringed by their Contribution(s) alone or by combination of their
Contribution(s) with the Work to which such Contribution(s) was submitted.
If You institute patent litigation against any entity (including a cross-claim
or counterclaim in a lawsuit) alleging that the Work or a Contribution
incorporated within the Work constitutes direct or contributory patent
infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that
You changed the files; and
You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices
from the Source form of the Work, excluding those notices that do not
pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable
copy of the attribution notices contained within such NOTICE file, excluding
those notices that do not pertain to any part of the Derivative Works, in
at least one of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or documentation, if
provided along with the Derivative Works; or, within a display generated by
the Derivative Works, if and wherever such third-party notices normally
appear. The contents of the NOTICE file are for informational purposes
only and do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside or as
an addendum to the NOTICE text from the Work, provided that such additional
attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without any
additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you
may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as
required for reasonable and customary use in describing the origin of the
Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
in writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
ANY KIND, either express or implied, including, without limitation, any
warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or
FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining
the appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including
any direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other
commercial damages or losses), even if such Contributor has been advised
of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the
Work or Derivative Works thereof, You may choose to offer, and charge a
fee for, acceptance of support, warranty, indemnity, or other liability
obligations and/or rights consistent with this License. However, in accepting
such obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any
liability incurred by, or claims asserted against, such Contributor by
reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

7
third_party/etc1/README.google vendored Normal file
View File

@ -0,0 +1,7 @@
URL: https://android.googlesource.com/platform/frameworks/native/+/master/opengl/
Version: 01cc538b
License: Apache 2.0
License File: LICENSE
Description: PKM file format (ETC1 data) support
Local Modifications: Created LICENSE file for compliance purposes. Not included in original
distribution.

678
third_party/etc1/etc1.cpp vendored Normal file
View File

@ -0,0 +1,678 @@
// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//////////////////////////////////////////////////////////////////////////////////////////
// This is a fork of the AOSP project ETC1 codec. The original code can be found
// at the following web site:
// https://android.googlesource.com/platform/frameworks/native/+/master/opengl/include/ETC1/
//////////////////////////////////////////////////////////////////////////////////////////
#include "etc1.h"
#include <cstring>
/* From http://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
The number of bits that represent a 4x4 texel block is 64 bits if
<internalformat> is given by ETC1_RGB8_OES.
The data for a block is a number of bytes,
{q0, q1, q2, q3, q4, q5, q6, q7}
where byte q0 is located at the lowest memory address and q7 at
the highest. The 64 bits specifying the block is then represented
by the following 64 bit integer:
int64bit = 256*(256*(256*(256*(256*(256*(256*q0+q1)+q2)+q3)+q4)+q5)+q6)+q7;
ETC1_RGB8_OES:
a) bit layout in bits 63 through 32 if diffbit = 0
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
-----------------------------------------------
| base col1 | base col2 | base col1 | base col2 |
| R1 (4bits)| R2 (4bits)| G1 (4bits)| G2 (4bits)|
-----------------------------------------------
47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
---------------------------------------------------
| base col1 | base col2 | table | table |diff|flip|
| B1 (4bits)| B2 (4bits)| cw 1 | cw 2 |bit |bit |
---------------------------------------------------
b) bit layout in bits 63 through 32 if diffbit = 1
63 62 61 60 59 58 57 56 55 54 53 52 51 50 49 48
-----------------------------------------------
| base col1 | dcol 2 | base col1 | dcol 2 |
| R1' (5 bits) | dR2 | G1' (5 bits) | dG2 |
-----------------------------------------------
47 46 45 44 43 42 41 40 39 38 37 36 35 34 33 32
---------------------------------------------------
| base col 1 | dcol 2 | table | table |diff|flip|
| B1' (5 bits) | dB2 | cw 1 | cw 2 |bit |bit |
---------------------------------------------------
c) bit layout in bits 31 through 0 (in both cases)
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
-----------------------------------------------
| most significant pixel index bits |
| p| o| n| m| l| k| j| i| h| g| f| e| d| c| b| a|
-----------------------------------------------
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
--------------------------------------------------
| least significant pixel index bits |
| p| o| n| m| l| k| j| i| h| g| f| e| d| c | b | a |
--------------------------------------------------
Add table 3.17.2: Intensity modifier sets for ETC1 compressed textures:
table codeword modifier table
------------------ ----------------------
0 -8 -2 2 8
1 -17 -5 5 17
2 -29 -9 9 29
3 -42 -13 13 42
4 -60 -18 18 60
5 -80 -24 24 80
6 -106 -33 33 106
7 -183 -47 47 183
Add table 3.17.3 Mapping from pixel index values to modifier values for
ETC1 compressed textures:
pixel index value
---------------
msb lsb resulting modifier value
----- ----- -------------------------
1 1 -b (large negative value)
1 0 -a (small negative value)
0 0 a (small positive value)
0 1 b (large positive value)
*/
static const int kModifierTable[] = {
/* 0 */2, 8, -2, -8,
/* 1 */5, 17, -5, -17,
/* 2 */9, 29, -9, -29,
/* 3 */13, 42, -13, -42,
/* 4 */18, 60, -18, -60,
/* 5 */24, 80, -24, -80,
/* 6 */33, 106, -33, -106,
/* 7 */47, 183, -47, -183 };
static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
static inline etc1_byte clamp(int x) {
return (etc1_byte) (x >= 0 ? (x < 255 ? x : 255) : 0);
}
static
inline int convert4To8(int b) {
int c = b & 0xf;
return (c << 4) | c;
}
static
inline int convert5To8(int b) {
int c = b & 0x1f;
return (c << 3) | (c >> 2);
}
static
inline int convert6To8(int b) {
int c = b & 0x3f;
return (c << 2) | (c >> 4);
}
static
inline int divideBy255(int d) {
return (d + 128 + (d >> 8)) >> 8;
}
static
inline int convert8To4(int b) {
int c = b & 0xff;
return divideBy255(c * 15);
}
static
inline int convert8To5(int b) {
int c = b & 0xff;
return divideBy255(c * 31);
}
static
inline int convertDiff(int base, int diff) {
return convert5To8((0x1f & base) + kLookup[0x7 & diff]);
}
static
void decode_subblock(etc1_byte* pOut, int r, int g, int b, const int* table,
etc1_uint32 low, bool second, bool flipped) {
int baseX = 0;
int baseY = 0;
if (second) {
if (flipped) {
baseY = 2;
} else {
baseX = 2;
}
}
for (int i = 0; i < 8; i++) {
int x, y;
if (flipped) {
x = baseX + (i >> 1);
y = baseY + (i & 1);
} else {
x = baseX + (i >> 2);
y = baseY + (i & 3);
}
int k = y + (x * 4);
int offset = ((low >> k) & 1) | ((low >> (k + 15)) & 2);
int delta = table[offset];
etc1_byte* q = pOut + 3 * (x + 4 * y);
*q++ = clamp(r + delta);
*q++ = clamp(g + delta);
*q++ = clamp(b + delta);
}
}
// Input is an ETC1 compressed version of the data.
// Output is a 4 x 4 square of 3-byte pixels in form R, G, B
void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut) {
etc1_uint32 high = (pIn[0] << 24) | (pIn[1] << 16) | (pIn[2] << 8) | pIn[3];
etc1_uint32 low = (pIn[4] << 24) | (pIn[5] << 16) | (pIn[6] << 8) | pIn[7];
int r1, r2, g1, g2, b1, b2;
if (high & 2) {
// differential
int rBase = high >> 27;
int gBase = high >> 19;
int bBase = high >> 11;
r1 = convert5To8(rBase);
r2 = convertDiff(rBase, high >> 24);
g1 = convert5To8(gBase);
g2 = convertDiff(gBase, high >> 16);
b1 = convert5To8(bBase);
b2 = convertDiff(bBase, high >> 8);
} else {
// not differential
r1 = convert4To8(high >> 28);
r2 = convert4To8(high >> 24);
g1 = convert4To8(high >> 20);
g2 = convert4To8(high >> 16);
b1 = convert4To8(high >> 12);
b2 = convert4To8(high >> 8);
}
int tableIndexA = 7 & (high >> 5);
int tableIndexB = 7 & (high >> 2);
const int* tableA = kModifierTable + tableIndexA * 4;
const int* tableB = kModifierTable + tableIndexB * 4;
bool flipped = (high & 1) != 0;
decode_subblock(pOut, r1, g1, b1, tableA, low, false, flipped);
decode_subblock(pOut, r2, g2, b2, tableB, low, true, flipped);
}
typedef struct {
etc1_uint32 high;
etc1_uint32 low;
etc1_uint32 score; // Lower is more accurate
} etc_compressed;
static
inline void take_best(etc_compressed* a, const etc_compressed* b) {
if (a->score > b->score) {
*a = *b;
}
}
static
void etc_average_colors_subblock(const etc1_byte* pIn, etc1_uint32 inMask,
etc1_byte* pColors, bool flipped, bool second) {
int r = 0;
int g = 0;
int b = 0;
if (flipped) {
int by = 0;
if (second) {
by = 2;
}
for (int y = 0; y < 2; y++) {
int yy = by + y;
for (int x = 0; x < 4; x++) {
int i = x + 4 * yy;
if (inMask & (1 << i)) {
const etc1_byte* p = pIn + i * 3;
r += *(p++);
g += *(p++);
b += *(p++);
}
}
}
} else {
int bx = 0;
if (second) {
bx = 2;
}
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 2; x++) {
int xx = bx + x;
int i = xx + 4 * y;
if (inMask & (1 << i)) {
const etc1_byte* p = pIn + i * 3;
r += *(p++);
g += *(p++);
b += *(p++);
}
}
}
}
pColors[0] = (etc1_byte)((r + 4) >> 3);
pColors[1] = (etc1_byte)((g + 4) >> 3);
pColors[2] = (etc1_byte)((b + 4) >> 3);
}
static
inline int square(int x) {
return x * x;
}
static etc1_uint32 chooseModifier(const etc1_byte* pBaseColors,
const etc1_byte* pIn, etc1_uint32 *pLow, int bitIndex,
const int* pModifierTable) {
etc1_uint32 bestScore = ~0;
int bestIndex = 0;
int pixelR = pIn[0];
int pixelG = pIn[1];
int pixelB = pIn[2];
int r = pBaseColors[0];
int g = pBaseColors[1];
int b = pBaseColors[2];
for (int i = 0; i < 4; i++) {
int modifier = pModifierTable[i];
int decodedG = clamp(g + modifier);
etc1_uint32 score = (etc1_uint32) (6 * square(decodedG - pixelG));
if (score >= bestScore) {
continue;
}
int decodedR = clamp(r + modifier);
score += (etc1_uint32) (3 * square(decodedR - pixelR));
if (score >= bestScore) {
continue;
}
int decodedB = clamp(b + modifier);
score += (etc1_uint32) square(decodedB - pixelB);
if (score < bestScore) {
bestScore = score;
bestIndex = i;
}
}
etc1_uint32 lowMask = (((bestIndex >> 1) << 16) | (bestIndex & 1))
<< bitIndex;
*pLow |= lowMask;
return bestScore;
}
static
void etc_encode_subblock_helper(const etc1_byte* pIn, etc1_uint32 inMask,
etc_compressed* pCompressed, bool flipped, bool second,
const etc1_byte* pBaseColors, const int* pModifierTable) {
int score = pCompressed->score;
if (flipped) {
int by = 0;
if (second) {
by = 2;
}
for (int y = 0; y < 2; y++) {
int yy = by + y;
for (int x = 0; x < 4; x++) {
int i = x + 4 * yy;
if (inMask & (1 << i)) {
score += chooseModifier(pBaseColors, pIn + i * 3,
&pCompressed->low, yy + x * 4, pModifierTable);
}
}
}
} else {
int bx = 0;
if (second) {
bx = 2;
}
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 2; x++) {
int xx = bx + x;
int i = xx + 4 * y;
if (inMask & (1 << i)) {
score += chooseModifier(pBaseColors, pIn + i * 3,
&pCompressed->low, y + xx * 4, pModifierTable);
}
}
}
}
pCompressed->score = score;
}
static bool inRange4bitSigned(int color) {
return color >= -4 && color <= 3;
}
static void etc_encodeBaseColors(etc1_byte* pBaseColors,
const etc1_byte* pColors, etc_compressed* pCompressed) {
int r1, g1, b1, r2, g2, b2; // 8 bit base colors for sub-blocks
bool differential;
{
int r51 = convert8To5(pColors[0]);
int g51 = convert8To5(pColors[1]);
int b51 = convert8To5(pColors[2]);
int r52 = convert8To5(pColors[3]);
int g52 = convert8To5(pColors[4]);
int b52 = convert8To5(pColors[5]);
r1 = convert5To8(r51);
g1 = convert5To8(g51);
b1 = convert5To8(b51);
int dr = r52 - r51;
int dg = g52 - g51;
int db = b52 - b51;
differential = inRange4bitSigned(dr) && inRange4bitSigned(dg)
&& inRange4bitSigned(db);
if (differential) {
r2 = convert5To8(r51 + dr);
g2 = convert5To8(g51 + dg);
b2 = convert5To8(b51 + db);
pCompressed->high |= (r51 << 27) | ((7 & dr) << 24) | (g51 << 19)
| ((7 & dg) << 16) | (b51 << 11) | ((7 & db) << 8) | 2;
}
}
if (!differential) {
int r41 = convert8To4(pColors[0]);
int g41 = convert8To4(pColors[1]);
int b41 = convert8To4(pColors[2]);
int r42 = convert8To4(pColors[3]);
int g42 = convert8To4(pColors[4]);
int b42 = convert8To4(pColors[5]);
r1 = convert4To8(r41);
g1 = convert4To8(g41);
b1 = convert4To8(b41);
r2 = convert4To8(r42);
g2 = convert4To8(g42);
b2 = convert4To8(b42);
pCompressed->high |= (r41 << 28) | (r42 << 24) | (g41 << 20) | (g42
<< 16) | (b41 << 12) | (b42 << 8);
}
pBaseColors[0] = r1;
pBaseColors[1] = g1;
pBaseColors[2] = b1;
pBaseColors[3] = r2;
pBaseColors[4] = g2;
pBaseColors[5] = b2;
}
static
void etc_encode_block_helper(const etc1_byte* pIn, etc1_uint32 inMask,
const etc1_byte* pColors, etc_compressed* pCompressed, bool flipped) {
pCompressed->score = ~0;
pCompressed->high = (flipped ? 1 : 0);
pCompressed->low = 0;
etc1_byte pBaseColors[6];
etc_encodeBaseColors(pBaseColors, pColors, pCompressed);
int originalHigh = pCompressed->high;
const int* pModifierTable = kModifierTable;
for (int i = 0; i < 8; i++, pModifierTable += 4) {
etc_compressed temp;
temp.score = 0;
temp.high = originalHigh | (i << 5);
temp.low = 0;
etc_encode_subblock_helper(pIn, inMask, &temp, flipped, false,
pBaseColors, pModifierTable);
take_best(pCompressed, &temp);
}
pModifierTable = kModifierTable;
etc_compressed firstHalf = *pCompressed;
for (int i = 0; i < 8; i++, pModifierTable += 4) {
etc_compressed temp;
temp.score = firstHalf.score;
temp.high = firstHalf.high | (i << 2);
temp.low = firstHalf.low;
etc_encode_subblock_helper(pIn, inMask, &temp, flipped, true,
pBaseColors + 3, pModifierTable);
if (i == 0) {
*pCompressed = temp;
} else {
take_best(pCompressed, &temp);
}
}
}
static void writeBigEndian(etc1_byte* pOut, etc1_uint32 d) {
pOut[0] = (etc1_byte)(d >> 24);
pOut[1] = (etc1_byte)(d >> 16);
pOut[2] = (etc1_byte)(d >> 8);
pOut[3] = (etc1_byte) d;
}
// Input is a 4 x 4 square of 3-byte pixels in form R, G, B
// inmask is a 16-bit mask where bit (1 << (x + y * 4)) tells whether the corresponding (x,y)
// pixel is valid or not. Invalid pixel color values are ignored when compressing.
// Output is an ETC1 compressed version of the data.
void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 inMask,
etc1_byte* pOut) {
etc1_byte colors[6];
etc1_byte flippedColors[6];
etc_average_colors_subblock(pIn, inMask, colors, false, false);
etc_average_colors_subblock(pIn, inMask, colors + 3, false, true);
etc_average_colors_subblock(pIn, inMask, flippedColors, true, false);
etc_average_colors_subblock(pIn, inMask, flippedColors + 3, true, true);
etc_compressed a, b;
etc_encode_block_helper(pIn, inMask, colors, &a, false);
etc_encode_block_helper(pIn, inMask, flippedColors, &b, true);
take_best(&a, &b);
writeBigEndian(pOut, a.high);
writeBigEndian(pOut + 4, a.low);
}
// Return the size of the encoded image data (does not include size of PKM header).
etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height) {
return (((width + 3) & ~3) * ((height + 3) & ~3)) >> 1;
}
// Encode an entire image.
// pIn - pointer to the image data. Formatted such that the Red component of
// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset;
// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut) {
if (pixelSize < 2 || pixelSize > 3) {
return -1;
}
static const unsigned short kYMask[] = { 0x0, 0xf, 0xff, 0xfff, 0xffff };
static const unsigned short kXMask[] = { 0x0, 0x1111, 0x3333, 0x7777,
0xffff };
etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
etc1_byte encoded[ETC1_ENCODED_BLOCK_SIZE];
etc1_uint32 encodedWidth = (width + 3) & ~3;
etc1_uint32 encodedHeight = (height + 3) & ~3;
for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
etc1_uint32 yEnd = height - y;
if (yEnd > 4) {
yEnd = 4;
}
int ymask = kYMask[yEnd];
for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
etc1_uint32 xEnd = width - x;
if (xEnd > 4) {
xEnd = 4;
}
int mask = ymask & kXMask[xEnd];
for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
etc1_byte* q = block + (cy * 4) * 3;
const etc1_byte* p = pIn + pixelSize * x + stride * (y + cy);
if (pixelSize == 3) {
memcpy(q, p, xEnd * 3);
} else {
for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
int pixel = (p[1] << 8) | p[0];
*q++ = convert5To8(pixel >> 11);
*q++ = convert6To8(pixel >> 5);
*q++ = convert5To8(pixel);
p += pixelSize;
}
}
}
etc1_encode_block(block, mask, encoded);
memcpy(pOut, encoded, sizeof(encoded));
pOut += sizeof(encoded);
}
}
return 0;
}
// Decode an entire image.
// pIn - pointer to encoded data.
// pOut - pointer to the image data. Will be written such that the Red component of
// pixel (x,y) is at pIn + pixelSize * x + stride * y + redOffset. Must be
// large enough to store entire image.
int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
etc1_uint32 width, etc1_uint32 height,
etc1_uint32 pixelSize, etc1_uint32 stride) {
if (pixelSize < 2 || pixelSize > 3) {
return -1;
}
etc1_byte block[ETC1_DECODED_BLOCK_SIZE];
etc1_uint32 encodedWidth = (width + 3) & ~3;
etc1_uint32 encodedHeight = (height + 3) & ~3;
for (etc1_uint32 y = 0; y < encodedHeight; y += 4) {
etc1_uint32 yEnd = height - y;
if (yEnd > 4) {
yEnd = 4;
}
for (etc1_uint32 x = 0; x < encodedWidth; x += 4) {
etc1_uint32 xEnd = width - x;
if (xEnd > 4) {
xEnd = 4;
}
etc1_decode_block(pIn, block);
pIn += ETC1_ENCODED_BLOCK_SIZE;
for (etc1_uint32 cy = 0; cy < yEnd; cy++) {
const etc1_byte* q = block + (cy * 4) * 3;
etc1_byte* p = pOut + pixelSize * x + stride * (y + cy);
if (pixelSize == 3) {
memcpy(p, q, xEnd * 3);
} else {
for (etc1_uint32 cx = 0; cx < xEnd; cx++) {
etc1_byte r = *q++;
etc1_byte g = *q++;
etc1_byte b = *q++;
etc1_uint32 pixel = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
*p++ = (etc1_byte) pixel;
*p++ = (etc1_byte) (pixel >> 8);
}
}
}
}
}
return 0;
}
static const char kMagic[] = { 'P', 'K', 'M', ' ', '1', '0' };
static const etc1_uint32 ETC1_PKM_FORMAT_OFFSET = 6;
static const etc1_uint32 ETC1_PKM_ENCODED_WIDTH_OFFSET = 8;
static const etc1_uint32 ETC1_PKM_ENCODED_HEIGHT_OFFSET = 10;
static const etc1_uint32 ETC1_PKM_WIDTH_OFFSET = 12;
static const etc1_uint32 ETC1_PKM_HEIGHT_OFFSET = 14;
static const etc1_uint32 ETC1_RGB_NO_MIPMAPS = 0;
static void writeBEUint16(etc1_byte* pOut, etc1_uint32 data) {
pOut[0] = (etc1_byte) (data >> 8);
pOut[1] = (etc1_byte) data;
}
static etc1_uint32 readBEUint16(const etc1_byte* pIn) {
return (pIn[0] << 8) | pIn[1];
}
// Format a PKM header
void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height) {
memcpy(pHeader, kMagic, sizeof(kMagic));
etc1_uint32 encodedWidth = (width + 3) & ~3;
etc1_uint32 encodedHeight = (height + 3) & ~3;
writeBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET, ETC1_RGB_NO_MIPMAPS);
writeBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET, encodedWidth);
writeBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET, encodedHeight);
writeBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET, width);
writeBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET, height);
}
// Check if a PKM header is correctly formatted.
etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader) {
if (memcmp(pHeader, kMagic, sizeof(kMagic))) {
return false;
}
etc1_uint32 format = readBEUint16(pHeader + ETC1_PKM_FORMAT_OFFSET);
etc1_uint32 encodedWidth = readBEUint16(pHeader + ETC1_PKM_ENCODED_WIDTH_OFFSET);
etc1_uint32 encodedHeight = readBEUint16(pHeader + ETC1_PKM_ENCODED_HEIGHT_OFFSET);
etc1_uint32 width = readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
etc1_uint32 height = readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
return format == ETC1_RGB_NO_MIPMAPS &&
encodedWidth >= width && encodedWidth - width < 4 &&
encodedHeight >= height && encodedHeight - height < 4;
}
// Read the image width from a PKM header
etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader) {
return readBEUint16(pHeader + ETC1_PKM_WIDTH_OFFSET);
}
// Read the image height from a PKM header
etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader){
return readBEUint16(pHeader + ETC1_PKM_HEIGHT_OFFSET);
}

114
third_party/etc1/etc1.h vendored Normal file
View File

@ -0,0 +1,114 @@
// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//////////////////////////////////////////////////////////////////////////////////////////
// This is a fork of the AOSP project ETC1 codec. The original code can be found
// at the following web site:
// https://android.googlesource.com/platform/frameworks/native/+/master/opengl/libs/ETC1/
//////////////////////////////////////////////////////////////////////////////////////////
#ifndef __etc1_h__
#define __etc1_h__
#define ETC1_ENCODED_BLOCK_SIZE 8
#define ETC1_DECODED_BLOCK_SIZE 48
#ifndef ETC1_RGB8_OES
#define ETC1_RGB8_OES 0x8D64
#endif
typedef unsigned char etc1_byte;
typedef int etc1_bool;
typedef unsigned int etc1_uint32;
#ifdef __cplusplus
extern "C" {
#endif
// Encode a block of pixels.
//
// pIn is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
// value of pixel (x, y).
//
// validPixelMask is a 16-bit mask where bit (1 << (x + y * 4)) indicates whether
// the corresponding (x,y) pixel is valid. Invalid pixel color values are ignored when compressing.
//
// pOut is an ETC1 compressed version of the data.
void etc1_encode_block(const etc1_byte* pIn, etc1_uint32 validPixelMask, etc1_byte* pOut);
// Decode a block of pixels.
//
// pIn is an ETC1 compressed version of the data.
//
// pOut is a pointer to a ETC_DECODED_BLOCK_SIZE array of bytes that represent a
// 4 x 4 square of 3-byte pixels in form R, G, B. Byte (3 * (x + 4 * y) is the R
// value of pixel (x, y).
void etc1_decode_block(const etc1_byte* pIn, etc1_byte* pOut);
// Return the size of the encoded image data (does not include size of PKM header).
etc1_uint32 etc1_get_encoded_data_size(etc1_uint32 width, etc1_uint32 height);
// Encode an entire image.
// pIn - pointer to the image data. Formatted such that
// pixel (x,y) is at pIn + pixelSize * x + stride * y;
// pOut - pointer to encoded data. Must be large enough to store entire encoded image.
// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image.
// returns non-zero if there is an error.
int etc1_encode_image(const etc1_byte* pIn, etc1_uint32 width, etc1_uint32 height,
etc1_uint32 pixelSize, etc1_uint32 stride, etc1_byte* pOut);
// Decode an entire image.
// pIn - pointer to encoded data.
// pOut - pointer to the image data. Will be written such that
// pixel (x,y) is at pIn + pixelSize * x + stride * y. Must be
// large enough to store entire image.
// pixelSize can be 2 or 3. 2 is an GL_UNSIGNED_SHORT_5_6_5 image, 3 is a GL_BYTE RGB image.
// returns non-zero if there is an error.
int etc1_decode_image(const etc1_byte* pIn, etc1_byte* pOut,
etc1_uint32 width, etc1_uint32 height,
etc1_uint32 pixelSize, etc1_uint32 stride);
// Size of a PKM header, in bytes.
#define ETC_PKM_HEADER_SIZE 16
// Format a PKM header
void etc1_pkm_format_header(etc1_byte* pHeader, etc1_uint32 width, etc1_uint32 height);
// Check if a PKM header is correctly formatted.
etc1_bool etc1_pkm_is_valid(const etc1_byte* pHeader);
// Read the image width from a PKM header
etc1_uint32 etc1_pkm_get_width(const etc1_byte* pHeader);
// Read the image height from a PKM header
etc1_uint32 etc1_pkm_get_height(const etc1_byte* pHeader);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -360,6 +360,11 @@ private:
return nullptr;
}
GrTexture* onCreateCompressedTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
const SkTArray<GrMipLevel>& texels) override {
return nullptr;
}
sk_sp<GrTexture> onWrapBackendTexture(const GrBackendTexture&,
GrSurfaceOrigin,
GrBackendTextureFlags,