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->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 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->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