blob: 01c7537c9d9970cc89ed66e3ffdb1eb02737c69d [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "Test.h"
#if SK_SUPPORT_GPU
#include "GrContext.h"
#include "GrRenderTargetContext.h"
#include "GrTexture.h"
#include "effects/GrSimpleTextureEffect.h"
template <typename I>
static SK_WHEN(std::is_integral<I>::value && 4 == sizeof(I), void)
check_pixels(skiatest::Reporter* reporter, int w, int h,
const I exepctedData[], const I actualData[]) {
for (int j = 0; j < h; ++j) {
for (int i = 0; i < w; ++i) {
I expected = exepctedData[j * w + i];
I actual = actualData[j * w + i];
if (expected != actual) {
ERRORF(reporter, "Expected 0x08%x, got 0x%08x at %d, %d.", expected, actual, i, j);
return;
}
}
}
}
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(IntTexture, reporter, ctxInfo) {
GrContext* context = ctxInfo.grContext();
if (!context->caps()->isConfigTexturable(kRGBA_8888_sint_GrPixelConfig)) {
return;
}
static const int kS = UINT8_MAX + 1;
GrSurfaceDesc desc;
desc.fConfig = kRGBA_8888_sint_GrPixelConfig;
desc.fWidth = kS;
desc.fHeight = kS;
sk_sp<GrTexture> texture;
std::unique_ptr<int32_t> testData(new int32_t[kS * kS]);
for (int j = 0; j < kS; ++j) {
for (int i = 0; i < kS; ++i) {
int32_t r = i - INT8_MIN;
int32_t g = j - INT8_MIN;
int32_t b = INT8_MAX - r;
int32_t a = INT8_MAX - g;
testData.get()[j * kS + i] = a << 24 | b << 16 | g << 8 | r;
}
}
// Test that attempting to create a integer texture with multiple MIP level fails.
GrMipLevel levels[2];
levels[0].fPixels = testData.get();
levels[0].fRowBytes = kS * sizeof(int32_t);
levels[1].fPixels = testData.get();
levels[1].fRowBytes = (kS / 2) * sizeof(int32_t);
texture.reset(context->textureProvider()->createMipMappedTexture(desc, SkBudgeted::kYes, levels,
2));
REPORTER_ASSERT(reporter, !texture);
// Test that we can create a integer texture.
texture.reset(context->textureProvider()->createTexture(desc, SkBudgeted::kYes,
levels[0].fPixels,
levels[0].fRowBytes));
REPORTER_ASSERT(reporter, texture);
if (!texture) {
return;
}
// Test that reading to a non-integer config fails.
std::unique_ptr<int32_t> readData(new int32_t[kS * kS]);
bool success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_GrPixelConfig, readData.get());
REPORTER_ASSERT(reporter, !success);
std::unique_ptr<uint16_t> halfData(new uint16_t[4 * kS * kS]);
success = texture->readPixels(0, 0, kS, kS, kRGBA_half_GrPixelConfig, halfData.get());
REPORTER_ASSERT(reporter, !success);
// Can read back as ints. (ES only requires being able to read back into 32bit ints which
// we don't support. Right now this test is counting on GR_RGBA_INTEGER/GL_BYTE being the
// implementation-dependent second format).
sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, readData.get());
REPORTER_ASSERT(reporter, success);
if (success) {
check_pixels(reporter, kS, kS, testData.get(), readData.get());
}
// readPixels should fail if we attempt to use the unpremul flag with an integer texture.
success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, readData.get(), 0,
GrContext::kUnpremul_PixelOpsFlag);
REPORTER_ASSERT(reporter, !success);
// Test that copying from one integer texture to another succeeds.
sk_sp<GrTexture> copy(context->textureProvider()->createTexture(desc, SkBudgeted::kYes));
REPORTER_ASSERT(reporter, copy);
if (!copy) {
return;
}
success = context->copySurface(copy.get(), texture.get());
REPORTER_ASSERT(reporter, success);
if (!success) {
return;
}
sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, readData.get());
REPORTER_ASSERT(reporter, success);
if (success) {
check_pixels(reporter, kS, kS, testData.get(), readData.get());
}
// Test that copying to a non-integer texture fails.
GrSurfaceDesc nonIntDesc = desc;
nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig;
copy.reset(context->textureProvider()->createTexture(nonIntDesc, SkBudgeted::kYes));
REPORTER_ASSERT(reporter, copy);
if (!copy) {
return;
}
success = context->copySurface(copy.get(), texture.get());
REPORTER_ASSERT(reporter, !success);
nonIntDesc.fConfig = kRGBA_half_GrPixelConfig;
copy.reset(context->textureProvider()->createTexture(nonIntDesc, SkBudgeted::kYes));
REPORTER_ASSERT(reporter, copy ||
!context->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig));
if (copy) {
success = context->copySurface(copy.get(), texture.get());
REPORTER_ASSERT(reporter, !success);
}
// We overwrite the top left quarter of the texture with the bottom right quarter of the
// original data.
const void* bottomRightQuarter = testData.get() + kS / 2 * kS + kS / 2;
size_t rowBytes = kS * sizeof(int32_t);
// Can't write pixels from a non-int config.
success = texture->writePixels(0, 0, kS/2, kS/2, kRGBA_8888_GrPixelConfig, bottomRightQuarter,
rowBytes);
REPORTER_ASSERT(reporter, !success);
// Can't use unpremul flag.
success = texture->writePixels(0, 0, kS/2, kS/2, kRGBA_8888_sint_GrPixelConfig,
bottomRightQuarter, rowBytes,
GrContext::kUnpremul_PixelOpsFlag);
REPORTER_ASSERT(reporter, !success);
success = texture->writePixels(0, 0, kS/2, kS/2, kRGBA_8888_sint_GrPixelConfig,
bottomRightQuarter, rowBytes);
REPORTER_ASSERT(reporter, success);
if (!success) {
return;
}
sk_bzero(readData.get(), sizeof(int32_t) * kS * kS);
success = texture->readPixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, readData.get());
REPORTER_ASSERT(reporter, success);
if (!success) {
return;
}
std::unique_ptr<int32_t> overwrittenTestData(new int32_t[kS * kS]);
memcpy(overwrittenTestData.get(), testData.get(), sizeof(int32_t) * kS * kS);
char* dst = (char*)overwrittenTestData.get();
char* src = (char*)(testData.get() + kS/2 * kS + kS/2);
for (int i = 0; i < kS/2; ++i) {
memcpy(dst, src, sizeof(int32_t) * kS/2);
dst += rowBytes;
src += rowBytes;
}
check_pixels(reporter, kS, kS, overwrittenTestData.get(), readData.get());
// Test drawing from the integer texture to a fixed point texture. To avoid any premul issues
// we init the int texture with 0s and 1s and make alpha always be 1. We expect that 1s turn
// into 0xffs and zeros stay zero.
std::unique_ptr<uint32_t> expectedData(new uint32_t[kS * kS]);
std::unique_ptr<uint32_t> actualData(new uint32_t[kS * kS]);
for (int i = 0; i < kS*kS; ++i) {
int32_t a = 0x1;
int32_t b = ((i & 0x1) ? 1 : 0);
int32_t g = ((i & 0x1) ? 0 : 1);
int32_t r = ((i & 0x2) ? 1 : 0);
testData.get()[i] = (a << 24) | (b << 16) | (g << 8) | r;
expectedData.get()[i] = ((0xFF * a) << 24) | ((0xFF * b) << 16) |
((0xFF * g) << 8) | (0xFF * r);
}
texture->writePixels(0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, testData.get());
sk_sp<GrRenderTargetContext> rtContext = context->makeRenderTargetContext(
SkBackingFit::kExact, kS, kS, kRGBA_8888_GrPixelConfig, nullptr);
for (auto filter : {GrTextureParams::kNone_FilterMode,
GrTextureParams::kBilerp_FilterMode,
GrTextureParams::kMipMap_FilterMode}) {
SkMatrix m;
m.setIDiv(kS, kS);
sk_sp<GrFragmentProcessor> fp(GrSimpleTextureEffect::Make(texture.get(), nullptr, m,
filter));
REPORTER_ASSERT(reporter, fp);
if (!fp) {
return;
}
rtContext->clear(nullptr, 0xDDAABBCC, true);
GrPaint paint;
paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
paint.addColorFragmentProcessor(fp);
rtContext->drawPaint(GrNoClip(), paint, SkMatrix::I());
SkImageInfo readInfo = SkImageInfo::Make(kS, kS, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
rtContext->readPixels(readInfo, actualData.get(), 0, 0, 0);
check_pixels(reporter, kS, kS, expectedData.get(), actualData.get());
}
// No rendering to integer textures.
GrSurfaceDesc intRTDesc = desc;
intRTDesc.fFlags = kRenderTarget_GrSurfaceFlag;
texture.reset(context->textureProvider()->createTexture(intRTDesc, SkBudgeted::kYes));
REPORTER_ASSERT(reporter, !texture);
}
#endif