1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2011  VMware, Inc.  All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included
14 * in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
20 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24
25/**
26 * \file texstorage.c
27 * GL_ARB_texture_storage functions
28 */
29
30
31
32#include "glheader.h"
33#include "context.h"
34#include "enums.h"
35#include "imports.h"
36#include "macros.h"
37#include "mfeatures.h"
38#include "teximage.h"
39#include "texobj.h"
40#include "texstorage.h"
41#include "mtypes.h"
42
43
44
45/**
46 * Check if the given texture target is a legal texture object target
47 * for a glTexStorage() command.
48 * This is a bit different than legal_teximage_target() when it comes
49 * to cube maps.
50 */
51static GLboolean
52legal_texobj_target(struct gl_context *ctx, GLuint dims, GLenum target)
53{
54   switch (dims) {
55   case 1:
56      switch (target) {
57      case GL_TEXTURE_1D:
58      case GL_PROXY_TEXTURE_1D:
59         return GL_TRUE;
60      default:
61         return GL_FALSE;
62      }
63   case 2:
64      switch (target) {
65      case GL_TEXTURE_2D:
66      case GL_PROXY_TEXTURE_2D:
67         return GL_TRUE;
68      case GL_TEXTURE_CUBE_MAP:
69      case GL_PROXY_TEXTURE_CUBE_MAP:
70         return ctx->Extensions.ARB_texture_cube_map;
71      case GL_TEXTURE_RECTANGLE:
72      case GL_PROXY_TEXTURE_RECTANGLE:
73         return ctx->Extensions.NV_texture_rectangle;
74      case GL_TEXTURE_1D_ARRAY:
75      case GL_PROXY_TEXTURE_1D_ARRAY:
76         return (ctx->Extensions.MESA_texture_array ||
77                 ctx->Extensions.EXT_texture_array);
78      default:
79         return GL_FALSE;
80      }
81   case 3:
82      switch (target) {
83      case GL_TEXTURE_3D:
84      case GL_PROXY_TEXTURE_3D:
85         return GL_TRUE;
86      case GL_TEXTURE_2D_ARRAY:
87      case GL_PROXY_TEXTURE_2D_ARRAY:
88         return (ctx->Extensions.MESA_texture_array ||
89                 ctx->Extensions.EXT_texture_array);
90      default:
91         return GL_FALSE;
92      }
93   default:
94      _mesa_problem(ctx, "invalid dims=%u in legal_texobj_target()", dims);
95      return GL_FALSE;
96   }
97}
98
99
100/**
101 * Compute the size of the next mipmap level.
102 */
103static void
104next_mipmap_level_size(GLenum target,
105                       GLint *width, GLint *height, GLint *depth)
106{
107   if (*width > 1) {
108      *width /= 2;
109   }
110
111   if ((*height > 1) && (target != GL_TEXTURE_1D_ARRAY)) {
112      *height /= 2;
113   }
114
115   if ((*depth > 1) && (target != GL_TEXTURE_2D_ARRAY)) {
116      *depth /= 2;
117   }
118}
119
120
121/**
122 * Do actual memory allocation for glTexStorage1/2/3D().
123 */
124static void
125setup_texstorage(struct gl_context *ctx,
126                 struct gl_texture_object *texObj,
127                 GLuint dims,
128                 GLsizei levels, GLenum internalFormat,
129                 GLsizei width, GLsizei height, GLsizei depth)
130{
131   const GLenum target = texObj->Target;
132   const GLuint numFaces = _mesa_num_tex_faces(target);
133   gl_format texFormat;
134   GLint level, levelWidth = width, levelHeight = height, levelDepth = depth;
135   GLuint face;
136
137   assert(levels > 0);
138   assert(width > 0);
139   assert(height > 0);
140   assert(depth > 0);
141
142   texFormat = _mesa_choose_texture_format(ctx, texObj, target, 0,
143                                           internalFormat, GL_NONE, GL_NONE);
144
145   /* Set up all the texture object's gl_texture_images */
146   for (level = 0; level < levels; level++) {
147      for (face = 0; face < numFaces; face++) {
148         const GLenum faceTarget =
149            (target == GL_TEXTURE_CUBE_MAP)
150            ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + face : target;
151         struct gl_texture_image *texImage =
152            _mesa_get_tex_image(ctx, texObj, faceTarget, level);
153
154	 if (!texImage) {
155	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexImage%uD", dims);
156            return;
157	 }
158
159         _mesa_init_teximage_fields(ctx, texImage,
160                                    levelWidth, levelHeight, levelDepth,
161                                    0, internalFormat, texFormat);
162      }
163
164      next_mipmap_level_size(target, &levelWidth, &levelHeight, &levelDepth);
165   }
166
167   assert(levelWidth > 0);
168   assert(levelHeight > 0);
169   assert(levelDepth > 0);
170
171   if (!_mesa_is_proxy_texture(texObj->Target)) {
172      /* Do actual texture memory allocation */
173      if (!ctx->Driver.AllocTextureStorage(ctx, texObj, levels,
174                                           width, height, depth)) {
175         /* Reset the texture images' info to zeros.
176          * Strictly speaking, we probably don't have to do this since
177          * generating GL_OUT_OF_MEMORY can leave things in an undefined
178          * state but this puts things in a consistent state.
179          */
180         for (level = 0; level < levels; level++) {
181            for (face = 0; face < numFaces; face++) {
182               struct gl_texture_image *texImage = texObj->Image[face][level];
183               if (texImage) {
184                  _mesa_init_teximage_fields(ctx, texImage,
185                                             0, 0, 0, 0,
186                                             GL_NONE, MESA_FORMAT_NONE);
187               }
188            }
189         }
190
191         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexStorage%uD", dims);
192
193         return;
194      }
195
196      /* Only set this field for non-proxy texture objects */
197      texObj->Immutable = GL_TRUE;
198   }
199}
200
201
202/**
203 * Clear all fields of texture object to zeros.  Used for proxy texture tests.
204 */
205static void
206clear_image_fields(struct gl_context *ctx,
207                   GLuint dims,
208                   struct gl_texture_object *texObj)
209{
210   const GLenum target = texObj->Target;
211   const GLuint numFaces = _mesa_num_tex_faces(target);
212   GLint level;
213   GLuint face;
214
215   for (level = 0; level < Elements(texObj->Image[0]); level++) {
216      for (face = 0; face < numFaces; face++) {
217         const GLenum faceTarget =
218            (target == GL_TEXTURE_CUBE_MAP)
219            ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + face : target;
220         struct gl_texture_image *texImage =
221            _mesa_get_tex_image(ctx, texObj, faceTarget, level);
222
223	 if (!texImage) {
224	    _mesa_error(ctx, GL_OUT_OF_MEMORY, "glTexStorage%uD", dims);
225            return;
226	 }
227
228         _mesa_init_teximage_fields(ctx, texImage,
229                                    0, 0, 0, 0, GL_NONE, MESA_FORMAT_NONE);
230      }
231   }
232}
233
234
235/**
236 * Do error checking for calls to glTexStorage1/2/3D().
237 * If an error is found, record it with _mesa_error(), unless the target
238 * is a proxy texture.
239 * \return GL_TRUE if any error, GL_FALSE otherwise.
240 */
241static GLboolean
242tex_storage_error_check(struct gl_context *ctx, GLuint dims, GLenum target,
243                        GLsizei levels, GLenum internalformat,
244                        GLsizei width, GLsizei height, GLsizei depth)
245{
246   struct gl_texture_object *texObj;
247   GLboolean legalFormat;
248
249   /* check internal format - note that only sized formats are allowed */
250   switch (internalformat) {
251   case GL_ALPHA:
252   case GL_LUMINANCE:
253   case GL_LUMINANCE_ALPHA:
254   case GL_INTENSITY:
255   case GL_RED:
256   case GL_RG:
257   case GL_RGB:
258   case GL_RGBA:
259   case GL_BGRA:
260   case GL_DEPTH_COMPONENT:
261   case GL_DEPTH_STENCIL:
262   case GL_COMPRESSED_ALPHA:
263   case GL_COMPRESSED_LUMINANCE_ALPHA:
264   case GL_COMPRESSED_LUMINANCE:
265   case GL_COMPRESSED_INTENSITY:
266   case GL_COMPRESSED_RGB:
267   case GL_COMPRESSED_RGBA:
268   case GL_COMPRESSED_SRGB:
269   case GL_COMPRESSED_SRGB_ALPHA:
270   case GL_COMPRESSED_SLUMINANCE:
271   case GL_COMPRESSED_SLUMINANCE_ALPHA:
272   case GL_RED_INTEGER:
273   case GL_GREEN_INTEGER:
274   case GL_BLUE_INTEGER:
275   case GL_ALPHA_INTEGER:
276   case GL_RGB_INTEGER:
277   case GL_RGBA_INTEGER:
278   case GL_BGR_INTEGER:
279   case GL_BGRA_INTEGER:
280   case GL_LUMINANCE_INTEGER_EXT:
281   case GL_LUMINANCE_ALPHA_INTEGER_EXT:
282      /* these unsized formats are illegal */
283      legalFormat = GL_FALSE;
284      break;
285   default:
286      legalFormat = _mesa_base_tex_format(ctx, internalformat) > 0;
287   }
288
289   if (!legalFormat) {
290      _mesa_error(ctx, GL_INVALID_ENUM,
291                  "glTexStorage%uD(internalformat = %s)", dims,
292                  _mesa_lookup_enum_by_nr(internalformat));
293      return GL_TRUE;
294   }
295
296   /* size check */
297   if (width < 1 || height < 1 || depth < 1) {
298      _mesa_error(ctx, GL_INVALID_VALUE,
299                  "glTexStorage%uD(width, height or depth < 1)", dims);
300      return GL_TRUE;
301   }
302
303   /* levels check */
304   if (levels < 1 || height < 1 || depth < 1) {
305      _mesa_error(ctx, GL_INVALID_VALUE, "glTexStorage%uD(levels < 1)",
306                  dims);
307      return GL_TRUE;
308   }
309
310   /* target check */
311   if (!legal_texobj_target(ctx, dims, target)) {
312      _mesa_error(ctx, GL_INVALID_ENUM,
313                  "glTexStorage%uD(illegal target=%s)",
314                  dims, _mesa_lookup_enum_by_nr(target));
315      return GL_TRUE;
316   }
317
318   /* check levels against maximum */
319   if (levels > _mesa_max_texture_levels(ctx, target)) {
320      _mesa_error(ctx, GL_INVALID_OPERATION,
321                  "glTexStorage%uD(levels too large)", dims);
322      return GL_TRUE;
323   }
324
325   /* check levels against width/height/depth */
326   if (levels > _mesa_get_tex_max_num_levels(target, width, height, depth)) {
327      _mesa_error(ctx, GL_INVALID_OPERATION,
328                  "glTexStorage%uD(too many levels for max texture dimension)",
329                  dims);
330      return GL_TRUE;
331   }
332
333   /* non-default texture object check */
334   texObj = _mesa_get_current_tex_object(ctx, target);
335   if (!texObj || (texObj->Name == 0)) {
336      _mesa_error(ctx, GL_INVALID_OPERATION,
337                  "glTexStorage%uD(texture object 0)", dims);
338      return GL_TRUE;
339   }
340
341   /* Check if texObj->Immutable is set */
342   if (texObj->Immutable) {
343      _mesa_error(ctx, GL_INVALID_OPERATION, "glTexStorage%uD(immutable)",
344                  dims);
345      return GL_TRUE;
346   }
347
348   return GL_FALSE;
349}
350
351
352/**
353 * Helper used by _mesa_TexStorage1/2/3D().
354 */
355static void
356texstorage(GLuint dims, GLenum target, GLsizei levels, GLenum internalformat,
357           GLsizei width, GLsizei height, GLsizei depth)
358{
359   struct gl_texture_object *texObj;
360   GLboolean sizeOK;
361   GLenum proxyTarget = _mesa_get_proxy_target(target);
362
363   GET_CURRENT_CONTEXT(ctx);
364
365   texObj = _mesa_get_current_tex_object(ctx, target);
366
367   if (tex_storage_error_check(ctx, dims, target, levels,
368                               internalformat, width, height, depth)) {
369      return; /* error was recorded */
370   }
371
372   sizeOK = ctx->Driver.TestProxyTexImage(ctx, proxyTarget, 0,
373                                          internalformat, GL_NONE, GL_NONE,
374                                          width, height, depth, 0);
375
376   if (!sizeOK) {
377      if (_mesa_is_proxy_texture(texObj->Target)) {
378         /* clear all image fields for [levels] */
379         clear_image_fields(ctx, dims, texObj);
380      }
381      else {
382         _mesa_error(ctx, GL_INVALID_VALUE,
383                     "glTexStorage%uD(invalid width, height or depth)",
384                     dims);
385         return;
386      }
387   }
388   else {
389      setup_texstorage(ctx, texObj, dims, levels, internalformat,
390                       width, height, depth);
391   }
392}
393
394
395void GLAPIENTRY
396_mesa_TexStorage1D(GLenum target, GLsizei levels, GLenum internalformat,
397                   GLsizei width)
398{
399   texstorage(1, target, levels, internalformat, width, 1, 1);
400}
401
402
403void GLAPIENTRY
404_mesa_TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat,
405                   GLsizei width, GLsizei height)
406{
407   texstorage(2, target, levels, internalformat, width, height, 1);
408}
409
410
411void GLAPIENTRY
412_mesa_TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat,
413                   GLsizei width, GLsizei height, GLsizei depth)
414{
415   texstorage(3, target, levels, internalformat, width, height, depth);
416}
417
418
419
420/*
421 * Note: we don't support GL_EXT_direct_state_access and the spec says
422 * we don't need the following functions.  However, glew checks for the
423 * presence of all six functions and will say that GL_ARB_texture_storage
424 * is not supported if these functions are missing.
425 */
426
427
428void GLAPIENTRY
429_mesa_TextureStorage1DEXT(GLuint texture, GLenum target, GLsizei levels,
430                          GLenum internalformat,
431                          GLsizei width)
432{
433   /* no-op */
434}
435
436
437void GLAPIENTRY
438_mesa_TextureStorage2DEXT(GLuint texture, GLenum target, GLsizei levels,
439                          GLenum internalformat,
440                          GLsizei width, GLsizei height)
441{
442   /* no-op */
443}
444
445
446
447void GLAPIENTRY
448_mesa_TextureStorage3DEXT(GLuint texture, GLenum target, GLsizei levels,
449                          GLenum internalformat,
450                          GLsizei width, GLsizei height, GLsizei depth)
451{
452   /* no-op */
453}
454