1/*
2 * Mesa 3-D graphics library
3 *
4 * Copyright (C) 2009  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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 * OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25/**
26 * Meta operations.  Some GL operations can be expressed in terms of
27 * other GL operations.  For example, glBlitFramebuffer() can be done
28 * with texture mapping and glClear() can be done with polygon rendering.
29 *
30 * \author Brian Paul
31 */
32
33#include "main/arrayobj.h"
34#include "main/blend.h"
35#include "main/buffers.h"
36#include "main/enums.h"
37#include "main/enable.h"
38#include "main/fbobject.h"
39#include "main/framebuffer.h"
40#include "main/macros.h"
41#include "main/mipmap.h"
42#include "main/teximage.h"
43#include "main/texobj.h"
44#include "main/texparam.h"
45#include "main/varray.h"
46#include "main/viewport.h"
47#include "drivers/common/meta.h"
48#include "program/prog_instruction.h"
49
50
51/**
52 * Check if the call to _mesa_meta_GenerateMipmap() will require a
53 * software fallback.  The fallback path will require that the texture
54 * images are mapped.
55 * \return GL_TRUE if a fallback is needed, GL_FALSE otherwise
56 */
57static bool
58fallback_required(struct gl_context *ctx, GLenum target,
59                  struct gl_texture_object *texObj)
60{
61   struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
62   struct gl_texture_image *baseImage;
63   GLuint srcLevel;
64   GLenum status;
65
66   /* check for fallbacks */
67   if (target == GL_TEXTURE_3D) {
68      _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
69                       "glGenerateMipmap() to %s target\n",
70                       _mesa_enum_to_string(target));
71      return true;
72   }
73
74   srcLevel = texObj->BaseLevel;
75   baseImage = _mesa_select_tex_image(texObj, target, srcLevel);
76   if (!baseImage) {
77      _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
78                       "glGenerateMipmap() couldn't find base teximage\n");
79      return true;
80   }
81
82   if (_mesa_is_format_compressed(baseImage->TexFormat)) {
83      _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
84                       "glGenerateMipmap() with %s format\n",
85                       _mesa_get_format_name(baseImage->TexFormat));
86      return true;
87   }
88
89   if (_mesa_get_format_color_encoding(baseImage->TexFormat) == GL_SRGB &&
90       !ctx->Extensions.EXT_texture_sRGB_decode) {
91      /* The texture format is sRGB but we can't turn off sRGB->linear
92       * texture sample conversion.  So we won't be able to generate the
93       * right colors when rendering.  Need to use a fallback.
94       */
95      _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
96                       "glGenerateMipmap() of sRGB texture without "
97                       "sRGB decode\n");
98      return true;
99   }
100
101   /*
102    * Test that we can actually render in the texture's format.
103    */
104   if (mipmap->fb == NULL) {
105      mipmap->fb = ctx->Driver.NewFramebuffer(ctx, 0xDEADBEEF);
106      if (mipmap->fb == NULL) {
107         _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
108                          "glGenerateMipmap() ran out of memory\n");
109         return true;
110      }
111   }
112
113   _mesa_meta_framebuffer_texture_image(ctx, mipmap->fb,
114                                        GL_COLOR_ATTACHMENT0, baseImage, 0);
115
116   status = _mesa_check_framebuffer_status(ctx, mipmap->fb);
117   if (status != GL_FRAMEBUFFER_COMPLETE_EXT) {
118      _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH,
119                       "glGenerateMipmap() got incomplete FBO\n");
120      return true;
121   }
122
123   return false;
124}
125
126void
127_mesa_meta_glsl_generate_mipmap_cleanup(struct gl_context *ctx,
128                                        struct gen_mipmap_state *mipmap)
129{
130   if (mipmap->VAO == 0)
131      return;
132   _mesa_DeleteVertexArrays(1, &mipmap->VAO);
133   mipmap->VAO = 0;
134   _mesa_reference_buffer_object(ctx, &mipmap->buf_obj, NULL);
135   _mesa_reference_sampler_object(ctx, &mipmap->samp_obj, NULL);
136   _mesa_reference_framebuffer(&mipmap->fb, NULL);
137
138   _mesa_meta_blit_shader_table_cleanup(ctx, &mipmap->shaders);
139}
140
141
142/**
143 * Called via ctx->Driver.GenerateMipmap()
144 * Note: We don't yet support 3D textures, or texture borders.
145 */
146void
147_mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target,
148                          struct gl_texture_object *texObj)
149{
150   struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap;
151   struct vertex verts[4];
152   const GLuint baseLevel = texObj->BaseLevel;
153   const GLuint maxLevel = texObj->MaxLevel;
154   const GLint maxLevelSave = texObj->MaxLevel;
155   const GLboolean genMipmapSave = texObj->GenerateMipmap;
156   const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader &&
157                                      ctx->Extensions.ARB_fragment_shader;
158   GLenum faceTarget;
159   GLuint dstLevel;
160   struct gl_sampler_object *samp_obj_save = NULL;
161   GLint swizzle[4];
162   GLboolean swizzleSaved = GL_FALSE;
163
164   /* GLint so the compiler won't complain about type signedness mismatch in
165    * the calls to _mesa_texture_parameteriv below.
166    */
167   static const GLint always_false = GL_FALSE;
168   static const GLint always_true = GL_TRUE;
169
170   if (fallback_required(ctx, target, texObj)) {
171      _mesa_generate_mipmap(ctx, target, texObj);
172      return;
173   }
174
175   if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
176       target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
177      faceTarget = target;
178      target = GL_TEXTURE_CUBE_MAP;
179   } else {
180      faceTarget = target;
181   }
182
183   _mesa_meta_begin(ctx, MESA_META_ALL & ~MESA_META_DRAW_BUFFERS);
184   _mesa_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
185   _mesa_Disable(GL_DITHER);
186
187   /* Choose between glsl version and fixed function version of
188    * GenerateMipmap function.
189    */
190   if (use_glsl_version) {
191      _mesa_meta_setup_vertex_objects(ctx, &mipmap->VAO, &mipmap->buf_obj, true,
192                                      2, 4, 0);
193      _mesa_meta_setup_blit_shader(ctx, target, false, &mipmap->shaders);
194   } else {
195      _mesa_meta_setup_ff_tnl_for_blit(ctx, &mipmap->VAO, &mipmap->buf_obj, 3);
196      _mesa_set_enable(ctx, target, GL_TRUE);
197   }
198
199   _mesa_reference_sampler_object(ctx, &samp_obj_save,
200                                  ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler);
201
202   /* We may have been called from glGenerateTextureMipmap with CurrentUnit
203    * still set to 0, so we don't know when we can skip binding the texture.
204    * Assume that _mesa_bind_texture will be fast if we're rebinding the same
205    * texture.
206    */
207   _mesa_bind_texture(ctx, target, texObj);
208
209   if (mipmap->samp_obj == NULL) {
210      mipmap->samp_obj =  ctx->Driver.NewSamplerObject(ctx, 0xDEADBEEF);
211      if (mipmap->samp_obj == NULL) {
212         /* This is a bit lazy.  Flag out of memory, and then don't bother to
213          * clean up.  Once out of memory is flagged, the only realistic next
214          * move is to destroy the context.  That will trigger all the right
215          * clean up.
216          */
217         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenerateMipmap");
218         return;
219      }
220
221      _mesa_set_sampler_filters(ctx, mipmap->samp_obj, GL_LINEAR_MIPMAP_LINEAR,
222                                GL_LINEAR);
223      _mesa_set_sampler_wrap(ctx, mipmap->samp_obj, GL_CLAMP_TO_EDGE,
224                             GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
225   }
226
227   if (ctx->Extensions.EXT_texture_sRGB_decode) {
228      const struct gl_texture_image *baseImage =
229         _mesa_select_tex_image(texObj, target, texObj->BaseLevel);
230      const bool srgb =
231         _mesa_get_format_color_encoding(baseImage->TexFormat) == GL_SRGB;
232
233      _mesa_set_sampler_srgb_decode(ctx, mipmap->samp_obj,
234                                    srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT);
235      _mesa_set_framebuffer_srgb(ctx, srgb);
236   }
237
238   _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, mipmap->samp_obj);
239
240   assert(mipmap->fb != NULL);
241   _mesa_bind_framebuffers(ctx, mipmap->fb, mipmap->fb);
242
243   _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_false, false);
244
245   if (texObj->_Swizzle != SWIZZLE_NOOP) {
246      static const GLint swizzleNoop[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
247      memcpy(swizzle, texObj->Swizzle, sizeof(swizzle));
248      swizzleSaved = GL_TRUE;
249      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA,
250                                swizzleNoop, false);
251   }
252
253   /* Silence valgrind warnings about reading uninitialized stack. */
254   memset(verts, 0, sizeof(verts));
255
256   /* setup vertex positions */
257   verts[0].x = -1.0F;
258   verts[0].y = -1.0F;
259   verts[1].x =  1.0F;
260   verts[1].y = -1.0F;
261   verts[2].x =  1.0F;
262   verts[2].y =  1.0F;
263   verts[3].x = -1.0F;
264   verts[3].y =  1.0F;
265
266   /* texture is already locked, unlock now */
267   _mesa_unlock_texture(ctx, texObj);
268
269   _mesa_prepare_mipmap_levels(ctx, texObj, baseLevel, maxLevel);
270
271   for (dstLevel = baseLevel + 1; dstLevel <= maxLevel; dstLevel++) {
272      const struct gl_texture_image *srcImage;
273      struct gl_texture_image *dstImage;
274      const GLuint srcLevel = dstLevel - 1;
275      GLuint layer;
276      GLsizei srcWidth, srcHeight, srcDepth;
277      GLsizei dstWidth, dstHeight, dstDepth;
278
279      srcImage = _mesa_select_tex_image(texObj, faceTarget, srcLevel);
280      assert(srcImage->Border == 0);
281
282      /* src size */
283      srcWidth = srcImage->Width;
284      if (target == GL_TEXTURE_1D_ARRAY) {
285         srcHeight = 1;
286         srcDepth = srcImage->Height;
287      } else {
288         srcHeight = srcImage->Height;
289         srcDepth = srcImage->Depth;
290      }
291
292      /* new dst size */
293      dstWidth = minify(srcWidth, 1);
294      dstHeight = minify(srcHeight, 1);
295      dstDepth = target == GL_TEXTURE_3D ? minify(srcDepth, 1) : srcDepth;
296
297      if (dstWidth == srcWidth &&
298          dstHeight == srcHeight &&
299          dstDepth == srcDepth) {
300         /* all done */
301         break;
302      }
303
304      /* Allocate storage for the destination mipmap image(s) */
305
306      /* Set MaxLevel large enough to hold the new level when we allocate it */
307      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL,
308                                (GLint *) &dstLevel, false);
309
310      dstImage = _mesa_select_tex_image(texObj, faceTarget, dstLevel);
311
312      /* All done.  We either ran out of memory or we would go beyond the last
313       * valid level of an immutable texture if we continued.
314       */
315      if (dstImage == NULL)
316         break;
317
318      /* limit minification to src level */
319      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL,
320                                (GLint *) &srcLevel, false);
321
322      /* setup viewport */
323      _mesa_set_viewport(ctx, 0, 0, 0, dstWidth, dstHeight);
324      _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0);
325
326      for (layer = 0; layer < dstDepth; ++layer) {
327         /* Setup texture coordinates */
328         _mesa_meta_setup_texture_coords(faceTarget,
329                                         layer,
330                                         0, 0, /* xoffset, yoffset */
331                                         srcWidth, srcHeight, /* img size */
332                                         srcWidth, srcHeight, srcDepth,
333                                         verts[0].tex,
334                                         verts[1].tex,
335                                         verts[2].tex,
336                                         verts[3].tex);
337
338         /* upload vertex data */
339         _mesa_buffer_data(ctx, mipmap->buf_obj, GL_NONE, sizeof(verts), verts,
340                           GL_DYNAMIC_DRAW, __func__);
341
342         _mesa_meta_framebuffer_texture_image(ctx, ctx->DrawBuffer,
343                                              GL_COLOR_ATTACHMENT0, dstImage,
344                                              layer);
345
346         /* sanity check */
347         if (_mesa_check_framebuffer_status(ctx, ctx->DrawBuffer) !=
348             GL_FRAMEBUFFER_COMPLETE) {
349            _mesa_problem(ctx, "Unexpected incomplete framebuffer in "
350                          "_mesa_meta_GenerateMipmap()");
351            break;
352         }
353
354         assert(dstWidth == ctx->DrawBuffer->Width);
355         if (target == GL_TEXTURE_1D_ARRAY) {
356            assert(dstHeight == 1);
357         } else {
358            assert(dstHeight == ctx->DrawBuffer->Height);
359         }
360
361         _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
362      }
363   }
364
365   _mesa_lock_texture(ctx, texObj); /* relock */
366
367   _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, samp_obj_save);
368   _mesa_reference_sampler_object(ctx, &samp_obj_save, NULL);
369
370   _mesa_meta_end(ctx);
371
372   _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, &maxLevelSave,
373                             false);
374   if (genMipmapSave)
375      _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_true,
376                                false);
377   if (swizzleSaved)
378      _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA, swizzle,
379                                false);
380}
381