/* * Copyright 2023 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkTypes.h" #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26 #include "include/core/SkBitmap.h" #include "include/core/SkCanvas.h" #include "include/core/SkColorSpace.h" #include "tools/ToolUtils.h" #include "tools/viewer/Slide.h" #if defined(SK_GANESH) #include "include/android/SkImageAndroid.h" #include "include/android/SkSurfaceAndroid.h" #include "include/gpu/ganesh/GrDirectContext.h" #include "include/gpu/ganesh/SkSurfaceGanesh.h" #endif #if defined(SK_GRAPHITE) #include "include/android/graphite/SurfaceAndroid.h" #include "include/gpu/graphite/Context.h" #include "include/gpu/graphite/Surface.h" #include "src/gpu/graphite/RecorderPriv.h" #else namespace skgpu::graphite { class Recorder; } #endif #include using namespace skgpu::graphite; namespace { static void release_buffer(AHardwareBuffer* buffer) { if (buffer) { AHardwareBuffer_release(buffer); } } sk_sp wrap_buffer(GrDirectContext* dContext, Recorder* recorder, AHardwareBuffer* buffer) { #if defined(SK_GANESH) if (dContext) { return SkSurfaces::WrapAndroidHardwareBuffer(dContext, buffer, kTopLeft_GrSurfaceOrigin, /* colorSpace= */ nullptr, /* surfaceProps= */ nullptr); } #endif #if defined(SK_GRAPHITE) if (recorder) { return SkSurfaces::WrapAndroidHardwareBuffer(recorder, buffer, /* colorSpace= */ nullptr, /* surfaceProps= */ nullptr); } #endif return nullptr; } sk_sp create_protected_render_target(GrDirectContext* dContext, Recorder* recorder, const SkImageInfo& ii) { #if defined(SK_GANESH) if (dContext) { return SkSurfaces::RenderTarget(dContext, skgpu::Budgeted::kYes, ii, /* sampleCount= */ 1, kTopLeft_GrSurfaceOrigin, /* surfaceProps= */ nullptr, /* shouldCreateWithMips= */ false, /* isProtected= */ true); } #endif #if defined(SK_GRAPHITE) if (recorder) { // Protected-ness is pulled off of the recorder return SkSurfaces::RenderTarget(recorder, ii, skgpu::Mipmapped::kNo, /* props= */ nullptr); } #endif return nullptr; } AHardwareBuffer* create_protected_buffer(int width, int height) { AHardwareBuffer* buffer = nullptr; AHardwareBuffer_Desc hwbDesc; hwbDesc.width = width; hwbDesc.height = height; hwbDesc.layers = 1; hwbDesc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | AHARDWAREBUFFER_USAGE_CPU_WRITE_NEVER | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE | AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT; hwbDesc.usage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT; hwbDesc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; // The following three are not used in the allocate hwbDesc.stride = 0; hwbDesc.rfu0= 0; hwbDesc.rfu1= 0; if (int error = AHardwareBuffer_allocate(&hwbDesc, &buffer)) { SkDebugf("Failed to allocated hardware buffer, error: %d\n", error); release_buffer(buffer); return nullptr; } return buffer; } sk_sp create_protected_AHB_image(GrDirectContext* dContext, Recorder* recorder, AHardwareBuffer* buffer, SkColor color) { sk_sp surf = wrap_buffer(dContext, recorder, buffer); if (!surf) { SkDebugf("Failed to make SkSurface.\n"); return nullptr; } ToolUtils::draw_checkerboard(surf->getCanvas(), color, SK_ColorTRANSPARENT, 32); return surf->makeImageSnapshot(); } sk_sp create_protected_skia_image(GrDirectContext* dContext, Recorder* recorder, int width, int height, SkColor color) { SkImageInfo ii = SkImageInfo::Make(width, height, kRGBA_8888_SkColorType, kPremul_SkAlphaType); sk_sp tmpSurface = create_protected_render_target(dContext, recorder, ii); if (!tmpSurface) { return nullptr; } ToolUtils::draw_checkerboard(tmpSurface->getCanvas(), color, SK_ColorTRANSPARENT, 32); return tmpSurface->makeImageSnapshot(); } } // anonymous namespace class ProtectedSlide : public Slide { public: ProtectedSlide() { fName = "Protected"; } SkISize getDimensions() const override { return {kSize, 2*kSize}; } void draw(SkCanvas* origCanvas) override { origCanvas->clear(SK_ColorDKGRAY); skgpu::graphite::Recorder* recorder = origCanvas->recorder(); GrDirectContext* dContext = GrAsDirectContext(origCanvas->recordingContext()); #if defined(SK_GANESH) if (dContext && !dContext->supportsProtectedContent()) { origCanvas->clear(SK_ColorGREEN); return; } #endif #if defined(SK_GRAPHITE) if (recorder && recorder->priv().isProtected() == skgpu::Protected::kNo) { origCanvas->clear(SK_ColorBLUE); return; } #endif if (!dContext && !recorder) { origCanvas->clear(SK_ColorRED); return; } AHardwareBuffer* buffer = create_protected_buffer(kSize, kSize); sk_sp protectedAHBImage = create_protected_AHB_image(dContext, recorder, buffer, SK_ColorRED); sk_sp protectedSkImage = create_protected_skia_image(dContext, recorder, kSize, kSize, SK_ColorBLUE); // Pick one of the two protected images to draw. Only the protected AHB-backed image will // reproduce the bug (b/242266174). SkImage* imgToUse = protectedAHBImage.get(); // SkImage* imgToUse = protectedSkImage.get(); sk_sp indirectImg; { SkImageInfo ii = SkImageInfo::Make(kSize, kSize, kRGBA_8888_SkColorType, kPremul_SkAlphaType); sk_sp tmpS = create_protected_render_target(dContext, recorder, ii); tmpS->getCanvas()->clear(SK_ColorMAGENTA); tmpS->getCanvas()->drawCircle(64, 64, 32, SkPaint()); // For protected AHB-backed images this draw seems to poison all above the draws too tmpS->getCanvas()->drawImage(imgToUse, 0, 0); indirectImg = tmpS->makeImageSnapshot(); } origCanvas->drawImage(imgToUse, 0, 0); origCanvas->drawImage(indirectImg, 0, kSize); protectedAHBImage.reset(); protectedSkImage.reset(); release_buffer(buffer); } private: static const int kSize = 128; }; DEF_SLIDE( return new ProtectedSlide(); ) #endif