/* * Copyright 2022 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "tests/Test.h" #include "include/core/SkColorSpace.h" #include "include/gpu/graphite/BackendTexture.h" #include "include/gpu/graphite/Context.h" #include "include/gpu/graphite/Image.h" #include "include/gpu/graphite/Recorder.h" #include "include/gpu/graphite/Surface.h" #include "src/core/SkAutoPixmapStorage.h" #include "src/gpu/graphite/Caps.h" #include "src/gpu/graphite/ContextPriv.h" #include "src/gpu/graphite/Surface_Graphite.h" #include "tests/TestUtils.h" #include "tools/ToolUtils.h" #include "tools/graphite/GraphiteTestContext.h" using namespace skgpu; using namespace skgpu::graphite; namespace { const SkISize kSize = { 32, 32 }; constexpr int kNumMipLevels = 6; constexpr SkColor4f kColors[6] = { { 1.0f, 0.0f, 0.0f, 1.0f }, // R { 0.0f, 1.0f, 0.0f, 0.9f }, // G { 0.0f, 0.0f, 1.0f, 0.7f }, // B { 0.0f, 1.0f, 1.0f, 0.5f }, // C { 1.0f, 0.0f, 1.0f, 0.3f }, // M { 1.0f, 1.0f, 0.0f, 0.2f }, // Y }; constexpr SkColor4f kColorsNew[6] = { { 1.0f, 1.0f, 0.0f, 0.2f }, // Y { 1.0f, 0.0f, 0.0f, 1.0f }, // R { 0.0f, 1.0f, 0.0f, 0.9f }, // G { 0.0f, 0.0f, 1.0f, 0.7f }, // B { 0.0f, 1.0f, 1.0f, 0.5f }, // C { 1.0f, 0.0f, 1.0f, 0.3f }, // M }; void check_solid_pixmap(skiatest::Reporter* reporter, const SkColor4f& expected, const SkPixmap& actual, SkColorType ct, const char* label) { const float kTols[4] = { 0.01f, 0.01f, 0.01f, 0.01f }; auto error = std::function( [reporter, ct, label, expected](int x, int y, const float diffs[4]) { SkASSERT(x >= 0 && y >= 0); ERRORF(reporter, "%s %s - mismatch at %d, %d " "expected: (%.2f, %.2f, %.2f, %.2f) " "- diffs: (%.2f, %.2f, %.2f, %.2f)", ToolUtils::colortype_name(ct), label, x, y, expected.fR, expected.fG, expected.fB, expected.fA, diffs[0], diffs[1], diffs[2], diffs[3]); }); CheckSolidPixels(expected, actual, kTols, error); } void update_backend_texture(Recorder* recorder, const BackendTexture& backendTex, SkColorType ct, bool withMips, const SkColor4f colors[6], GpuFinishedProc finishedProc = nullptr, GpuFinishedContext finishedCtx = nullptr) { SkPixmap pixmaps[6]; std::unique_ptr memForPixmaps; int numMipLevels = ToolUtils::make_pixmaps(ct, kPremul_SkAlphaType, withMips, colors, pixmaps, &memForPixmaps); SkASSERT(numMipLevels == 1 || numMipLevels == kNumMipLevels); SkASSERT(kSize == pixmaps[0].dimensions()); recorder->updateBackendTexture(backendTex, pixmaps, numMipLevels, finishedProc, finishedCtx); } BackendTexture create_backend_texture(skiatest::Reporter* reporter, const Caps* caps, Recorder* recorder, SkColorType ct, bool withMips, Renderable renderable, skgpu::Protected isProtected, const SkColor4f colors[6], GpuFinishedProc finishedProc = nullptr, GpuFinishedContext finishedCtx = nullptr) { Mipmapped mipmapped = withMips ? Mipmapped::kYes : Mipmapped::kNo; TextureInfo info = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected, renderable); BackendTexture backendTex = recorder->createBackendTexture(kSize, info); REPORTER_ASSERT(reporter, backendTex.isValid()); update_backend_texture(recorder, backendTex, ct, withMips, colors, finishedProc, finishedCtx); return backendTex; } sk_sp wrap_backend_texture(skiatest::Reporter* reporter, Recorder* recorder, const skgpu::graphite::BackendTexture& backendTex, SkColorType ct, bool withMips) { sk_sp image = SkImages::WrapTexture(recorder, backendTex, ct, kPremul_SkAlphaType, /* colorSpace= */ nullptr); REPORTER_ASSERT(reporter, image); REPORTER_ASSERT(reporter, image->hasMipmaps() == withMips); return image; } void check_levels(skiatest::Reporter* reporter, Context* context, Recorder* recorder, SkImage* image, bool withMips, const SkColor4f colors[6]) { int numLevels = withMips ? kNumMipLevels : 1; SkSamplingOptions sampling = withMips ? SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNearest) : SkSamplingOptions(SkFilterMode::kNearest); SkImageInfo surfaceII = SkImageInfo::Make(kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType); sk_sp surf = SkSurfaces::RenderTarget(recorder, surfaceII, Mipmapped::kNo); SkCanvas* canvas = surf->getCanvas(); for (int i = 0, drawSize = kSize.width(); i < numLevels; ++i, drawSize /= 2) { if (i == 5) { // TODO: Metal currently never draws the top-most mip-level (skbug.com/13792) continue; } SkImageInfo readbackII = SkImageInfo::Make({drawSize, drawSize}, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType); SkAutoPixmapStorage actual; SkAssertResult(actual.tryAlloc(readbackII)); actual.erase(SkColors::kTransparent); SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); canvas->clear(SkColors::kTransparent); #if 0 // This option gives greater control over the tilemodes and texture scaling SkMatrix lm; lm.setScale(1.0f / (1 << i), 1.0f / (1 << i)); paint.setShader(image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, sampling, lm)); canvas->drawRect(SkRect::MakeWH(drawSize, drawSize), paint); #else canvas->drawImageRect(image, SkRect::MakeWH(drawSize, drawSize), sampling, &paint); #endif if (!surf->readPixels(actual, 0, 0)) { ERRORF(reporter, "readPixels failed"); return; } SkString str; str.appendf("mip-level %d", i); check_solid_pixmap(reporter, colors[i], actual, image->colorType(), str.c_str()); } } } // anonymous namespace DEF_GRAPHITE_TEST_FOR_RENDERING_CONTEXTS(UpdateImageBackendTextureTest, reporter, context, CtsEnforcement::kApiLevel_V) { const Caps* caps = context->priv().caps(); std::unique_ptr recorder = context->makeRecorder(); skgpu::Protected isProtected = skgpu::Protected(caps->protectedSupport()); // TODO: test more than just RGBA8 for (SkColorType ct : { kRGBA_8888_SkColorType }) { for (bool withMips : { true, false }) { for (Renderable renderable : { Renderable::kYes, Renderable::kNo }) { BackendTexture backendTex = create_backend_texture(reporter, caps, recorder.get(), ct, withMips, renderable, isProtected, kColors); sk_sp image = wrap_backend_texture(reporter, recorder.get(), backendTex, ct, withMips); if (!image) { continue; } if (isProtected == skgpu::Protected::kNo) { check_levels(reporter, context, recorder.get(), image.get(), withMips, kColors); } image.reset(); update_backend_texture(recorder.get(), backendTex, ct, withMips, kColorsNew); image = wrap_backend_texture(reporter, recorder.get(), backendTex, ct, withMips); if (!image) { continue; } if (isProtected == skgpu::Protected::kNo) { check_levels(reporter, context, recorder.get(), image.get(), withMips, kColorsNew); } image.reset(); recorder->deleteBackendTexture(backendTex); } } } } DEF_CONDITIONAL_GRAPHITE_TEST_FOR_ALL_CONTEXTS(UpdateBackendTextureFinishedProcTest, reporter, context, testContext, true, CtsEnforcement::kNextRelease) { const Caps* caps = context->priv().caps(); std::unique_ptr recorder = context->makeRecorder(); skgpu::Protected isProtected = skgpu::Protected(caps->protectedSupport()); struct FinishContext { bool fFinishedUpdate = false; skiatest::Reporter* fReporter = nullptr; }; FinishContext finishCtx; finishCtx.fReporter = reporter; auto finishedProc = [](void* ctx, CallbackResult) { FinishContext* finishedCtx = (FinishContext*) ctx; REPORTER_ASSERT(finishedCtx->fReporter, !(finishedCtx->fFinishedUpdate)); finishedCtx->fFinishedUpdate = true; }; BackendTexture backendTex = create_backend_texture(reporter, caps, recorder.get(), kRGBA_8888_SkColorType, /*withMips=*/false, Renderable::kNo, isProtected, kColors, finishedProc, &finishCtx); REPORTER_ASSERT(reporter, !finishCtx.fFinishedUpdate); std::unique_ptr recording = recorder->snap(); if (!recording) { ERRORF(reporter, "Failed to make recording"); return; } InsertRecordingInfo insertInfo; insertInfo.fRecording = recording.get(); context->insertRecording(insertInfo); REPORTER_ASSERT(reporter, !finishCtx.fFinishedUpdate); testContext->syncedSubmit(context); REPORTER_ASSERT(reporter, finishCtx.fFinishedUpdate); recorder->deleteBackendTexture(backendTex); }