| /* |
| * Mesa 3-D graphics library |
| * |
| * Copyright (C) 2009 VMware, Inc. All Rights Reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included |
| * in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| * OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /** |
| * Meta operations. Some GL operations can be expressed in terms of |
| * other GL operations. For example, glBlitFramebuffer() can be done |
| * with texture mapping and glClear() can be done with polygon rendering. |
| * |
| * \author Brian Paul |
| */ |
| |
| #include "main/arrayobj.h" |
| #include "main/blend.h" |
| #include "main/buffers.h" |
| #include "main/enums.h" |
| #include "main/enable.h" |
| #include "main/fbobject.h" |
| #include "main/framebuffer.h" |
| #include "main/macros.h" |
| #include "main/mipmap.h" |
| #include "main/teximage.h" |
| #include "main/texobj.h" |
| #include "main/texparam.h" |
| #include "main/varray.h" |
| #include "main/viewport.h" |
| #include "drivers/common/meta.h" |
| #include "program/prog_instruction.h" |
| |
| |
| /** |
| * Check if the call to _mesa_meta_GenerateMipmap() will require a |
| * software fallback. The fallback path will require that the texture |
| * images are mapped. |
| * \return GL_TRUE if a fallback is needed, GL_FALSE otherwise |
| */ |
| static bool |
| fallback_required(struct gl_context *ctx, GLenum target, |
| struct gl_texture_object *texObj) |
| { |
| struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap; |
| struct gl_texture_image *baseImage; |
| GLuint srcLevel; |
| GLenum status; |
| |
| /* check for fallbacks */ |
| if (target == GL_TEXTURE_3D) { |
| _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH, |
| "glGenerateMipmap() to %s target\n", |
| _mesa_enum_to_string(target)); |
| return true; |
| } |
| |
| srcLevel = texObj->BaseLevel; |
| baseImage = _mesa_select_tex_image(texObj, target, srcLevel); |
| if (!baseImage) { |
| _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH, |
| "glGenerateMipmap() couldn't find base teximage\n"); |
| return true; |
| } |
| |
| if (_mesa_is_format_compressed(baseImage->TexFormat)) { |
| _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH, |
| "glGenerateMipmap() with %s format\n", |
| _mesa_get_format_name(baseImage->TexFormat)); |
| return true; |
| } |
| |
| if (_mesa_get_format_color_encoding(baseImage->TexFormat) == GL_SRGB && |
| !ctx->Extensions.EXT_texture_sRGB_decode) { |
| /* The texture format is sRGB but we can't turn off sRGB->linear |
| * texture sample conversion. So we won't be able to generate the |
| * right colors when rendering. Need to use a fallback. |
| */ |
| _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH, |
| "glGenerateMipmap() of sRGB texture without " |
| "sRGB decode\n"); |
| return true; |
| } |
| |
| /* |
| * Test that we can actually render in the texture's format. |
| */ |
| if (mipmap->fb == NULL) { |
| mipmap->fb = ctx->Driver.NewFramebuffer(ctx, 0xDEADBEEF); |
| if (mipmap->fb == NULL) { |
| _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH, |
| "glGenerateMipmap() ran out of memory\n"); |
| return true; |
| } |
| } |
| |
| _mesa_meta_framebuffer_texture_image(ctx, mipmap->fb, |
| GL_COLOR_ATTACHMENT0, baseImage, 0); |
| |
| status = _mesa_check_framebuffer_status(ctx, mipmap->fb); |
| if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { |
| _mesa_perf_debug(ctx, MESA_DEBUG_SEVERITY_HIGH, |
| "glGenerateMipmap() got incomplete FBO\n"); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void |
| _mesa_meta_glsl_generate_mipmap_cleanup(struct gl_context *ctx, |
| struct gen_mipmap_state *mipmap) |
| { |
| if (mipmap->VAO == 0) |
| return; |
| _mesa_DeleteVertexArrays(1, &mipmap->VAO); |
| mipmap->VAO = 0; |
| _mesa_reference_buffer_object(ctx, &mipmap->buf_obj, NULL); |
| _mesa_reference_sampler_object(ctx, &mipmap->samp_obj, NULL); |
| _mesa_reference_framebuffer(&mipmap->fb, NULL); |
| |
| _mesa_meta_blit_shader_table_cleanup(ctx, &mipmap->shaders); |
| } |
| |
| |
| /** |
| * Called via ctx->Driver.GenerateMipmap() |
| * Note: We don't yet support 3D textures, or texture borders. |
| */ |
| void |
| _mesa_meta_GenerateMipmap(struct gl_context *ctx, GLenum target, |
| struct gl_texture_object *texObj) |
| { |
| struct gen_mipmap_state *mipmap = &ctx->Meta->Mipmap; |
| struct vertex verts[4]; |
| const GLuint baseLevel = texObj->BaseLevel; |
| const GLuint maxLevel = texObj->MaxLevel; |
| const GLint maxLevelSave = texObj->MaxLevel; |
| const GLboolean genMipmapSave = texObj->GenerateMipmap; |
| const GLboolean use_glsl_version = ctx->Extensions.ARB_vertex_shader && |
| ctx->Extensions.ARB_fragment_shader; |
| GLenum faceTarget; |
| GLuint dstLevel; |
| struct gl_sampler_object *samp_obj_save = NULL; |
| GLint swizzle[4]; |
| GLboolean swizzleSaved = GL_FALSE; |
| |
| /* GLint so the compiler won't complain about type signedness mismatch in |
| * the calls to _mesa_texture_parameteriv below. |
| */ |
| static const GLint always_false = GL_FALSE; |
| static const GLint always_true = GL_TRUE; |
| |
| if (fallback_required(ctx, target, texObj)) { |
| _mesa_generate_mipmap(ctx, target, texObj); |
| return; |
| } |
| |
| if (target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && |
| target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) { |
| faceTarget = target; |
| target = GL_TEXTURE_CUBE_MAP; |
| } else { |
| faceTarget = target; |
| } |
| |
| _mesa_meta_begin(ctx, MESA_META_ALL & ~MESA_META_DRAW_BUFFERS); |
| _mesa_ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); |
| _mesa_Disable(GL_DITHER); |
| |
| /* Choose between glsl version and fixed function version of |
| * GenerateMipmap function. |
| */ |
| if (use_glsl_version) { |
| _mesa_meta_setup_vertex_objects(ctx, &mipmap->VAO, &mipmap->buf_obj, true, |
| 2, 4, 0); |
| _mesa_meta_setup_blit_shader(ctx, target, false, &mipmap->shaders); |
| } else { |
| _mesa_meta_setup_ff_tnl_for_blit(ctx, &mipmap->VAO, &mipmap->buf_obj, 3); |
| _mesa_set_enable(ctx, target, GL_TRUE); |
| } |
| |
| _mesa_reference_sampler_object(ctx, &samp_obj_save, |
| ctx->Texture.Unit[ctx->Texture.CurrentUnit].Sampler); |
| |
| /* We may have been called from glGenerateTextureMipmap with CurrentUnit |
| * still set to 0, so we don't know when we can skip binding the texture. |
| * Assume that _mesa_BindTexture will be fast if we're rebinding the same |
| * texture. |
| */ |
| _mesa_BindTexture(target, texObj->Name); |
| |
| if (mipmap->samp_obj == NULL) { |
| mipmap->samp_obj = ctx->Driver.NewSamplerObject(ctx, 0xDEADBEEF); |
| if (mipmap->samp_obj == NULL) { |
| /* This is a bit lazy. Flag out of memory, and then don't bother to |
| * clean up. Once out of memory is flagged, the only realistic next |
| * move is to destroy the context. That will trigger all the right |
| * clean up. |
| */ |
| _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGenerateMipmap"); |
| return; |
| } |
| |
| _mesa_set_sampler_filters(ctx, mipmap->samp_obj, GL_LINEAR_MIPMAP_LINEAR, |
| GL_LINEAR); |
| _mesa_set_sampler_wrap(ctx, mipmap->samp_obj, GL_CLAMP_TO_EDGE, |
| GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); |
| } |
| |
| if (ctx->Extensions.EXT_texture_sRGB_decode) { |
| const struct gl_texture_image *baseImage = |
| _mesa_select_tex_image(texObj, target, texObj->BaseLevel); |
| const bool srgb = |
| _mesa_get_format_color_encoding(baseImage->TexFormat) == GL_SRGB; |
| |
| _mesa_set_sampler_srgb_decode(ctx, mipmap->samp_obj, |
| srgb ? GL_DECODE_EXT : GL_SKIP_DECODE_EXT); |
| _mesa_set_framebuffer_srgb(ctx, srgb); |
| } |
| |
| _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, mipmap->samp_obj); |
| |
| assert(mipmap->fb != NULL); |
| _mesa_bind_framebuffers(ctx, mipmap->fb, mipmap->fb); |
| |
| _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_false, false); |
| |
| if (texObj->_Swizzle != SWIZZLE_NOOP) { |
| static const GLint swizzleNoop[4] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; |
| memcpy(swizzle, texObj->Swizzle, sizeof(swizzle)); |
| swizzleSaved = GL_TRUE; |
| _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA, |
| swizzleNoop, false); |
| } |
| |
| /* Silence valgrind warnings about reading uninitialized stack. */ |
| memset(verts, 0, sizeof(verts)); |
| |
| /* setup vertex positions */ |
| verts[0].x = -1.0F; |
| verts[0].y = -1.0F; |
| verts[1].x = 1.0F; |
| verts[1].y = -1.0F; |
| verts[2].x = 1.0F; |
| verts[2].y = 1.0F; |
| verts[3].x = -1.0F; |
| verts[3].y = 1.0F; |
| |
| /* texture is already locked, unlock now */ |
| _mesa_unlock_texture(ctx, texObj); |
| |
| _mesa_prepare_mipmap_levels(ctx, texObj, baseLevel, maxLevel); |
| |
| for (dstLevel = baseLevel + 1; dstLevel <= maxLevel; dstLevel++) { |
| const struct gl_texture_image *srcImage; |
| struct gl_texture_image *dstImage; |
| const GLuint srcLevel = dstLevel - 1; |
| GLuint layer; |
| GLsizei srcWidth, srcHeight, srcDepth; |
| GLsizei dstWidth, dstHeight, dstDepth; |
| |
| srcImage = _mesa_select_tex_image(texObj, faceTarget, srcLevel); |
| assert(srcImage->Border == 0); |
| |
| /* src size */ |
| srcWidth = srcImage->Width; |
| if (target == GL_TEXTURE_1D_ARRAY) { |
| srcHeight = 1; |
| srcDepth = srcImage->Height; |
| } else { |
| srcHeight = srcImage->Height; |
| srcDepth = srcImage->Depth; |
| } |
| |
| /* new dst size */ |
| dstWidth = minify(srcWidth, 1); |
| dstHeight = minify(srcHeight, 1); |
| dstDepth = target == GL_TEXTURE_3D ? minify(srcDepth, 1) : srcDepth; |
| |
| if (dstWidth == srcWidth && |
| dstHeight == srcHeight && |
| dstDepth == srcDepth) { |
| /* all done */ |
| break; |
| } |
| |
| /* Allocate storage for the destination mipmap image(s) */ |
| |
| /* Set MaxLevel large enough to hold the new level when we allocate it */ |
| _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, |
| (GLint *) &dstLevel, false); |
| |
| dstImage = _mesa_select_tex_image(texObj, faceTarget, dstLevel); |
| |
| /* All done. We either ran out of memory or we would go beyond the last |
| * valid level of an immutable texture if we continued. |
| */ |
| if (dstImage == NULL) |
| break; |
| |
| /* limit minification to src level */ |
| _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, |
| (GLint *) &srcLevel, false); |
| |
| /* setup viewport */ |
| _mesa_set_viewport(ctx, 0, 0, 0, dstWidth, dstHeight); |
| _mesa_DrawBuffer(GL_COLOR_ATTACHMENT0); |
| |
| for (layer = 0; layer < dstDepth; ++layer) { |
| /* Setup texture coordinates */ |
| _mesa_meta_setup_texture_coords(faceTarget, |
| layer, |
| 0, 0, /* xoffset, yoffset */ |
| srcWidth, srcHeight, /* img size */ |
| srcWidth, srcHeight, srcDepth, |
| verts[0].tex, |
| verts[1].tex, |
| verts[2].tex, |
| verts[3].tex); |
| |
| /* upload vertex data */ |
| _mesa_buffer_data(ctx, mipmap->buf_obj, GL_NONE, sizeof(verts), verts, |
| GL_DYNAMIC_DRAW, __func__); |
| |
| _mesa_meta_framebuffer_texture_image(ctx, ctx->DrawBuffer, |
| GL_COLOR_ATTACHMENT0, dstImage, |
| layer); |
| |
| /* sanity check */ |
| if (_mesa_check_framebuffer_status(ctx, ctx->DrawBuffer) != |
| GL_FRAMEBUFFER_COMPLETE) { |
| _mesa_problem(ctx, "Unexpected incomplete framebuffer in " |
| "_mesa_meta_GenerateMipmap()"); |
| break; |
| } |
| |
| assert(dstWidth == ctx->DrawBuffer->Width); |
| if (target == GL_TEXTURE_1D_ARRAY) { |
| assert(dstHeight == 1); |
| } else { |
| assert(dstHeight == ctx->DrawBuffer->Height); |
| } |
| |
| _mesa_DrawArrays(GL_TRIANGLE_FAN, 0, 4); |
| } |
| } |
| |
| _mesa_lock_texture(ctx, texObj); /* relock */ |
| |
| _mesa_bind_sampler(ctx, ctx->Texture.CurrentUnit, samp_obj_save); |
| _mesa_reference_sampler_object(ctx, &samp_obj_save, NULL); |
| |
| _mesa_meta_end(ctx); |
| |
| _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_MAX_LEVEL, &maxLevelSave, |
| false); |
| if (genMipmapSave) |
| _mesa_texture_parameteriv(ctx, texObj, GL_GENERATE_MIPMAP, &always_true, |
| false); |
| if (swizzleSaved) |
| _mesa_texture_parameteriv(ctx, texObj, GL_TEXTURE_SWIZZLE_RGBA, swizzle, |
| false); |
| } |