static void GLAPIENTRY _mesa_GetnPixelMapusvARB( GLenum map, GLsizei bufSize, GLushort *values ) { GET_CURRENT_CONTEXT(ctx); GLint mapsize, i; const struct gl_pixelmap *pm; ASSERT_OUTSIDE_BEGIN_END(ctx); pm = get_pixelmap(ctx, map); if (!pm) { _mesa_error(ctx, GL_INVALID_ENUM, "glGetPixelMapusv(map)"); return; } mapsize = pm->Size; if (!validate_pbo_access(ctx, &ctx->Pack, mapsize, GL_INTENSITY, GL_UNSIGNED_SHORT, bufSize, values)) { return; } values = (GLushort *) _mesa_map_pbo_dest(ctx, &ctx->Pack, values); if (!values) { if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glGetPixelMapusv(PBO is mapped)"); } return; } switch (map) { /* special cases */ case GL_PIXEL_MAP_I_TO_I: for (i = 0; i < mapsize; i++) { values[i] = (GLushort) CLAMP(ctx->PixelMaps.ItoI.Map[i], 0.0, 65535.); } break; case GL_PIXEL_MAP_S_TO_S: for (i = 0; i < mapsize; i++) { values[i] = (GLushort) CLAMP(ctx->PixelMaps.StoS.Map[i], 0.0, 65535.); } break; default: for (i = 0; i < mapsize; i++) { CLAMPED_FLOAT_TO_USHORT(values[i], pm->Map[i] ); } } _mesa_unmap_pbo_dest(ctx, &ctx->Pack); }
/** * Software fallback routine for ctx->Driver.ReadPixels(). * By time we get here, all error checking will have been done. */ void _mesa_readpixels(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *packing, GLvoid *pixels) { struct gl_pixelstore_attrib clippedPacking = *packing; if (ctx->NewState) _mesa_update_state(ctx); /* Do all needed clipping here, so that we can forget about it later */ if (_mesa_clip_readpixels(ctx, &x, &y, &width, &height, &clippedPacking)) { pixels = _mesa_map_pbo_dest(ctx, &clippedPacking, pixels); if (pixels) { /* Try memcpy first. */ if (readpixels_memcpy(ctx, x, y, width, height, format, type, pixels, packing)) { _mesa_unmap_pbo_dest(ctx, &clippedPacking); return; } /* Otherwise take the slow path. */ switch (format) { case GL_STENCIL_INDEX: read_stencil_pixels(ctx, x, y, width, height, type, pixels, &clippedPacking); break; case GL_DEPTH_COMPONENT: read_depth_pixels(ctx, x, y, width, height, type, pixels, &clippedPacking); break; case GL_DEPTH_STENCIL_EXT: read_depth_stencil_pixels(ctx, x, y, width, height, type, pixels, &clippedPacking); break; default: /* all other formats should be color formats */ read_rgba_pixels(ctx, x, y, width, height, format, type, pixels, &clippedPacking); } _mesa_unmap_pbo_dest(ctx, &clippedPacking); } } }
static void GLAPIENTRY _mesa_GetnPixelMapuivARB( GLenum map, GLsizei bufSize, GLuint *values ) { GET_CURRENT_CONTEXT(ctx); GLint mapsize, i; const struct gl_pixelmap *pm; ASSERT_OUTSIDE_BEGIN_END(ctx); pm = get_pixelmap(ctx, map); if (!pm) { _mesa_error(ctx, GL_INVALID_ENUM, "glGetPixelMapuiv(map)"); return; } mapsize = pm->Size; if (!validate_pbo_access(ctx, &ctx->Pack, mapsize, GL_INTENSITY, GL_UNSIGNED_INT, bufSize, values)) { return; } values = (GLuint *) _mesa_map_pbo_dest(ctx, &ctx->Pack, values); if (!values) { if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) { _mesa_error(ctx, GL_INVALID_OPERATION, "glGetPixelMapuiv(PBO is mapped)"); } return; } if (map == GL_PIXEL_MAP_S_TO_S) { /* special case */ memcpy(values, ctx->PixelMaps.StoS.Map, mapsize * sizeof(GLint)); } else { for (i = 0; i < mapsize; i++) { values[i] = FLOAT_TO_UINT( pm->Map[i] ); } } _mesa_unmap_pbo_dest(ctx, &ctx->Pack); }
/** * Combine PBO-write validation and mapping. * If any GL errors are detected, they'll be recorded and NULL returned. * \sa _mesa_validate_pbo_access * \sa _mesa_map_pbo_dest * A call to this function should have a matching call to * _mesa_unmap_pbo_dest(). */ GLvoid * _mesa_map_validate_pbo_dest(struct gl_context *ctx, GLuint dimensions, const struct gl_pixelstore_attrib *unpack, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei clientMemSize, GLvoid *ptr, const char *where) { assert(dimensions == 1 || dimensions == 2 || dimensions == 3); if (!_mesa_validate_pbo_access(dimensions, unpack, width, height, depth, format, type, clientMemSize, ptr)) { if (_mesa_is_bufferobj(unpack->BufferObj)) { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds PBO access)", where); } else { _mesa_error(ctx, GL_INVALID_OPERATION, "%s(out of bounds access: bufSize (%d) is too small)", where, clientMemSize); } return NULL; } if (!_mesa_is_bufferobj(unpack->BufferObj)) { /* non-PBO access: no further validation to be done */ return ptr; } if (_mesa_check_disallowed_mapping(unpack->BufferObj)) { /* buffer is already mapped - that's an error */ _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", where); return NULL; } ptr = _mesa_map_pbo_dest(ctx, unpack, ptr); return ptr; }
/** * This uses a blit to copy the read buffer to a texture format which matches * the format and type combo and then a fast read-back is done using memcpy. * We can do arbitrary X/Y/Z/W/0/1 swizzling here as long as there is * a format which matches the swizzling. * * If such a format isn't available, we fall back to _mesa_readpixels. * * NOTE: Some drivers use a blit to convert between tiled and linear * texture layouts during texture uploads/downloads, so the blit * we do here should be free in such cases. */ static void st_readpixels(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *pack, GLvoid *pixels) { struct st_context *st = st_context(ctx); struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); struct st_renderbuffer *strb = st_renderbuffer(rb); struct pipe_context *pipe = st->pipe; struct pipe_screen *screen = pipe->screen; struct pipe_resource *src; struct pipe_resource *dst = NULL; struct pipe_resource dst_templ; enum pipe_format dst_format, src_format; struct pipe_blit_info blit; unsigned bind = PIPE_BIND_TRANSFER_READ; struct pipe_transfer *tex_xfer; ubyte *map = NULL; /* Validate state (to be sure we have up-to-date framebuffer surfaces) * and flush the bitmap cache prior to reading. */ st_validate_state(st); st_flush_bitmap_cache(st); if (!st->prefer_blit_based_texture_transfer) { goto fallback; } /* This must be done after state validation. */ src = strb->texture; /* XXX Fallback for depth-stencil formats due to an incomplete * stencil blit implementation in some drivers. */ if (format == GL_DEPTH_STENCIL) { goto fallback; } /* We are creating a texture of the size of the region being read back. * Need to check for NPOT texture support. */ if (!screen->get_param(screen, PIPE_CAP_NPOT_TEXTURES) && (!util_is_power_of_two(width) || !util_is_power_of_two(height))) { goto fallback; } /* If the base internal format and the texture format don't match, we have * to use the slow path. */ if (rb->_BaseFormat != _mesa_get_format_base_format(rb->Format)) { goto fallback; } /* See if the texture format already matches the format and type, * in which case the memcpy-based fast path will likely be used and * we don't have to blit. */ if (_mesa_format_matches_format_and_type(rb->Format, format, type, pack->SwapBytes)) { goto fallback; } if (_mesa_readpixels_needs_slow_path(ctx, format, type, GL_TRUE)) { goto fallback; } /* Convert the source format to what is expected by ReadPixels * and see if it's supported. */ src_format = util_format_linear(src->format); src_format = util_format_luminance_to_red(src_format); src_format = util_format_intensity_to_red(src_format); if (!src_format || !screen->is_format_supported(screen, src_format, src->target, src->nr_samples, PIPE_BIND_SAMPLER_VIEW)) { goto fallback; } if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL) bind |= PIPE_BIND_DEPTH_STENCIL; else bind |= PIPE_BIND_RENDER_TARGET; /* Choose the destination format by finding the best match * for the format+type combo. */ dst_format = st_choose_matching_format(screen, bind, format, type, pack->SwapBytes); if (dst_format == PIPE_FORMAT_NONE) { goto fallback; } /* create the destination texture */ memset(&dst_templ, 0, sizeof(dst_templ)); dst_templ.target = PIPE_TEXTURE_2D; dst_templ.format = dst_format; dst_templ.bind = bind; dst_templ.usage = PIPE_USAGE_STAGING; st_gl_texture_dims_to_pipe_dims(GL_TEXTURE_2D, width, height, 1, &dst_templ.width0, &dst_templ.height0, &dst_templ.depth0, &dst_templ.array_size); dst = screen->resource_create(screen, &dst_templ); if (!dst) { goto fallback; } memset(&blit, 0, sizeof(blit)); blit.src.resource = src; blit.src.level = strb->surface->u.tex.level; blit.src.format = src_format; blit.dst.resource = dst; blit.dst.level = 0; blit.dst.format = dst->format; blit.src.box.x = x; blit.dst.box.x = 0; blit.src.box.y = y; blit.dst.box.y = 0; blit.src.box.z = strb->surface->u.tex.first_layer; blit.dst.box.z = 0; blit.src.box.width = blit.dst.box.width = width; blit.src.box.height = blit.dst.box.height = height; blit.src.box.depth = blit.dst.box.depth = 1; blit.mask = st_get_blit_mask(rb->_BaseFormat, format); blit.filter = PIPE_TEX_FILTER_NEAREST; blit.scissor_enable = FALSE; if (st_fb_orientation(ctx->ReadBuffer) == Y_0_TOP) { blit.src.box.y = rb->Height - blit.src.box.y; blit.src.box.height = -blit.src.box.height; } /* blit */ st->pipe->blit(st->pipe, &blit); /* map resources */ pixels = _mesa_map_pbo_dest(ctx, pack, pixels); map = pipe_transfer_map_3d(pipe, dst, 0, PIPE_TRANSFER_READ, 0, 0, 0, width, height, 1, &tex_xfer); if (!map) { _mesa_unmap_pbo_dest(ctx, pack); pipe_resource_reference(&dst, NULL); goto fallback; } /* memcpy data into a user buffer */ { const uint bytesPerRow = width * util_format_get_blocksize(dst_format); GLuint row; for (row = 0; row < (unsigned) height; row++) { GLvoid *dest = _mesa_image_address3d(pack, pixels, width, height, format, type, 0, row, 0); memcpy(dest, map, bytesPerRow); map += tex_xfer->stride; } } pipe_transfer_unmap(pipe, tex_xfer); _mesa_unmap_pbo_dest(ctx, pack); pipe_resource_reference(&dst, NULL); return; fallback: _mesa_readpixels(ctx, x, y, width, height, format, type, pack, pixels); }
/** * glGetTexImage() helper: decompress a compressed texture by rendering * a textured quad. Store the results in the user's buffer. */ static void decompress_with_blit(struct gl_context * ctx, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct st_texture_image *stImage = st_texture_image(texImage); struct st_texture_object *stObj = st_texture_object(texImage->TexObject); const GLuint width = texImage->Width; const GLuint height = texImage->Height; struct pipe_resource *dst_texture; struct pipe_blit_info blit; unsigned bind = (PIPE_BIND_RENDER_TARGET | PIPE_BIND_TRANSFER_READ); struct pipe_transfer *tex_xfer; ubyte *map; /* create temp / dest surface */ if (!util_create_rgba_texture(pipe, width, height, bind, &dst_texture)) { _mesa_problem(ctx, "util_create_rgba_texture() failed " "in decompress_with_blit()"); return; } blit.src.resource = stObj->pt; blit.src.level = texImage->Level; blit.src.format = util_format_linear(stObj->pt->format); blit.dst.resource = dst_texture; blit.dst.level = 0; blit.dst.format = dst_texture->format; blit.src.box.x = blit.dst.box.x = 0; blit.src.box.y = blit.dst.box.y = 0; blit.src.box.z = 0; /* XXX compressed array textures? */ blit.dst.box.z = 0; blit.src.box.width = blit.dst.box.width = width; blit.src.box.height = blit.dst.box.height = height; blit.src.box.depth = blit.dst.box.depth = 1; blit.mask = PIPE_MASK_RGBA; blit.filter = PIPE_TEX_FILTER_NEAREST; blit.scissor_enable = FALSE; /* blit/render/decompress */ st->pipe->blit(st->pipe, &blit); pixels = _mesa_map_pbo_dest(ctx, &ctx->Pack, pixels); map = pipe_transfer_map(pipe, dst_texture, 0, 0, PIPE_TRANSFER_READ, 0, 0, width, height, &tex_xfer); if (!map) { goto end; } /* copy/pack data into user buffer */ if (_mesa_format_matches_format_and_type(stImage->base.TexFormat, format, type, ctx->Pack.SwapBytes)) { /* memcpy */ const uint bytesPerRow = width * util_format_get_blocksize(stImage->pt->format); /* map the dst_surface so we can read from it */ GLuint row; for (row = 0; row < height; row++) { GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); memcpy(dest, map, bytesPerRow); map += tex_xfer->stride; } pipe_transfer_unmap(pipe, tex_xfer); } else { /* format translation via floats */ GLuint row; enum pipe_format pformat = util_format_linear(dst_texture->format); GLfloat *rgba; rgba = malloc(width * 4 * sizeof(GLfloat)); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); goto end; } for (row = 0; row < height; row++) { const GLbitfield transferOps = 0x0; /* bypassed for glGetTexImage() */ GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); if (ST_DEBUG & DEBUG_FALLBACK) debug_printf("%s: fallback format translation\n", __FUNCTION__); /* get float[4] rgba row from surface */ pipe_get_tile_rgba_format(tex_xfer, map, 0, row, width, 1, pformat, rgba); _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format, type, dest, &ctx->Pack, transferOps); } free(rgba); } end: if (map) pipe_transfer_unmap(pipe, tex_xfer); _mesa_unmap_pbo_dest(ctx, &ctx->Pack); pipe_resource_reference(&dst_texture, NULL); }
/** * glGetTexImage() helper: decompress a compressed texture by rendering * a textured quad. Store the results in the user's buffer. */ static void decompress_with_blit(struct gl_context * ctx, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage) { struct st_context *st = st_context(ctx); struct pipe_context *pipe = st->pipe; struct st_texture_image *stImage = st_texture_image(texImage); struct st_texture_object *stObj = st_texture_object(texImage->TexObject); struct pipe_sampler_view *src_view; const GLuint width = texImage->Width; const GLuint height = texImage->Height; struct pipe_surface *dst_surface; struct pipe_resource *dst_texture; struct pipe_transfer *tex_xfer; unsigned bind = (PIPE_BIND_RENDER_TARGET | /* util_blit may choose to render */ PIPE_BIND_TRANSFER_READ); /* create temp / dest surface */ if (!util_create_rgba_surface(pipe, width, height, bind, &dst_texture, &dst_surface)) { _mesa_problem(ctx, "util_create_rgba_surface() failed " "in decompress_with_blit()"); return; } /* Disable conditional rendering. */ if (st->render_condition) { pipe->render_condition(pipe, NULL, 0); } /* Create sampler view that limits fetches to the source mipmap level */ { struct pipe_sampler_view sv_temp; u_sampler_view_default_template(&sv_temp, stObj->pt, stObj->pt->format); sv_temp.format = util_format_linear(sv_temp.format); sv_temp.u.tex.first_level = sv_temp.u.tex.last_level = texImage->Level; src_view = pipe->create_sampler_view(pipe, stObj->pt, &sv_temp); if (!src_view) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage"); return; } } /* blit/render/decompress */ util_blit_pixels_tex(st->blit, src_view, /* pipe_resource (src) */ 0, 0, /* src x0, y0 */ width, height, /* src x1, y1 */ dst_surface, /* pipe_surface (dst) */ 0, 0, /* dst x0, y0 */ width, height, /* dst x1, y1 */ 0.0, /* z */ PIPE_TEX_MIPFILTER_NEAREST); /* Restore conditional rendering state. */ if (st->render_condition) { pipe->render_condition(pipe, st->render_condition, st->condition_mode); } /* map the dst_surface so we can read from it */ tex_xfer = pipe_get_transfer(pipe, dst_texture, 0, 0, PIPE_TRANSFER_READ, 0, 0, width, height); pixels = _mesa_map_pbo_dest(ctx, &ctx->Pack, pixels); /* copy/pack data into user buffer */ if (_mesa_format_matches_format_and_type(stImage->base.TexFormat, format, type, ctx->Pack.SwapBytes)) { /* memcpy */ const uint bytesPerRow = width * util_format_get_blocksize(stImage->pt->format); ubyte *map = pipe_transfer_map(pipe, tex_xfer); GLuint row; for (row = 0; row < height; row++) { GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); memcpy(dest, map, bytesPerRow); map += tex_xfer->stride; } pipe_transfer_unmap(pipe, tex_xfer); } else { /* format translation via floats */ GLuint row; enum pipe_format pformat = util_format_linear(dst_texture->format); GLfloat *rgba; rgba = (GLfloat *) malloc(width * 4 * sizeof(GLfloat)); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); goto end; } for (row = 0; row < height; row++) { const GLbitfield transferOps = 0x0; /* bypassed for glGetTexImage() */ GLvoid *dest = _mesa_image_address2d(&ctx->Pack, pixels, width, height, format, type, row, 0); if (ST_DEBUG & DEBUG_FALLBACK) debug_printf("%s: fallback format translation\n", __FUNCTION__); /* get float[4] rgba row from surface */ pipe_get_tile_rgba_format(pipe, tex_xfer, 0, row, width, 1, pformat, rgba); _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format, type, dest, &ctx->Pack, transferOps); } free(rgba); } end: _mesa_unmap_pbo_dest(ctx, &ctx->Pack); pipe->transfer_destroy(pipe, tex_xfer); /* destroy the temp / dest surface */ util_destroy_rgba_surface(dst_texture, dst_surface); pipe_sampler_view_release(pipe, &src_view); }