1#include "precompiled.h"
2//
3// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
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// validationES3.cpp: Validation functions for OpenGL ES 3.0 entry point parameters
9
10#include "libGLESv2/validationES3.h"
11#include "libGLESv2/validationES.h"
12#include "libGLESv2/Context.h"
13#include "libGLESv2/Texture.h"
14#include "libGLESv2/Framebuffer.h"
15#include "libGLESv2/Renderbuffer.h"
16#include "libGLESv2/formatutils.h"
17#include "libGLESv2/main.h"
18
19#include "common/mathutil.h"
20
21namespace gl
22{
23
24bool ValidateES3TexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat, bool isCompressed, bool isSubImage,
25                                   GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
26                                   GLint border, GLenum format, GLenum type, const GLvoid *pixels)
27{
28    if (!ValidTexture2DDestinationTarget(context, target))
29    {
30        return gl::error(GL_INVALID_ENUM, false);
31    }
32
33    // Validate image size
34    if (!ValidImageSize(context, target, level, width, height, depth))
35    {
36        return gl::error(GL_INVALID_VALUE, false);
37    }
38
39    // Verify zero border
40    if (border != 0)
41    {
42        return gl::error(GL_INVALID_VALUE, false);
43    }
44
45    if (xoffset < 0 || yoffset < 0 || zoffset < 0 ||
46        std::numeric_limits<GLsizei>::max() - xoffset < width ||
47        std::numeric_limits<GLsizei>::max() - yoffset < height ||
48        std::numeric_limits<GLsizei>::max() - zoffset < depth)
49    {
50        return gl::error(GL_INVALID_VALUE, false);
51    }
52
53    gl::Texture *texture = NULL;
54    bool textureCompressed = false;
55    GLenum textureInternalFormat = GL_NONE;
56    GLint textureLevelWidth = 0;
57    GLint textureLevelHeight = 0;
58    GLint textureLevelDepth = 0;
59    switch (target)
60    {
61      case GL_TEXTURE_2D:
62        {
63            if (width > (context->getMaximum2DTextureDimension() >> level) ||
64                height > (context->getMaximum2DTextureDimension() >> level))
65            {
66                return gl::error(GL_INVALID_VALUE, false);
67            }
68
69            gl::Texture2D *texture2d = context->getTexture2D();
70            if (texture2d)
71            {
72                textureCompressed = texture2d->isCompressed(level);
73                textureInternalFormat = texture2d->getInternalFormat(level);
74                textureLevelWidth = texture2d->getWidth(level);
75                textureLevelHeight = texture2d->getHeight(level);
76                textureLevelDepth = 1;
77                texture = texture2d;
78            }
79        }
80        break;
81
82      case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
83      case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
84      case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
85      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
86      case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
87      case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
88        {
89            if (!isSubImage && width != height)
90            {
91                return gl::error(GL_INVALID_VALUE, false);
92            }
93
94            if (width > (context->getMaximumCubeTextureDimension() >> level))
95            {
96                return gl::error(GL_INVALID_VALUE, false);
97            }
98
99            gl::TextureCubeMap *textureCube = context->getTextureCubeMap();
100            if (textureCube)
101            {
102                textureCompressed = textureCube->isCompressed(target, level);
103                textureInternalFormat = textureCube->getInternalFormat(target, level);
104                textureLevelWidth = textureCube->getWidth(target, level);
105                textureLevelHeight = textureCube->getHeight(target, level);
106                textureLevelDepth = 1;
107                texture = textureCube;
108            }
109        }
110        break;
111
112      case GL_TEXTURE_3D:
113        {
114            if (width > (context->getMaximum3DTextureDimension() >> level) ||
115                height > (context->getMaximum3DTextureDimension() >> level) ||
116                depth > (context->getMaximum3DTextureDimension() >> level))
117            {
118                return gl::error(GL_INVALID_VALUE, false);
119            }
120
121            gl::Texture3D *texture3d = context->getTexture3D();
122            if (texture3d)
123            {
124                textureCompressed = texture3d->isCompressed(level);
125                textureInternalFormat = texture3d->getInternalFormat(level);
126                textureLevelWidth = texture3d->getWidth(level);
127                textureLevelHeight = texture3d->getHeight(level);
128                textureLevelDepth = texture3d->getDepth(level);
129                texture = texture3d;
130            }
131        }
132        break;
133
134        case GL_TEXTURE_2D_ARRAY:
135          {
136              if (width > (context->getMaximum2DTextureDimension() >> level) ||
137                  height > (context->getMaximum2DTextureDimension() >> level) ||
138                  depth > (context->getMaximum2DArrayTextureLayers() >> level))
139              {
140                  return gl::error(GL_INVALID_VALUE, false);
141              }
142
143              gl::Texture2DArray *texture2darray = context->getTexture2DArray();
144              if (texture2darray)
145              {
146                  textureCompressed = texture2darray->isCompressed(level);
147                  textureInternalFormat = texture2darray->getInternalFormat(level);
148                  textureLevelWidth = texture2darray->getWidth(level);
149                  textureLevelHeight = texture2darray->getHeight(level);
150                  textureLevelDepth = texture2darray->getLayers(level);
151                  texture = texture2darray;
152              }
153          }
154          break;
155
156      default:
157        return gl::error(GL_INVALID_ENUM, false);
158    }
159
160    if (!texture)
161    {
162        return gl::error(GL_INVALID_OPERATION, false);
163    }
164
165    if (texture->isImmutable() && !isSubImage)
166    {
167        return gl::error(GL_INVALID_OPERATION, false);
168    }
169
170    // Validate texture formats
171    GLenum actualInternalFormat = isSubImage ? textureInternalFormat : internalformat;
172    int clientVersion = context->getClientVersion();
173    if (isCompressed)
174    {
175        if (!ValidCompressedImageSize(context, actualInternalFormat, width, height))
176        {
177            return gl::error(GL_INVALID_OPERATION, false);
178        }
179
180        if (!gl::IsFormatCompressed(actualInternalFormat, clientVersion))
181        {
182            return gl::error(GL_INVALID_ENUM, false);
183        }
184
185        if (target == GL_TEXTURE_3D)
186        {
187            return gl::error(GL_INVALID_OPERATION, false);
188        }
189    }
190    else
191    {
192        // Note: dEQP 2013.4 expects an INVALID_VALUE error for TexImage3D with an invalid
193        // internal format. (dEQP-GLES3.functional.negative_api.texture.teximage3d)
194        if (!gl::IsValidInternalFormat(actualInternalFormat, context) ||
195            !gl::IsValidFormat(format, clientVersion) ||
196            !gl::IsValidType(type, clientVersion))
197        {
198            return gl::error(GL_INVALID_ENUM, false);
199        }
200
201        if (!gl::IsValidFormatCombination(actualInternalFormat, format, type, clientVersion))
202        {
203            return gl::error(GL_INVALID_OPERATION, false);
204        }
205
206        if (target == GL_TEXTURE_3D && (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL))
207        {
208            return gl::error(GL_INVALID_OPERATION, false);
209        }
210    }
211
212    // Validate sub image parameters
213    if (isSubImage)
214    {
215        if (isCompressed != textureCompressed)
216        {
217            return gl::error(GL_INVALID_OPERATION, false);
218        }
219
220        if (isCompressed)
221        {
222            if ((width % 4 != 0 && width != textureLevelWidth) ||
223                (height % 4 != 0 && height != textureLevelHeight))
224            {
225                return gl::error(GL_INVALID_OPERATION, false);
226            }
227        }
228
229        if (width == 0 || height == 0 || depth == 0)
230        {
231            return false;
232        }
233
234        if (xoffset < 0 || yoffset < 0 || zoffset < 0)
235        {
236            return gl::error(GL_INVALID_VALUE, false);
237        }
238
239        if (std::numeric_limits<GLsizei>::max() - xoffset < width ||
240            std::numeric_limits<GLsizei>::max() - yoffset < height ||
241            std::numeric_limits<GLsizei>::max() - zoffset < depth)
242        {
243            return gl::error(GL_INVALID_VALUE, false);
244        }
245
246        if (xoffset + width > textureLevelWidth ||
247            yoffset + height > textureLevelHeight ||
248            zoffset + depth > textureLevelDepth)
249        {
250            return gl::error(GL_INVALID_VALUE, false);
251        }
252    }
253
254    // Check for pixel unpack buffer related API errors
255    gl::Buffer *pixelUnpackBuffer = context->getPixelUnpackBuffer();
256    if (pixelUnpackBuffer != NULL)
257    {
258        // ...the data would be unpacked from the buffer object such that the memory reads required
259        // would exceed the data store size.
260        size_t widthSize = static_cast<size_t>(width);
261        size_t heightSize = static_cast<size_t>(height);
262        size_t depthSize = static_cast<size_t>(depth);
263        GLenum sizedFormat = gl::IsSizedInternalFormat(actualInternalFormat, clientVersion) ?
264                             actualInternalFormat :
265                             gl::GetSizedInternalFormat(actualInternalFormat, type, clientVersion);
266
267        size_t pixelBytes = static_cast<size_t>(gl::GetPixelBytes(sizedFormat, clientVersion));
268
269        if (!rx::IsUnsignedMultiplicationSafe(widthSize, heightSize) ||
270            !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize, depthSize) ||
271            !rx::IsUnsignedMultiplicationSafe(widthSize * heightSize * depthSize, pixelBytes))
272        {
273            // Overflow past the end of the buffer
274            return gl::error(GL_INVALID_OPERATION, false);
275        }
276
277        size_t copyBytes = widthSize * heightSize * depthSize * pixelBytes;
278        size_t offset = reinterpret_cast<size_t>(pixels);
279
280        if (!rx::IsUnsignedAdditionSafe(offset, copyBytes) ||
281            ((offset + copyBytes) > static_cast<size_t>(pixelUnpackBuffer->size())))
282        {
283            // Overflow past the end of the buffer
284            return gl::error(GL_INVALID_OPERATION, false);
285        }
286
287        // ...data is not evenly divisible into the number of bytes needed to store in memory a datum
288        // indicated by type.
289        size_t dataBytesPerPixel = static_cast<size_t>(gl::GetTypeBytes(type));
290
291        if ((offset % dataBytesPerPixel) != 0)
292        {
293            return gl::error(GL_INVALID_OPERATION, false);
294        }
295
296        // ...the buffer object's data store is currently mapped.
297        if (pixelUnpackBuffer->mapped())
298        {
299            return gl::error(GL_INVALID_OPERATION, false);
300        }
301    }
302
303    return true;
304}
305
306bool ValidateES3CopyTexImageParameters(gl::Context *context, GLenum target, GLint level, GLenum internalformat,
307                                       bool isSubImage, GLint xoffset, GLint yoffset, GLint zoffset,
308                                       GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
309{
310    GLenum textureInternalFormat;
311    if (!ValidateCopyTexImageParametersBase(context, target, level, internalformat, isSubImage,
312                                            xoffset, yoffset, zoffset, x, y, width, height,
313                                            border, &textureInternalFormat))
314    {
315        return false;
316    }
317
318    gl::Framebuffer *framebuffer = context->getReadFramebuffer();
319
320    if (framebuffer->completeness() != GL_FRAMEBUFFER_COMPLETE)
321    {
322        return gl::error(GL_INVALID_FRAMEBUFFER_OPERATION, false);
323    }
324
325    if (context->getReadFramebufferHandle() != 0 && framebuffer->getSamples() != 0)
326    {
327        return gl::error(GL_INVALID_OPERATION, false);
328    }
329
330    gl::FramebufferAttachment *source = framebuffer->getReadColorbuffer();
331    GLenum colorbufferInternalFormat = source->getInternalFormat();
332
333    if (isSubImage)
334    {
335        if (!gl::IsValidCopyTexImageCombination(textureInternalFormat, colorbufferInternalFormat,
336                                                context->getReadFramebufferHandle(),
337                                                context->getClientVersion()))
338        {
339            return gl::error(GL_INVALID_OPERATION, false);
340        }
341    }
342    else
343    {
344        if (!gl::IsValidCopyTexImageCombination(internalformat, colorbufferInternalFormat,
345                                                context->getReadFramebufferHandle(),
346                                                context->getClientVersion()))
347        {
348            return gl::error(GL_INVALID_OPERATION, false);
349        }
350    }
351
352    // If width or height is zero, it is a no-op.  Return false without setting an error.
353    return (width > 0 && height > 0);
354}
355
356bool ValidateES3TexStorageParameters(gl::Context *context, GLenum target, GLsizei levels, GLenum internalformat,
357                                     GLsizei width, GLsizei height, GLsizei depth)
358{
359    if (width < 1 || height < 1 || depth < 1 || levels < 1)
360    {
361        return gl::error(GL_INVALID_VALUE, false);
362    }
363
364    if (levels > gl::log2(std::max(std::max(width, height), depth)) + 1)
365    {
366        return gl::error(GL_INVALID_OPERATION, false);
367    }
368
369    gl::Texture *texture = NULL;
370    switch (target)
371    {
372      case GL_TEXTURE_2D:
373        {
374            texture = context->getTexture2D();
375
376            if (width > (context->getMaximum2DTextureDimension()) ||
377                height > (context->getMaximum2DTextureDimension()))
378            {
379                return gl::error(GL_INVALID_VALUE, false);
380            }
381        }
382        break;
383
384      case GL_TEXTURE_CUBE_MAP:
385        {
386            texture = context->getTextureCubeMap();
387
388            if (width != height)
389            {
390                return gl::error(GL_INVALID_VALUE, false);
391            }
392
393            if (width > (context->getMaximumCubeTextureDimension()))
394            {
395                return gl::error(GL_INVALID_VALUE, false);
396            }
397        }
398        break;
399
400      case GL_TEXTURE_3D:
401        {
402            texture = context->getTexture3D();
403
404            if (width > (context->getMaximum3DTextureDimension()) ||
405                height > (context->getMaximum3DTextureDimension()) ||
406                depth > (context->getMaximum3DTextureDimension()))
407            {
408                return gl::error(GL_INVALID_VALUE, false);
409            }
410        }
411        break;
412
413      case GL_TEXTURE_2D_ARRAY:
414        {
415            texture = context->getTexture2DArray();
416
417            if (width > (context->getMaximum2DTextureDimension()) ||
418                height > (context->getMaximum2DTextureDimension()) ||
419                depth > (context->getMaximum2DArrayTextureLayers()))
420            {
421                return gl::error(GL_INVALID_VALUE, false);
422            }
423        }
424        break;
425
426      default:
427        return gl::error(GL_INVALID_ENUM, false);
428    }
429
430    if (!texture || texture->id() == 0)
431    {
432        return gl::error(GL_INVALID_OPERATION, false);
433    }
434
435    if (texture->isImmutable())
436    {
437        return gl::error(GL_INVALID_OPERATION, false);
438    }
439
440    if (!gl::IsValidInternalFormat(internalformat, context))
441    {
442        return gl::error(GL_INVALID_ENUM, false);
443    }
444
445    if (!gl::IsSizedInternalFormat(internalformat, context->getClientVersion()))
446    {
447        return gl::error(GL_INVALID_ENUM, false);
448    }
449
450    return true;
451}
452
453bool ValidateES3FramebufferTextureParameters(gl::Context *context, GLenum target, GLenum attachment,
454                                             GLenum textarget, GLuint texture, GLint level, GLint layer,
455                                             bool layerCall)
456{
457    if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER)
458    {
459        return gl::error(GL_INVALID_ENUM, false);
460    }
461
462    if (attachment >= GL_COLOR_ATTACHMENT0 && attachment <= GL_COLOR_ATTACHMENT15)
463    {
464        const unsigned int colorAttachment = (attachment - GL_COLOR_ATTACHMENT0);
465        if (colorAttachment >= context->getMaximumRenderTargets())
466        {
467            return gl::error(GL_INVALID_VALUE, false);
468        }
469    }
470    else
471    {
472        switch (attachment)
473        {
474          case GL_DEPTH_ATTACHMENT:
475          case GL_STENCIL_ATTACHMENT:
476          case GL_DEPTH_STENCIL_ATTACHMENT:
477            break;
478          default:
479            return gl::error(GL_INVALID_ENUM, false);
480        }
481    }
482
483    if (texture != 0)
484    {
485        gl::Texture *tex = context->getTexture(texture);
486
487        if (tex == NULL)
488        {
489            return gl::error(GL_INVALID_OPERATION, false);
490        }
491
492        if (level < 0)
493        {
494            return gl::error(GL_INVALID_VALUE, false);
495        }
496
497        if (layer < 0)
498        {
499            return gl::error(GL_INVALID_VALUE, false);
500        }
501
502        if (!layerCall)
503        {
504            switch (textarget)
505            {
506              case GL_TEXTURE_2D:
507                {
508                    if (level > gl::log2(context->getMaximum2DTextureDimension()))
509                    {
510                        return gl::error(GL_INVALID_VALUE, false);
511                    }
512                    if (tex->getTarget() != GL_TEXTURE_2D)
513                    {
514                        return gl::error(GL_INVALID_OPERATION, false);
515                    }
516                    gl::Texture2D *tex2d = static_cast<gl::Texture2D *>(tex);
517                    if (tex2d->isCompressed(level))
518                    {
519                        return gl::error(GL_INVALID_OPERATION, false);
520                    }
521                    break;
522                }
523
524              case GL_TEXTURE_CUBE_MAP_POSITIVE_X:
525              case GL_TEXTURE_CUBE_MAP_NEGATIVE_X:
526              case GL_TEXTURE_CUBE_MAP_POSITIVE_Y:
527              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y:
528              case GL_TEXTURE_CUBE_MAP_POSITIVE_Z:
529              case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z:
530                {
531                    if (level > gl::log2(context->getMaximumCubeTextureDimension()))
532                    {
533                        return gl::error(GL_INVALID_VALUE, false);
534                    }
535                    if (tex->getTarget() != GL_TEXTURE_CUBE_MAP)
536                    {
537                        return gl::error(GL_INVALID_OPERATION, false);
538                    }
539                    gl::TextureCubeMap *texcube = static_cast<gl::TextureCubeMap *>(tex);
540                    if (texcube->isCompressed(textarget, level))
541                    {
542                        return gl::error(GL_INVALID_OPERATION, false);
543                    }
544                    break;
545                }
546
547              default:
548                return gl::error(GL_INVALID_ENUM, false);
549            }
550        }
551        else
552        {
553            switch (tex->getTarget())
554            {
555              case GL_TEXTURE_2D_ARRAY:
556                {
557                    if (level > gl::log2(context->getMaximum2DTextureDimension()))
558                    {
559                        return gl::error(GL_INVALID_VALUE, false);
560                    }
561
562                    if (layer >= context->getMaximum2DArrayTextureLayers())
563                    {
564                        return gl::error(GL_INVALID_VALUE, false);
565                    }
566
567                    gl::Texture2DArray *texArray = static_cast<gl::Texture2DArray *>(tex);
568                    if (texArray->isCompressed(level))
569                    {
570                        return gl::error(GL_INVALID_OPERATION, false);
571                    }
572
573                    break;
574                }
575
576              case GL_TEXTURE_3D:
577                {
578                    if (level > gl::log2(context->getMaximum3DTextureDimension()))
579                    {
580                        return gl::error(GL_INVALID_VALUE, false);
581                    }
582
583                    if (layer >= context->getMaximum3DTextureDimension())
584                    {
585                        return gl::error(GL_INVALID_VALUE, false);
586                    }
587
588                    gl::Texture3D *tex3d = static_cast<gl::Texture3D *>(tex);
589                    if (tex3d->isCompressed(level))
590                    {
591                        return gl::error(GL_INVALID_OPERATION, false);
592                    }
593
594                    break;
595                }
596
597              default:
598                return gl::error(GL_INVALID_OPERATION, false);
599            }
600        }
601    }
602
603    gl::Framebuffer *framebuffer = NULL;
604    GLuint framebufferHandle = 0;
605    if (target == GL_READ_FRAMEBUFFER)
606    {
607        framebuffer = context->getReadFramebuffer();
608        framebufferHandle = context->getReadFramebufferHandle();
609    }
610    else
611    {
612        framebuffer = context->getDrawFramebuffer();
613        framebufferHandle = context->getDrawFramebufferHandle();
614    }
615
616    if (framebufferHandle == 0 || !framebuffer)
617    {
618        return gl::error(GL_INVALID_OPERATION, false);
619    }
620
621    return true;
622}
623
624bool ValidES3ReadFormatType(gl::Context *context, GLenum internalFormat, GLenum format, GLenum type)
625{
626    switch (format)
627    {
628      case GL_RGBA:
629        switch (type)
630        {
631          case GL_UNSIGNED_BYTE:
632            break;
633          case GL_UNSIGNED_INT_2_10_10_10_REV:
634            if (internalFormat != GL_RGB10_A2)
635            {
636                return false;
637            }
638            break;
639          case GL_FLOAT:
640            if (gl::GetComponentType(internalFormat, 3) != GL_FLOAT)
641            {
642                return false;
643            }
644            break;
645          default:
646            return false;
647        }
648        break;
649      case GL_RGBA_INTEGER:
650        switch (type)
651        {
652          case GL_INT:
653            if (gl::GetComponentType(internalFormat, 3) != GL_INT)
654            {
655                return false;
656            }
657            break;
658          case GL_UNSIGNED_INT:
659            if (gl::GetComponentType(internalFormat, 3) != GL_UNSIGNED_INT)
660            {
661                return false;
662            }
663            break;
664          default:
665            return false;
666        }
667        break;
668      case GL_BGRA_EXT:
669        switch (type)
670        {
671          case GL_UNSIGNED_BYTE:
672          case GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT:
673          case GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT:
674            break;
675          default:
676            return false;
677        }
678        break;
679      case GL_RG_EXT:
680      case GL_RED_EXT:
681        if (!context->supportsRGTextures())
682        {
683            return false;
684        }
685        switch (type)
686        {
687        case GL_UNSIGNED_BYTE:
688            break;
689        default:
690            return false;
691        }
692        break;
693      default:
694        return false;
695    }
696    return true;
697}
698
699bool ValidateInvalidateFramebufferParameters(gl::Context *context, GLenum target, GLsizei numAttachments,
700                                             const GLenum* attachments)
701{
702    bool defaultFramebuffer = false;
703
704    switch (target)
705    {
706      case GL_DRAW_FRAMEBUFFER:
707      case GL_FRAMEBUFFER:
708        defaultFramebuffer = context->getDrawFramebufferHandle() == 0;
709        break;
710      case GL_READ_FRAMEBUFFER:
711        defaultFramebuffer = context->getReadFramebufferHandle() == 0;
712        break;
713      default:
714        return gl::error(GL_INVALID_ENUM, false);
715    }
716
717    for (int i = 0; i < numAttachments; ++i)
718    {
719        if (attachments[i] >= GL_COLOR_ATTACHMENT0 && attachments[i] <= GL_COLOR_ATTACHMENT15)
720        {
721            if (defaultFramebuffer)
722            {
723                return gl::error(GL_INVALID_ENUM, false);
724            }
725
726            if (attachments[i] >= GL_COLOR_ATTACHMENT0 + context->getMaximumRenderTargets())
727            {
728                return gl::error(GL_INVALID_OPERATION, false);
729            }
730        }
731        else
732        {
733            switch (attachments[i])
734            {
735              case GL_DEPTH_ATTACHMENT:
736              case GL_STENCIL_ATTACHMENT:
737              case GL_DEPTH_STENCIL_ATTACHMENT:
738                if (defaultFramebuffer)
739                {
740                    return gl::error(GL_INVALID_ENUM, false);
741                }
742                break;
743              case GL_COLOR:
744              case GL_DEPTH:
745              case GL_STENCIL:
746                if (!defaultFramebuffer)
747                {
748                    return gl::error(GL_INVALID_ENUM, false);
749                }
750                break;
751              default:
752                return gl::error(GL_INVALID_ENUM, false);
753            }
754        }
755    }
756
757    return true;
758}
759
760}
761