• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 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 "src/gpu/GrSurfaceProxy.h"
9 #include "src/gpu/GrSurfaceProxyPriv.h"
10 
11 #include "include/gpu/GrContext.h"
12 #include "include/private/GrRecordingContext.h"
13 #include "src/core/SkMathPriv.h"
14 #include "src/core/SkMipMap.h"
15 #include "src/gpu/GrCaps.h"
16 #include "src/gpu/GrClip.h"
17 #include "src/gpu/GrContextPriv.h"
18 #include "src/gpu/GrGpuResourcePriv.h"
19 #include "src/gpu/GrOpList.h"
20 #include "src/gpu/GrProxyProvider.h"
21 #include "src/gpu/GrRecordingContextPriv.h"
22 #include "src/gpu/GrStencilAttachment.h"
23 #include "src/gpu/GrSurfacePriv.h"
24 #include "src/gpu/GrTextureContext.h"
25 #include "src/gpu/GrTexturePriv.h"
26 #include "src/gpu/GrTextureRenderTargetProxy.h"
27 
28 #ifdef SK_DEBUG
29 #include "src/gpu/GrRenderTarget.h"
30 #include "src/gpu/GrRenderTargetPriv.h"
31 
is_valid_fully_lazy(const GrSurfaceDesc & desc,SkBackingFit fit)32 static bool is_valid_fully_lazy(const GrSurfaceDesc& desc, SkBackingFit fit) {
33     return desc.fWidth <= 0 &&
34            desc.fHeight <= 0 &&
35            desc.fConfig != kUnknown_GrPixelConfig &&
36            SkBackingFit::kApprox == fit;
37 }
38 
is_valid_partially_lazy(const GrSurfaceDesc & desc)39 static bool is_valid_partially_lazy(const GrSurfaceDesc& desc) {
40     return ((desc.fWidth > 0 && desc.fHeight > 0) ||
41             (desc.fWidth <= 0 && desc.fHeight <= 0))  &&
42            desc.fConfig != kUnknown_GrPixelConfig;
43 }
44 
is_valid_non_lazy(const GrSurfaceDesc & desc)45 static bool is_valid_non_lazy(const GrSurfaceDesc& desc) {
46     return desc.fWidth > 0 &&
47            desc.fHeight > 0 &&
48            desc.fConfig != kUnknown_GrPixelConfig;
49 }
50 #endif
51 
52 // Lazy-callback version
GrSurfaceProxy(LazyInstantiateCallback && callback,LazyInstantiationType lazyType,const GrBackendFormat & format,const GrSurfaceDesc & desc,GrRenderable renderable,GrSurfaceOrigin origin,const GrSwizzle & textureSwizzle,SkBackingFit fit,SkBudgeted budgeted,GrProtected isProtected,GrInternalSurfaceFlags surfaceFlags)53 GrSurfaceProxy::GrSurfaceProxy(LazyInstantiateCallback&& callback, LazyInstantiationType lazyType,
54                                const GrBackendFormat& format, const GrSurfaceDesc& desc,
55                                GrRenderable renderable, GrSurfaceOrigin origin,
56                                const GrSwizzle& textureSwizzle, SkBackingFit fit,
57                                SkBudgeted budgeted, GrProtected isProtected,
58                                GrInternalSurfaceFlags surfaceFlags)
59         : fSurfaceFlags(surfaceFlags)
60         , fFormat(format)
61         , fConfig(desc.fConfig)
62         , fWidth(desc.fWidth)
63         , fHeight(desc.fHeight)
64         , fOrigin(origin)
65         , fTextureSwizzle(textureSwizzle)
66         , fFit(fit)
67         , fBudgeted(budgeted)
68         , fLazyInstantiateCallback(std::move(callback))
69         , fLazyInstantiationType(lazyType)
70         , fIsProtected(isProtected)
71         , fGpuMemorySize(kInvalidGpuMemorySize)
72         , fLastRenderTask(nullptr) {
73     SkASSERT(fFormat.isValid());
74     // NOTE: the default fUniqueID ctor pulls a value from the same pool as the GrGpuResources.
75     if (fLazyInstantiateCallback) {
76         SkASSERT(is_valid_fully_lazy(desc, fit) || is_valid_partially_lazy(desc));
77     } else {
78         SkASSERT(is_valid_non_lazy(desc));
79     }
80 
81     if (GrPixelConfigIsCompressed(desc.fConfig)) {
82         SkASSERT(renderable == GrRenderable::kNo);
83         fSurfaceFlags |= GrInternalSurfaceFlags::kReadOnly;
84     }
85 }
86 
87 // Wrapped version
GrSurfaceProxy(sk_sp<GrSurface> surface,GrSurfaceOrigin origin,const GrSwizzle & textureSwizzle,SkBackingFit fit)88 GrSurfaceProxy::GrSurfaceProxy(sk_sp<GrSurface> surface, GrSurfaceOrigin origin,
89                                const GrSwizzle& textureSwizzle, SkBackingFit fit)
90         : fTarget(std::move(surface))
91         , fSurfaceFlags(fTarget->surfacePriv().flags())
92         , fFormat(fTarget->backendFormat())
93         , fConfig(fTarget->config())
94         , fWidth(fTarget->width())
95         , fHeight(fTarget->height())
96         , fOrigin(origin)
97         , fTextureSwizzle(textureSwizzle)
98         , fFit(fit)
99         , fBudgeted(fTarget->resourcePriv().budgetedType() == GrBudgetedType::kBudgeted
100                             ? SkBudgeted::kYes
101                             : SkBudgeted::kNo)
102         , fUniqueID(fTarget->uniqueID())  // Note: converting from unique resource ID to a proxy ID!
103         , fIsProtected(fTarget->isProtected() ? GrProtected::kYes : GrProtected::kNo)
104         , fGpuMemorySize(kInvalidGpuMemorySize)
105         , fLastRenderTask(nullptr) {
106     SkASSERT(fFormat.isValid());
107 }
108 
~GrSurfaceProxy()109 GrSurfaceProxy::~GrSurfaceProxy() {
110     // For this to be deleted the opList that held a ref on it (if there was one) must have been
111     // deleted. Which would have cleared out this back pointer.
112     SkASSERT(!fLastRenderTask);
113 }
114 
AttachStencilIfNeeded(GrResourceProvider * resourceProvider,GrSurface * surface,int minStencilSampleCount)115 bool GrSurfaceProxyPriv::AttachStencilIfNeeded(GrResourceProvider* resourceProvider,
116                                                GrSurface* surface, int minStencilSampleCount) {
117     if (minStencilSampleCount) {
118         GrRenderTarget* rt = surface->asRenderTarget();
119         if (!rt) {
120             SkASSERT(0);
121             return false;
122         }
123 
124         if (!resourceProvider->attachStencilAttachment(rt, minStencilSampleCount)) {
125             return false;
126         }
127     }
128 
129     return true;
130 }
131 
createSurfaceImpl(GrResourceProvider * resourceProvider,int sampleCnt,int minStencilSampleCount,GrRenderable renderable,GrMipMapped mipMapped) const132 sk_sp<GrSurface> GrSurfaceProxy::createSurfaceImpl(GrResourceProvider* resourceProvider,
133                                                    int sampleCnt,
134                                                    int minStencilSampleCount,
135                                                    GrRenderable renderable,
136                                                    GrMipMapped mipMapped) const {
137     SkASSERT(GrSurfaceProxy::LazyState::kNot == this->lazyInstantiationState());
138     SkASSERT(!fTarget);
139     GrSurfaceDesc desc;
140     desc.fWidth = fWidth;
141     desc.fHeight = fHeight;
142     desc.fConfig = fConfig;
143     desc.setGrTag(fGrProxyTag.fPid, fGrProxyTag.fTid, fGrProxyTag.fWid, fGrProxyTag.fFid);
144 
145     // The explicit resource allocator requires that any resources it pulls out of the
146     // cache have no pending IO.
147     GrResourceProvider::Flags resourceProviderFlags = GrResourceProvider::Flags::kNoPendingIO;
148 
149     sk_sp<GrSurface> surface;
150     if (GrMipMapped::kYes == mipMapped) {
151         SkASSERT(SkBackingFit::kExact == fFit);
152 
153         // SkMipMap doesn't include the base level in the level count so we have to add 1
154         int mipCount = SkMipMap::ComputeLevelCount(desc.fWidth, desc.fHeight) + 1;
155         // We should have caught the case where mipCount == 1 when making the proxy and instead
156         // created a non-mipmapped proxy.
157         SkASSERT(mipCount > 1);
158         std::unique_ptr<GrMipLevel[]> texels(new GrMipLevel[mipCount]);
159 
160         // We don't want to upload any texel data
161         for (int i = 0; i < mipCount; i++) {
162             texels[i].fPixels = nullptr;
163             texels[i].fRowBytes = 0;
164         }
165         surface = resourceProvider->createTexture(desc, fFormat, renderable, sampleCnt, fBudgeted,
166                                                   fIsProtected, texels.get(), mipCount);
167 #ifdef SK_DEBUG
168         if (surface) {
169             const GrTextureProxy* thisTexProxy = this->asTextureProxy();
170             SkASSERT(thisTexProxy);
171 
172             GrTexture* texture = surface->asTexture();
173             SkASSERT(texture);
174 
175             SkASSERT(GrMipMapped::kYes == texture->texturePriv().mipMapped());
176             SkASSERT(thisTexProxy->fInitialMipMapsStatus == texture->texturePriv().mipMapsStatus());
177         }
178 #endif
179     } else {
180         if (SkBackingFit::kApprox == fFit) {
181             surface = resourceProvider->createApproxTexture(desc, fFormat, renderable, sampleCnt,
182                                                             fIsProtected, resourceProviderFlags);
183         } else {
184             surface =
185                     resourceProvider->createTexture(desc, fFormat, renderable, sampleCnt, fBudgeted,
186                                                     fIsProtected, resourceProviderFlags);
187         }
188     }
189     if (!surface) {
190         return nullptr;
191     }
192 
193     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, surface.get(),
194                                                    minStencilSampleCount)) {
195         return nullptr;
196     }
197 
198     return surface;
199 }
200 
canSkipResourceAllocator() const201 bool GrSurfaceProxy::canSkipResourceAllocator() const {
202     if (this->ignoredByResourceAllocator()) {
203         // Usually an atlas or onFlush proxy
204         return true;
205     }
206 
207     auto peek = this->peekSurface();
208     if (!peek) {
209         return false;
210     }
211     // If this resource is already allocated and not recyclable then the resource allocator does
212     // not need to do anything with it.
213     return !peek->resourcePriv().getScratchKey().isValid();
214 }
215 
assign(sk_sp<GrSurface> surface)216 void GrSurfaceProxy::assign(sk_sp<GrSurface> surface) {
217     SkASSERT(!fTarget && surface);
218 
219     SkDEBUGCODE(this->validateSurface(surface.get());)
220 
221     fTarget = std::move(surface);
222 
223 #ifdef SK_DEBUG
224     if (this->asRenderTargetProxy()) {
225         SkASSERT(fTarget->asRenderTarget());
226         if (int minStencilSampleCount = this->asRenderTargetProxy()->numStencilSamples()) {
227             auto* stencil = fTarget->asRenderTarget()->renderTargetPriv().getStencilAttachment();
228             SkASSERT(stencil);
229             SkASSERT(stencil->numSamples() >= minStencilSampleCount);
230         }
231     }
232 
233     if (kInvalidGpuMemorySize != this->getRawGpuMemorySize_debugOnly()) {
234         SkASSERT(fTarget->gpuMemorySize() <= this->getRawGpuMemorySize_debugOnly());
235     }
236 #endif
237 }
238 
instantiateImpl(GrResourceProvider * resourceProvider,int sampleCnt,int minStencilSampleCount,GrRenderable renderable,GrMipMapped mipMapped,const GrUniqueKey * uniqueKey)239 bool GrSurfaceProxy::instantiateImpl(GrResourceProvider* resourceProvider, int sampleCnt,
240                                      int minStencilSampleCount, GrRenderable renderable,
241                                      GrMipMapped mipMapped, const GrUniqueKey* uniqueKey) {
242     SkASSERT(LazyState::kNot == this->lazyInstantiationState());
243     if (fTarget) {
244         if (uniqueKey && uniqueKey->isValid()) {
245             SkASSERT(fTarget->getUniqueKey().isValid() && fTarget->getUniqueKey() == *uniqueKey);
246         }
247         return GrSurfaceProxyPriv::AttachStencilIfNeeded(resourceProvider, fTarget.get(),
248                                                          minStencilSampleCount);
249     }
250 
251     sk_sp<GrSurface> surface = this->createSurfaceImpl(
252             resourceProvider, sampleCnt, minStencilSampleCount, renderable, mipMapped);
253     if (!surface) {
254         return false;
255     }
256 
257     // If there was an invalidation message pending for this key, we might have just processed it,
258     // causing the key (stored on this proxy) to become invalid.
259     if (uniqueKey && uniqueKey->isValid()) {
260         resourceProvider->assignUniqueKeyToResource(*uniqueKey, surface.get());
261     }
262 
263     this->assign(std::move(surface));
264 
265     return true;
266 }
267 
deinstantiate()268 void GrSurfaceProxy::deinstantiate() {
269     SkASSERT(this->isInstantiated());
270     fTarget = nullptr;
271 }
272 
computeScratchKey(GrScratchKey * key) const273 void GrSurfaceProxy::computeScratchKey(GrScratchKey* key) const {
274     SkASSERT(LazyState::kFully != this->lazyInstantiationState());
275     GrRenderable renderable = GrRenderable::kNo;
276     int sampleCount = 1;
277     if (const auto* rtp = this->asRenderTargetProxy()) {
278         renderable = GrRenderable::kYes;
279         sampleCount = rtp->numSamples();
280     }
281 
282     const GrTextureProxy* tp = this->asTextureProxy();
283     GrMipMapped mipMapped = GrMipMapped::kNo;
284     if (tp) {
285         mipMapped = tp->mipMapped();
286     }
287 
288     int width = this->worstCaseWidth();
289     int height = this->worstCaseHeight();
290 
291     GrTexturePriv::ComputeScratchKey(this->config(), width, height, renderable, sampleCount,
292                                      mipMapped, key);
293 }
294 
setLastRenderTask(GrRenderTask * renderTask)295 void GrSurfaceProxy::setLastRenderTask(GrRenderTask* renderTask) {
296 #ifdef SK_DEBUG
297     if (fLastRenderTask) {
298         SkASSERT(fLastRenderTask->isClosed());
299     }
300 #endif
301 
302     // Un-reffed
303     fLastRenderTask = renderTask;
304 }
305 
getLastRenderTargetOpList()306 GrRenderTargetOpList* GrSurfaceProxy::getLastRenderTargetOpList() {
307     return fLastRenderTask ? fLastRenderTask->asRenderTargetOpList() : nullptr;
308 }
309 
getLastTextureOpList()310 GrTextureOpList* GrSurfaceProxy::getLastTextureOpList() {
311     return fLastRenderTask ? fLastRenderTask->asTextureOpList() : nullptr;
312 }
313 
worstCaseWidth() const314 int GrSurfaceProxy::worstCaseWidth() const {
315     SkASSERT(LazyState::kFully != this->lazyInstantiationState());
316     if (fTarget) {
317         return fTarget->width();
318     }
319 
320     if (SkBackingFit::kExact == fFit) {
321         return fWidth;
322     }
323     return GrResourceProvider::MakeApprox(fWidth);
324 }
325 
worstCaseHeight() const326 int GrSurfaceProxy::worstCaseHeight() const {
327     SkASSERT(LazyState::kFully != this->lazyInstantiationState());
328     if (fTarget) {
329         return fTarget->height();
330     }
331 
332     if (SkBackingFit::kExact == fFit) {
333         return fHeight;
334     }
335     return GrResourceProvider::MakeApprox(fHeight);
336 }
337 
338 #ifdef SK_DEBUG
validate(GrContext_Base * context) const339 void GrSurfaceProxy::validate(GrContext_Base* context) const {
340     if (fTarget) {
341         SkASSERT(fTarget->getContext() == context);
342     }
343 }
344 #endif
345 
Copy(GrRecordingContext * context,GrSurfaceProxy * src,GrMipMapped mipMapped,SkIRect srcRect,SkBackingFit fit,SkBudgeted budgeted,RectsMustMatch rectsMustMatch)346 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context,
347                                            GrSurfaceProxy* src,
348                                            GrMipMapped mipMapped,
349                                            SkIRect srcRect,
350                                            SkBackingFit fit,
351                                            SkBudgeted budgeted,
352                                            RectsMustMatch rectsMustMatch) {
353     SkASSERT(LazyState::kFully != src->lazyInstantiationState());
354     GrProtected isProtected = src->isProtected() ? GrProtected::kYes : GrProtected::kNo;
355     int width;
356     int height;
357 
358     SkIPoint dstPoint;
359     if (rectsMustMatch == RectsMustMatch::kYes) {
360         width = src->width();
361         height = src->height();
362         dstPoint = {srcRect.fLeft, srcRect.fTop};
363     } else {
364         width = srcRect.width();
365         height = srcRect.height();
366         dstPoint = {0, 0};
367     }
368 
369     if (!srcRect.intersect(SkIRect::MakeWH(src->width(), src->height()))) {
370         return nullptr;
371     }
372     auto colorType = GrPixelConfigToColorType(src->config());
373     if (src->backendFormat().textureType() != GrTextureType::kExternal) {
374         sk_sp<GrTextureContext> dstContext(context->priv().makeDeferredTextureContext(
375                 fit, width, height, colorType, kUnknown_SkAlphaType, nullptr, mipMapped,
376                 src->origin(), budgeted, isProtected));
377         if (!dstContext) {
378             return nullptr;
379         }
380         if (dstContext->copy(src, srcRect, dstPoint)) {
381             return dstContext->asTextureProxyRef();
382         }
383     }
384     if (src->asTextureProxy()) {
385         sk_sp<GrRenderTargetContext> dstContext = context->priv().makeDeferredRenderTargetContext(
386                 fit, width, height, colorType, nullptr, 1, mipMapped, src->origin(), nullptr,
387                 budgeted);
388 
389         if (dstContext && dstContext->blitTexture(src->asTextureProxy(), srcRect, dstPoint)) {
390             return dstContext->asTextureProxyRef();
391         }
392     }
393     // Can't use backend copies or draws.
394     return nullptr;
395 }
396 
Copy(GrRecordingContext * context,GrSurfaceProxy * src,GrMipMapped mipMapped,SkBackingFit fit,SkBudgeted budgeted)397 sk_sp<GrTextureProxy> GrSurfaceProxy::Copy(GrRecordingContext* context, GrSurfaceProxy* src,
398                                            GrMipMapped mipMapped, SkBackingFit fit,
399                                            SkBudgeted budgeted) {
400     SkASSERT(LazyState::kFully != src->lazyInstantiationState());
401     return Copy(context, src, mipMapped, SkIRect::MakeWH(src->width(), src->height()), fit,
402                 budgeted);
403 }
404 
405 #if GR_TEST_UTILS
testingOnly_getBackingRefCnt() const406 int32_t GrSurfaceProxy::testingOnly_getBackingRefCnt() const {
407     if (fTarget) {
408         return fTarget->testingOnly_getRefCnt();
409     }
410 
411     return -1; // no backing GrSurface
412 }
413 
testingOnly_getFlags() const414 GrInternalSurfaceFlags GrSurfaceProxy::testingOnly_getFlags() const {
415     return fSurfaceFlags;
416 }
417 #endif
418 
exactify(bool allocatedCaseOnly)419 void GrSurfaceProxyPriv::exactify(bool allocatedCaseOnly) {
420     SkASSERT(GrSurfaceProxy::LazyState::kFully != fProxy->lazyInstantiationState());
421     if (this->isExact()) {
422         return;
423     }
424 
425     SkASSERT(SkBackingFit::kApprox == fProxy->fFit);
426 
427     if (fProxy->fTarget) {
428         // The kApprox but already instantiated case. Setting the proxy's width & height to
429         // the instantiated width & height could have side-effects going forward, since we're
430         // obliterating the area of interest information. This call (exactify) only used
431         // when converting an SkSpecialImage to an SkImage so the proxy shouldn't be
432         // used for additional draws.
433         fProxy->fWidth = fProxy->fTarget->width();
434         fProxy->fHeight = fProxy->fTarget->height();
435         return;
436     }
437 
438 #ifndef SK_CRIPPLE_TEXTURE_REUSE
439     // In the post-implicit-allocation world we can't convert this proxy to be exact fit
440     // at this point. With explicit allocation switching this to exact will result in a
441     // different allocation at flush time. With implicit allocation, allocation would occur
442     // at draw time (rather than flush time) so this pathway was encountered less often (if
443     // at all).
444     if (allocatedCaseOnly) {
445         return;
446     }
447 #endif
448 
449     // The kApprox uninstantiated case. Making this proxy be exact should be okay.
450     // It could mess things up if prior decisions were based on the approximate size.
451     fProxy->fFit = SkBackingFit::kExact;
452     // If fGpuMemorySize is used when caching specialImages for the image filter DAG. If it has
453     // already been computed we want to leave it alone so that amount will be removed when
454     // the special image goes away. If it hasn't been computed yet it might as well compute the
455     // exact amount.
456 }
457 
doLazyInstantiation(GrResourceProvider * resourceProvider)458 bool GrSurfaceProxyPriv::doLazyInstantiation(GrResourceProvider* resourceProvider) {
459     SkASSERT(GrSurfaceProxy::LazyState::kNot != fProxy->lazyInstantiationState());
460 
461     sk_sp<GrSurface> surface;
462     if (fProxy->asTextureProxy() && fProxy->asTextureProxy()->getUniqueKey().isValid()) {
463         // First try to reattach to a cached version if the proxy is uniquely keyed
464         surface = resourceProvider->findByUniqueKey<GrSurface>(
465                                                         fProxy->asTextureProxy()->getUniqueKey());
466     }
467 
468     bool syncKey = true;
469     if (!surface) {
470         auto result = fProxy->fLazyInstantiateCallback(resourceProvider);
471         surface = std::move(result.fSurface);
472         syncKey = result.fKeyMode == GrSurfaceProxy::LazyInstantiationKeyMode::kSynced;
473     }
474     if (GrSurfaceProxy::LazyInstantiationType::kSingleUse == fProxy->fLazyInstantiationType) {
475         fProxy->fLazyInstantiateCallback = nullptr;
476     }
477     if (!surface) {
478         fProxy->fWidth = 0;
479         fProxy->fHeight = 0;
480         return false;
481     }
482 
483     if (fProxy->fWidth <= 0 || fProxy->fHeight <= 0) {
484         // This was a fully lazy proxy. We need to fill in the width & height. For partially
485         // lazy proxies we must preserve the original width & height since that indicates
486         // the content area.
487         SkASSERT(fProxy->fWidth <= 0 && fProxy->fHeight <= 0);
488         fProxy->fWidth = surface->width();
489         fProxy->fHeight = surface->height();
490     }
491 
492     SkASSERT(fProxy->fWidth <= surface->width());
493     SkASSERT(fProxy->fHeight <= surface->height());
494 
495     int minStencilSampleCount = (fProxy->asRenderTargetProxy())
496             ? fProxy->asRenderTargetProxy()->numSamples()
497             : 0;
498 
499     if (!GrSurfaceProxyPriv::AttachStencilIfNeeded(
500             resourceProvider, surface.get(), minStencilSampleCount)) {
501         return false;
502     }
503 
504     if (GrTextureProxy* texProxy = fProxy->asTextureProxy()) {
505         texProxy->setTargetKeySync(syncKey);
506         if (syncKey) {
507             const GrUniqueKey& key = texProxy->getUniqueKey();
508             if (key.isValid()) {
509                 if (!surface->asTexture()->getUniqueKey().isValid()) {
510                     // If 'surface' is newly created, attach the unique key
511                     resourceProvider->assignUniqueKeyToResource(key, surface.get());
512                 } else {
513                     // otherwise we had better have reattached to a cached version
514                     SkASSERT(surface->asTexture()->getUniqueKey() == key);
515                 }
516             } else {
517                 SkASSERT(!surface->getUniqueKey().isValid());
518             }
519         }
520     }
521 
522     this->assign(std::move(surface));
523     return true;
524 }
525 
526 #ifdef SK_DEBUG
validateSurface(const GrSurface * surface)527 void GrSurfaceProxy::validateSurface(const GrSurface* surface) {
528     SkASSERT(surface->config() == fConfig);
529 
530     this->onValidateSurface(surface);
531 }
532 #endif
533