1 /*
2 * Copyright 2021 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/graphite/Image_Graphite.h"
9
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColorSpace.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkSurface.h"
14 #include "include/gpu/graphite/Image.h"
15 #include "include/gpu/graphite/Recorder.h"
16 #include "include/gpu/graphite/Surface.h"
17 #include "src/gpu/SkBackingFit.h"
18 #include "src/gpu/graphite/Caps.h"
19 #include "src/gpu/graphite/Device.h"
20 #include "src/gpu/graphite/Log.h"
21 #include "src/gpu/graphite/RecorderPriv.h"
22 #include "src/gpu/graphite/ResourceProvider.h"
23 #include "src/gpu/graphite/Texture.h"
24 #include "src/gpu/graphite/TextureUtils.h"
25 #include "src/gpu/graphite/task/CopyTask.h"
26
27 #if defined(GPU_TEST_UTILS)
28 #include "include/gpu/graphite/Context.h"
29 #include "src/gpu/graphite/ContextPriv.h"
30 #endif
31
32 namespace skgpu::graphite {
33
34 // Graphite does not cache based on the image's unique ID so always request a new one.
Image(TextureProxyView view,const SkColorInfo & info)35 Image::Image(TextureProxyView view,
36 const SkColorInfo& info)
37 : Image_Base(SkImageInfo::Make(view.proxy()->dimensions(), info), kNeedNewImageUniqueID)
38 , fTextureProxyView(std::move(view)) {}
39
40 Image::~Image() = default;
41
WrapDevice(sk_sp<Device> device)42 sk_sp<Image> Image::WrapDevice(sk_sp<Device> device) {
43 TextureProxyView proxy = device->readSurfaceView();
44 if (!proxy) {
45 return nullptr;
46 }
47 // NOTE: If the device was created with an approx backing fit, its SkImageInfo reports the
48 // logical dimensions, but its proxy has the approximate fit. These larger dimensions are
49 // propagated to the SkImageInfo of this image view.
50 sk_sp<Image> image = sk_make_sp<Image>(std::move(proxy),
51 device->imageInfo().colorInfo());
52 image->linkDevice(std::move(device));
53 return image;
54 }
55
Copy(Recorder * recorder,const TextureProxyView & srcView,const SkColorInfo & srcColorInfo,const SkIRect & subset,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,std::string_view label)56 sk_sp<Image> Image::Copy(Recorder* recorder,
57 const TextureProxyView& srcView,
58 const SkColorInfo& srcColorInfo,
59 const SkIRect& subset,
60 Budgeted budgeted,
61 Mipmapped mipmapped,
62 SkBackingFit backingFit,
63 std::string_view label) {
64 SkASSERT(!(mipmapped == Mipmapped::kYes && backingFit == SkBackingFit::kApprox));
65 if (!srcView) {
66 return nullptr;
67 }
68
69 SkASSERT(srcView.proxy()->isFullyLazy() ||
70 SkIRect::MakeSize(srcView.proxy()->dimensions()).contains(subset));
71
72 if (!recorder->priv().caps()->supportsReadPixels(srcView.proxy()->textureInfo())) {
73 if (!recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo())) {
74 // The texture is not blittable nor texturable so copying cannot be done.
75 return nullptr;
76 }
77 // Copy-as-draw
78 sk_sp<Image> srcImage(new Image(srcView, srcColorInfo));
79 return CopyAsDraw(recorder, srcImage.get(), subset, srcColorInfo,
80 budgeted, mipmapped, backingFit, std::move(label));
81 }
82
83
84 skgpu::graphite::TextureInfo textureInfo =
85 recorder->priv().caps()->getTextureInfoForSampledCopy(srcView.proxy()->textureInfo(),
86 mipmapped);
87
88 sk_sp<TextureProxy> dst = TextureProxy::Make(
89 recorder->priv().caps(),
90 recorder->priv().resourceProvider(),
91 backingFit == SkBackingFit::kApprox ? GetApproxSize(subset.size()) : subset.size(),
92 textureInfo,
93 std::move(label),
94 budgeted);
95 if (!dst) {
96 return nullptr;
97 }
98
99 auto copyTask = CopyTextureToTextureTask::Make(srcView.refProxy(), subset, dst, {0, 0});
100 if (!copyTask) {
101 return nullptr;
102 }
103
104 recorder->priv().add(std::move(copyTask));
105
106 if (mipmapped == Mipmapped::kYes) {
107 if (!GenerateMipmaps(recorder, dst, srcColorInfo)) {
108 SKGPU_LOG_W("Image::Copy failed to generate mipmaps");
109 return nullptr;
110 }
111 }
112
113 return sk_sp<Image>(new Image({std::move(dst), srcView.swizzle()}, srcColorInfo));
114 }
115
textureSize() const116 size_t Image::textureSize() const {
117 if (!fTextureProxyView.proxy()) {
118 return 0;
119 }
120
121 if (!fTextureProxyView.proxy()->texture()) {
122 return fTextureProxyView.proxy()->uninstantiatedGpuMemorySize();
123 }
124
125 return fTextureProxyView.proxy()->texture()->gpuMemorySize();
126 }
127
copyImage(Recorder * recorder,const SkIRect & subset,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,std::string_view label) const128 sk_sp<Image> Image::copyImage(Recorder* recorder,
129 const SkIRect& subset,
130 Budgeted budgeted,
131 Mipmapped mipmapped,
132 SkBackingFit backingFit,
133 std::string_view label) const {
134 this->notifyInUse(recorder, /*drawContext=*/nullptr);
135 return Image::Copy(recorder, fTextureProxyView, this->imageInfo().colorInfo(),
136 subset, budgeted, mipmapped, backingFit, std::move(label));
137 }
138
onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const139 sk_sp<SkImage> Image::onReinterpretColorSpace(sk_sp<SkColorSpace> newCS) const {
140 sk_sp<Image> view = sk_make_sp<Image>(fTextureProxyView,
141 this->imageInfo().colorInfo()
142 .makeColorSpace(std::move(newCS)));
143 // The new Image object shares the same texture proxy, so it should also share linked Devices
144 view->linkDevices(this);
145 return view;
146 }
147
148 #if defined(GPU_TEST_UTILS)
readPixelsGraphite(Recorder * recorder,const SkPixmap & dst,int srcX,int srcY) const149 bool Image::readPixelsGraphite(Recorder* recorder, const SkPixmap& dst, int srcX, int srcY) const {
150 if (Context* context = recorder->priv().context()) {
151 // Add all previous commands generated to the command buffer.
152 // If the client snaps later they'll only get post-read commands in their Recording,
153 // but since they're doing a readPixels in the middle that shouldn't be unexpected.
154 std::unique_ptr<Recording> recording = recorder->snap();
155 if (!recording) {
156 return false;
157 }
158 InsertRecordingInfo info;
159 info.fRecording = recording.get();
160 if (!context->insertRecording(info)) {
161 return false;
162 }
163 return context->priv().readPixels(dst,
164 fTextureProxyView.proxy(),
165 this->imageInfo(),
166 srcX,
167 srcY);
168 }
169 return false;
170 }
171 #endif
172
173 } // namespace skgpu::graphite
174