Make GrDirectContext::updateBackendTexture handle pixmaps with non-type row bytes.

Some GL contexts don't support GL_UNPACK_ROW_LENGTH and we must
copy the src data to a pixmap with tight row bytes.

Bug: chromium:1170392

Change-Id: I4590f20dbc80cb792f30f0059536716cf106f6c3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/361717
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
Brian Salomon 2021-01-29 09:45:15 -05:00 committed by Skia Commit-Bot
parent 99da77ae93
commit 07d8c0d11c
4 changed files with 87 additions and 38 deletions

View File

@ -292,6 +292,11 @@ struct SK_API GrContextOptions {
*/
bool fRandomGLOOM = false;
/**
* Force off support for write pixels row bytes in caps.
*/
bool fDisallowWritePixelRowBytes = false;
/**
* Include or exclude specific GPU path renderers.
*/

View File

@ -153,6 +153,9 @@ void GrCaps::applyOptionsOverrides(const GrContextOptions& options) {
if (options.fClearAllTextures) {
fShouldInitializeTextures = true;
}
if (options.fDisallowWritePixelRowBytes) {
fWritePixelsRowBytesSupport = false;
}
#endif
if (options.fSuppressMipmapSupport) {
fMipmapSupport = false;

View File

@ -489,26 +489,34 @@ static bool update_texture_with_pixmaps(GrGpu* gpu,
const GrBackendTexture& backendTexture,
GrSurfaceOrigin textureOrigin,
sk_sp<GrRefCntedCallback> finishedCallback) {
bool flip = textureOrigin == kBottomLeft_GrSurfaceOrigin;
bool mustBeTight = !gpu->caps()->writePixelsRowBytesSupport();
size_t size = 0;
for (int i = 0; i < numLevels; ++i) {
size_t minRowBytes = srcData[i].info().minRowBytes();
if (flip || (mustBeTight && srcData[i].rowBytes() != minRowBytes)) {
size += minRowBytes * srcData[i].height();
}
}
std::unique_ptr<char[]> tempStorage;
SkAutoSTArray<15, GrPixmap> tempPixmaps(numLevels);
if (textureOrigin == kBottomLeft_GrSurfaceOrigin) {
size_t size = 0;
for (int i = 0; i < numLevels; ++i) {
size += srcData[i].info().minRowBytes()*srcData[i].height();
}
if (size) {
tempStorage.reset(new char[size]);
size = 0;
for (int i = 0; i < numLevels; ++i) {
size_t tempRB = srcData[i].info().minRowBytes();
tempPixmaps[i] = {srcData[i].info(), tempStorage.get() + size, tempRB};
SkAssertResult(GrConvertPixels(tempPixmaps[i], srcData[i], /*flip*/ true));
size += tempRB*srcData[i].height();
}
} else {
for (int i = 0; i < numLevels; ++i) {
}
size = 0;
SkAutoSTArray<15, GrPixmap> tempPixmaps(numLevels);
for (int i = 0; i < numLevels; ++i) {
size_t minRowBytes = srcData[i].info().minRowBytes();
if (flip || (mustBeTight && srcData[i].rowBytes() != minRowBytes)) {
tempPixmaps[i] = {srcData[i].info(), tempStorage.get() + size, minRowBytes};
SkAssertResult(GrConvertPixels(tempPixmaps[i], srcData[i], flip));
size += minRowBytes*srcData[i].height();
} else {
tempPixmaps[i] = srcData[i];
}
}
GrGpu::BackendTextureData data(tempPixmaps.get());
return gpu->updateBackendTexture(backendTexture, std::move(finishedCallback), &data);
}

View File

@ -425,16 +425,31 @@ static void check_mipmaps(GrDirectContext* dContext,
}
}
static int make_pixmaps(SkColorType skColorType, GrMipmapped mipMapped,
const SkColor4f colors[6], SkAutoPixmapStorage pixmaps[6]) {
static int make_pixmaps(SkColorType skColorType,
GrMipmapped mipmapped,
const SkColor4f colors[6],
SkPixmap pixmaps[6],
std::unique_ptr<char[]>* mem) {
int levelSize = 32;
int numMipLevels = mipMapped == GrMipmapped::kYes ? 6 : 1;
int numMipLevels = mipmapped == GrMipmapped::kYes ? 6 : 1;
size_t size = 0;
SkImageInfo ii[6];
size_t rowBytes[6];
for (int level = 0; level < numMipLevels; ++level) {
SkImageInfo ii = SkImageInfo::Make(levelSize,
levelSize,
skColorType,
kUnpremul_SkAlphaType);
pixmaps[level].alloc(ii);
ii[level] = SkImageInfo::Make(levelSize, levelSize, skColorType, kUnpremul_SkAlphaType);
rowBytes[level] = ii[level].minRowBytes();
// Make sure we test row bytes that aren't tight.
if (!(level % 2)) {
rowBytes[level] += (level + 1)*SkColorTypeBytesPerPixel(ii[level].colorType());
}
size += rowBytes[level]*ii[level].height();
levelSize /= 2;
}
mem->reset(new char[size]);
char* addr = mem->get();
for (int level = 0; level < numMipLevels; ++level) {
pixmaps[level].reset(ii[level], addr, rowBytes[level]);
addr += rowBytes[level]*ii[level].height();
pixmaps[level].erase(colors[level]);
levelSize /= 2;
}
@ -451,9 +466,10 @@ static void test_pixmap_init(GrDirectContext* dContext,
GrRenderable)> create,
SkColorType skColorType,
GrSurfaceOrigin origin,
GrMipmapped mipMapped,
GrMipmapped mipmapped,
GrRenderable renderable) {
SkAutoPixmapStorage pixmapMem[6];
SkPixmap pixmaps[6];
std::unique_ptr<char[]> memForPixmaps;
SkColor4f colors[6] = {
{ 1.0f, 0.0f, 0.0f, 1.0f }, // R
{ 0.0f, 1.0f, 0.0f, 0.9f }, // G
@ -463,15 +479,9 @@ static void test_pixmap_init(GrDirectContext* dContext,
{ 1.0f, 1.0f, 0.0f, 0.2f }, // Y
};
int numMipLevels = make_pixmaps(skColorType, mipMapped, colors, pixmapMem);
int numMipLevels = make_pixmaps(skColorType, mipmapped, colors, pixmaps, &memForPixmaps);
SkASSERT(numMipLevels);
// TODO: this is tedious. Should we pass in an array of SkBitmaps instead?
SkPixmap pixmaps[6];
for (int i = 0; i < numMipLevels; ++i) {
pixmaps[i].reset(pixmapMem[i].info(), pixmapMem[i].addr(), pixmapMem[i].rowBytes());
}
sk_sp<ManagedBackendTexture> mbet = create(dContext, pixmaps, numMipLevels, origin, renderable);
if (!mbet) {
// errors here should be reported by the test_wrapping test
@ -485,7 +495,7 @@ static void test_pixmap_init(GrDirectContext* dContext,
auto checkBackendTexture = [&](SkColor4f colors[6]) {
GrColorType grColorType = SkColorTypeToGrColorType(skColorType);
if (mipMapped == GrMipmapped::kYes) {
if (mipmapped == GrMipmapped::kYes) {
SkColor4f expectedColors[6] = {
get_expected_color(colors[0], grColorType),
get_expected_color(colors[1], grColorType),
@ -514,10 +524,7 @@ static void test_pixmap_init(GrDirectContext* dContext,
{0.0f, 1.0f, 1.0f, 0.5f}, // C
{1.0f, 0.0f, 1.0f, 0.3f}, // M
};
make_pixmaps(skColorType, mipMapped, colorsNew, pixmapMem);
for (int i = 0; i < numMipLevels; ++i) {
pixmaps[i].reset(pixmapMem[i].info(), pixmapMem[i].addr(), pixmapMem[i].rowBytes());
}
make_pixmaps(skColorType, mipmapped, colorsNew, pixmaps, &memForPixmaps);
// Upload new data and make sure everything still works
dContext->updateBackendTexture(mbet->texture(),
@ -560,7 +567,8 @@ void check_vk_layout(const GrBackendTexture& backendTex, VkLayout layout) {
}
///////////////////////////////////////////////////////////////////////////////
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ColorTypeBackendAllocationTest, reporter, ctxInfo) {
void color_type_backend_allocation_test(const sk_gpu_test::ContextInfo& ctxInfo,
skiatest::Reporter* reporter) {
auto context = ctxInfo.directContext();
const GrCaps* caps = context->priv().caps();
@ -718,6 +726,31 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ColorTypeBackendAllocationTest, reporter, ctx
}
}
DEF_GPUTEST(ColorTypeBackendAllocationTest, reporter, options) {
for (int t = 0; t < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++t) {
auto type = static_cast<sk_gpu_test::GrContextFactory::ContextType>(t);
if (!sk_gpu_test::GrContextFactory::IsRenderingContext(type)) {
continue;
}
sk_gpu_test::GrContextFactory factory(options);
sk_gpu_test::ContextInfo info = factory.getContextInfo(type);
if (!info.directContext()) {
continue;
}
color_type_backend_allocation_test(info, reporter);
// The GL backend must support contexts that don't allow GL_UNPACK_ROW_LENGTH. Other
// backends are not required to work with this cap disabled.
if (info.directContext()->priv().caps()->writePixelsRowBytesSupport() &&
info.directContext()->backend() == GrBackendApi::kOpenGL) {
GrContextOptions overrideOptions = options;
overrideOptions.fDisallowWritePixelRowBytes = true;
sk_gpu_test::GrContextFactory overrideFactory(overrideOptions);
info = overrideFactory.getContextInfo(type);
color_type_backend_allocation_test(info, reporter);
}
}
}
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_GL