/* * 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 "GrClip.h" #include "GrContext.h" #include "GrContextPriv.h" #include "GrProxyProvider.h" #include "GrRenderTargetContext.h" #include "GrResourceProvider.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[], const char* testName) { 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, "[%s] Expected 0x08%x, got 0x%08x at %d, %d.", testName, 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; } GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider(); auto resourceProvider = context->contextPriv().resourceProvider(); static const int kS = UINT8_MAX + 1; static const size_t kRowBytes = kS * sizeof(int32_t); GrSurfaceDesc desc; desc.fOrigin = kTopLeft_GrSurfaceOrigin; desc.fConfig = kRGBA_8888_sint_GrPixelConfig; desc.fWidth = kS; desc.fHeight = kS; 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) { uint32_t r = i - INT8_MIN; uint32_t g = j - INT8_MIN; uint32_t b = INT8_MAX - r; uint32_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 levels fails. { GrMipLevel levels[2]; levels[0].fPixels = testData.get(); levels[0].fRowBytes = kRowBytes; levels[1].fPixels = testData.get(); levels[1].fRowBytes = (kS / 2) * sizeof(int32_t); sk_sp<GrTextureProxy> temp = proxyProvider->createMipMapProxy( desc, SkBudgeted::kYes, levels, 2); REPORTER_ASSERT(reporter, !temp); } sk_sp<GrSurfaceContext> sContext; // Test that we can create a non-mipmapped integer texture. { sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(desc, SkBudgeted::kYes, testData.get(), kRowBytes); REPORTER_ASSERT(reporter, proxy); if (!proxy) { return; } sContext = context->contextPriv().makeWrappedSurfaceContext(std::move(proxy)); if (!sContext) { return; } } std::unique_ptr<int32_t[]> readData(new int32_t[kS * kS]); // Test that reading to a non-integer config fails. { bool success = context->contextPriv().readSurfacePixels(sContext.get(), 0, 0, kS, kS, kRGBA_8888_GrPixelConfig, nullptr, readData.get()); REPORTER_ASSERT(reporter, !success); } { std::unique_ptr<uint16_t[]> halfData(new uint16_t[4 * kS * kS]); bool success = context->contextPriv().readSurfacePixels(sContext.get(), 0, 0, kS, kS, kRGBA_half_GrPixelConfig, nullptr, 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); bool success = context->contextPriv().readSurfacePixels(sContext.get(), 0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, nullptr, readData.get()); REPORTER_ASSERT(reporter, success); if (success) { check_pixels(reporter, kS, kS, testData.get(), readData.get(), "readPixels"); } } { // readPixels should fail if we attempt to use the unpremul flag with an integer texture. bool success = context->contextPriv().readSurfacePixels( sContext.get(), 0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, nullptr, readData.get(), 0, GrContextPriv::kUnpremul_PixelOpsFlag); REPORTER_ASSERT(reporter, !success); } // Test that copying from one integer texture to another succeeds. { sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, desc, sContext->asSurfaceProxy())); REPORTER_ASSERT(reporter, dstContext); if (!dstContext || !dstContext->asTextureProxy()) { return; } sk_bzero(readData.get(), sizeof(int32_t) * kS * kS); bool success = context->contextPriv().readSurfacePixels(dstContext.get(), 0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, nullptr, readData.get()); REPORTER_ASSERT(reporter, success); if (success) { check_pixels(reporter, kS, kS, testData.get(), readData.get(), "copyIntegerToInteger"); } } // Test that copying to a non-integer (8888) texture fails. { GrSurfaceDesc nonIntDesc = desc; nonIntDesc.fConfig = kRGBA_8888_GrPixelConfig; sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc, sContext->asSurfaceProxy())); REPORTER_ASSERT(reporter, !dstContext); } // Test that copying to a non-integer (RGBA_half) texture fails. if (context->caps()->isConfigTexturable(kRGBA_half_GrPixelConfig)) { GrSurfaceDesc nonIntDesc = desc; nonIntDesc.fConfig = kRGBA_half_GrPixelConfig; sk_sp<GrSurfaceContext> dstContext(GrSurfaceProxy::TestCopy(context, nonIntDesc, sContext->asSurfaceProxy())); REPORTER_ASSERT(reporter, !dstContext); } // 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; { // Can't write pixels from a non-int config. bool success = context->contextPriv().writeSurfacePixels(sContext.get(), 0, 0, kS/2, kS/2, kRGBA_8888_GrPixelConfig, nullptr, bottomRightQuarter, kRowBytes); REPORTER_ASSERT(reporter, !success); } { // Can't use unpremul flag. bool success = context->contextPriv().writeSurfacePixels( sContext.get(), 0, 0, kS/2, kS/2, kRGBA_8888_sint_GrPixelConfig, nullptr, bottomRightQuarter, kRowBytes, GrContextPriv::kUnpremul_PixelOpsFlag); REPORTER_ASSERT(reporter, !success); } { bool success = context->contextPriv().writeSurfacePixels(sContext.get(), 0, 0, kS/2, kS/2, kRGBA_8888_sint_GrPixelConfig, nullptr, bottomRightQuarter, kRowBytes); REPORTER_ASSERT(reporter, success); if (!success) { return; } sk_bzero(readData.get(), sizeof(int32_t) * kS * kS); success = context->contextPriv().readSurfacePixels(sContext.get(), 0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, nullptr, readData.get(), 0); 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 += kRowBytes; src += kRowBytes; } check_pixels(reporter, kS, kS, overwrittenTestData.get(), readData.get(), "overwrite"); } // 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); } context->contextPriv().writeSurfacePixels(sContext.get(), 0, 0, kS, kS, kRGBA_8888_sint_GrPixelConfig, nullptr, testData.get(), 0); sk_sp<GrRenderTargetContext> rtContext = context->makeDeferredRenderTargetContext( SkBackingFit::kExact, kS, kS, kRGBA_8888_GrPixelConfig, nullptr); struct { GrSamplerState::Filter fMode; const char* fName; } kNamedFilters[] = {{GrSamplerState::Filter::kNearest, "filter-none"}, {GrSamplerState::Filter::kBilerp, "filter-bilerp"}, {GrSamplerState::Filter::kMipMap, "filter-mipmap"}}; for (auto filter : kNamedFilters) { auto fp = GrSimpleTextureEffect::Make(sContext->asTextureProxyRef(), SkMatrix::I(), filter.fMode); REPORTER_ASSERT(reporter, fp); if (!fp) { return; } rtContext->clear(nullptr, 0xDDAABBCC, GrRenderTargetContext::CanClearFullscreen::kYes); GrPaint paint; paint.setPorterDuffXPFactory(SkBlendMode::kSrc); paint.addColorFragmentProcessor(std::move(fp)); rtContext->drawPaint(GrNoClip(), std::move(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(), filter.fName); } { // No rendering to integer textures. GrSurfaceDesc intRTDesc = desc; intRTDesc.fFlags = kRenderTarget_GrSurfaceFlag; sk_sp<GrTexture> temp(resourceProvider->createTexture(intRTDesc, SkBudgeted::kYes)); REPORTER_ASSERT(reporter, !temp); } } #endif