X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Fmipmap.c;h=88e6baa08ad15240359521f2ba00c825dbd8d378;hb=38ab39f6501f78ea7048e8a16a97fdb075b9d8c7;hp=75bc9a09b4fb0d64d693b4d8f1318f149f7d2774;hpb=7d1a2056a84b5af75adf84e34a57e5d75f550139;p=mesa.git diff --git a/src/mesa/main/mipmap.c b/src/mesa/main/mipmap.c index 75bc9a09b4f..88e6baa08ad 100644 --- a/src/mesa/main/mipmap.c +++ b/src/mesa/main/mipmap.c @@ -1,6 +1,5 @@ /* * Mesa 3-D graphics library - * Version: 7.1 * * Copyright (C) 1999-2007 Brian Paul All Rights Reserved. * @@ -17,9 +16,10 @@ * 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 - * BRIAN PAUL 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. + * 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. */ @@ -27,26 +27,63 @@ * \file mipmap.c mipmap generation and teximage resizing functions. */ +#include "errors.h" #include "imports.h" #include "formats.h" +#include "glformats.h" #include "mipmap.h" #include "mtypes.h" #include "teximage.h" +#include "texobj.h" #include "texstore.h" #include "image.h" +#include "macros.h" +#include "util/half_float.h" +#include "util/format_rgb9e5.h" +#include "util/format_r11g11b10f.h" +/** + * Compute the expected number of mipmap levels in the texture given + * the width/height/depth of the base image and the GL_TEXTURE_BASE_LEVEL/ + * GL_TEXTURE_MAX_LEVEL settings. This will tell us how many mipmap + * levels should be generated. + */ +unsigned +_mesa_compute_num_levels(struct gl_context *ctx, + struct gl_texture_object *texObj, + GLenum target) +{ + const struct gl_texture_image *baseImage; + GLuint numLevels; + + baseImage = _mesa_get_tex_image(ctx, texObj, target, texObj->BaseLevel); + + numLevels = texObj->BaseLevel + baseImage->MaxNumLevels; + numLevels = MIN2(numLevels, (GLuint) texObj->MaxLevel + 1); + if (texObj->Immutable) + numLevels = MIN2(numLevels, texObj->NumLevels); + assert(numLevels >= 1); + + return numLevels; +} static GLint bytes_per_pixel(GLenum datatype, GLuint comps) { - GLint b = _mesa_sizeof_packed_type(datatype); + GLint b; + + if (datatype == GL_UNSIGNED_INT_8_24_REV_MESA || + datatype == GL_UNSIGNED_INT_24_8_MESA) + return 4; + + b = _mesa_sizeof_packed_type(datatype); assert(b >= 0); if (_mesa_type_is_packed(datatype)) - return b; + return b; else - return b * comps; + return b * comps; } @@ -142,8 +179,8 @@ do_row(GLenum datatype, GLuint comps, GLint srcWidth, const GLuint k0 = (srcWidth == dstWidth) ? 0 : 1; const GLuint colStride = (srcWidth == dstWidth) ? 1 : 2; - ASSERT(comps >= 1); - ASSERT(comps <= 4); + assert(comps >= 1); + assert(comps <= 4); /* This assertion is no longer valid with non-power-of-2 textures assert(srcWidth == dstWidth || srcWidth == 2 * dstWidth); @@ -467,7 +504,7 @@ do_row(GLenum datatype, GLuint comps, GLint srcWidth, GLuint *dst = (GLuint *) dstRow; for (i = j = 0, k = k0; i < (GLuint) dstWidth; i++, j += colStride, k += colStride) { - dst[i] = (GLfloat)(rowA[j] / 4 + rowA[k] / 4 + rowB[j] / 4 + rowB[k] / 4); + dst[i] = rowA[j] / 4 + rowA[k] / 4 + rowB[j] / 4 + rowB[k] / 4; } } @@ -634,8 +671,118 @@ do_row(GLenum datatype, GLuint comps, GLint srcWidth, } } + else if (datatype == GL_UNSIGNED_INT_2_10_10_10_REV && comps == 4) { + GLuint i, j, k; + const GLuint *rowA = (const GLuint *) srcRowA; + const GLuint *rowB = (const GLuint *) srcRowB; + GLuint *dst = (GLuint *) dstRow; + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + const GLint rowAr0 = rowA[j] & 0x3ff; + const GLint rowAr1 = rowA[k] & 0x3ff; + const GLint rowBr0 = rowB[j] & 0x3ff; + const GLint rowBr1 = rowB[k] & 0x3ff; + const GLint rowAg0 = (rowA[j] >> 10) & 0x3ff; + const GLint rowAg1 = (rowA[k] >> 10) & 0x3ff; + const GLint rowBg0 = (rowB[j] >> 10) & 0x3ff; + const GLint rowBg1 = (rowB[k] >> 10) & 0x3ff; + const GLint rowAb0 = (rowA[j] >> 20) & 0x3ff; + const GLint rowAb1 = (rowA[k] >> 20) & 0x3ff; + const GLint rowBb0 = (rowB[j] >> 20) & 0x3ff; + const GLint rowBb1 = (rowB[k] >> 20) & 0x3ff; + const GLint rowAa0 = (rowA[j] >> 30) & 0x3; + const GLint rowAa1 = (rowA[k] >> 30) & 0x3; + const GLint rowBa0 = (rowB[j] >> 30) & 0x3; + const GLint rowBa1 = (rowB[k] >> 30) & 0x3; + const GLint red = (rowAr0 + rowAr1 + rowBr0 + rowBr1) >> 2; + const GLint green = (rowAg0 + rowAg1 + rowBg0 + rowBg1) >> 2; + const GLint blue = (rowAb0 + rowAb1 + rowBb0 + rowBb1) >> 2; + const GLint alpha = (rowAa0 + rowAa1 + rowBa0 + rowBa1) >> 2; + dst[i] = (alpha << 30) | (blue << 20) | (green << 10) | red; + } + } + + else if (datatype == GL_UNSIGNED_INT_5_9_9_9_REV && comps == 3) { + GLuint i, j, k; + const GLuint *rowA = (const GLuint*) srcRowA; + const GLuint *rowB = (const GLuint*) srcRowB; + GLuint *dst = (GLuint*)dstRow; + GLfloat res[3], rowAj[3], rowBj[3], rowAk[3], rowBk[3]; + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + rgb9e5_to_float3(rowA[j], rowAj); + rgb9e5_to_float3(rowB[j], rowBj); + rgb9e5_to_float3(rowA[k], rowAk); + rgb9e5_to_float3(rowB[k], rowBk); + res[0] = (rowAj[0] + rowAk[0] + rowBj[0] + rowBk[0]) * 0.25F; + res[1] = (rowAj[1] + rowAk[1] + rowBj[1] + rowBk[1]) * 0.25F; + res[2] = (rowAj[2] + rowAk[2] + rowBj[2] + rowBk[2]) * 0.25F; + dst[i] = float3_to_rgb9e5(res); + } + } + + else if (datatype == GL_UNSIGNED_INT_10F_11F_11F_REV && comps == 3) { + GLuint i, j, k; + const GLuint *rowA = (const GLuint*) srcRowA; + const GLuint *rowB = (const GLuint*) srcRowB; + GLuint *dst = (GLuint*)dstRow; + GLfloat res[3], rowAj[3], rowBj[3], rowAk[3], rowBk[3]; + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + r11g11b10f_to_float3(rowA[j], rowAj); + r11g11b10f_to_float3(rowB[j], rowBj); + r11g11b10f_to_float3(rowA[k], rowAk); + r11g11b10f_to_float3(rowB[k], rowBk); + res[0] = (rowAj[0] + rowAk[0] + rowBj[0] + rowBk[0]) * 0.25F; + res[1] = (rowAj[1] + rowAk[1] + rowBj[1] + rowBk[1]) * 0.25F; + res[2] = (rowAj[2] + rowAk[2] + rowBj[2] + rowBk[2]) * 0.25F; + dst[i] = float3_to_r11g11b10f(res); + } + } + + else if (datatype == GL_FLOAT_32_UNSIGNED_INT_24_8_REV && comps == 1) { + GLuint i, j, k; + const GLfloat *rowA = (const GLfloat *) srcRowA; + const GLfloat *rowB = (const GLfloat *) srcRowB; + GLfloat *dst = (GLfloat *) dstRow; + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + dst[i*2] = (rowA[j*2] + rowA[k*2] + rowB[j*2] + rowB[k*2]) * 0.25F; + } + } + + else if (datatype == GL_UNSIGNED_INT_24_8_MESA && comps == 2) { + GLuint i, j, k; + const GLuint *rowA = (const GLuint *) srcRowA; + const GLuint *rowB = (const GLuint *) srcRowB; + GLuint *dst = (GLuint *) dstRow; + /* note: averaging stencil values seems weird, but what else? */ + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + GLuint z = (((rowA[j] >> 8) + (rowA[k] >> 8) + + (rowB[j] >> 8) + (rowB[k] >> 8)) / 4) << 8; + GLuint s = ((rowA[j] & 0xff) + (rowA[k] & 0xff) + + (rowB[j] & 0xff) + (rowB[k] & 0xff)) / 4; + dst[i] = z | s; + } + } + else if (datatype == GL_UNSIGNED_INT_8_24_REV_MESA && comps == 2) { + GLuint i, j, k; + const GLuint *rowA = (const GLuint *) srcRowA; + const GLuint *rowB = (const GLuint *) srcRowB; + GLuint *dst = (GLuint *) dstRow; + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + GLuint z = ((rowA[j] & 0xffffff) + (rowA[k] & 0xffffff) + + (rowB[j] & 0xffffff) + (rowB[k] & 0xffffff)) / 4; + GLuint s = (((rowA[j] >> 24) + (rowA[k] >> 24) + + (rowB[j] >> 24) + (rowB[k] >> 24)) / 4) << 24; + dst[i] = z | s; + } + } + else { - _mesa_problem(NULL, "bad format in do_row()"); + unreachable("bad format in do_row()"); } } @@ -667,8 +814,8 @@ do_row_3D(GLenum datatype, GLuint comps, GLint srcWidth, const GLuint colStride = (srcWidth == dstWidth) ? 1 : 2; GLuint i, j, k; - ASSERT(comps >= 1); - ASSERT(comps <= 4); + assert(comps >= 1); + assert(comps <= 4); if ((datatype == GL_UNSIGNED_BYTE) && (comps == 4)) { DECLARE_ROW_POINTERS(GLubyte, 4); @@ -872,7 +1019,7 @@ do_row_3D(GLenum datatype, GLuint comps, GLint srcWidth, } } else if ((datatype == GL_HALF_FLOAT_ARB) && (comps == 3)) { - DECLARE_ROW_POINTERS(GLhalfARB, 4); + DECLARE_ROW_POINTERS(GLhalfARB, 3); for (i = j = 0, k = k0; i < (GLuint) dstWidth; i++, j += colStride, k += colStride) { @@ -882,7 +1029,7 @@ do_row_3D(GLenum datatype, GLuint comps, GLint srcWidth, } } else if ((datatype == GL_HALF_FLOAT_ARB) && (comps == 2)) { - DECLARE_ROW_POINTERS(GLhalfARB, 4); + DECLARE_ROW_POINTERS(GLhalfARB, 2); for (i = j = 0, k = k0; i < (GLuint) dstWidth; i++, j += colStride, k += colStride) { @@ -891,7 +1038,7 @@ do_row_3D(GLenum datatype, GLuint comps, GLint srcWidth, } } else if ((datatype == GL_HALF_FLOAT_ARB) && (comps == 1)) { - DECLARE_ROW_POINTERS(GLhalfARB, 4); + DECLARE_ROW_POINTERS(GLhalfARB, 1); for (i = j = 0, k = k0; i < (GLuint) dstWidth; i++, j += colStride, k += colStride) { @@ -1165,8 +1312,121 @@ do_row_3D(GLenum datatype, GLuint comps, GLint srcWidth, dst[i] = (g << 4) | r; } } + else if ((datatype == GL_UNSIGNED_INT_2_10_10_10_REV) && (comps == 4)) { + DECLARE_ROW_POINTERS0(GLuint); + + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + const GLint rowAr0 = rowA[j] & 0x3ff; + const GLint rowAr1 = rowA[k] & 0x3ff; + const GLint rowBr0 = rowB[j] & 0x3ff; + const GLint rowBr1 = rowB[k] & 0x3ff; + const GLint rowCr0 = rowC[j] & 0x3ff; + const GLint rowCr1 = rowC[k] & 0x3ff; + const GLint rowDr0 = rowD[j] & 0x3ff; + const GLint rowDr1 = rowD[k] & 0x3ff; + const GLint rowAg0 = (rowA[j] >> 10) & 0x3ff; + const GLint rowAg1 = (rowA[k] >> 10) & 0x3ff; + const GLint rowBg0 = (rowB[j] >> 10) & 0x3ff; + const GLint rowBg1 = (rowB[k] >> 10) & 0x3ff; + const GLint rowCg0 = (rowC[j] >> 10) & 0x3ff; + const GLint rowCg1 = (rowC[k] >> 10) & 0x3ff; + const GLint rowDg0 = (rowD[j] >> 10) & 0x3ff; + const GLint rowDg1 = (rowD[k] >> 10) & 0x3ff; + const GLint rowAb0 = (rowA[j] >> 20) & 0x3ff; + const GLint rowAb1 = (rowA[k] >> 20) & 0x3ff; + const GLint rowBb0 = (rowB[j] >> 20) & 0x3ff; + const GLint rowBb1 = (rowB[k] >> 20) & 0x3ff; + const GLint rowCb0 = (rowC[j] >> 20) & 0x3ff; + const GLint rowCb1 = (rowC[k] >> 20) & 0x3ff; + const GLint rowDb0 = (rowD[j] >> 20) & 0x3ff; + const GLint rowDb1 = (rowD[k] >> 20) & 0x3ff; + const GLint rowAa0 = (rowA[j] >> 30) & 0x3; + const GLint rowAa1 = (rowA[k] >> 30) & 0x3; + const GLint rowBa0 = (rowB[j] >> 30) & 0x3; + const GLint rowBa1 = (rowB[k] >> 30) & 0x3; + const GLint rowCa0 = (rowC[j] >> 30) & 0x3; + const GLint rowCa1 = (rowC[k] >> 30) & 0x3; + const GLint rowDa0 = (rowD[j] >> 30) & 0x3; + const GLint rowDa1 = (rowD[k] >> 30) & 0x3; + const GLint r = FILTER_SUM_3D(rowAr0, rowAr1, rowBr0, rowBr1, + rowCr0, rowCr1, rowDr0, rowDr1); + const GLint g = FILTER_SUM_3D(rowAg0, rowAg1, rowBg0, rowBg1, + rowCg0, rowCg1, rowDg0, rowDg1); + const GLint b = FILTER_SUM_3D(rowAb0, rowAb1, rowBb0, rowBb1, + rowCb0, rowCb1, rowDb0, rowDb1); + const GLint a = FILTER_SUM_3D(rowAa0, rowAa1, rowBa0, rowBa1, + rowCa0, rowCa1, rowDa0, rowDa1); + + dst[i] = (a << 30) | (b << 20) | (g << 10) | r; + } + } + + else if (datatype == GL_UNSIGNED_INT_5_9_9_9_REV && comps == 3) { + DECLARE_ROW_POINTERS0(GLuint); + + GLfloat res[3]; + GLfloat rowAj[3], rowBj[3], rowCj[3], rowDj[3]; + GLfloat rowAk[3], rowBk[3], rowCk[3], rowDk[3]; + + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + rgb9e5_to_float3(rowA[j], rowAj); + rgb9e5_to_float3(rowB[j], rowBj); + rgb9e5_to_float3(rowC[j], rowCj); + rgb9e5_to_float3(rowD[j], rowDj); + rgb9e5_to_float3(rowA[k], rowAk); + rgb9e5_to_float3(rowB[k], rowBk); + rgb9e5_to_float3(rowC[k], rowCk); + rgb9e5_to_float3(rowD[k], rowDk); + res[0] = (rowAj[0] + rowAk[0] + rowBj[0] + rowBk[0] + + rowCj[0] + rowCk[0] + rowDj[0] + rowDk[0]) * 0.125F; + res[1] = (rowAj[1] + rowAk[1] + rowBj[1] + rowBk[1] + + rowCj[1] + rowCk[1] + rowDj[1] + rowDk[1]) * 0.125F; + res[2] = (rowAj[2] + rowAk[2] + rowBj[2] + rowBk[2] + + rowCj[2] + rowCk[2] + rowDj[2] + rowDk[2]) * 0.125F; + dst[i] = float3_to_rgb9e5(res); + } + } + + else if (datatype == GL_UNSIGNED_INT_10F_11F_11F_REV && comps == 3) { + DECLARE_ROW_POINTERS0(GLuint); + + GLfloat res[3]; + GLfloat rowAj[3], rowBj[3], rowCj[3], rowDj[3]; + GLfloat rowAk[3], rowBk[3], rowCk[3], rowDk[3]; + + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + r11g11b10f_to_float3(rowA[j], rowAj); + r11g11b10f_to_float3(rowB[j], rowBj); + r11g11b10f_to_float3(rowC[j], rowCj); + r11g11b10f_to_float3(rowD[j], rowDj); + r11g11b10f_to_float3(rowA[k], rowAk); + r11g11b10f_to_float3(rowB[k], rowBk); + r11g11b10f_to_float3(rowC[k], rowCk); + r11g11b10f_to_float3(rowD[k], rowDk); + res[0] = (rowAj[0] + rowAk[0] + rowBj[0] + rowBk[0] + + rowCj[0] + rowCk[0] + rowDj[0] + rowDk[0]) * 0.125F; + res[1] = (rowAj[1] + rowAk[1] + rowBj[1] + rowBk[1] + + rowCj[1] + rowCk[1] + rowDj[1] + rowDk[1]) * 0.125F; + res[2] = (rowAj[2] + rowAk[2] + rowBj[2] + rowBk[2] + + rowCj[2] + rowCk[2] + rowDj[2] + rowDk[2]) * 0.125F; + dst[i] = float3_to_r11g11b10f(res); + } + } + + else if (datatype == GL_FLOAT_32_UNSIGNED_INT_24_8_REV && comps == 1) { + DECLARE_ROW_POINTERS(GLfloat, 2); + + for (i = j = 0, k = k0; i < (GLuint) dstWidth; + i++, j += colStride, k += colStride) { + FILTER_F_3D(0); + } + } + else { - _mesa_problem(NULL, "bad format in do_row()"); + unreachable("bad format in do_row()"); } } @@ -1210,16 +1470,14 @@ make_1d_mipmap(GLenum datatype, GLuint comps, GLint border, static void make_2d_mipmap(GLenum datatype, GLuint comps, GLint border, GLint srcWidth, GLint srcHeight, - const GLubyte *srcPtr, GLint srcRowStride, + const GLubyte *srcPtr, GLint srcRowStride, GLint dstWidth, GLint dstHeight, - GLubyte *dstPtr, GLint dstRowStride) + GLubyte *dstPtr, GLint dstRowStride) { const GLint bpt = bytes_per_pixel(datatype, comps); const GLint srcWidthNB = srcWidth - 2 * border; /* sizes w/out border */ const GLint dstWidthNB = dstWidth - 2 * border; const GLint dstHeightNB = dstHeight - 2 * border; - const GLint srcRowBytes = bpt * srcRowStride; - const GLint dstRowBytes = bpt * dstRowStride; const GLubyte *srcA, *srcB; GLubyte *dst; GLint row, srcRowStep; @@ -1228,7 +1486,7 @@ make_2d_mipmap(GLenum datatype, GLuint comps, GLint border, srcA = srcPtr + border * ((srcWidth + 1) * bpt); if (srcHeight > 1 && srcHeight > dstHeight) { /* sample from two source rows */ - srcB = srcA + srcRowBytes; + srcB = srcA + srcRowStride; srcRowStep = 2; } else { @@ -1242,9 +1500,9 @@ make_2d_mipmap(GLenum datatype, GLuint comps, GLint border, for (row = 0; row < dstHeightNB; row++) { do_row(datatype, comps, srcWidthNB, srcA, srcB, dstWidthNB, dst); - srcA += srcRowStep * srcRowBytes; - srcB += srcRowStep * srcRowBytes; - dst += dstRowBytes; + srcA += srcRowStep * srcRowStride; + srcB += srcRowStep * srcRowStride; + dst += dstRowStride; } /* This is ugly but probably won't be used much */ @@ -1304,9 +1562,9 @@ make_2d_mipmap(GLenum datatype, GLuint comps, GLint border, static void make_3d_mipmap(GLenum datatype, GLuint comps, GLint border, GLint srcWidth, GLint srcHeight, GLint srcDepth, - const GLubyte *srcPtr, GLint srcRowStride, + const GLubyte **srcPtr, GLint srcRowStride, GLint dstWidth, GLint dstHeight, GLint dstDepth, - GLubyte *dstPtr, GLint dstRowStride) + GLubyte **dstPtr, GLint dstRowStride) { const GLint bpt = bytes_per_pixel(datatype, comps); const GLint srcWidthNB = srcWidth - 2 * border; /* sizes w/out border */ @@ -1316,23 +1574,18 @@ make_3d_mipmap(GLenum datatype, GLuint comps, GLint border, const GLint dstDepthNB = dstDepth - 2 * border; GLint img, row; GLint bytesPerSrcImage, bytesPerDstImage; - GLint bytesPerSrcRow, bytesPerDstRow; GLint srcImageOffset, srcRowOffset; (void) srcDepthNB; /* silence warnings */ - - bytesPerSrcImage = srcWidth * srcHeight * bpt; - bytesPerDstImage = dstWidth * dstHeight * bpt; - - bytesPerSrcRow = srcWidth * bpt; - bytesPerDstRow = dstWidth * bpt; + bytesPerSrcImage = srcRowStride * srcHeight * bpt; + bytesPerDstImage = dstRowStride * dstHeight * bpt; /* Offset between adjacent src images to be averaged together */ - srcImageOffset = (srcDepth == dstDepth) ? 0 : bytesPerSrcImage; + srcImageOffset = (srcDepth == dstDepth) ? 0 : 1; /* Offset between adjacent src rows to be averaged together */ - srcRowOffset = (srcHeight == dstHeight) ? 0 : srcWidth * bpt; + srcRowOffset = (srcHeight == dstHeight) ? 0 : srcRowStride; /* * Need to average together up to 8 src pixels for each dest pixel. @@ -1349,15 +1602,15 @@ make_3d_mipmap(GLenum datatype, GLuint comps, GLint border, for (img = 0; img < dstDepthNB; img++) { /* first source image pointer, skipping border */ - const GLubyte *imgSrcA = srcPtr - + (bytesPerSrcImage + bytesPerSrcRow + border) * bpt * border - + img * (bytesPerSrcImage + srcImageOffset); + const GLubyte *imgSrcA = srcPtr[img * 2 + border] + + srcRowStride * border + bpt * border; /* second source image pointer, skipping border */ - const GLubyte *imgSrcB = imgSrcA + srcImageOffset; + const GLubyte *imgSrcB = srcPtr[img * 2 + srcImageOffset + border] + + srcRowStride * border + bpt * border; + /* address of the dest image, skipping border */ - GLubyte *imgDst = dstPtr - + (bytesPerDstImage + bytesPerDstRow + border) * bpt * border - + img * bytesPerDstImage; + GLubyte *imgDst = dstPtr[img + border] + + dstRowStride * border + bpt * border; /* setup the four source row pointers and the dest row pointer */ const GLubyte *srcImgARowA = imgSrcA; @@ -1373,11 +1626,11 @@ make_3d_mipmap(GLenum datatype, GLuint comps, GLint border, dstWidthNB, dstImgRow); /* advance to next rows */ - srcImgARowA += bytesPerSrcRow + srcRowOffset; - srcImgARowB += bytesPerSrcRow + srcRowOffset; - srcImgBRowA += bytesPerSrcRow + srcRowOffset; - srcImgBRowB += bytesPerSrcRow + srcRowOffset; - dstImgRow += bytesPerDstRow; + srcImgARowA += srcRowStride + srcRowOffset; + srcImgARowB += srcRowStride + srcRowOffset; + srcImgBRowA += srcRowStride + srcRowOffset; + srcImgBRowB += srcRowStride + srcRowOffset; + dstImgRow += dstRowStride; } } @@ -1385,13 +1638,14 @@ make_3d_mipmap(GLenum datatype, GLuint comps, GLint border, /* Luckily we can leverage the make_2d_mipmap() function here! */ if (border > 0) { /* do front border image */ - make_2d_mipmap(datatype, comps, 1, srcWidth, srcHeight, srcPtr, srcRowStride, - dstWidth, dstHeight, dstPtr, dstRowStride); + make_2d_mipmap(datatype, comps, 1, + srcWidth, srcHeight, srcPtr[0], srcRowStride, + dstWidth, dstHeight, dstPtr[0], dstRowStride); /* do back border image */ - make_2d_mipmap(datatype, comps, 1, srcWidth, srcHeight, - srcPtr + bytesPerSrcImage * (srcDepth - 1), srcRowStride, - dstWidth, dstHeight, - dstPtr + bytesPerDstImage * (dstDepth - 1), dstRowStride); + make_2d_mipmap(datatype, comps, 1, + srcWidth, srcHeight, srcPtr[srcDepth - 1], srcRowStride, + dstWidth, dstHeight, dstPtr[dstDepth - 1], dstRowStride); + /* do four remaining border edges that span the image slices */ if (srcDepth == dstDepth) { /* just copy border pixels from src to dst */ @@ -1400,200 +1654,58 @@ make_3d_mipmap(GLenum datatype, GLuint comps, GLint border, GLubyte *dst; /* do border along [img][row=0][col=0] */ - src = srcPtr + (img + 1) * bytesPerSrcImage; - dst = dstPtr + (img + 1) * bytesPerDstImage; + src = srcPtr[img * 2]; + dst = dstPtr[img]; memcpy(dst, src, bpt); /* do border along [img][row=dstHeight-1][col=0] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage - + (srcHeight - 1) * bytesPerSrcRow; - dst = dstPtr + (img + 1) * bytesPerDstImage - + (dstHeight - 1) * bytesPerDstRow; + src = srcPtr[img * 2] + (srcHeight - 1) * srcRowStride; + dst = dstPtr[img] + (dstHeight - 1) * dstRowStride; memcpy(dst, src, bpt); /* do border along [img][row=0][col=dstWidth-1] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage - + (srcWidth - 1) * bpt; - dst = dstPtr + (img + 1) * bytesPerDstImage - + (dstWidth - 1) * bpt; + src = srcPtr[img * 2] + (srcWidth - 1) * bpt; + dst = dstPtr[img] + (dstWidth - 1) * bpt; memcpy(dst, src, bpt); /* do border along [img][row=dstHeight-1][col=dstWidth-1] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage - + (bytesPerSrcImage - bpt); - dst = dstPtr + (img + 1) * bytesPerDstImage - + (bytesPerDstImage - bpt); + src = srcPtr[img * 2] + (bytesPerSrcImage - bpt); + dst = dstPtr[img] + (bytesPerDstImage - bpt); memcpy(dst, src, bpt); } } else { /* average border pixels from adjacent src image pairs */ - ASSERT(srcDepthNB == 2 * dstDepthNB); + assert(srcDepthNB == 2 * dstDepthNB); for (img = 0; img < dstDepthNB; img++) { - const GLubyte *src; + const GLubyte *srcA, *srcB; GLubyte *dst; /* do border along [img][row=0][col=0] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage; - dst = dstPtr + (img + 1) * bytesPerDstImage; - do_row(datatype, comps, 1, src, src + srcImageOffset, 1, dst); + srcA = srcPtr[img * 2 + 0]; + srcB = srcPtr[img * 2 + srcImageOffset]; + dst = dstPtr[img]; + do_row(datatype, comps, 1, srcA, srcB, 1, dst); /* do border along [img][row=dstHeight-1][col=0] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage - + (srcHeight - 1) * bytesPerSrcRow; - dst = dstPtr + (img + 1) * bytesPerDstImage - + (dstHeight - 1) * bytesPerDstRow; - do_row(datatype, comps, 1, src, src + srcImageOffset, 1, dst); + srcA = srcPtr[img * 2 + 0] + + (srcHeight - 1) * srcRowStride; + srcB = srcPtr[img * 2 + srcImageOffset] + + (srcHeight - 1) * srcRowStride; + dst = dstPtr[img] + (dstHeight - 1) * dstRowStride; + do_row(datatype, comps, 1, srcA, srcB, 1, dst); /* do border along [img][row=0][col=dstWidth-1] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage - + (srcWidth - 1) * bpt; - dst = dstPtr + (img + 1) * bytesPerDstImage - + (dstWidth - 1) * bpt; - do_row(datatype, comps, 1, src, src + srcImageOffset, 1, dst); + srcA = srcPtr[img * 2 + 0] + (srcWidth - 1) * bpt; + srcB = srcPtr[img * 2 + srcImageOffset] + (srcWidth - 1) * bpt; + dst = dstPtr[img] + (dstWidth - 1) * bpt; + do_row(datatype, comps, 1, srcA, srcB, 1, dst); /* do border along [img][row=dstHeight-1][col=dstWidth-1] */ - src = srcPtr + (img * 2 + 1) * bytesPerSrcImage - + (bytesPerSrcImage - bpt); - dst = dstPtr + (img + 1) * bytesPerDstImage - + (bytesPerDstImage - bpt); - do_row(datatype, comps, 1, src, src + srcImageOffset, 1, dst); - } - } - } -} - - -static void -make_1d_stack_mipmap(GLenum datatype, GLuint comps, GLint border, - GLint srcWidth, const GLubyte *srcPtr, GLuint srcRowStride, - GLint dstWidth, GLint dstHeight, - GLubyte *dstPtr, GLuint dstRowStride ) -{ - const GLint bpt = bytes_per_pixel(datatype, comps); - const GLint srcWidthNB = srcWidth - 2 * border; /* sizes w/out border */ - const GLint dstWidthNB = dstWidth - 2 * border; - const GLint dstHeightNB = dstHeight - 2 * border; - const GLint srcRowBytes = bpt * srcRowStride; - const GLint dstRowBytes = bpt * dstRowStride; - const GLubyte *src; - GLubyte *dst; - GLint row; - - /* Compute src and dst pointers, skipping any border */ - src = srcPtr + border * ((srcWidth + 1) * bpt); - dst = dstPtr + border * ((dstWidth + 1) * bpt); - - for (row = 0; row < dstHeightNB; row++) { - do_row(datatype, comps, srcWidthNB, src, src, - dstWidthNB, dst); - src += srcRowBytes; - dst += dstRowBytes; - } - - if (border) { - /* copy left-most pixel from source */ - assert(dstPtr); - assert(srcPtr); - memcpy(dstPtr, srcPtr, bpt); - /* copy right-most pixel from source */ - memcpy(dstPtr + (dstWidth - 1) * bpt, - srcPtr + (srcWidth - 1) * bpt, - bpt); - } -} - - -/** - * \bug - * There is quite a bit of refactoring that could be done with this function - * and \c make_2d_mipmap. - */ -static void -make_2d_stack_mipmap(GLenum datatype, GLuint comps, GLint border, - GLint srcWidth, GLint srcHeight, - const GLubyte *srcPtr, GLint srcRowStride, - GLint dstWidth, GLint dstHeight, GLint dstDepth, - GLubyte *dstPtr, GLint dstRowStride) -{ - const GLint bpt = bytes_per_pixel(datatype, comps); - const GLint srcWidthNB = srcWidth - 2 * border; /* sizes w/out border */ - const GLint dstWidthNB = dstWidth - 2 * border; - const GLint dstHeightNB = dstHeight - 2 * border; - const GLint dstDepthNB = dstDepth - 2 * border; - const GLint srcRowBytes = bpt * srcRowStride; - const GLint dstRowBytes = bpt * dstRowStride; - const GLubyte *srcA, *srcB; - GLubyte *dst; - GLint layer; - GLint row; - - /* Compute src and dst pointers, skipping any border */ - srcA = srcPtr + border * ((srcWidth + 1) * bpt); - if (srcHeight > 1) - srcB = srcA + srcRowBytes; - else - srcB = srcA; - dst = dstPtr + border * ((dstWidth + 1) * bpt); - - for (layer = 0; layer < dstDepthNB; layer++) { - for (row = 0; row < dstHeightNB; row++) { - do_row(datatype, comps, srcWidthNB, srcA, srcB, - dstWidthNB, dst); - srcA += 2 * srcRowBytes; - srcB += 2 * srcRowBytes; - dst += dstRowBytes; - } - - /* This is ugly but probably won't be used much */ - if (border > 0) { - /* fill in dest border */ - /* lower-left border pixel */ - assert(dstPtr); - assert(srcPtr); - memcpy(dstPtr, srcPtr, bpt); - /* lower-right border pixel */ - memcpy(dstPtr + (dstWidth - 1) * bpt, - srcPtr + (srcWidth - 1) * bpt, bpt); - /* upper-left border pixel */ - memcpy(dstPtr + dstWidth * (dstHeight - 1) * bpt, - srcPtr + srcWidth * (srcHeight - 1) * bpt, bpt); - /* upper-right border pixel */ - memcpy(dstPtr + (dstWidth * dstHeight - 1) * bpt, - srcPtr + (srcWidth * srcHeight - 1) * bpt, bpt); - /* lower border */ - do_row(datatype, comps, srcWidthNB, - srcPtr + bpt, - srcPtr + bpt, - dstWidthNB, dstPtr + bpt); - /* upper border */ - do_row(datatype, comps, srcWidthNB, - srcPtr + (srcWidth * (srcHeight - 1) + 1) * bpt, - srcPtr + (srcWidth * (srcHeight - 1) + 1) * bpt, - dstWidthNB, - dstPtr + (dstWidth * (dstHeight - 1) + 1) * bpt); - /* left and right borders */ - if (srcHeight == dstHeight) { - /* copy border pixel from src to dst */ - for (row = 1; row < srcHeight; row++) { - memcpy(dstPtr + dstWidth * row * bpt, - srcPtr + srcWidth * row * bpt, bpt); - memcpy(dstPtr + (dstWidth * row + dstWidth - 1) * bpt, - srcPtr + (srcWidth * row + srcWidth - 1) * bpt, bpt); - } - } - else { - /* average two src pixels each dest pixel */ - for (row = 0; row < dstHeightNB; row += 2) { - do_row(datatype, comps, 1, - srcPtr + (srcWidth * (row * 2 + 1)) * bpt, - srcPtr + (srcWidth * (row * 2 + 2)) * bpt, - 1, dstPtr + (dstWidth * row + 1) * bpt); - do_row(datatype, comps, 1, - srcPtr + (srcWidth * (row * 2 + 1) + srcWidth - 1) * bpt, - srcPtr + (srcWidth * (row * 2 + 2) + srcWidth - 1) * bpt, - 1, dstPtr + (dstWidth * row + 1 + dstWidth - 1) * bpt); - } + srcA = srcPtr[img * 2 + 0] + (bytesPerSrcImage - bpt); + srcB = srcPtr[img * 2 + srcImageOffset] + (bytesPerSrcImage - bpt); + dst = dstPtr[img] + (bytesPerDstImage - bpt); + do_row(datatype, comps, 1, srcA, srcB, 1, dst); } } } @@ -1603,39 +1715,40 @@ make_2d_stack_mipmap(GLenum datatype, GLuint comps, GLint border, /** * Down-sample a texture image to produce the next lower mipmap level. * \param comps components per texel (1, 2, 3 or 4) - * \param srcRowStride stride between source rows, in texels - * \param dstRowStride stride between destination rows, in texels + * \param srcData array[slice] of pointers to source image slices + * \param dstData array[slice] of pointers to dest image slices + * \param srcRowStride stride between source rows, in bytes + * \param dstRowStride stride between destination rows, in bytes */ void _mesa_generate_mipmap_level(GLenum target, GLenum datatype, GLuint comps, GLint border, GLint srcWidth, GLint srcHeight, GLint srcDepth, - const GLubyte *srcData, + const GLubyte **srcData, GLint srcRowStride, GLint dstWidth, GLint dstHeight, GLint dstDepth, - GLubyte *dstData, + GLubyte **dstData, GLint dstRowStride) { - /* - * We use simple 2x2 averaging to compute the next mipmap level. - */ + int i; + switch (target) { case GL_TEXTURE_1D: make_1d_mipmap(datatype, comps, border, - srcWidth, srcData, - dstWidth, dstData); + srcWidth, srcData[0], + dstWidth, dstData[0]); break; case GL_TEXTURE_2D: - case GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB: + case GL_TEXTURE_CUBE_MAP_POSITIVE_X: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: + case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: make_2d_mipmap(datatype, comps, border, - srcWidth, srcHeight, srcData, srcRowStride, - dstWidth, dstHeight, dstData, dstRowStride); + srcWidth, srcHeight, srcData[0], srcRowStride, + dstWidth, dstHeight, dstData[0], dstRowStride); break; case GL_TEXTURE_3D: make_3d_mipmap(datatype, comps, border, @@ -1645,24 +1758,28 @@ _mesa_generate_mipmap_level(GLenum target, dstData, dstRowStride); break; case GL_TEXTURE_1D_ARRAY_EXT: - make_1d_stack_mipmap(datatype, comps, border, - srcWidth, srcData, srcRowStride, - dstWidth, dstHeight, - dstData, dstRowStride); + assert(srcHeight == 1); + assert(dstHeight == 1); + for (i = 0; i < dstDepth; i++) { + make_1d_mipmap(datatype, comps, border, + srcWidth, srcData[i], + dstWidth, dstData[i]); + } break; case GL_TEXTURE_2D_ARRAY_EXT: - make_2d_stack_mipmap(datatype, comps, border, - srcWidth, srcHeight, - srcData, srcRowStride, - dstWidth, dstHeight, - dstDepth, dstData, dstRowStride); + case GL_TEXTURE_CUBE_MAP_ARRAY: + for (i = 0; i < dstDepth; i++) { + make_2d_mipmap(datatype, comps, border, + srcWidth, srcHeight, srcData[i], srcRowStride, + dstWidth, dstHeight, dstData[i], dstRowStride); + } break; case GL_TEXTURE_RECTANGLE_NV: + case GL_TEXTURE_EXTERNAL_OES: /* no mipmaps, do nothing */ break; default: - _mesa_problem(NULL, "bad dimensions in _mesa_generate_mipmaps"); - return; + unreachable("bad tex target in _mesa_generate_mipmaps"); } } @@ -1671,8 +1788,8 @@ _mesa_generate_mipmap_level(GLenum target, * compute next (level+1) image size * \return GL_FALSE if no smaller size can be generated (eg. src is 1x1x1 size) */ -static GLboolean -next_mipmap_level_size(GLenum target, GLint border, +GLboolean +_mesa_next_mipmap_level_size(GLenum target, GLint border, GLint srcWidth, GLint srcHeight, GLint srcDepth, GLint *dstWidth, GLint *dstHeight, GLint *dstDepth) { @@ -1683,8 +1800,9 @@ next_mipmap_level_size(GLenum target, GLint border, *dstWidth = srcWidth; /* can't go smaller */ } - if ((srcHeight - 2 * border > 1) && - (target != GL_TEXTURE_1D_ARRAY_EXT)) { + if ((srcHeight - 2 * border > 1) && + target != GL_TEXTURE_1D_ARRAY_EXT && + target != GL_PROXY_TEXTURE_1D_ARRAY_EXT) { *dstHeight = (srcHeight - 2 * border) / 2 + 2 * border; } else { @@ -1692,7 +1810,10 @@ next_mipmap_level_size(GLenum target, GLint border, } if ((srcDepth - 2 * border > 1) && - (target != GL_TEXTURE_2D_ARRAY_EXT)) { + target != GL_TEXTURE_2D_ARRAY_EXT && + target != GL_PROXY_TEXTURE_2D_ARRAY_EXT && + target != GL_TEXTURE_CUBE_MAP_ARRAY && + target != GL_PROXY_TEXTURE_CUBE_MAP_ARRAY) { *dstDepth = (srcDepth - 2 * border) / 2 + 2 * border; } else { @@ -1710,317 +1831,425 @@ next_mipmap_level_size(GLenum target, GLint border, } - - /** - * Automatic mipmap generation. - * This is the fallback/default function for ctx->Driver.GenerateMipmap(). - * Generate a complete set of mipmaps from texObj's BaseLevel image. - * Stop at texObj's MaxLevel or when we get to the 1x1 texture. - * For cube maps, target will be one of - * GL_TEXTURE_CUBE_MAP_POSITIVE/NEGATIVE_X/Y/Z; never GL_TEXTURE_CUBE_MAP. + * Helper function for mipmap generation. + * Make sure the specified destination mipmap level is the right size/format + * for mipmap generation. If not, (re) allocate it. + * \return GL_TRUE if successful, GL_FALSE if mipmap generation should stop */ -void -_mesa_generate_mipmap(struct gl_context *ctx, GLenum target, - struct gl_texture_object *texObj) +static GLboolean +prepare_mipmap_level(struct gl_context *ctx, + struct gl_texture_object *texObj, GLuint level, + GLsizei width, GLsizei height, GLsizei depth, + GLsizei border, GLenum intFormat, mesa_format format) { - const struct gl_texture_image *srcImage; - gl_format convertFormat; - const GLubyte *srcData = NULL; - GLubyte *dstData = NULL; - GLint level, maxLevels; - GLenum datatype; - GLuint comps; + const GLuint numFaces = _mesa_num_tex_faces(texObj->Target); + GLuint face; - ASSERT(texObj); - srcImage = _mesa_select_tex_image(ctx, texObj, target, texObj->BaseLevel); - ASSERT(srcImage); + if (texObj->Immutable) { + /* The texture was created with glTexStorage() so the number/size of + * mipmap levels is fixed and the storage for all images is already + * allocated. + */ + if (!texObj->Image[0][level]) { + /* No more levels to create - we're done */ + return GL_FALSE; + } + else { + /* Nothing to do - the texture memory must have already been + * allocated to the right size so we're all set. + */ + return GL_TRUE; + } + } - maxLevels = _mesa_max_texture_levels(ctx, texObj->Target); - ASSERT(maxLevels > 0); /* bad target */ + for (face = 0; face < numFaces; face++) { + struct gl_texture_image *dstImage; + const GLenum target = _mesa_cube_face_target(texObj->Target, face); - /* Find convertFormat - the format that do_row() will process */ + dstImage = _mesa_get_tex_image(ctx, texObj, target, level); + if (!dstImage) { + /* out of memory */ + return GL_FALSE; + } - if (_mesa_is_format_compressed(srcImage->TexFormat)) { - /* setup for compressed textures - need to allocate temporary - * image buffers to hold uncompressed images. - */ - GLuint row; - GLint components, size; - GLchan *dst; - - assert(texObj->Target == GL_TEXTURE_2D || - texObj->Target == GL_TEXTURE_CUBE_MAP_ARB); - - if (srcImage->_BaseFormat == GL_RGB) { - convertFormat = MESA_FORMAT_RGB888; - components = 3; - } else if (srcImage->_BaseFormat == GL_RED) { - convertFormat = MESA_FORMAT_R8; - components = 1; - } else if (srcImage->_BaseFormat == GL_RG) { - convertFormat = MESA_FORMAT_RG88; - components = 2; - } else if (srcImage->_BaseFormat == GL_RGBA) { - convertFormat = MESA_FORMAT_RGBA8888; - components = 4; - } else if (srcImage->_BaseFormat == GL_LUMINANCE) { - convertFormat = MESA_FORMAT_L8; - components = 1; - } else if (srcImage->_BaseFormat == GL_LUMINANCE_ALPHA) { - convertFormat = MESA_FORMAT_AL88; - components = 2; - } else { - _mesa_problem(ctx, "bad srcImage->_BaseFormat in _mesa_generate_mipmaps"); - return; - } - - /* allocate storage for uncompressed GL_RGB or GL_RGBA images */ - size = _mesa_bytes_per_pixel(srcImage->_BaseFormat, CHAN_TYPE) - * srcImage->Width * srcImage->Height * srcImage->Depth + 20; - /* 20 extra bytes, just be safe when calling last FetchTexel */ - srcData = (GLubyte *) malloc(size); - if (!srcData) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "generate mipmaps"); - return; - } - dstData = (GLubyte *) malloc(size / 2); /* 1/4 would probably be OK */ - if (!dstData) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "generate mipmaps"); - free((void *) srcData); - return; - } - - /* decompress base image here */ - dst = (GLchan *) srcData; - for (row = 0; row < srcImage->Height; row++) { - GLuint col; - for (col = 0; col < srcImage->Width; col++) { - srcImage->FetchTexelc(srcImage, col, row, 0, dst); - dst += components; - } + if (dstImage->Width != width || + dstImage->Height != height || + dstImage->Depth != depth || + dstImage->Border != border || + dstImage->InternalFormat != intFormat || + dstImage->TexFormat != format) { + /* need to (re)allocate image */ + ctx->Driver.FreeTextureImageBuffer(ctx, dstImage); + + _mesa_init_teximage_fields(ctx, dstImage, + width, height, depth, + border, intFormat, format); + + ctx->Driver.AllocTextureImageBuffer(ctx, dstImage); + + /* in case the mipmap level is part of an FBO: */ + _mesa_update_fbo_texture(ctx, texObj, face, level); + + ctx->NewState |= _NEW_TEXTURE_OBJECT; } } - else { - /* uncompressed */ - convertFormat = srcImage->TexFormat; + + return GL_TRUE; +} + + +/** + * Prepare all mipmap levels beyond 'baseLevel' for mipmap generation. + * When finished, all the gl_texture_image structures for the smaller + * mipmap levels will be consistent with the base level (in terms of + * dimensions, format, etc). + */ +void +_mesa_prepare_mipmap_levels(struct gl_context *ctx, + struct gl_texture_object *texObj, + unsigned baseLevel, unsigned maxLevel) +{ + const struct gl_texture_image *baseImage = + _mesa_select_tex_image(texObj, texObj->Target, baseLevel); + const GLint border = 0; + GLint width = baseImage->Width; + GLint height = baseImage->Height; + GLint depth = baseImage->Depth; + const GLenum intFormat = baseImage->InternalFormat; + const mesa_format texFormat = baseImage->TexFormat; + GLint newWidth, newHeight, newDepth; + + /* Prepare baseLevel + 1, baseLevel + 2, ... */ + for (unsigned level = baseLevel + 1; level <= maxLevel; level++) { + if (!_mesa_next_mipmap_level_size(texObj->Target, border, + width, height, depth, + &newWidth, &newHeight, &newDepth)) { + /* all done */ + break; + } + + if (!prepare_mipmap_level(ctx, texObj, level, + newWidth, newHeight, newDepth, + border, intFormat, texFormat)) { + break; + } + + width = newWidth; + height = newHeight; + depth = newDepth; } +} - _mesa_format_to_type_and_comps(convertFormat, &datatype, &comps); - for (level = texObj->BaseLevel; level < texObj->MaxLevel - && level < maxLevels - 1; level++) { +static void +generate_mipmap_uncompressed(struct gl_context *ctx, GLenum target, + struct gl_texture_object *texObj, + const struct gl_texture_image *srcImage, + GLuint maxLevel) +{ + GLuint level; + GLenum datatype; + GLuint comps; + + _mesa_uncompressed_format_to_type_and_comps(srcImage->TexFormat, &datatype, &comps); + + for (level = texObj->BaseLevel; level < maxLevel; level++) { /* generate image[level+1] from image[level] */ - const struct gl_texture_image *srcImage; - struct gl_texture_image *dstImage; + struct gl_texture_image *srcImage, *dstImage; + GLint srcRowStride, dstRowStride; GLint srcWidth, srcHeight, srcDepth; GLint dstWidth, dstHeight, dstDepth; GLint border; - GLboolean nextLevel; + GLint slice; + GLubyte **srcMaps, **dstMaps; + GLboolean success = GL_TRUE; /* get src image parameters */ - srcImage = _mesa_select_tex_image(ctx, texObj, target, level); - ASSERT(srcImage); + srcImage = _mesa_select_tex_image(texObj, target, level); + assert(srcImage); srcWidth = srcImage->Width; srcHeight = srcImage->Height; srcDepth = srcImage->Depth; border = srcImage->Border; - nextLevel = next_mipmap_level_size(target, border, - srcWidth, srcHeight, srcDepth, - &dstWidth, &dstHeight, &dstDepth); - if (!nextLevel) { - /* all done */ - if (_mesa_is_format_compressed(srcImage->TexFormat)) { - free((void *) srcData); - free(dstData); - } - return; - } - /* get dest gl_texture_image */ - dstImage = _mesa_get_tex_image(ctx, texObj, target, level + 1); + dstImage = _mesa_select_tex_image(texObj, target, level + 1); if (!dstImage) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "generating mipmaps"); - return; + break; + } + dstWidth = dstImage->Width; + dstHeight = dstImage->Height; + dstDepth = dstImage->Depth; + + if (target == GL_TEXTURE_1D_ARRAY) { + srcDepth = srcHeight; + dstDepth = dstHeight; + srcHeight = 1; + dstHeight = 1; + } + + /* Map src texture image slices */ + srcMaps = calloc(srcDepth, sizeof(GLubyte *)); + if (srcMaps) { + for (slice = 0; slice < srcDepth; slice++) { + ctx->Driver.MapTextureImage(ctx, srcImage, slice, + 0, 0, srcWidth, srcHeight, + GL_MAP_READ_BIT, + &srcMaps[slice], &srcRowStride); + if (!srcMaps[slice]) { + success = GL_FALSE; + break; + } + } + } + else { + success = GL_FALSE; + } + + /* Map dst texture image slices */ + dstMaps = calloc(dstDepth, sizeof(GLubyte *)); + if (dstMaps) { + for (slice = 0; slice < dstDepth; slice++) { + ctx->Driver.MapTextureImage(ctx, dstImage, slice, + 0, 0, dstWidth, dstHeight, + GL_MAP_WRITE_BIT, + &dstMaps[slice], &dstRowStride); + if (!dstMaps[slice]) { + success = GL_FALSE; + break; + } + } + } + else { + success = GL_FALSE; } - /* Free old image data */ - if (dstImage->Data) - ctx->Driver.FreeTexImageData(ctx, dstImage); - - /* initialize new image */ - _mesa_init_teximage_fields(ctx, target, dstImage, dstWidth, dstHeight, - dstDepth, border, srcImage->InternalFormat, - srcImage->TexFormat); - dstImage->DriverData = NULL; - dstImage->FetchTexelc = srcImage->FetchTexelc; - dstImage->FetchTexelf = srcImage->FetchTexelf; + if (success) { + /* generate one mipmap level (for 1D/2D/3D/array/etc texture) */ + _mesa_generate_mipmap_level(target, datatype, comps, border, + srcWidth, srcHeight, srcDepth, + (const GLubyte **) srcMaps, srcRowStride, + dstWidth, dstHeight, dstDepth, + dstMaps, dstRowStride); + } - /* Alloc new teximage data buffer */ - { - GLuint size = _mesa_format_image_size(dstImage->TexFormat, - dstWidth, dstHeight, dstDepth); - dstImage->Data = _mesa_alloc_texmemory(size); - if (!dstImage->Data) { - _mesa_error(ctx, GL_OUT_OF_MEMORY, "generating mipmaps"); - return; + /* Unmap src image slices */ + if (srcMaps) { + for (slice = 0; slice < srcDepth; slice++) { + if (srcMaps[slice]) { + ctx->Driver.UnmapTextureImage(ctx, srcImage, slice); + } } + free(srcMaps); } - /* Setup src and dest data pointers */ - if (_mesa_is_format_compressed(dstImage->TexFormat)) { - /* srcData and dstData are already set */ - ASSERT(srcData); - ASSERT(dstData); + /* Unmap dst image slices */ + if (dstMaps) { + for (slice = 0; slice < dstDepth; slice++) { + if (dstMaps[slice]) { + ctx->Driver.UnmapTextureImage(ctx, dstImage, slice); + } + } + free(dstMaps); } - else { - srcData = (const GLubyte *) srcImage->Data; - dstData = (GLubyte *) dstImage->Data; + + if (!success) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "mipmap generation"); + break; } + } /* loop over mipmap levels */ +} + + +static void +generate_mipmap_compressed(struct gl_context *ctx, GLenum target, + struct gl_texture_object *texObj, + struct gl_texture_image *srcImage, + GLuint maxLevel) +{ + GLuint level; + mesa_format temp_format; + GLint components; + GLuint temp_src_row_stride, temp_src_img_stride; /* in bytes */ + GLubyte *temp_src = NULL, *temp_dst = NULL; + GLenum temp_datatype; + GLenum temp_base_format; + GLubyte **temp_src_slices = NULL, **temp_dst_slices = NULL; + + /* only two types of compressed textures at this time */ + assert(texObj->Target == GL_TEXTURE_2D || + texObj->Target == GL_TEXTURE_2D_ARRAY || + texObj->Target == GL_TEXTURE_CUBE_MAP || + texObj->Target == GL_TEXTURE_CUBE_MAP_ARRAY); + + /* + * Choose a format for the temporary, uncompressed base image. + * Then, get number of components, choose temporary image datatype, + * and get base format. + */ + temp_format = _mesa_get_uncompressed_format(srcImage->TexFormat); + + components = _mesa_format_num_components(temp_format); - ASSERT(dstImage->TexFormat); - ASSERT(dstImage->FetchTexelc); - ASSERT(dstImage->FetchTexelf); + switch (_mesa_get_format_datatype(srcImage->TexFormat)) { + case GL_FLOAT: + temp_datatype = GL_FLOAT; + break; + case GL_SIGNED_NORMALIZED: + /* Revisit this if we get compressed formats with >8 bits per component */ + temp_datatype = GL_BYTE; + break; + default: + temp_datatype = GL_UNSIGNED_BYTE; + } + + temp_base_format = _mesa_get_format_base_format(temp_format); - _mesa_generate_mipmap_level(target, datatype, comps, border, - srcWidth, srcHeight, srcDepth, - srcData, srcImage->RowStride, - dstWidth, dstHeight, dstDepth, - dstData, dstImage->RowStride); + /* allocate storage for the temporary, uncompressed image */ + temp_src_row_stride = _mesa_format_row_stride(temp_format, srcImage->Width); + temp_src_img_stride = _mesa_format_image_size(temp_format, srcImage->Width, + srcImage->Height, 1); + temp_src = malloc(temp_src_img_stride * srcImage->Depth); - if (_mesa_is_format_compressed(dstImage->TexFormat)) { - GLubyte *temp; - /* compress image from dstData into dstImage->Data */ - const GLenum srcFormat = _mesa_get_format_base_format(convertFormat); - GLint dstRowStride - = _mesa_format_row_stride(dstImage->TexFormat, dstWidth); + /* Allocate storage for arrays of slice pointers */ + temp_src_slices = malloc(srcImage->Depth * sizeof(GLubyte *)); + temp_dst_slices = malloc(srcImage->Depth * sizeof(GLubyte *)); - _mesa_texstore(ctx, 2, dstImage->_BaseFormat, - dstImage->TexFormat, - dstImage->Data, - 0, 0, 0, /* dstX/Y/Zoffset */ - dstRowStride, 0, /* strides */ - dstWidth, dstHeight, 1, /* size */ - srcFormat, CHAN_TYPE, - dstData, /* src data, actually */ - &ctx->DefaultPacking); + if (!temp_src || !temp_src_slices || !temp_dst_slices) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "generate mipmaps"); + goto end; + } + + /* decompress base image to the temporary src buffer */ + { + /* save pixel packing mode */ + struct gl_pixelstore_attrib save = ctx->Pack; + /* use default/tight packing parameters */ + ctx->Pack = ctx->DefaultPacking; - /* swap src and dest pointers */ - temp = (GLubyte *) srcData; - srcData = dstData; - dstData = temp; + /* Get the uncompressed image */ + assert(srcImage->Level == texObj->BaseLevel); + ctx->Driver.GetTexSubImage(ctx, + 0, 0, 0, + srcImage->Width, srcImage->Height, + srcImage->Depth, + temp_base_format, temp_datatype, + temp_src, srcImage); + /* restore packing mode */ + ctx->Pack = save; + } + + for (level = texObj->BaseLevel; level < maxLevel; level++) { + /* generate image[level+1] from image[level] */ + const struct gl_texture_image *srcImage; + struct gl_texture_image *dstImage; + GLint srcWidth, srcHeight, srcDepth; + GLint dstWidth, dstHeight, dstDepth; + GLint border; + GLuint temp_dst_row_stride, temp_dst_img_stride; /* in bytes */ + GLint i; + + /* get src image parameters */ + srcImage = _mesa_select_tex_image(texObj, target, level); + assert(srcImage); + srcWidth = srcImage->Width; + srcHeight = srcImage->Height; + srcDepth = srcImage->Depth; + border = srcImage->Border; + + /* get dest gl_texture_image */ + dstImage = _mesa_select_tex_image(texObj, target, level + 1); + if (!dstImage) { + break; + } + dstWidth = dstImage->Width; + dstHeight = dstImage->Height; + dstDepth = dstImage->Depth; + + /* Compute dst image strides and alloc memory on first iteration */ + temp_dst_row_stride = _mesa_format_row_stride(temp_format, dstWidth); + temp_dst_img_stride = _mesa_format_image_size(temp_format, dstWidth, + dstHeight, 1); + if (!temp_dst) { + temp_dst = malloc(temp_dst_img_stride * dstDepth); + if (!temp_dst) { + _mesa_error(ctx, GL_OUT_OF_MEMORY, "generate mipmaps"); + goto end; + } } + /* for 2D arrays, setup array[depth] of slice pointers */ + for (i = 0; i < srcDepth; i++) { + temp_src_slices[i] = temp_src + temp_src_img_stride * i; + } + for (i = 0; i < dstDepth; i++) { + temp_dst_slices[i] = temp_dst + temp_dst_img_stride * i; + } + + /* Rescale src image to dest image. + * This will loop over the slices of a 2D array. + */ + _mesa_generate_mipmap_level(target, temp_datatype, components, border, + srcWidth, srcHeight, srcDepth, + (const GLubyte **) temp_src_slices, + temp_src_row_stride, + dstWidth, dstHeight, dstDepth, + temp_dst_slices, temp_dst_row_stride); + + /* The image space was allocated above so use glTexSubImage now */ + ctx->Driver.TexSubImage(ctx, 2, dstImage, + 0, 0, 0, dstWidth, dstHeight, dstDepth, + temp_base_format, temp_datatype, + temp_dst, &ctx->DefaultPacking); + + /* swap src and dest pointers */ + { + GLubyte *temp = temp_src; + temp_src = temp_dst; + temp_dst = temp; + temp_src_row_stride = temp_dst_row_stride; + temp_src_img_stride = temp_dst_img_stride; + } } /* loop over mipmap levels */ -} +end: + free(temp_src); + free(temp_dst); + free(temp_src_slices); + free(temp_dst_slices); +} /** - * Helper function for drivers which need to rescale texture images to - * certain aspect ratios. - * Nearest filtering only (for broken hardware that can't support - * all aspect ratios). This can be made a lot faster, but I don't - * really care enough... + * Automatic mipmap generation. + * This is the fallback/default function for ctx->Driver.GenerateMipmap(). + * Generate a complete set of mipmaps from texObj's BaseLevel image. + * Stop at texObj's MaxLevel or when we get to the 1x1 texture. + * For cube maps, target will be one of + * GL_TEXTURE_CUBE_MAP_POSITIVE/NEGATIVE_X/Y/Z; never GL_TEXTURE_CUBE_MAP. */ void -_mesa_rescale_teximage2d(GLuint bytesPerPixel, - GLuint srcStrideInPixels, - GLuint dstRowStride, - GLint srcWidth, GLint srcHeight, - GLint dstWidth, GLint dstHeight, - const GLvoid *srcImage, GLvoid *dstImage) +_mesa_generate_mipmap(struct gl_context *ctx, GLenum target, + struct gl_texture_object *texObj) { - GLint row, col; - -#define INNER_LOOP( TYPE, HOP, WOP ) \ - for ( row = 0 ; row < dstHeight ; row++ ) { \ - GLint srcRow = row HOP hScale; \ - for ( col = 0 ; col < dstWidth ; col++ ) { \ - GLint srcCol = col WOP wScale; \ - dst[col] = src[srcRow * srcStrideInPixels + srcCol]; \ - } \ - dst = (TYPE *) ((GLubyte *) dst + dstRowStride); \ - } \ - -#define RESCALE_IMAGE( TYPE ) \ -do { \ - const TYPE *src = (const TYPE *)srcImage; \ - TYPE *dst = (TYPE *)dstImage; \ - \ - if ( srcHeight < dstHeight ) { \ - const GLint hScale = dstHeight / srcHeight; \ - if ( srcWidth < dstWidth ) { \ - const GLint wScale = dstWidth / srcWidth; \ - INNER_LOOP( TYPE, /, / ); \ - } \ - else { \ - const GLint wScale = srcWidth / dstWidth; \ - INNER_LOOP( TYPE, /, * ); \ - } \ - } \ - else { \ - const GLint hScale = srcHeight / dstHeight; \ - if ( srcWidth < dstWidth ) { \ - const GLint wScale = dstWidth / srcWidth; \ - INNER_LOOP( TYPE, *, / ); \ - } \ - else { \ - const GLint wScale = srcWidth / dstWidth; \ - INNER_LOOP( TYPE, *, * ); \ - } \ - } \ -} while (0) - - switch ( bytesPerPixel ) { - case 4: - RESCALE_IMAGE( GLuint ); - break; + struct gl_texture_image *srcImage; + GLint maxLevel; - case 2: - RESCALE_IMAGE( GLushort ); - break; + assert(texObj); + srcImage = _mesa_select_tex_image(texObj, target, texObj->BaseLevel); + assert(srcImage); - case 1: - RESCALE_IMAGE( GLubyte ); - break; - default: - _mesa_problem(NULL,"unexpected bytes/pixel in _mesa_rescale_teximage2d"); - } -} + maxLevel = _mesa_max_texture_levels(ctx, texObj->Target) - 1; + assert(maxLevel >= 0); /* bad target */ + maxLevel = MIN2(maxLevel, texObj->MaxLevel); -/** - * Upscale an image by replication, not (typical) stretching. - * We use this when the image width or height is less than a - * certain size (4, 8) and we need to upscale an image. - */ -void -_mesa_upscale_teximage2d(GLsizei inWidth, GLsizei inHeight, - GLsizei outWidth, GLsizei outHeight, - GLint comps, const GLchan *src, GLint srcRowStride, - GLchan *dest ) -{ - GLint i, j, k; - - ASSERT(outWidth >= inWidth); - ASSERT(outHeight >= inHeight); -#if 0 - ASSERT(inWidth == 1 || inWidth == 2 || inHeight == 1 || inHeight == 2); - ASSERT((outWidth & 3) == 0); - ASSERT((outHeight & 3) == 0); -#endif - - for (i = 0; i < outHeight; i++) { - const GLint ii = i % inHeight; - for (j = 0; j < outWidth; j++) { - const GLint jj = j % inWidth; - for (k = 0; k < comps; k++) { - dest[(i * outWidth + j) * comps + k] - = src[ii * srcRowStride + jj * comps + k]; - } - } + _mesa_prepare_mipmap_levels(ctx, texObj, texObj->BaseLevel, maxLevel); + + if (_mesa_is_format_compressed(srcImage->TexFormat)) { + generate_mipmap_compressed(ctx, target, texObj, srcImage, maxLevel); + } else { + generate_mipmap_uncompressed(ctx, target, texObj, srcImage, maxLevel); } } -