• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
57 static bool
fallback_required(struct gl_context * ctx,GLenum target,struct gl_texture_object * texObj)58 fallback_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 
126 void
_mesa_meta_glsl_generate_mipmap_cleanup(struct gl_context * ctx,struct gen_mipmap_state * mipmap)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  */
146 void
_mesa_meta_GenerateMipmap(struct gl_context * ctx,GLenum target,struct gl_texture_object * texObj)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