• 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->Attrib.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_is_format_srgb(baseImage->TexFormat) &&
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->Attrib.BaseLevel;
153    const GLuint maxLevel = texObj->Attrib.MaxLevel;
154    const GLint maxLevelSave = texObj->Attrib.MaxLevel;
155    const GLboolean genMipmapSave = texObj->Attrib.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->Attrib.BaseLevel);
230       const bool srgb = _mesa_is_format_srgb(baseImage->TexFormat);
231 
232       _mesa_set_sampler_srgb_decode(ctx, mipmap->samp_obj,
233                                     srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT);
234       _mesa_set_framebuffer_srgb(ctx, srgb);
235    }
236 
237    _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, mipmap->samp_obj);
238 
239    assert(mipmap->fb != NULL);
240    _mesa_bind_framebuffers(ctx, mipmap->fb, mipmap->fb);
241 
242    _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_false, false);
243 
244    if (texObj->Attrib._Swizzle != SWIZZLE_NOOP) {
245       static const GLint swizzleNoop[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
246       memcpy(swizzle, texObj->Attrib.Swizzle, sizeof(swizzle));
247       swizzleSaved = GL_TRUE;
248       _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA,
249                                 swizzleNoop, false);
250    }
251 
252    /* Silence valgrind warnings about reading uninitialized stack. */
253    memset(verts, 0, sizeof(verts));
254 
255    /* setup vertex positions */
256    verts[0].x = -1.0F;
257    verts[0].y = -1.0F;
258    verts[1].x =  1.0F;
259    verts[1].y = -1.0F;
260    verts[2].x =  1.0F;
261    verts[2].y =  1.0F;
262    verts[3].x = -1.0F;
263    verts[3].y =  1.0F;
264 
265    /* texture is already locked, unlock now */
266    _mesa_unlock_texture(ctx, texObj);
267 
268    _mesa_prepare_mipmap_levels(ctx, texObj, baseLevel, maxLevel);
269 
270    for (dstLevel = baseLevel + 1; dstLevel <= maxLevel; dstLevel++) {
271       const struct gl_texture_image *srcImage;
272       struct gl_texture_image *dstImage;
273       const GLuint srcLevel = dstLevel - 1;
274       GLuint layer;
275       GLsizei srcWidth, srcHeight, srcDepth;
276       GLsizei dstWidth, dstHeight, dstDepth;
277 
278       srcImage = _mesa_select_tex_image(texObj, faceTarget, srcLevel);
279       assert(srcImage->Border == 0);
280 
281       /* src size */
282       srcWidth = srcImage->Width;
283       if (target == GL_TEXTURE_1D_ARRAY) {
284          srcHeight = 1;
285          srcDepth = srcImage->Height;
286       } else {
287          srcHeight = srcImage->Height;
288          srcDepth = srcImage->Depth;
289       }
290 
291       /* new dst size */
292       dstWidth = minify(srcWidth, 1);
293       dstHeight = minify(srcHeight, 1);
294       dstDepth = target == GL_TEXTURE_3D ? minify(srcDepth, 1) : srcDepth;
295 
296       if (dstWidth == srcWidth &&
297           dstHeight == srcHeight &&
298           dstDepth == srcDepth) {
299          /* all done */
300          break;
301       }
302 
303       /* Allocate storage for the destination mipmap image(s) */
304 
305       /* Set MaxLevel large enough to hold the new level when we allocate it */
306       _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL,
307                                 (GLint *) &dstLevel, false);
308 
309       dstImage = _mesa_select_tex_image(texObj, faceTarget, dstLevel);
310 
311       /* All done.  We either ran out of memory or we would go beyond the last
312        * valid level of an immutable texture if we continued.
313        */
314       if (dstImage == NULL)
315          break;
316 
317       /* limit minification to src level */
318       _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL,
319                                 (GLint *) &srcLevel, false);
320 
321       /* setup viewport */
322       _mesa_set_viewport(ctx, 0, 0, 0, dstWidth, dstHeight);
323       _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0);
324 
325       for (layer = 0; layer < dstDepth; ++layer) {
326          /* Setup texture coordinates */
327          _mesa_meta_setup_texture_coords(faceTarget,
328                                          layer,
329                                          0, 0, /* xoffset, yoffset */
330                                          srcWidth, srcHeight, /* img size */
331                                          srcWidth, srcHeight, srcDepth,
332                                          verts[0].tex,
333                                          verts[1].tex,
334                                          verts[2].tex,
335                                          verts[3].tex);
336 
337          /* upload vertex data */
338          _mesa_buffer_data(ctx, mipmap->buf_obj, GL_NONE, sizeof(verts), verts,
339                            GL_DYNAMIC_DRAW, __func__);
340 
341          _mesa_meta_framebuffer_texture_image(ctx, ctx->DrawBuffer,
342                                               GL_COLOR_ATTACHMENT0, dstImage,
343                                               layer);
344 
345          /* sanity check */
346          if (_mesa_check_framebuffer_status(ctx, ctx->DrawBuffer) !=
347              GL_FRAMEBUFFER_COMPLETE) {
348             _mesa_problem(ctx, "Unexpected incomplete framebuffer in "
349                           "_mesa_meta_GenerateMipmap()");
350             break;
351          }
352 
353          assert(dstWidth == ctx->DrawBuffer->Width);
354          if (target == GL_TEXTURE_1D_ARRAY) {
355             assert(dstHeight == 1);
356          } else {
357             assert(dstHeight == ctx->DrawBuffer->Height);
358          }
359 
360          _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4);
361       }
362    }
363 
364    _mesa_lock_texture(ctx, texObj); /* relock */
365 
366    _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, samp_obj_save);
367    _mesa_reference_sampler_object(ctx, &samp_obj_save, NULL);
368 
369    _mesa_meta_end(ctx);
370 
371    _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, &maxLevelSave,
372                              false);
373    if (genMipmapSave)
374       _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_true,
375                                 false);
376    if (swizzleSaved)
377       _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA, swizzle,
378                                 false);
379 }
380