| /* | |
| * Copyright 2023 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #ifndef skgpu_graphite_Image_Base_Graphite_DEFINED | |
| #define skgpu_graphite_Image_Base_Graphite_DEFINED | |
| #include "include/core/SkRecorder.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkSpan.h" | |
| #include "include/gpu/GpuTypes.h" | |
| #include "include/private/SkPixelStorage.h" | |
| #include "include/private/SkTArray.h" | |
| #include "src/core/SkSpinlock.h" | |
| #include "src/image/SkImage_Base.h" | |
| #include <string_view> | |
| enum class SkBackingFit; | |
| namespace skgpu::graphite { | |
| class Context; | |
| class Device; | |
| class DrawContext; | |
| class Image; | |
| class Recorder; | |
| class TextureProxy; | |
| class TextureProxyView; | |
| class Image_Base : public SkImage_Base { | |
| public: | |
| ~Image_Base() override; | |
| // Must be called at the time of recording an action that reads from the image, be it a draw | |
| // or a copy operation. `drawContext` can be null if the "use" is scoped by a draw. | |
| void notifyInUse(Recorder* recorder, DrawContext* drawContext) const { | |
| this->notifyInUse(recorder, drawContext, /*unlinkDevices=*/false); | |
| } | |
| // Returns true if this image is linked to a device that may render their shared texture(s). | |
| bool isDynamic() const; | |
| // Switches the image's proxies to be non-budgeted and instantiates them as non-shareable. | |
| // Records any linked tasks (e.g. isDynamic() returned true) into the root task list of the | |
| // recorder, and breaks the link between the scratch Device(s) and the returned image. | |
| // | |
| // Proxies that are already non-budgeted and non-shareable are unmodified. Scratch or shareable | |
| // proxies that are instantiated cannot be made non-budgeted, in which case they are copied. | |
| // | |
| // If possible, this modifies the calling Image, but since it can return a copy, the usage | |
| // pattern should always be "image = image->makeNonBudgeted(recorder)". | |
| sk_sp<Image_Base> makeNonBudgeted(Recorder* recorder); | |
| // Always copy this image, even if 'subset' and mipmapping match this image exactly. | |
| // The base implementation performs all copies as draws. | |
| virtual sk_sp<Image> copyImage(Recorder*, | |
| const SkIRect& subset, | |
| Budgeted, | |
| Mipmapped, | |
| SkBackingFit, | |
| std::string_view label) const; | |
| virtual SkSpan<TextureProxyView> textureProxyViews() = 0; | |
| virtual SkSpan<const TextureProxyView> textureProxyViews() const = 0; | |
| // From SkImage.h | |
| bool isValid(SkRecorder* recorder) const final { | |
| return recorder && recorder->type() == SkRecorder::Type::kGraphite; | |
| } | |
| // From SkImage_Base.h | |
| sk_sp<SkImage> onMakeSubset(SkRecorder*, const SkIRect&, RequiredProperties) const final; | |
| sk_sp<SkImage> makeColorTypeAndColorSpace(SkRecorder*, | |
| SkColorType targetCT, | |
| sk_sp<SkColorSpace> targetCS, | |
| RequiredProperties) const final; | |
| // No-ops for Ganesh APIs | |
| bool onReadPixels(GrDirectContext*, | |
| const SkImageInfo& dstInfo, | |
| void* dstPixels, | |
| size_t dstRowBytes, | |
| int srcX, | |
| int srcY, | |
| CachingHint) const final { | |
| return false; | |
| } | |
| bool getROPixels(GrDirectContext*, SkBitmap*, CachingHint = kAllow_CachingHint) const final { | |
| return false; | |
| } | |
| sk_sp<SkSurface> onMakeSurface(SkRecorder*, const SkImageInfo&) const final; | |
| void onAsyncRescaleAndReadPixels(const SkImageInfo&, | |
| SkIRect srcRect, | |
| RescaleGamma, | |
| RescaleMode, | |
| ReadPixelsCallback, | |
| ReadPixelsContext) const override; | |
| void onAsyncRescaleAndReadPixelsYUV420(SkYUVColorSpace, | |
| bool readAlpha, | |
| sk_sp<SkColorSpace>, | |
| SkIRect srcRect, | |
| SkISize dstSize, | |
| RescaleGamma, | |
| RescaleMode, | |
| ReadPixelsCallback, | |
| ReadPixelsContext) const override; | |
| protected: | |
| Image_Base(const SkImageInfo& info, uint32_t uniqueID, sk_sp<SkPixelStorage> backingStorage); | |
| // If the passed-in image is linked with Devices that modify its texture, copy the links to | |
| // this Image. This is used when a new Image is created that shares the same texture proxy as | |
| // a dynamic image. This can only be called before the Image has been returned from a factory. | |
| void linkDevices(const Image_Base*); | |
| // Link this image to the Device that can write to their shared texture proxy, so that when the | |
| // image is sampled in a draw, any pending work from the Device is automatically flushed. This | |
| // can only be called before the Image has been returned from a factory function. | |
| void linkDevice(sk_sp<Device>); | |
| // Notifies any linked devices as in-use w/o a DrawContext to push tasks to the root task list | |
| // and then removes all links so the image is no longer dynamic. | |
| void unlinkDevices(Recorder* recorder) { | |
| this->notifyInUse(recorder, /*drawContext=*/nullptr, /*unlinkDevices=*/true); | |
| SkASSERT(!this->isDynamic()); | |
| } | |
| private: | |
| void notifyInUse(Recorder*, DrawContext* drawContext, bool unlinkDevices) const; | |
| // Devices are flushed in notifyImageInUse(). If a linked device is uniquely held by the image | |
| // or if it's marked as immutable, it will be unlinked (allowing it to be destroyed eventually). | |
| // If all linked devices are removed, this array will become empty. Other than initialization, | |
| // this array cannot transition from an empty state to having linked devices, so while it's | |
| // empty no locking is required. | |
| mutable skia_private::STArray<1, sk_sp<Device>> fLinkedDevices SK_GUARDED_BY(fDeviceLinkLock); | |
| mutable SkSpinlock fDeviceLinkLock; | |
| }; | |
| } // namespace skgpu::graphite | |
| #endif // skgpu_graphite_Image_Base_Graphite_DEFINED |