/* * Mesa 3-D graphics library * * Copyright (C) 2011 VMware, Inc. * * 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. */ /** * Functions for mapping/unmapping texture images. */ #include "main/context.h" #include "main/fbobject.h" #include "main/teximage.h" #include "main/texobj.h" #include "util/u_memory.h" #include "util/u_math.h" #include "swrast/swrast.h" #include "swrast/s_context.h" /** * Allocate a new swrast_texture_image (a subclass of gl_texture_image). * Called via ctx->Driver.NewTextureImage(). */ struct gl_texture_image * _swrast_new_texture_image( struct gl_context *ctx ) { (void) ctx; return (struct gl_texture_image *) CALLOC_STRUCT(swrast_texture_image); } /** * Free a swrast_texture_image (a subclass of gl_texture_image). * Called via ctx->Driver.DeleteTextureImage(). */ void _swrast_delete_texture_image(struct gl_context *ctx, struct gl_texture_image *texImage) { /* Nothing special for the subclass yet */ _mesa_delete_texture_image(ctx, texImage); } static unsigned int texture_slices(const struct gl_texture_image *texImage) { if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) return texImage->Height; else return texImage->Depth; } unsigned int _swrast_teximage_slice_height(struct gl_texture_image *texImage) { /* For 1D array textures, the slices are all 1 pixel high, and Height is * the number of slices. */ if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) return 1; else return texImage->Height; } /** * Called via ctx->Driver.AllocTextureImageBuffer() */ GLboolean _swrast_alloc_texture_image_buffer(struct gl_context *ctx, struct gl_texture_image *texImage) { struct swrast_texture_image *swImg = swrast_texture_image(texImage); GLuint bytesPerSlice; GLuint slices = texture_slices(texImage); GLuint i; if (!_swrast_init_texture_image(texImage)) return GL_FALSE; bytesPerSlice = _mesa_format_image_size(texImage->TexFormat, texImage->Width, _swrast_teximage_slice_height(texImage), 1); assert(!swImg->Buffer); swImg->Buffer = align_malloc(bytesPerSlice * slices, 512); if (!swImg->Buffer) return GL_FALSE; /* RowStride and ImageSlices[] describe how to address texels in 'Data' */ swImg->RowStride = _mesa_format_row_stride(texImage->TexFormat, texImage->Width); for (i = 0; i < slices; i++) { swImg->ImageSlices[i] = swImg->Buffer + bytesPerSlice * i; } return GL_TRUE; } /** * Code that overrides ctx->Driver.AllocTextureImageBuffer may use this to * initialize the fields of swrast_texture_image without allocating the image * buffer or initializing RowStride or the contents of ImageSlices. * * Returns GL_TRUE on success, GL_FALSE on memory allocation failure. */ GLboolean _swrast_init_texture_image(struct gl_texture_image *texImage) { struct swrast_texture_image *swImg = swrast_texture_image(texImage); if ((texImage->Width == 1 || util_is_power_of_two_or_zero(texImage->Width2)) && (texImage->Height == 1 || util_is_power_of_two_or_zero(texImage->Height2)) && (texImage->Depth == 1 || util_is_power_of_two_or_zero(texImage->Depth2))) swImg->_IsPowerOfTwo = GL_TRUE; else swImg->_IsPowerOfTwo = GL_FALSE; /* Compute Width/Height/DepthScale for mipmap lod computation */ if (texImage->TexObject->Target == GL_TEXTURE_RECTANGLE_NV) { /* scale = 1.0 since texture coords directly map to texels */ swImg->WidthScale = 1.0; swImg->HeightScale = 1.0; swImg->DepthScale = 1.0; } else { swImg->WidthScale = (GLfloat) texImage->Width; swImg->HeightScale = (GLfloat) texImage->Height; swImg->DepthScale = (GLfloat) texImage->Depth; } assert(!swImg->ImageSlices); swImg->ImageSlices = calloc(texture_slices(texImage), sizeof(void *)); if (!swImg->ImageSlices) return GL_FALSE; return GL_TRUE; } /** * Called via ctx->Driver.FreeTextureImageBuffer() */ void _swrast_free_texture_image_buffer(struct gl_context *ctx, struct gl_texture_image *texImage) { struct swrast_texture_image *swImage = swrast_texture_image(texImage); align_free(swImage->Buffer); swImage->Buffer = NULL; free(swImage->ImageSlices); swImage->ImageSlices = NULL; } /** * Error checking for debugging only. */ static void check_map_teximage(const struct gl_texture_image *texImage, GLuint slice, GLuint x, GLuint y, GLuint w, GLuint h) { if (texImage->TexObject->Target == GL_TEXTURE_1D) assert(y == 0 && h == 1); assert(x < texImage->Width || texImage->Width == 0); assert(y < texImage->Height || texImage->Height == 0); assert(x + w <= texImage->Width); assert(y + h <= texImage->Height); assert(slice < texture_slices(texImage)); } /** * Map a 2D slice of a texture image into user space. * (x,y,w,h) defines a region of interest (ROI). Reading/writing texels * outside of the ROI is undefined. * * \param texImage the texture image * \param slice the 3D image slice or array texture slice * \param x, y, w, h region of interest * \param mode bitmask of GL_MAP_READ_BIT, GL_MAP_WRITE_BIT * \param mapOut returns start of mapping of region of interest * \param rowStrideOut returns row stride (in bytes) */ void _swrast_map_teximage(struct gl_context *ctx, struct gl_texture_image *texImage, GLuint slice, GLuint x, GLuint y, GLuint w, GLuint h, GLbitfield mode, GLubyte **mapOut, GLint *rowStrideOut) { struct swrast_texture_image *swImage = swrast_texture_image(texImage); GLubyte *map; GLint stride, texelSize; GLuint bw, bh; check_map_teximage(texImage, slice, x, y, w, h); if (!swImage->Buffer) { /* Either glTexImage was called with a NULL argument or * we ran out of memory when allocating texture memory, */ *mapOut = NULL; *rowStrideOut = 0; return; } texelSize = _mesa_get_format_bytes(texImage->TexFormat); stride = _mesa_format_row_stride(texImage->TexFormat, texImage->Width); _mesa_get_format_block_size(texImage->TexFormat, &bw, &bh); assert(x % bw == 0); assert(y % bh == 0); /* This function can only be used with a swrast-allocated buffer, in which * case ImageSlices is populated with pointers into Buffer. */ assert(swImage->Buffer); assert(swImage->Buffer == swImage->ImageSlices[0]); map = swImage->ImageSlices[slice]; /* apply x/y offset to map address */ map += stride * (y / bh) + texelSize * (x / bw); *mapOut = map; *rowStrideOut = stride; } void _swrast_unmap_teximage(struct gl_context *ctx, struct gl_texture_image *texImage, GLuint slice) { /* nop */ } void _swrast_map_texture(struct gl_context *ctx, struct gl_texture_object *texObj) { const GLuint faces = _mesa_num_tex_faces(texObj->Target); GLuint face, level; for (face = 0; face < faces; face++) { for (level = texObj->BaseLevel; level < MAX_TEXTURE_LEVELS; level++) { struct gl_texture_image *texImage = texObj->Image[face][level]; struct swrast_texture_image *swImage = swrast_texture_image(texImage); unsigned int i, slices; if (!texImage) continue; /* In the case of a swrast-allocated texture buffer, the ImageSlices * and RowStride are always available. */ if (swImage->Buffer) { assert(swImage->ImageSlices[0] == swImage->Buffer); continue; } if (!swImage->ImageSlices) { swImage->ImageSlices = calloc(texture_slices(texImage), sizeof(void *)); if (!swImage->ImageSlices) continue; } slices = texture_slices(texImage); for (i = 0; i < slices; i++) { GLubyte *map; GLint rowStride; if (swImage->ImageSlices[i]) continue; ctx->Driver.MapTextureImage(ctx, texImage, i, 0, 0, texImage->Width, texImage->Height, GL_MAP_READ_BIT | GL_MAP_WRITE_BIT, &map, &rowStride); swImage->ImageSlices[i] = map; /* A swrast-using driver has to return the same rowstride for * every slice of the same texture, since we don't track them * separately. */ if (i == 0) swImage->RowStride = rowStride; else assert(swImage->RowStride == rowStride); } } } } void _swrast_unmap_texture(struct gl_context *ctx, struct gl_texture_object *texObj) { const GLuint faces = _mesa_num_tex_faces(texObj->Target); GLuint face, level; for (face = 0; face < faces; face++) { for (level = texObj->BaseLevel; level < MAX_TEXTURE_LEVELS; level++) { struct gl_texture_image *texImage = texObj->Image[face][level]; struct swrast_texture_image *swImage = swrast_texture_image(texImage); unsigned int i, slices; if (!texImage) continue; if (swImage->Buffer) return; if (!swImage->ImageSlices) continue; slices = texture_slices(texImage); for (i = 0; i < slices; i++) { if (swImage->ImageSlices[i]) { ctx->Driver.UnmapTextureImage(ctx, texImage, i); swImage->ImageSlices[i] = NULL; } } } } } /** * Map all textures for reading prior to software rendering. */ void _swrast_map_textures(struct gl_context *ctx) { int unit; for (unit = 0; unit <= ctx->Texture._MaxEnabledTexImageUnit; unit++) { struct gl_texture_object *texObj = ctx->Texture.Unit[unit]._Current; if (texObj) _swrast_map_texture(ctx, texObj); } } /** * Unmap all textures for reading prior to software rendering. */ void _swrast_unmap_textures(struct gl_context *ctx) { int unit; for (unit = 0; unit <= ctx->Texture._MaxEnabledTexImageUnit; unit++) { struct gl_texture_object *texObj = ctx->Texture.Unit[unit]._Current; if (texObj) _swrast_unmap_texture(ctx, texObj); } }