| /* | |
| * Copyright 2021 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/graphite/Image_Graphite.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColorSpace.h" | |
| #include "include/core/SkImage.h" | |
| #include "include/core/SkSurface.h" | |
| #include "include/gpu/graphite/Image.h" | |
| #include "include/gpu/graphite/Recorder.h" | |
| #include "include/gpu/graphite/Surface.h" | |
| #include "src/gpu/SkBackingFit.h" | |
| #include "src/gpu/graphite/Caps.h" | |
| #include "src/gpu/graphite/Device.h" | |
| #include "src/gpu/graphite/DrawContext.h" | |
| #include "src/gpu/graphite/Log.h" | |
| #include "src/gpu/graphite/RecorderPriv.h" | |
| #include "src/gpu/graphite/ResourceProvider.h" | |
| #include "src/gpu/graphite/Texture.h" | |
| #include "src/gpu/graphite/TextureUtils.h" | |
| #include "src/gpu/graphite/task/CopyTask.h" | |
| #if defined(GPU_TEST_UTILS) | |
| #include "include/gpu/graphite/Context.h" | |
| #include "src/gpu/graphite/ContextPriv.h" | |
| #endif | |
| namespace skgpu::graphite { | |
| // Graphite does not cache based on the image's unique ID so always request a new one. | |
| Image::Image(TextureProxyView view, | |
| const SkColorInfo& info) | |
| : Image_Base(SkImageInfo::Make(view.proxy()->dimensions(), info), kNeedNewImageUniqueID) | |
| , fTextureProxyView(std::move(view)) {} | |
| Image::~Image() = default; | |
| sk_sp<Image> Image::WrapDevice(sk_sp<Device> device) { | |
| TextureProxyView proxy = device->readSurfaceView(); | |
| if (!proxy) { | |
| return nullptr; | |
| } | |
| // NOTE: If the device was created with an approx backing fit, its SkImageInfo reports the | |
| // logical dimensions, but its proxy has the approximate fit. These larger dimensions are | |
| // propagated to the SkImageInfo of this image view. | |
| sk_sp<Image> image = sk_make_sp<Image>(std::move(proxy), | |
| device->imageInfo().colorInfo()); | |
| image->linkDevice(std::move(device)); | |
| return image; | |
| } | |
| sk_sp<Image> Image::Copy(Recorder* recorder, | |
| DrawContext* drawContext, | |
| const TextureProxyView& srcView, | |
| const SkColorInfo& srcColorInfo, | |
| const SkIRect& subset, | |
| Budgeted budgeted, | |
| Mipmapped mipmapped, | |
| SkBackingFit backingFit, | |
| std::string_view label) { | |
| SkASSERT(!drawContext || budgeted == Budgeted::kYes); | |
| SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox)); | |
| if (!srcView) { | |
| return nullptr; | |
| } | |
| SkASSERT(srcView.proxy()->isFullyLazy() || | |
| SkIRect::MakeSize(srcView.proxy()->dimensions()).contains(subset)); | |
| if (!recorder->priv().caps()->supportsReadPixels(srcView.proxy()->textureInfo())) { | |
| if (!recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo())) { | |
| // The texture is not blittable nor texturable so copying cannot be done. | |
| return nullptr; | |
| } | |
| // Copy-as-draw | |
| sk_sp<Image> srcImage(new Image(srcView, srcColorInfo)); | |
| return CopyAsDraw(recorder, drawContext, srcImage.get(), subset, srcColorInfo, | |
| budgeted, mipmapped, backingFit, std::move(label)); | |
| } | |
| skgpu::graphite::TextureInfo textureInfo = | |
| recorder->priv().caps()->getTextureInfoForSampledCopy(srcView.proxy()->textureInfo(), | |
| mipmapped); | |
| sk_sp<TextureProxy> dst = TextureProxy::Make( | |
| recorder->priv().caps(), | |
| recorder->priv().resourceProvider(), | |
| backingFit == SkBackingFit::kApprox ? GetApproxSize(subset.size()) : subset.size(), | |
| textureInfo, | |
| std::move(label), | |
| budgeted); | |
| if (!dst) { | |
| return nullptr; | |
| } | |
| auto copyTask = CopyTextureToTextureTask::Make(srcView.refProxy(), subset, dst, {0, 0}); | |
| if (!copyTask) { | |
| return nullptr; | |
| } | |
| if (drawContext) { | |
| drawContext->recordDependency(std::move(copyTask)); | |
| } else { | |
| recorder->priv().add(std::move(copyTask)); | |
| } | |
| if (mipmapped == Mipmapped::kYes) { | |
| if (!GenerateMipmaps(recorder, drawContext, dst, srcColorInfo)) { | |
| SKGPU_LOG_W("Image::Copy failed to generate mipmaps"); | |
| return nullptr; | |
| } | |
| } | |
| return sk_sp<Image>(new Image({std::move(dst), srcView.swizzle()}, srcColorInfo)); | |
| } | |
| size_t Image::textureSize() const { | |
| if (!fTextureProxyView.proxy()) { | |
| return 0; | |
| } | |
| if (!fTextureProxyView.proxy()->texture()) { | |
| return fTextureProxyView.proxy()->uninstantiatedGpuMemorySize(); | |
| } | |
| return fTextureProxyView.proxy()->texture()->gpuMemorySize(); | |
| } | |
| sk_sp<Image> Image::copyImage(Recorder* recorder, | |
| const SkIRect& subset, | |
| Budgeted budgeted, | |
| Mipmapped mipmapped, | |
| SkBackingFit backingFit, | |
| std::string_view label) const { | |
| this->notifyInUse(recorder, /*drawContext=*/nullptr); | |
| return Image::Copy(recorder, /*drawContext=*/nullptr, | |
| fTextureProxyView, this->imageInfo().colorInfo(), | |
| subset, budgeted, mipmapped, backingFit, std::move(label)); | |
| } | |
| sk_sp<SkImage> Image::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const { | |
| sk_sp<Image> view = sk_make_sp<Image>(fTextureProxyView, | |
| this->imageInfo().colorInfo() | |
| .makeColorSpace(std::move(newCS))); | |
| // The new Image object shares the same texture proxy, so it should also share linked Devices | |
| view->linkDevices(this); | |
| return view; | |
| } | |
| #if defined(GPU_TEST_UTILS) | |
| bool Image::readPixelsGraphite(SkRecorder* recorder, | |
| const SkPixmap& dst, | |
| int srcX, | |
| int srcY) const { | |
| auto gRecorder = AsGraphiteRecorder(recorder); | |
| if (!gRecorder) { | |
| return false; | |
| } | |
| if (Context* context = gRecorder->priv().context()) { | |
| // Add all previous commands generated to the command buffer. | |
| // If the client snaps later they'll only get post-read commands in their Recording, | |
| // but since they're doing a readPixels in the middle that shouldn't be unexpected. | |
| std::unique_ptr<Recording> recording = gRecorder->snap(); | |
| if (!recording) { | |
| return false; | |
| } | |
| InsertRecordingInfo info; | |
| info.fRecording = recording.get(); | |
| if (!context->insertRecording(info)) { | |
| return false; | |
| } | |
| return context->priv().readPixels(dst, | |
| fTextureProxyView.proxy(), | |
| this->imageInfo(), | |
| srcX, | |
| srcY); | |
| } | |
| return false; | |
| } | |
| #endif | |
| } // namespace skgpu::graphite |