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 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 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 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_BindTexture will be fast if we're rebinding the same 205 * texture. 206 */ 207 _mesa_BindTexture(target, texObj->Name); 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