static GLboolean readpixels_can_use_memcpy(const struct gl_context *ctx, GLenum format, GLenum type, const struct gl_pixelstore_attrib *packing) { struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); ASSERT(rb); if (_mesa_readpixels_needs_slow_path(ctx, format, type, GL_FALSE)) { return GL_FALSE; } /* The base internal format and the base Mesa format must match. */ if (rb->_BaseFormat != _mesa_get_format_base_format(rb->Format)) { return GL_FALSE; } /* The Mesa format must match the input format and type. */ if (!_mesa_format_matches_format_and_type(rb->Format, format, type, packing->SwapBytes)) { return GL_FALSE; } return GL_TRUE; }
static GLboolean fast_read_rgba_pixels_memcpy( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels, const struct gl_pixelstore_attrib *packing, GLbitfield transferOps ) { struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; GLubyte *dst, *map; int dstStride, stride, j, texelBytes; if (!_mesa_format_matches_format_and_type(rb->Format, format, type)) return GL_FALSE; /* check for things we can't handle here */ if (packing->SwapBytes || packing->LsbFirst) { return GL_FALSE; } dstStride = _mesa_image_row_stride(packing, width, format, type); dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height, format, type, 0, 0); ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, &map, &stride); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return GL_TRUE; /* don't bother trying the slow path */ } texelBytes = _mesa_get_format_bytes(rb->Format); for (j = 0; j < height; j++) { memcpy(dst, map, width * texelBytes); dst += dstStride; map += stride; } ctx->Driver.UnmapRenderbuffer(ctx, rb); return GL_TRUE; }
GLboolean _mesa_texstore_can_use_memcpy(struct gl_context *ctx, GLenum baseInternalFormat, mesa_format dstFormat, GLenum srcFormat, GLenum srcType, const struct gl_pixelstore_attrib *srcPacking) { if (_mesa_texstore_needs_transfer_ops(ctx, baseInternalFormat, dstFormat)) { return GL_FALSE; } /* The base internal format and the base Mesa format must match. */ if (baseInternalFormat != _mesa_get_format_base_format(dstFormat)) { return GL_FALSE; } /* The Mesa format must match the input format and type. */ if (!_mesa_format_matches_format_and_type(dstFormat, srcFormat, srcType, srcPacking->SwapBytes)) { return GL_FALSE; } /* Depth texture data needs clamping in following cases: * - Floating point dstFormat with signed srcType: clamp to [0.0, 1.0]. * - Fixed point dstFormat with signed srcType: clamp to [0, 2^n -1]. * * All the cases except one (float dstFormat with float srcType) are ruled * out by _mesa_format_matches_format_and_type() check above. Handle the * remaining case here. */ if ((baseInternalFormat == GL_DEPTH_COMPONENT || baseInternalFormat == GL_DEPTH_STENCIL) && (srcType == GL_FLOAT || srcType == GL_FLOAT_32_UNSIGNED_INT_24_8_REV)) { return GL_FALSE; } return GL_TRUE; }
/** * 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); }
/* XXX: Do this for TexSubImage also: */ static bool try_pbo_upload(struct gl_context *ctx, struct gl_texture_image *image, const struct gl_pixelstore_attrib *unpack, GLenum format, GLenum type, const void *pixels) { struct intel_texture_image *intelImage = intel_texture_image(image); struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *pbo = intel_buffer_object(unpack->BufferObj); GLuint src_offset; drm_intel_bo *src_buffer; if (!_mesa_is_bufferobj(unpack->BufferObj)) return false; DBG("trying pbo upload\n"); if (intel->ctx._ImageTransferState || unpack->SkipPixels || unpack->SkipRows) { DBG("%s: image transfer\n", __FUNCTION__); return false; } ctx->Driver.AllocTextureImageBuffer(ctx, image); if (!intelImage->mt) { DBG("%s: no miptree\n", __FUNCTION__); return false; } if (!_mesa_format_matches_format_and_type(intelImage->mt->format, format, type, false)) { DBG("%s: format mismatch (upload to %s with format 0x%x, type 0x%x)\n", __FUNCTION__, _mesa_get_format_name(intelImage->mt->format), format, type); return false; } if (image->TexObject->Target == GL_TEXTURE_1D_ARRAY || image->TexObject->Target == GL_TEXTURE_2D_ARRAY) { DBG("%s: no support for array textures\n", __FUNCTION__); return false; } src_buffer = intel_bufferobj_source(intel, pbo, 64, &src_offset); /* note: potential 64-bit ptr to 32-bit int cast */ src_offset += (GLuint) (unsigned long) pixels; int src_stride = _mesa_image_row_stride(unpack, image->Width, format, type); struct intel_mipmap_tree *pbo_mt = intel_miptree_create_for_bo(intel, src_buffer, intelImage->mt->format, src_offset, image->Width, image->Height, src_stride, I915_TILING_NONE); if (!pbo_mt) return false; if (!intel_miptree_blit(intel, pbo_mt, 0, 0, 0, 0, false, intelImage->mt, image->Level, image->Face, 0, 0, false, image->Width, image->Height, GL_COPY)) { DBG("%s: blit failed\n", __FUNCTION__); intel_miptree_release(&pbo_mt); return false; } intel_miptree_release(&pbo_mt); DBG("%s: success\n", __FUNCTION__); return true; }
/** * Try to do a fast and simple RGB(a) glDrawPixels. * Return: GL_TRUE if success, GL_FALSE if slow path must be used instead */ static GLboolean fast_draw_rgba_pixels(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *userUnpack, const GLvoid *pixels) { struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0]; SWcontext *swrast = SWRAST_CONTEXT(ctx); struct gl_pixelstore_attrib unpack; if (!rb) return GL_TRUE; /* no-op */ if (ctx->DrawBuffer->_NumColorDrawBuffers > 1 || (swrast->_RasterMask & ~CLIP_BIT) || ctx->Texture._EnabledCoordUnits || userUnpack->SwapBytes || ctx->Pixel.ZoomX != 1.0f || fabsf(ctx->Pixel.ZoomY) != 1.0f || ctx->_ImageTransferState) { /* can't handle any of those conditions */ return GL_FALSE; } unpack = *userUnpack; /* clipping */ if (!_mesa_clip_drawpixels(ctx, &x, &y, &width, &height, &unpack)) { /* image was completely clipped: no-op, all done */ return GL_TRUE; } if (format == GL_RGB && type == GL_UNSIGNED_BYTE && (rb->Format == MESA_FORMAT_XRGB8888 || rb->Format == MESA_FORMAT_ARGB8888)) { fast_draw_rgb_ubyte_pixels(ctx, rb, x, y, width, height, &unpack, pixels); return GL_TRUE; } if (format == GL_RGBA && type == GL_UNSIGNED_BYTE && (rb->Format == MESA_FORMAT_XRGB8888 || rb->Format == MESA_FORMAT_ARGB8888)) { fast_draw_rgba_ubyte_pixels(ctx, rb, x, y, width, height, &unpack, pixels); return GL_TRUE; } if (_mesa_format_matches_format_and_type(rb->Format, format, type, ctx->Unpack.SwapBytes)) { fast_draw_generic_pixels(ctx, rb, x, y, width, height, format, type, &unpack, pixels); return GL_TRUE; } /* can't handle this pixel format and/or data type */ return GL_FALSE; }
static bool do_blit_drawpixels(struct gl_context * ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid * pixels) { struct brw_context *brw = brw_context(ctx); struct intel_buffer_object *src = intel_buffer_object(unpack->BufferObj); GLuint src_offset; drm_intel_bo *src_buffer; DBG("%s\n", __FUNCTION__); if (!intel_check_blit_fragment_ops(ctx, false)) return false; if (ctx->DrawBuffer->_NumColorDrawBuffers != 1) { DBG("%s: fallback due to MRT\n", __FUNCTION__); return false; } struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0]; struct intel_renderbuffer *irb = intel_renderbuffer(rb); if (!_mesa_format_matches_format_and_type(irb->mt->format, format, type, false)) { DBG("%s: bad format for blit\n", __FUNCTION__); return false; } if (unpack->SwapBytes || unpack->LsbFirst || unpack->SkipPixels || unpack->SkipRows) { DBG("%s: bad packing params\n", __FUNCTION__); return false; } int src_stride = _mesa_image_row_stride(unpack, width, format, type); bool src_flip = false; /* Mesa flips the src_stride for unpack->Invert, but we want our mt to have * a normal src_stride. */ if (unpack->Invert) { src_stride = -src_stride; src_flip = true; } src_offset = (GLintptr)pixels; src_offset += _mesa_image_offset(2, unpack, width, height, format, type, 0, 0, 0); intel_prepare_render(brw); src_buffer = intel_bufferobj_buffer(brw, src, src_offset, width * height * irb->mt->cpp); struct intel_mipmap_tree *pbo_mt = intel_miptree_create_for_bo(brw, src_buffer, irb->mt->format, src_offset, width, height, src_stride, I915_TILING_NONE); if (!pbo_mt) return false; if (!intel_miptree_blit(brw, pbo_mt, 0, 0, 0, 0, src_flip, irb->mt, irb->mt_level, irb->mt_layer, x, y, _mesa_is_winsys_fbo(ctx->DrawBuffer), width, height, GL_COPY)) { DBG("%s: blit failed\n", __FUNCTION__); intel_miptree_release(&pbo_mt); return false; } intel_miptree_release(&pbo_mt); if (ctx->Query.CurrentOcclusionObject) ctx->Query.CurrentOcclusionObject->Result += width * height; intel_check_front_buffer_rendering(brw); DBG("%s: success\n", __FUNCTION__); return true; }
/* XXX: Do this for TexSubImage also: */ static bool try_pbo_upload(struct gl_context *ctx, struct gl_texture_image *image, const struct gl_pixelstore_attrib *unpack, GLenum format, GLenum type, const void *pixels) { struct intel_texture_image *intelImage = intel_texture_image(image); struct intel_context *intel = intel_context(ctx); struct intel_buffer_object *pbo = intel_buffer_object(unpack->BufferObj); GLuint src_offset, src_stride; GLuint dst_x, dst_y; drm_intel_bo *dst_buffer, *src_buffer; if (!_mesa_is_bufferobj(unpack->BufferObj)) return false; DBG("trying pbo upload\n"); if (intel->ctx._ImageTransferState || unpack->SkipPixels || unpack->SkipRows) { DBG("%s: image transfer\n", __FUNCTION__); return false; } if (!_mesa_format_matches_format_and_type(image->TexFormat, format, type, false)) { DBG("%s: format mismatch (upload to %s with format 0x%x, type 0x%x)\n", __FUNCTION__, _mesa_get_format_name(image->TexFormat), format, type); return false; } ctx->Driver.AllocTextureImageBuffer(ctx, image); if (!intelImage->mt) { DBG("%s: no miptree\n", __FUNCTION__); return false; } if (image->TexObject->Target == GL_TEXTURE_1D_ARRAY || image->TexObject->Target == GL_TEXTURE_2D_ARRAY) { DBG("%s: no support for array textures\n", __FUNCTION__); return false; } dst_buffer = intelImage->mt->region->bo; src_buffer = intel_bufferobj_source(intel, pbo, 64, &src_offset); /* note: potential 64-bit ptr to 32-bit int cast */ src_offset += (GLuint) (unsigned long) pixels; if (unpack->RowLength > 0) src_stride = unpack->RowLength; else src_stride = image->Width; src_stride *= intelImage->mt->region->cpp; intel_miptree_get_image_offset(intelImage->mt, intelImage->base.Base.Level, intelImage->base.Base.Face, &dst_x, &dst_y); if (!intelEmitCopyBlit(intel, intelImage->mt->cpp, src_stride, src_buffer, src_offset, false, intelImage->mt->region->pitch, dst_buffer, 0, intelImage->mt->region->tiling, 0, 0, dst_x, dst_y, image->Width, image->Height, GL_COPY)) { DBG("%s: blit failed\n", __FUNCTION__); return false; } DBG("%s: success\n", __FUNCTION__); return true; }
/** * 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); }
/** * Try to do glGetTexImage() with simple memcpy(). * \return GL_TRUE if done, GL_FALSE otherwise */ static GLboolean get_tex_memcpy(struct gl_context *ctx, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage) { const GLenum target = texImage->TexObject->Target; GLboolean memCopy = GL_FALSE; /* * Check if we can use memcpy to copy from the hardware texture * format to the user's format/type. * Note that GL's pixel transfer ops don't apply to glGetTexImage() */ if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE || _mesa_is_cube_face(target)) { memCopy = _mesa_format_matches_format_and_type(texImage->TexFormat, format, type, ctx->Pack.SwapBytes); } if (memCopy) { const GLuint bpp = _mesa_get_format_bytes(texImage->TexFormat); const GLuint bytesPerRow = texImage->Width * bpp; GLubyte *dst = _mesa_image_address2d(&ctx->Pack, pixels, texImage->Width, texImage->Height, format, type, 0, 0); const GLint dstRowStride = _mesa_image_row_stride(&ctx->Pack, texImage->Width, format, type); GLubyte *src; GLint srcRowStride; /* map src texture buffer */ ctx->Driver.MapTextureImage(ctx, texImage, 0, 0, 0, texImage->Width, texImage->Height, GL_MAP_READ_BIT, &src, &srcRowStride); if (src) { if (bytesPerRow == dstRowStride && bytesPerRow == srcRowStride) { memcpy(dst, src, bytesPerRow * texImage->Height); } else { GLuint row; for (row = 0; row < texImage->Height; row++) { memcpy(dst, src, bytesPerRow); dst += dstRowStride; src += srcRowStride; } } /* unmap src texture buffer */ ctx->Driver.UnmapTextureImage(ctx, texImage, 0); } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage"); } } return memCopy; }
static bool do_blit_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 brw_context *brw = brw_context(ctx); struct intel_buffer_object *dst = intel_buffer_object(pack->BufferObj); GLuint dst_offset; drm_intel_bo *dst_buffer; GLint dst_x, dst_y; GLuint dirty; DBG("%s\n", __FUNCTION__); assert(_mesa_is_bufferobj(pack->BufferObj)); struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; struct intel_renderbuffer *irb = intel_renderbuffer(rb); if (ctx->_ImageTransferState || !_mesa_format_matches_format_and_type(irb->mt->format, format, type, false)) { DBG("%s - bad format for blit\n", __FUNCTION__); return false; } if (pack->SwapBytes || pack->LsbFirst) { DBG("%s: bad packing params\n", __FUNCTION__); return false; } int dst_stride = _mesa_image_row_stride(pack, width, format, type); bool dst_flip = false; /* Mesa flips the dst_stride for pack->Invert, but we want our mt to have a * normal dst_stride. */ struct gl_pixelstore_attrib uninverted_pack = *pack; if (pack->Invert) { dst_stride = -dst_stride; dst_flip = true; uninverted_pack.Invert = false; } dst_offset = (GLintptr)pixels; dst_offset += _mesa_image_offset(2, &uninverted_pack, width, height, format, type, 0, 0, 0); if (!_mesa_clip_copytexsubimage(ctx, &dst_x, &dst_y, &x, &y, &width, &height)) { return true; } dirty = brw->front_buffer_dirty; intel_prepare_render(brw); brw->front_buffer_dirty = dirty; dst_buffer = intel_bufferobj_buffer(brw, dst, dst_offset, height * dst_stride); struct intel_mipmap_tree *pbo_mt = intel_miptree_create_for_bo(brw, dst_buffer, irb->mt->format, dst_offset, width, height, dst_stride, I915_TILING_NONE); if (!intel_miptree_blit(brw, irb->mt, irb->mt_level, irb->mt_layer, x, y, _mesa_is_winsys_fbo(ctx->ReadBuffer), pbo_mt, 0, 0, 0, 0, dst_flip, width, height, GL_COPY)) { return false; } intel_miptree_release(&pbo_mt); DBG("%s - DONE\n", __FUNCTION__); return true; }