• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017 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 #ifndef GrBackendSurface_DEFINED
9 #define GrBackendSurface_DEFINED
10 
11 #include "include/gpu/GrBackendSurfaceMutableState.h"
12 #include "include/gpu/GrSurfaceInfo.h"
13 #include "include/gpu/GrTypes.h"
14 #ifdef SK_GL
15 #include "include/gpu/gl/GrGLTypes.h"
16 #include "include/private/GrGLTypesPriv.h"
17 #endif
18 #include "include/gpu/mock/GrMockTypes.h"
19 #ifdef SK_VULKAN
20 #include "include/gpu/vk/GrVkTypes.h"
21 #include "include/private/GrVkTypesPriv.h"
22 #endif
23 
24 #ifdef SK_DAWN
25 #include "include/gpu/dawn/GrDawnTypes.h"
26 #endif
27 
28 class GrBackendSurfaceMutableStateImpl;
29 class GrVkImageLayout;
30 class GrGLTextureParameters;
31 class GrColorFormatDesc;
32 
33 #ifdef SK_DAWN
34 #include "dawn/webgpu_cpp.h"
35 #endif
36 
37 #ifdef SK_METAL
38 #include "include/gpu/mtl/GrMtlTypes.h"
39 #endif
40 
41 #ifdef SK_DIRECT3D
42 #include "include/private/GrD3DTypesMinimal.h"
43 class GrD3DResourceState;
44 #endif
45 
46 #if defined(SK_DEBUG) || GR_TEST_UTILS
47 class SkString;
48 #endif
49 
50 #if !SK_SUPPORT_GPU
51 
52 // SkSurfaceCharacterization always needs a minimal version of this
53 class SK_API GrBackendFormat {
54 public:
isValid()55     bool isValid() const { return false; }
56 };
57 
58 // SkSurface and SkImage rely on a minimal version of these always being available
59 class SK_API GrBackendTexture {
60 public:
GrBackendTexture()61     GrBackendTexture() {}
62 
isValid()63     bool isValid() const { return false; }
64 };
65 
66 class SK_API GrBackendRenderTarget {
67 public:
GrBackendRenderTarget()68     GrBackendRenderTarget() {}
69 
isValid()70     bool isValid() const { return false; }
isFramebufferOnly()71     bool isFramebufferOnly() const { return false; }
72 };
73 #else
74 
75 enum class GrGLFormat;
76 
77 class SK_API GrBackendFormat {
78 public:
79     // Creates an invalid backend format.
GrBackendFormat()80     GrBackendFormat() {}
81     GrBackendFormat(const GrBackendFormat&);
82     GrBackendFormat& operator=(const GrBackendFormat&);
83 
84 #ifdef SK_GL
MakeGL(GrGLenum format,GrGLenum target)85     static GrBackendFormat MakeGL(GrGLenum format, GrGLenum target) {
86         return GrBackendFormat(format, target);
87     }
88 #endif
89 
90 #ifdef SK_VULKAN
91     static GrBackendFormat MakeVk(VkFormat format, bool willUseDRMFormatModifiers = false) {
92         return GrBackendFormat(format, GrVkYcbcrConversionInfo(), willUseDRMFormatModifiers);
93     }
94 
95     static GrBackendFormat MakeVk(const GrVkYcbcrConversionInfo& ycbcrInfo,
96                                   bool willUseDRMFormatModifiers = false);
97 #endif
98 
99 #ifdef SK_DAWN
MakeDawn(wgpu::TextureFormat format)100     static GrBackendFormat MakeDawn(wgpu::TextureFormat format) {
101         return GrBackendFormat(format);
102     }
103 #endif
104 
105 #ifdef SK_METAL
MakeMtl(GrMTLPixelFormat format)106     static GrBackendFormat MakeMtl(GrMTLPixelFormat format) {
107         return GrBackendFormat(format);
108     }
109 #endif
110 
111 #ifdef SK_DIRECT3D
MakeDxgi(DXGI_FORMAT format)112     static GrBackendFormat MakeDxgi(DXGI_FORMAT format) {
113         return GrBackendFormat(format);
114     }
115 #endif
116 
117     static GrBackendFormat MakeMock(GrColorType colorType, SkImage::CompressionType compression,
118                                     bool isStencilFormat = false);
119 
120     bool operator==(const GrBackendFormat& that) const;
121     bool operator!=(const GrBackendFormat& that) const { return !(*this == that); }
122 
backend()123     GrBackendApi backend() const { return fBackend; }
textureType()124     GrTextureType textureType() const { return fTextureType; }
125 
126     /**
127      * Gets the channels present in the format as a bitfield of SkColorChannelFlag values.
128      * Luminance channels are reported as kGray_SkColorChannelFlag.
129      */
130     uint32_t channelMask() const;
131 
132     GrColorFormatDesc desc() const;
133 
134 #ifdef SK_GL
135     /**
136      * If the backend API is GL this gets the format as a GrGLFormat. Otherwise, returns
137      * GrGLFormat::kUnknown.
138      */
139     GrGLFormat asGLFormat() const;
140 #endif
141 
142 #ifdef SK_VULKAN
143     /**
144      * If the backend API is Vulkan this gets the format as a VkFormat and returns true. Otherwise,
145      * returns false.
146      */
147     bool asVkFormat(VkFormat*) const;
148 
149     const GrVkYcbcrConversionInfo* getVkYcbcrConversionInfo() const;
150 #endif
151 
152 #ifdef SK_DAWN
153     /**
154      * If the backend API is Dawn this gets the format as a wgpu::TextureFormat and returns true.
155      * Otherwise, returns false.
156      */
157     bool asDawnFormat(wgpu::TextureFormat*) const;
158 #endif
159 
160 #ifdef SK_METAL
161     /**
162      * If the backend API is Metal this gets the format as a GrMtlPixelFormat. Otherwise,
163      * Otherwise, returns MTLPixelFormatInvalid.
164      */
165     GrMTLPixelFormat asMtlFormat() const;
166 #endif
167 
168 #ifdef SK_DIRECT3D
169     /**
170      * If the backend API is Direct3D this gets the format as a DXGI_FORMAT and returns true.
171      * Otherwise, returns false.
172      */
173     bool asDxgiFormat(DXGI_FORMAT*) const;
174 #endif
175 
176     /**
177      * If the backend API is not Mock these three calls will return kUnknown, kNone or false,
178      * respectively. Otherwise, only one of the following can be true. The GrColorType is not
179      * kUnknown, the compression type is not kNone, or this is a mock stencil format.
180      */
181     GrColorType asMockColorType() const;
182     SkImage::CompressionType asMockCompressionType() const;
183     bool isMockStencilFormat() const;
184 
185     // If possible, copies the GrBackendFormat and forces the texture type to be Texture2D. If the
186     // GrBackendFormat was for Vulkan and it originally had a GrVkYcbcrConversionInfo, we will
187     // remove the conversion and set the format to be VK_FORMAT_R8G8B8A8_UNORM.
188     GrBackendFormat makeTexture2D() const;
189 
190     // Returns true if the backend format has been initialized.
isValid()191     bool isValid() const { return fValid; }
192 
193 #if defined(SK_DEBUG) || GR_TEST_UTILS
194     SkString toStr() const;
195 #endif
196 
197 private:
198 #ifdef SK_GL
199     GrBackendFormat(GrGLenum format, GrGLenum target);
200 #endif
201 
202 #ifdef SK_VULKAN
203     GrBackendFormat(const VkFormat vkFormat, const GrVkYcbcrConversionInfo&,
204                     bool willUseDRMFormatModifiers);
205 #endif
206 
207 #ifdef SK_DAWN
208     GrBackendFormat(wgpu::TextureFormat format);
209 #endif
210 
211 #ifdef SK_METAL
212     GrBackendFormat(const GrMTLPixelFormat mtlFormat);
213 #endif
214 
215 #ifdef SK_DIRECT3D
216     GrBackendFormat(DXGI_FORMAT dxgiFormat);
217 #endif
218 
219     GrBackendFormat(GrColorType, SkImage::CompressionType, bool isStencilFormat);
220 
221 #ifdef SK_DEBUG
222     bool validateMock() const;
223 #endif
224 
225     GrBackendApi fBackend = GrBackendApi::kMock;
226     bool         fValid = false;
227 
228     union {
229 #ifdef SK_GL
230         GrGLenum fGLFormat; // the sized, internal format of the GL resource
231 #endif
232 #ifdef SK_VULKAN
233         struct {
234             VkFormat                 fFormat;
235             GrVkYcbcrConversionInfo  fYcbcrConversionInfo;
236         } fVk;
237 #endif
238 #ifdef SK_DAWN
239         wgpu::TextureFormat fDawnFormat;
240 #endif
241 
242 #ifdef SK_METAL
243         GrMTLPixelFormat fMtlFormat;
244 #endif
245 
246 #ifdef SK_DIRECT3D
247         DXGI_FORMAT fDxgiFormat;
248 #endif
249         struct {
250             GrColorType              fColorType;
251             SkImage::CompressionType fCompressionType;
252             bool                     fIsStencilFormat;
253         } fMock;
254     };
255     GrTextureType fTextureType = GrTextureType::kNone;
256 };
257 
258 class SK_API GrBackendTexture {
259 public:
260     // Creates an invalid backend texture.
261     GrBackendTexture();
262 
263 #ifdef SK_GL
264     // The GrGLTextureInfo must have a valid fFormat.
265     GrBackendTexture(int width,
266                      int height,
267                      GrMipmapped,
268                      const GrGLTextureInfo& glInfo);
269 #endif
270 
271 #ifdef SK_VULKAN
272     GrBackendTexture(int width,
273                      int height,
274                      const GrVkImageInfo& vkInfo);
275 #endif
276 
277 #ifdef SK_METAL
278     GrBackendTexture(int width,
279                      int height,
280                      GrMipmapped,
281                      const GrMtlTextureInfo& mtlInfo);
282 #endif
283 
284 #ifdef SK_DIRECT3D
285     GrBackendTexture(int width,
286                      int height,
287                      const GrD3DTextureResourceInfo& d3dInfo);
288 #endif
289 
290 #ifdef SK_DAWN
291     GrBackendTexture(int width,
292                      int height,
293                      const GrDawnTextureInfo& dawnInfo);
294 #endif
295 
296     GrBackendTexture(int width,
297                      int height,
298                      GrMipmapped,
299                      const GrMockTextureInfo& mockInfo);
300 
301     GrBackendTexture(const GrBackendTexture& that);
302 
303     ~GrBackendTexture();
304 
305     GrBackendTexture& operator=(const GrBackendTexture& that);
306 
dimensions()307     SkISize dimensions() const { return {fWidth, fHeight}; }
width()308     int width() const { return fWidth; }
height()309     int height() const { return fHeight; }
mipmapped()310     GrMipmapped mipmapped() const { return fMipmapped; }
hasMipmaps()311     bool hasMipmaps() const { return fMipmapped == GrMipmapped::kYes; }
312     /** deprecated alias of hasMipmaps(). */
hasMipMaps()313     bool hasMipMaps() const { return this->hasMipmaps(); }
backend()314     GrBackendApi backend() const {return fBackend; }
textureType()315     GrTextureType textureType() const { return fTextureType; }
316 
317 #ifdef SK_GL
318     // If the backend API is GL, copies a snapshot of the GrGLTextureInfo struct into the passed in
319     // pointer and returns true. Otherwise returns false if the backend API is not GL.
320     bool getGLTextureInfo(GrGLTextureInfo*) const;
321 
322     // Call this to indicate that the texture parameters have been modified in the GL context
323     // externally to GrContext.
324     void glTextureParametersModified();
325 #endif
326 
327 #ifdef SK_DAWN
328     // If the backend API is Dawn, copies a snapshot of the GrDawnTextureInfo struct into the passed
329     // in pointer and returns true. Otherwise returns false if the backend API is not Dawn.
330     bool getDawnTextureInfo(GrDawnTextureInfo*) const;
331 #endif
332 
333 #ifdef SK_VULKAN
334     // If the backend API is Vulkan, copies a snapshot of the GrVkImageInfo struct into the passed
335     // in pointer and returns true. This snapshot will set the fImageLayout to the current layout
336     // state. Otherwise returns false if the backend API is not Vulkan.
337     bool getVkImageInfo(GrVkImageInfo*) const;
338 
339     // Anytime the client changes the VkImageLayout of the VkImage captured by this
340     // GrBackendTexture, they must call this function to notify Skia of the changed layout.
341     void setVkImageLayout(VkImageLayout);
342 #endif
343 
344 #ifdef SK_METAL
345     // If the backend API is Metal, copies a snapshot of the GrMtlTextureInfo struct into the passed
346     // in pointer and returns true. Otherwise returns false if the backend API is not Metal.
347     bool getMtlTextureInfo(GrMtlTextureInfo*) const;
348 #endif
349 
350 #ifdef SK_DIRECT3D
351     // If the backend API is Direct3D, copies a snapshot of the GrD3DTextureResourceInfo struct into
352     // the passed in pointer and returns true. This snapshot will set the fResourceState to the
353     // current resource state. Otherwise returns false if the backend API is not D3D.
354     bool getD3DTextureResourceInfo(GrD3DTextureResourceInfo*) const;
355 
356     // Anytime the client changes the D3D12_RESOURCE_STATES of the D3D12_RESOURCE captured by this
357     // GrBackendTexture, they must call this function to notify Skia of the changed layout.
358     void setD3DResourceState(GrD3DResourceStateEnum);
359 #endif
360 
361     // Get the GrBackendFormat for this texture (or an invalid format if this is not valid).
362     GrBackendFormat getBackendFormat() const;
363 
364     // If the backend API is Mock, copies a snapshot of the GrMockTextureInfo struct into the passed
365     // in pointer and returns true. Otherwise returns false if the backend API is not Mock.
366     bool getMockTextureInfo(GrMockTextureInfo*) const;
367 
368     // If the client changes any of the mutable backend of the GrBackendTexture they should call
369     // this function to inform Skia that those values have changed. The backend API specific state
370     // that can be set from this function are:
371     //
372     // Vulkan: VkImageLayout and QueueFamilyIndex
373     void setMutableState(const GrBackendSurfaceMutableState&);
374 
375     // Returns true if we are working with protected content.
376     bool isProtected() const;
377 
378     // Returns true if the backend texture has been initialized.
isValid()379     bool isValid() const { return fIsValid; }
380 
381     // Returns true if both textures are valid and refer to the same API texture.
382     bool isSameTexture(const GrBackendTexture&);
383 
384 #if GR_TEST_UTILS
385     static bool TestingOnly_Equals(const GrBackendTexture& , const GrBackendTexture&);
386 #endif
387 
388 private:
389     friend class GrVkGpu;  // for getMutableState
390     sk_sp<GrBackendSurfaceMutableStateImpl> getMutableState() const;
391 
392 #ifdef SK_GL
393     friend class GrGLTexture;
394     friend class GrGLGpu;    // for getGLTextureParams
395     GrBackendTexture(int width,
396                      int height,
397                      GrMipmapped,
398                      const GrGLTextureInfo,
399                      sk_sp<GrGLTextureParameters>);
400     sk_sp<GrGLTextureParameters> getGLTextureParams() const;
401 #endif
402 
403 #ifdef SK_VULKAN
404     friend class GrVkTexture;
405     GrBackendTexture(int width,
406                      int height,
407                      const GrVkImageInfo& vkInfo,
408                      sk_sp<GrBackendSurfaceMutableStateImpl> mutableState);
409 #endif
410 
411 #ifdef SK_DIRECT3D
412     friend class GrD3DTexture;
413     friend class GrD3DGpu;     // for getGrD3DResourceState
414     GrBackendTexture(int width,
415                      int height,
416                      const GrD3DTextureResourceInfo& vkInfo,
417                      sk_sp<GrD3DResourceState> state);
418     sk_sp<GrD3DResourceState> getGrD3DResourceState() const;
419 #endif
420 
421     // Free and release and resources being held by the GrBackendTexture.
422     void cleanup();
423 
424     bool fIsValid;
425     int fWidth;         //<! width in pixels
426     int fHeight;        //<! height in pixels
427     GrMipmapped fMipmapped;
428     GrBackendApi fBackend;
429     GrTextureType fTextureType;
430 
431     union {
432 #ifdef SK_GL
433         GrGLBackendTextureInfo fGLInfo;
434 #endif
435 #ifdef SK_VULKAN
436         GrVkBackendSurfaceInfo fVkInfo;
437 #endif
438         GrMockTextureInfo fMockInfo;
439 #ifdef SK_DIRECT3D
440         GrD3DBackendSurfaceInfo fD3DInfo;
441 #endif
442     };
443 #ifdef SK_METAL
444     GrMtlTextureInfo fMtlInfo;
445 #endif
446 #ifdef SK_DAWN
447     GrDawnTextureInfo fDawnInfo;
448 #endif
449 
450     sk_sp<GrBackendSurfaceMutableStateImpl> fMutableState;
451 };
452 
453 class SK_API GrBackendRenderTarget {
454 public:
455     // Creates an invalid backend texture.
456     GrBackendRenderTarget();
457 
458 #ifdef SK_GL
459     // The GrGLTextureInfo must have a valid fFormat. If wrapping in an SkSurface we require the
460     // stencil bits to be either 0, 8 or 16.
461     GrBackendRenderTarget(int width,
462                           int height,
463                           int sampleCnt,
464                           int stencilBits,
465                           const GrGLFramebufferInfo& glInfo);
466 #endif
467 
468 #ifdef SK_DAWN
469     // If wrapping in an SkSurface we require the stencil bits to be either 0, 8 or 16.
470     GrBackendRenderTarget(int width,
471                           int height,
472                           int sampleCnt,
473                           int stencilBits,
474                           const GrDawnRenderTargetInfo& dawnInfo);
475 #endif
476 
477 #ifdef SK_VULKAN
478     /** Deprecated. Sample count is now part of GrVkImageInfo. */
479     GrBackendRenderTarget(int width, int height, int sampleCnt, const GrVkImageInfo& vkInfo);
480 
481     GrBackendRenderTarget(int width, int height, const GrVkImageInfo& vkInfo);
482 #endif
483 
484 #ifdef SK_METAL
485     GrBackendRenderTarget(int width,
486                           int height,
487                           const GrMtlTextureInfo& mtlInfo);
488     /** Deprecated. Sample count is ignored and is instead retrieved from the MtlTexture. */
489     GrBackendRenderTarget(int width,
490                           int height,
491                           int sampleCnt,
492                           const GrMtlTextureInfo& mtlInfo);
493 #endif
494 
495 #ifdef SK_DIRECT3D
496     GrBackendRenderTarget(int width,
497                           int height,
498                           const GrD3DTextureResourceInfo& d3dInfo);
499 #endif
500 
501     GrBackendRenderTarget(int width,
502                           int height,
503                           int sampleCnt,
504                           int stencilBits,
505                           const GrMockRenderTargetInfo& mockInfo);
506 
507     ~GrBackendRenderTarget();
508 
509     GrBackendRenderTarget(const GrBackendRenderTarget& that);
510     GrBackendRenderTarget& operator=(const GrBackendRenderTarget&);
511 
dimensions()512     SkISize dimensions() const { return {fWidth, fHeight}; }
width()513     int width() const { return fWidth; }
height()514     int height() const { return fHeight; }
sampleCnt()515     int sampleCnt() const { return fSampleCnt; }
stencilBits()516     int stencilBits() const { return fStencilBits; }
backend()517     GrBackendApi backend() const {return fBackend; }
isFramebufferOnly()518     bool isFramebufferOnly() const { return fFramebufferOnly; }
519 
520 #ifdef SK_GL
521     // If the backend API is GL, copies a snapshot of the GrGLFramebufferInfo struct into the passed
522     // in pointer and returns true. Otherwise returns false if the backend API is not GL.
523     bool getGLFramebufferInfo(GrGLFramebufferInfo*) const;
524 #endif
525 
526 #ifdef SK_DAWN
527     // If the backend API is Dawn, copies a snapshot of the GrDawnRenderTargetInfo struct into the
528     // passed-in pointer and returns true. Otherwise returns false if the backend API is not Dawn.
529     bool getDawnRenderTargetInfo(GrDawnRenderTargetInfo*) const;
530 #endif
531 
532 #ifdef SK_VULKAN
533     // If the backend API is Vulkan, copies a snapshot of the GrVkImageInfo struct into the passed
534     // in pointer and returns true. This snapshot will set the fImageLayout to the current layout
535     // state. Otherwise returns false if the backend API is not Vulkan.
536     bool getVkImageInfo(GrVkImageInfo*) const;
537 
538     // Anytime the client changes the VkImageLayout of the VkImage captured by this
539     // GrBackendRenderTarget, they must call this function to notify Skia of the changed layout.
540     void setVkImageLayout(VkImageLayout);
541 #endif
542 
543 #ifdef SK_METAL
544     // If the backend API is Metal, copies a snapshot of the GrMtlTextureInfo struct into the passed
545     // in pointer and returns true. Otherwise returns false if the backend API is not Metal.
546     bool getMtlTextureInfo(GrMtlTextureInfo*) const;
547 #endif
548 
549 #ifdef SK_DIRECT3D
550     // If the backend API is Direct3D, copies a snapshot of the GrMtlTextureInfo struct into the
551     // passed in pointer and returns true. Otherwise returns false if the backend API is not D3D.
552     bool getD3DTextureResourceInfo(GrD3DTextureResourceInfo*) const;
553 
554     // Anytime the client changes the D3D12_RESOURCE_STATES of the D3D12_RESOURCE captured by this
555     // GrBackendTexture, they must call this function to notify Skia of the changed layout.
556     void setD3DResourceState(GrD3DResourceStateEnum);
557 #endif
558 
559     // Get the GrBackendFormat for this render target (or an invalid format if this is not valid).
560     GrBackendFormat getBackendFormat() const;
561 
562     // If the backend API is Mock, copies a snapshot of the GrMockTextureInfo struct into the passed
563     // in pointer and returns true. Otherwise returns false if the backend API is not Mock.
564     bool getMockRenderTargetInfo(GrMockRenderTargetInfo*) const;
565 
566     // If the client changes any of the mutable backend of the GrBackendTexture they should call
567     // this function to inform Skia that those values have changed. The backend API specific state
568     // that can be set from this function are:
569     //
570     // Vulkan: VkImageLayout and QueueFamilyIndex
571     void setMutableState(const GrBackendSurfaceMutableState&);
572 
573     // Returns true if we are working with protected content.
574     bool isProtected() const;
575 
576     // Returns true if the backend texture has been initialized.
isValid()577     bool isValid() const { return fIsValid; }
578 
579 
580 #if GR_TEST_UTILS
581     static bool TestingOnly_Equals(const GrBackendRenderTarget&, const GrBackendRenderTarget&);
582 #endif
583 
584 private:
585     friend class GrVkGpu; // for getMutableState
586     sk_sp<GrBackendSurfaceMutableStateImpl> getMutableState() const;
587 
588 #ifdef SK_VULKAN
589     friend class GrVkRenderTarget;
590     GrBackendRenderTarget(int width,
591                           int height,
592                           const GrVkImageInfo& vkInfo,
593                           sk_sp<GrBackendSurfaceMutableStateImpl> mutableState);
594 #endif
595 
596 #ifdef SK_DIRECT3D
597     friend class GrD3DGpu;
598     friend class GrD3DRenderTarget;
599     GrBackendRenderTarget(int width,
600                           int height,
601                           const GrD3DTextureResourceInfo& d3dInfo,
602                           sk_sp<GrD3DResourceState> state);
603     sk_sp<GrD3DResourceState> getGrD3DResourceState() const;
604 #endif
605 
606     // Free and release and resources being held by the GrBackendTexture.
607     void cleanup();
608 
609     bool fIsValid;
610     bool fFramebufferOnly = false;
611     int fWidth;         //<! width in pixels
612     int fHeight;        //<! height in pixels
613 
614     int fSampleCnt;
615     int fStencilBits;
616 
617     GrBackendApi fBackend;
618 
619     union {
620 #ifdef SK_GL
621         GrGLFramebufferInfo fGLInfo;
622 #endif
623 #ifdef SK_VULKAN
624         GrVkBackendSurfaceInfo fVkInfo;
625 #endif
626         GrMockRenderTargetInfo fMockInfo;
627 #ifdef SK_DIRECT3D
628         GrD3DBackendSurfaceInfo fD3DInfo;
629 #endif
630     };
631 #ifdef SK_METAL
632     GrMtlTextureInfo fMtlInfo;
633 #endif
634 #ifdef SK_DAWN
635     GrDawnRenderTargetInfo  fDawnInfo;
636 #endif
637     sk_sp<GrBackendSurfaceMutableStateImpl> fMutableState;
638 };
639 
640 #endif
641 
642 #endif
643 
644