/* * 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/SkBitmap.h" #include "include/core/SkCanvas.h" #include "include/core/SkPixmap.h" #include "include/core/SkPoint.h" #include "include/gpu/graphite/Context.h" #include "include/gpu/graphite/Recorder.h" #include "include/gpu/graphite/Recording.h" #include "include/gpu/graphite/Surface.h" #include "src/gpu/graphite/RecordingPriv.h" #include "src/gpu/graphite/Surface_Graphite.h" namespace skgpu::graphite { constexpr SkIVector kNoOffset = SkIVector::Make(0, 0); constexpr SkIRect kEmptyClip = SkIRect::MakeEmpty(); using DrawCallback = std::function; struct Expectation { int fX; int fY; SkColor4f fColor; }; void run_test(skiatest::Reporter* reporter, Context* context, SkISize surfaceSize, SkISize recordingSize, SkIVector replayOffset, SkIRect replayClip, skgpu::Mipmapped canvasMipmapped, skgpu::Mipmapped targetMipmapped, DrawCallback draw, const std::vector& expectations) { const SkImageInfo surfaceImageInfo = SkImageInfo::Make( surfaceSize, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); std::unique_ptr surfaceRecorder = context->makeRecorder(); sk_sp surface = SkSurfaces::RenderTarget(surfaceRecorder.get(), surfaceImageInfo, canvasMipmapped); Surface* graphiteSurface = static_cast(surface.get()); const TextureInfo& textureInfo = graphiteSurface->backingTextureProxy()->textureInfo(); // Flush the initial clear added by MakeGraphite. std::unique_ptr surfaceRecording = surfaceRecorder->snap(); context->insertRecording({surfaceRecording.get()}); // Snap a recording without a bound target. const SkImageInfo recordingImageInfo = surfaceImageInfo.makeDimensions(recordingSize); std::unique_ptr recorder = context->makeRecorder(); SkCanvas* canvas = recorder->makeDeferredCanvas(recordingImageInfo, textureInfo); draw(canvas); std::unique_ptr recording = recorder->snap(); // Play back recording. If the mipmap settings don't match, we have to create a new surface. if (canvasMipmapped != targetMipmapped) { surface = SkSurfaces::RenderTarget(surfaceRecorder.get(), surfaceImageInfo, targetMipmapped); // Flush the initial clear for this new surface. surfaceRecording = surfaceRecorder->snap(); context->insertRecording({surfaceRecording.get()}); } context->insertRecording({recording.get(), surface.get(), replayOffset, replayClip}); // Read pixels. SkBitmap bitmap; SkPixmap pixmap; bitmap.allocPixels(surfaceImageInfo); SkAssertResult(bitmap.peekPixels(&pixmap)); if (!surface->readPixels(pixmap, 0, 0)) { ERRORF(reporter, "readPixels failed"); return; } // Veryify expectations are met and recording is uninstantiated. REPORTER_ASSERT(reporter, !recording->priv().isTargetProxyInstantiated()); for (const Expectation& e : expectations) { SkColor4f color = pixmap.getColor4f(e.fX, e.fY); #ifdef SK_DEBUG if (color != e.fColor) { SkDebugf("Wrong color\n\texpected: %f %f %f %f\n\tactual: %f %f %f %f", e.fColor.fR, e.fColor.fG, e.fColor.fB, e.fColor.fA, color.fR, color.fG, color.fB, color.fA); } #endif REPORTER_ASSERT(reporter, color == e.fColor); } } // Tests that clear does not clear an entire replayed-to surface if recorded onto a smaller surface. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestClear, reporter, context, CtsEnforcement::kApiLevel_V) { SkISize surfaceSize = SkISize::Make(8, 4); SkISize recordingSize = SkISize::Make(4, 4); auto draw = [](SkCanvas* canvas) { canvas->clear(SkColors::kRed); }; std::vector expectations = {{0, 0, SkColors::kRed}, {4, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, kEmptyClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } // Tests that a draw is translated correctly when replayed with an offset. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestDraw, reporter, context, CtsEnforcement::kNextRelease) { SkISize surfaceSize = SkISize::Make(8, 4); SkISize recordingSize = SkISize::Make(4, 4); SkIVector replayOffset = SkIVector::Make(4, 0); auto draw = [](SkCanvas* canvas) { canvas->drawIRect(SkIRect::MakeXYWH(0, 0, 4, 4), SkPaint(SkColors::kRed)); }; std::vector expectations = {{0, 0, SkColors::kTransparent}, {4, 0, SkColors::kRed}}; run_test(reporter, context, surfaceSize, recordingSize, replayOffset, kEmptyClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } // Tests that writePixels is translated correctly when replayed with an offset. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestWritePixels, reporter, context, CtsEnforcement::kApiLevel_V) { SkBitmap bitmap; bitmap.allocN32Pixels(4, 4, true); SkCanvas bitmapCanvas(bitmap); SkPaint paint; paint.setColor(SkColors::kRed); bitmapCanvas.drawIRect(SkIRect::MakeXYWH(0, 0, 4, 4), paint); SkISize surfaceSize = SkISize::Make(8, 4); SkISize recordingSize = SkISize::Make(4, 4); SkIVector replayOffset = SkIVector::Make(4, 0); auto draw = [&bitmap](SkCanvas* canvas) { canvas->writePixels(bitmap, 0, 0); }; std::vector expectations = {{0, 0, SkColors::kTransparent}, {4, 0, SkColors::kRed}}; run_test(reporter, context, surfaceSize, recordingSize, replayOffset, kEmptyClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } // Tests that the result of writePixels is cropped correctly when offscreen. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestWritePixelsOffscreen, reporter, context, CtsEnforcement::kApiLevel_V) { SkBitmap bitmap; bitmap.allocN32Pixels(4, 4, true); SkCanvas bitmapCanvas(bitmap); SkPaint paint; paint.setColor(SkColors::kRed); bitmapCanvas.drawIRect(SkIRect::MakeXYWH(0, 0, 4, 4), paint); paint.setColor(SkColors::kGreen); bitmapCanvas.drawIRect(SkIRect::MakeXYWH(2, 2, 2, 2), paint); SkISize surfaceSize = SkISize::Make(4, 4); SkISize recordingSize = SkISize::Make(4, 4); SkIVector replayOffset = SkIVector::Make(-2, -2); auto draw = [&bitmap](SkCanvas* canvas) { canvas->writePixels(bitmap, 0, 0); }; std::vector expectations = {{0, 0, SkColors::kGreen}}; run_test(reporter, context, surfaceSize, recordingSize, replayOffset, kEmptyClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } // Tests that the result of a draw is cropped correctly with a provided clip on replay. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestDrawWithClip, reporter, context, CtsEnforcement::kNextRelease) { SkISize surfaceSize = SkISize::Make(8, 4); SkISize recordingSize = SkISize::Make(8, 4); auto draw = [](SkCanvas* canvas) { canvas->drawIRect(SkIRect::MakeXYWH(0, 0, 4, 4), SkPaint(SkColors::kRed)); canvas->drawIRect(SkIRect::MakeXYWH(4, 0, 4, 4), SkPaint(SkColors::kGreen)); }; { // Test that the clip applies in translated space. SkIVector replayOffset = SkIVector::Make(4, 0); SkIRect replayClip = SkIRect::MakeXYWH(0, 0, 2, 4); std::vector expectations = {{0, 0, SkColors::kTransparent}, {4, 0, SkColors::kRed}, {6, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, replayOffset, replayClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } { // Test that the draw is not translated by the clip. SkIRect replayClip = SkIRect::MakeXYWH(4, 0, 2, 4); std::vector expectations = {{4, 0, SkColors::kGreen}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, replayClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } } // Tests that the result of writePixels is cropped correctly with a provided clip on replay. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestWritePixelsWithClip, reporter, context, CtsEnforcement::kNextRelease) { SkBitmap bitmap; bitmap.allocN32Pixels(4, 4, true); SkCanvas bitmapCanvas(bitmap); SkPaint paint; paint.setColor(SkColors::kRed); bitmapCanvas.drawIRect(SkIRect::MakeXYWH(0, 0, 4, 4), paint); SkISize surfaceSize = SkISize::Make(8, 4); SkISize recordingSize = SkISize::Make(4, 4); SkIVector replayOffset = SkIVector::Make(4, 0); SkIRect replayClip = SkIRect::MakeXYWH(0, 0, 2, 4); auto draw = [&bitmap](SkCanvas* canvas) { canvas->writePixels(bitmap, 0, 0); }; std::vector expectations = {{0, 0, SkColors::kTransparent}, {4, 0, SkColors::kRed}, {6, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, replayOffset, replayClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kNo, draw, expectations); } DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestMipmapped, reporter, context, CtsEnforcement::kNextRelease) { SkISize recordingSize = SkISize::Make(1, 1); auto draw = [](SkCanvas* canvas) { canvas->clear(SkColors::kRed); }; { SkISize surfaceSize = SkISize::Make(1, 1); std::vector expectations = {{0, 0, SkColors::kRed}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, kEmptyClip, skgpu::Mipmapped::kYes, skgpu::Mipmapped::kYes, draw, expectations); } { // Test that draw fails if canvas and surface mipmap settings don't match. SkISize surfaceSize = SkISize::Make(1, 1); std::vector expectations = {{0, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, kEmptyClip, skgpu::Mipmapped::kYes, skgpu::Mipmapped::kNo, draw, expectations); run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, kEmptyClip, skgpu::Mipmapped::kNo, skgpu::Mipmapped::kYes, draw, expectations); } { // Test that draw fails if canvas and surface dimensions don't match. SkISize surfaceSize = SkISize::Make(2, 1); std::vector expectations = {{0, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, kEmptyClip, skgpu::Mipmapped::kYes, skgpu::Mipmapped::kYes, draw, expectations); } { // Test that draw fails if offset is provided. SkISize surfaceSize = SkISize::Make(1, 1); SkIVector replayOffset = SkIVector::Make(1, 0); std::vector expectations = {{0, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, replayOffset, kEmptyClip, skgpu::Mipmapped::kYes, skgpu::Mipmapped::kYes, draw, expectations); } { // Test that draw fails if clip is provided. SkISize surfaceSize = SkISize::Make(1, 1); SkIRect replayClip = SkIRect::MakeXYWH(0, 0, 1, 1); std::vector expectations = {{0, 0, SkColors::kTransparent}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, replayClip, skgpu::Mipmapped::kYes, skgpu::Mipmapped::kYes, draw, expectations); } } DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestMipmappedWritePixels, reporter, context, CtsEnforcement::kNextRelease) { SkBitmap bitmap; bitmap.allocN32Pixels(1, 1, true); SkCanvas bitmapCanvas(bitmap); SkPaint paint; paint.setColor(SkColors::kRed); bitmapCanvas.drawIRect(SkIRect::MakeXYWH(0, 0, 1, 1), paint); SkISize recordingSize = SkISize::Make(1, 1); SkISize surfaceSize = SkISize::Make(1, 1); auto draw = [&bitmap](SkCanvas* canvas) { canvas->writePixels(bitmap, 0, 0); }; std::vector expectations = {{0, 0, SkColors::kRed}}; run_test(reporter, context, surfaceSize, recordingSize, kNoOffset, kEmptyClip, skgpu::Mipmapped::kYes, skgpu::Mipmapped::kYes, draw, expectations); } // Tests that you can't create two deferred canvases before snapping the first. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestTwoCanvases, reporter, context, CtsEnforcement::kNextRelease) { const SkImageInfo kImageInfo = SkImageInfo::Make(SkISize::Make(1, 1), SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); std::unique_ptr recorder = context->makeRecorder(); sk_sp surface = SkSurfaces::RenderTarget(recorder.get(), kImageInfo, skgpu::Mipmapped::kNo); const TextureInfo& textureInfo = static_cast(surface.get())->backingTextureProxy()->textureInfo(); // First canvas is created successfully. REPORTER_ASSERT(reporter, recorder->makeDeferredCanvas(kImageInfo, textureInfo) != nullptr); // Second canvas fails to be created. REPORTER_ASSERT(reporter, recorder->makeDeferredCanvas(kImageInfo, textureInfo) == nullptr); } // Tests that inserting a recording with a surface does not crash even if no draws to a deferred // canvas were recorded. DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(RecordingSurfacesTestUnnecessarySurface, reporter, context, CtsEnforcement::kNextRelease) { const SkImageInfo kImageInfo = SkImageInfo::Make(SkISize::Make(1, 1), SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kPremul_SkAlphaType); std::unique_ptr recorder = context->makeRecorder(); sk_sp surface = SkSurfaces::RenderTarget(recorder.get(), kImageInfo, skgpu::Mipmapped::kNo); std::unique_ptr recording = recorder->snap(); REPORTER_ASSERT(reporter, context->insertRecording({recording.get(), surface.get()})); } } // namespace skgpu::graphite