1 /*
2 * Copyright 2012 Google Inc.
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 "SkSurface_Gpu.h"
9
10 #include "GrContextPriv.h"
11 #include "GrRenderTargetContextPriv.h"
12 #include "GrResourceProvider.h"
13
14 #include "SkCanvas.h"
15 #include "SkColorSpace_Base.h"
16 #include "SkGpuDevice.h"
17 #include "SkImage_Base.h"
18 #include "SkImage_Gpu.h"
19 #include "SkImagePriv.h"
20 #include "SkSurface_Base.h"
21
22 #if SK_SUPPORT_GPU
23
SkSurface_Gpu(sk_sp<SkGpuDevice> device)24 SkSurface_Gpu::SkSurface_Gpu(sk_sp<SkGpuDevice> device)
25 : INHERITED(device->width(), device->height(), &device->surfaceProps())
26 , fDevice(std::move(device)) {
27 SkASSERT(fDevice->accessRenderTargetContext()->asSurfaceProxy()->priv().isExact());
28 }
29
~SkSurface_Gpu()30 SkSurface_Gpu::~SkSurface_Gpu() {
31 }
32
prepare_rt_for_external_access(SkSurface_Gpu * surface,SkSurface::BackendHandleAccess access)33 static GrRenderTarget* prepare_rt_for_external_access(SkSurface_Gpu* surface,
34 SkSurface::BackendHandleAccess access) {
35 switch (access) {
36 case SkSurface::kFlushRead_BackendHandleAccess:
37 break;
38 case SkSurface::kFlushWrite_BackendHandleAccess:
39 case SkSurface::kDiscardWrite_BackendHandleAccess:
40 // for now we don't special-case on Discard, but we may in the future.
41 surface->notifyContentWillChange(SkSurface::kRetain_ContentChangeMode);
42 break;
43 }
44
45 // Grab the render target *after* firing notifications, as it may get switched if CoW kicks in.
46 surface->getDevice()->flush();
47 GrRenderTargetContext* rtc = surface->getDevice()->accessRenderTargetContext();
48 return rtc->accessRenderTarget();
49 }
50
onGetTextureHandle(BackendHandleAccess access)51 GrBackendObject SkSurface_Gpu::onGetTextureHandle(BackendHandleAccess access) {
52 GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
53 if (!rt) {
54 return 0;
55 }
56 GrTexture* texture = rt->asTexture();
57 if (texture) {
58 return texture->getTextureHandle();
59 }
60 return 0;
61 }
62
onGetRenderTargetHandle(GrBackendObject * obj,BackendHandleAccess access)63 bool SkSurface_Gpu::onGetRenderTargetHandle(GrBackendObject* obj, BackendHandleAccess access) {
64 GrRenderTarget* rt = prepare_rt_for_external_access(this, access);
65 if (!rt) {
66 return false;
67 }
68 *obj = rt->getRenderTargetHandle();
69 return true;
70 }
71
onNewCanvas()72 SkCanvas* SkSurface_Gpu::onNewCanvas() {
73 SkCanvas::InitFlags flags = SkCanvas::kDefault_InitFlags;
74 flags = static_cast<SkCanvas::InitFlags>(flags | SkCanvas::kConservativeRasterClip_InitFlag);
75
76 return new SkCanvas(fDevice.get(), flags);
77 }
78
onNewSurface(const SkImageInfo & info)79 sk_sp<SkSurface> SkSurface_Gpu::onNewSurface(const SkImageInfo& info) {
80 int sampleCount = fDevice->accessRenderTargetContext()->numColorSamples();
81 GrSurfaceOrigin origin = fDevice->accessRenderTargetContext()->origin();
82 // TODO: Make caller specify this (change virtual signature of onNewSurface).
83 static const SkBudgeted kBudgeted = SkBudgeted::kNo;
84 return SkSurface::MakeRenderTarget(fDevice->context(), kBudgeted, info, sampleCount,
85 origin, &this->props());
86 }
87
onNewImageSnapshot()88 sk_sp<SkImage> SkSurface_Gpu::onNewImageSnapshot() {
89 GrRenderTargetContext* rtc = fDevice->accessRenderTargetContext();
90 if (!rtc) {
91 return nullptr;
92 }
93
94 GrContext* ctx = fDevice->context();
95
96 if (!rtc->asSurfaceProxy()) {
97 return nullptr;
98 }
99
100 SkBudgeted budgeted = rtc->asSurfaceProxy()->isBudgeted();
101
102 sk_sp<GrTextureProxy> srcProxy = rtc->asTextureProxyRef();
103 // If the original render target is a buffer originally created by the client, then we don't
104 // want to ever retarget the SkSurface at another buffer we create. Force a copy now to avoid
105 // copy-on-write.
106 if (!srcProxy || rtc->priv().refsWrappedObjects()) {
107 // MDB TODO: replace this with GrSurfaceProxy::Copy?
108 GrSurfaceDesc desc = rtc->desc();
109 desc.fFlags = desc.fFlags & ~kRenderTarget_GrSurfaceFlag;
110
111 sk_sp<GrSurfaceContext> copyCtx = ctx->contextPriv().makeDeferredSurfaceContext(
112 desc,
113 SkBackingFit::kExact,
114 budgeted);
115 if (!copyCtx) {
116 return nullptr;
117 }
118
119 if (!copyCtx->copy(rtc->asSurfaceProxy())) {
120 return nullptr;
121 }
122
123 srcProxy = copyCtx->asTextureProxyRef();
124 }
125
126 const SkImageInfo info = fDevice->imageInfo();
127 sk_sp<SkImage> image;
128 if (srcProxy) {
129 // The renderTargetContext coming out of SkGpuDevice should always be exact and the
130 // above copy creates a kExact surfaceContext.
131 SkASSERT(srcProxy->priv().isExact());
132 image = sk_make_sp<SkImage_Gpu>(ctx, kNeedNewImageUniqueID,
133 info.alphaType(), std::move(srcProxy),
134 info.refColorSpace(), budgeted);
135 }
136 return image;
137 }
138
139 // Create a new render target and, if necessary, copy the contents of the old
140 // render target into it. Note that this flushes the SkGpuDevice but
141 // doesn't force an OpenGL flush.
onCopyOnWrite(ContentChangeMode mode)142 void SkSurface_Gpu::onCopyOnWrite(ContentChangeMode mode) {
143 GrRenderTarget* rt = fDevice->accessRenderTargetContext()->accessRenderTarget();
144 if (!rt) {
145 return;
146 }
147 // are we sharing our render target with the image? Note this call should never create a new
148 // image because onCopyOnWrite is only called when there is a cached image.
149 sk_sp<SkImage> image(this->refCachedImage());
150 SkASSERT(image);
151 // MDB TODO: this is unfortunate. The snapping of an Image_Gpu from a surface currently
152 // funnels down to a GrTexture. Once Image_Gpus are proxy-backed we should be able to
153 // compare proxy uniqueIDs.
154 if (rt->asTexture()->getTextureHandle() == image->getTextureHandle(false)) {
155 fDevice->replaceRenderTargetContext(SkSurface::kRetain_ContentChangeMode == mode);
156 } else if (kDiscard_ContentChangeMode == mode) {
157 this->SkSurface_Gpu::onDiscard();
158 }
159 }
160
onDiscard()161 void SkSurface_Gpu::onDiscard() {
162 fDevice->accessRenderTargetContext()->discard();
163 }
164
onPrepareForExternalIO()165 void SkSurface_Gpu::onPrepareForExternalIO() {
166 fDevice->flush();
167 }
168
169 ///////////////////////////////////////////////////////////////////////////////
170
Valid(const SkImageInfo & info)171 bool SkSurface_Gpu::Valid(const SkImageInfo& info) {
172 switch (info.colorType()) {
173 case kRGBA_F16_SkColorType:
174 return !info.colorSpace() || info.colorSpace()->gammaIsLinear();
175 case kRGBA_8888_SkColorType:
176 case kBGRA_8888_SkColorType:
177 return !info.colorSpace() || info.colorSpace()->gammaCloseToSRGB();
178 default:
179 return !info.colorSpace();
180 }
181 }
182
Valid(GrContext * context,GrPixelConfig config,SkColorSpace * colorSpace)183 bool SkSurface_Gpu::Valid(GrContext* context, GrPixelConfig config, SkColorSpace* colorSpace) {
184 switch (config) {
185 case kRGBA_half_GrPixelConfig:
186 return !colorSpace || colorSpace->gammaIsLinear();
187 case kSRGBA_8888_GrPixelConfig:
188 case kSBGRA_8888_GrPixelConfig:
189 return context->caps()->srgbSupport() && colorSpace && colorSpace->gammaCloseToSRGB();
190 case kRGBA_8888_GrPixelConfig:
191 case kBGRA_8888_GrPixelConfig:
192 // If we don't have sRGB support, we may get here with a color space. It still needs
193 // to be sRGB-like (so that the application will work correctly on sRGB devices.)
194 return !colorSpace ||
195 (colorSpace->gammaCloseToSRGB() && !context->caps()->srgbSupport());
196 default:
197 return !colorSpace;
198 }
199 }
200
MakeRenderTarget(GrContext * ctx,SkBudgeted budgeted,const SkImageInfo & info,int sampleCount,GrSurfaceOrigin origin,const SkSurfaceProps * props)201 sk_sp<SkSurface> SkSurface::MakeRenderTarget(GrContext* ctx, SkBudgeted budgeted,
202 const SkImageInfo& info, int sampleCount,
203 GrSurfaceOrigin origin, const SkSurfaceProps* props) {
204 if (!SkSurface_Gpu::Valid(info)) {
205 return nullptr;
206 }
207
208 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(
209 ctx, budgeted, info, sampleCount, origin, props, SkGpuDevice::kClear_InitContents));
210 if (!device) {
211 return nullptr;
212 }
213 return sk_make_sp<SkSurface_Gpu>(std::move(device));
214 }
215
MakeFromBackendTexture(GrContext * context,const GrBackendTextureDesc & desc,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)216 sk_sp<SkSurface> SkSurface::MakeFromBackendTexture(GrContext* context,
217 const GrBackendTextureDesc& desc,
218 sk_sp<SkColorSpace> colorSpace,
219 const SkSurfaceProps* props) {
220 if (!context) {
221 return nullptr;
222 }
223 if (!SkToBool(desc.fFlags & kRenderTarget_GrBackendTextureFlag)) {
224 return nullptr;
225 }
226 if (!SkSurface_Gpu::Valid(context, desc.fConfig, colorSpace.get())) {
227 return nullptr;
228 }
229
230 sk_sp<GrRenderTargetContext> rtc(context->contextPriv().makeBackendTextureRenderTargetContext(
231 desc,
232 std::move(colorSpace),
233 props));
234 if (!rtc) {
235 return nullptr;
236 }
237
238 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), desc.fWidth, desc.fHeight,
239 SkGpuDevice::kUninit_InitContents));
240 if (!device) {
241 return nullptr;
242 }
243 return sk_make_sp<SkSurface_Gpu>(std::move(device));
244 }
245
MakeFromBackendRenderTarget(GrContext * context,const GrBackendRenderTargetDesc & desc,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)246 sk_sp<SkSurface> SkSurface::MakeFromBackendRenderTarget(GrContext* context,
247 const GrBackendRenderTargetDesc& desc,
248 sk_sp<SkColorSpace> colorSpace,
249 const SkSurfaceProps* props) {
250 if (!context) {
251 return nullptr;
252 }
253 if (!SkSurface_Gpu::Valid(context, desc.fConfig, colorSpace.get())) {
254 return nullptr;
255 }
256
257 sk_sp<GrRenderTargetContext> rtc(
258 context->contextPriv().makeBackendRenderTargetRenderTargetContext(desc,
259 std::move(colorSpace),
260 props));
261 if (!rtc) {
262 return nullptr;
263 }
264
265 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), desc.fWidth, desc.fHeight,
266 SkGpuDevice::kUninit_InitContents));
267 if (!device) {
268 return nullptr;
269 }
270
271 return sk_make_sp<SkSurface_Gpu>(std::move(device));
272 }
273
MakeFromBackendTextureAsRenderTarget(GrContext * context,const GrBackendTextureDesc & desc,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props)274 sk_sp<SkSurface> SkSurface::MakeFromBackendTextureAsRenderTarget(GrContext* context,
275 const GrBackendTextureDesc& desc,
276 sk_sp<SkColorSpace> colorSpace,
277 const SkSurfaceProps* props) {
278 if (!context) {
279 return nullptr;
280 }
281 if (!SkSurface_Gpu::Valid(context, desc.fConfig, colorSpace.get())) {
282 return nullptr;
283 }
284
285 sk_sp<GrRenderTargetContext> rtc(
286 context->contextPriv().makeBackendTextureAsRenderTargetRenderTargetContext(
287 desc,
288 std::move(colorSpace),
289 props));
290 if (!rtc) {
291 return nullptr;
292 }
293
294 sk_sp<SkGpuDevice> device(SkGpuDevice::Make(context, std::move(rtc), desc.fWidth, desc.fHeight,
295 SkGpuDevice::kUninit_InitContents));
296 if (!device) {
297 return nullptr;
298 }
299 return sk_make_sp<SkSurface_Gpu>(std::move(device));
300 }
301
302 #endif
303