/** * Read pixels for format=GL_DEPTH_COMPONENT. */ static void read_depth_pixels( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum type, GLvoid *pixels, const struct gl_pixelstore_attrib *packing ) { struct gl_framebuffer *fb = ctx->ReadBuffer; struct gl_renderbuffer *rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; GLint j; GLubyte *dst, *map; int dstStride, stride; GLfloat *depthValues; if (!rb) return; /* clipping should have been done already */ ASSERT(x >= 0); ASSERT(y >= 0); ASSERT(x + width <= (GLint) rb->Width); ASSERT(y + height <= (GLint) rb->Height); if (type == GL_UNSIGNED_INT && read_uint_depth_pixels(ctx, x, y, width, height, type, pixels, packing)) { return; } dstStride = _mesa_image_row_stride(packing, width, GL_DEPTH_COMPONENT, type); dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height, GL_DEPTH_COMPONENT, 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; } depthValues = malloc(width * sizeof(GLfloat)); if (depthValues) { /* General case (slower) */ for (j = 0; j < height; j++, y++) { _mesa_unpack_float_z_row(rb->Format, width, map, depthValues); _mesa_pack_depth_span(ctx, width, dst, type, depthValues, packing); dst += dstStride; map += stride; } } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); } free(depthValues); ctx->Driver.UnmapRenderbuffer(ctx, rb); }
/** * Tries to implement glReadPixels() of GL_DEPTH_COMPONENT using memcpy of the * mapping. */ static GLboolean fast_read_depth_pixels( struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum type, GLvoid *pixels, const struct gl_pixelstore_attrib *packing ) { struct gl_framebuffer *fb = ctx->ReadBuffer; struct gl_renderbuffer *rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer; GLubyte *map, *dst; int stride, dstStride, j; if (ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0) return GL_FALSE; if (packing->SwapBytes) return GL_FALSE; if (_mesa_get_format_datatype(rb->Format) != GL_UNSIGNED_NORMALIZED) return GL_FALSE; if (!((type == GL_UNSIGNED_SHORT && rb->Format == MESA_FORMAT_Z16) || type == GL_UNSIGNED_INT)) return GL_FALSE; 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 */ } dstStride = _mesa_image_row_stride(packing, width, GL_DEPTH_COMPONENT, type); dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height, GL_DEPTH_COMPONENT, type, 0, 0); for (j = 0; j < height; j++) { if (type == GL_UNSIGNED_INT) { _mesa_unpack_uint_z_row(rb->Format, width, map, (GLuint *)dst); } else { ASSERT(type == GL_UNSIGNED_SHORT && rb->Format == MESA_FORMAT_Z16); memcpy(dst, map, width * 2); } map += stride; dst += dstStride; } ctx->Driver.UnmapRenderbuffer(ctx, rb); return GL_TRUE; }
static void slow_read_rgba_pixels( 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; const gl_format rbFormat = _mesa_get_srgb_format_linear(rb->Format); void *rgba; GLubyte *dst, *map; int dstStride, stride, j; 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; } rgba = malloc(width * MAX_PIXEL_BYTES); if (!rgba) goto done; for (j = 0; j < height; j++) { if (_mesa_is_integer_format(format)) { _mesa_unpack_uint_rgba_row(rbFormat, width, map, (GLuint (*)[4]) rgba); _mesa_pack_rgba_span_int(ctx, width, (GLuint (*)[4]) rgba, format, type, dst); } else { _mesa_unpack_rgba_row(rbFormat, width, map, (GLfloat (*)[4]) rgba); _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format, type, dst, packing, transferOps); } dst += dstStride; map += stride; } free(rgba); done: ctx->Driver.UnmapRenderbuffer(ctx, rb); }
static GLboolean _mesa_texstore_z32f_x24s8(TEXSTORE_PARAMS) { GLint img, row; const GLint srcRowStride = _mesa_image_row_stride(srcPacking, srcWidth, srcFormat, srcType) / sizeof(uint64_t); assert(dstFormat == MESA_FORMAT_Z32_FLOAT_S8X24_UINT); assert(srcFormat == GL_DEPTH_STENCIL || srcFormat == GL_DEPTH_COMPONENT || srcFormat == GL_STENCIL_INDEX); assert(srcFormat != GL_DEPTH_STENCIL || srcType == GL_UNSIGNED_INT_24_8 || srcType == GL_FLOAT_32_UNSIGNED_INT_24_8_REV); /* In case we only upload depth we need to preserve the stencil */ for (img = 0; img < srcDepth; img++) { uint64_t *dstRow = (uint64_t *) dstSlices[img]; const uint64_t *src = (const uint64_t *) _mesa_image_address(dims, srcPacking, srcAddr, srcWidth, srcHeight, srcFormat, srcType, img, 0, 0); for (row = 0; row < srcHeight; row++) { /* The unpack functions with: * dstType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV * only write their own dword, so the other dword (stencil * or depth) is preserved. */ if (srcFormat != GL_STENCIL_INDEX) _mesa_unpack_depth_span(ctx, srcWidth, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, /* dst type */ dstRow, /* dst addr */ ~0U, srcType, src, srcPacking); if (srcFormat != GL_DEPTH_COMPONENT) _mesa_unpack_stencil_span(ctx, srcWidth, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, /* dst type */ dstRow, /* dst addr */ srcType, src, srcPacking, ctx->_ImageTransferState); src += srcRowStride; dstRow += dstRowStride / sizeof(uint64_t); } } return GL_TRUE; }
/** * Teximage storage routine for when a simple memcpy will do. * No pixel transfer operations or special texel encodings allowed. * 1D, 2D and 3D images supported. */ static void memcpy_texture(struct gl_context *ctx, GLuint dimensions, mesa_format dstFormat, GLint dstRowStride, GLubyte **dstSlices, GLint srcWidth, GLint srcHeight, GLint srcDepth, GLenum srcFormat, GLenum srcType, const GLvoid *srcAddr, const struct gl_pixelstore_attrib *srcPacking) { const GLint srcRowStride = _mesa_image_row_stride(srcPacking, srcWidth, srcFormat, srcType); const GLint srcImageStride = _mesa_image_image_stride(srcPacking, srcWidth, srcHeight, srcFormat, srcType); const GLubyte *srcImage = (const GLubyte *) _mesa_image_address(dimensions, srcPacking, srcAddr, srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0); const GLuint texelBytes = _mesa_get_format_bytes(dstFormat); const GLint bytesPerRow = srcWidth * texelBytes; if (dstRowStride == srcRowStride && dstRowStride == bytesPerRow) { /* memcpy image by image */ GLint img; for (img = 0; img < srcDepth; img++) { GLubyte *dstImage = dstSlices[img]; memcpy(dstImage, srcImage, bytesPerRow * srcHeight); srcImage += srcImageStride; } } else { /* memcpy row by row */ GLint img, row; for (img = 0; img < srcDepth; img++) { const GLubyte *srcRow = srcImage; GLubyte *dstRow = dstSlices[img]; for (row = 0; row < srcHeight; row++) { memcpy(dstRow, srcRow, bytesPerRow); dstRow += dstRowStride; srcRow += srcRowStride; } srcImage += srcImageStride; } } }
/** * Store simple 8-bit/value stencil texture data. */ static GLboolean _mesa_texstore_s8(TEXSTORE_PARAMS) { assert(dstFormat == MESA_FORMAT_S_UINT8); assert(srcFormat == GL_STENCIL_INDEX); { const GLint srcRowStride = _mesa_image_row_stride(srcPacking, srcWidth, srcFormat, srcType); GLint img, row; GLubyte *stencil = malloc(srcWidth * sizeof(GLubyte)); if (!stencil) return GL_FALSE; for (img = 0; img < srcDepth; img++) { GLubyte *dstRow = dstSlices[img]; const GLubyte *src = (const GLubyte *) _mesa_image_address(dims, srcPacking, srcAddr, srcWidth, srcHeight, srcFormat, srcType, img, 0, 0); for (row = 0; row < srcHeight; row++) { GLint i; /* get the 8-bit stencil values */ _mesa_unpack_stencil_span(ctx, srcWidth, GL_UNSIGNED_BYTE, /* dst type */ stencil, /* dst addr */ srcType, src, srcPacking, ctx->_ImageTransferState); /* merge stencil values into depth values */ for (i = 0; i < srcWidth; i++) dstRow[i] = stencil[i]; src += srcRowStride; dstRow += dstRowStride / sizeof(GLubyte); } } free(stencil); } 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; }
/** * Handle a common case of drawing GL_RGBA/GL_UNSIGNED_BYTE into a * MESA_FORMAT_ARGB888 or MESA_FORMAT_xRGB888 renderbuffer. */ static void fast_draw_rgba_ubyte_pixels(struct gl_context *ctx, struct gl_renderbuffer *rb, GLint x, GLint y, GLsizei width, GLsizei height, const struct gl_pixelstore_attrib *unpack, const GLvoid *pixels) { const GLubyte *src = (const GLubyte *) _mesa_image_address2d(unpack, pixels, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0, 0); const GLint srcRowStride = _mesa_image_row_stride(unpack, width, GL_RGBA, GL_UNSIGNED_BYTE); GLint i, j; GLubyte *dst; GLint dstRowStride; ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_WRITE_BIT, &dst, &dstRowStride); if (!dst) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels"); return; } if (ctx->Pixel.ZoomY == -1.0f) { dst = dst + (height - 1) * dstRowStride; dstRowStride = -dstRowStride; } for (i = 0; i < height; i++) { GLuint *dst4 = (GLuint *) dst; for (j = 0; j < width; j++) { dst4[j] = PACK_COLOR_8888(src[j*4+3], src[j*4+0], src[j*4+1], src[j*4+2]); } dst += dstRowStride; src += srcRowStride; } ctx->Driver.UnmapRenderbuffer(ctx, rb); }
static GLboolean readpixels_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) { struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); GLubyte *dst, *map; int dstStride, stride, j, texelBytes; /* Fail if memcpy cannot be used. */ if (!readpixels_can_use_memcpy(ctx, format, type, packing)) { 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); /* memcpy*/ for (j = 0; j < height; j++) { memcpy(dst, map, width * texelBytes); dst += dstStride; map += stride; } ctx->Driver.UnmapRenderbuffer(ctx, rb); return GL_TRUE; }
/** * Handle a common case of drawing a format/type combination that * exactly matches the renderbuffer format. */ static void fast_draw_generic_pixels(struct gl_context *ctx, struct gl_renderbuffer *rb, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, const struct gl_pixelstore_attrib *unpack, const GLvoid *pixels) { const GLubyte *src = (const GLubyte *) _mesa_image_address2d(unpack, pixels, width, height, format, type, 0, 0); const GLint srcRowStride = _mesa_image_row_stride(unpack, width, format, type); const GLint rowLength = width * _mesa_get_format_bytes(rb->Format); GLint i; GLubyte *dst; GLint dstRowStride; ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_WRITE_BIT, &dst, &dstRowStride); if (!dst) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glDrawPixels"); return; } if (ctx->Pixel.ZoomY == -1.0f) { dst = dst + (height - 1) * dstRowStride; dstRowStride = -dstRowStride; } for (i = 0; i < height; i++) { memcpy(dst, src, rowLength); dst += dstRowStride; src += srcRowStride; } ctx->Driver.UnmapRenderbuffer(ctx, rb); }
/** * Read combined depth/stencil values. * We'll have already done error checking to be sure the expected * depth and stencil buffers really exist. */ static void read_depth_stencil_pixels(struct gl_context *ctx, GLint x, GLint y, GLsizei width, GLsizei height, GLenum type, GLvoid *pixels, const struct gl_pixelstore_attrib *packing ) { const GLboolean scaleOrBias = ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0; const GLboolean stencilTransfer = ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset || ctx->Pixel.MapStencilFlag; GLubyte *dst; int dstStride; dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height, GL_DEPTH_STENCIL_EXT, type, 0, 0); dstStride = _mesa_image_row_stride(packing, width, GL_DEPTH_STENCIL_EXT, type); /* Fast 24/8 reads. */ if (type == GL_UNSIGNED_INT_24_8 && !scaleOrBias && !stencilTransfer && !packing->SwapBytes) { if (fast_read_depth_stencil_pixels(ctx, x, y, width, height, dst, dstStride)) return; if (fast_read_depth_stencil_pixels_separate(ctx, x, y, width, height, (uint32_t *)dst, dstStride)) return; } slow_read_depth_stencil_pixels_separate(ctx, x, y, width, height, type, packing, dst, dstStride); }
/** * Helper function for storing 1D, 2D, 3D whole and subimages into texture * memory. * The source of the image data may be user memory or a PBO. In the later * case, we'll map the PBO, copy from it, then unmap it. */ static void store_texsubimage(struct gl_context *ctx, struct gl_texture_image *texImage, GLint xoffset, GLint yoffset, GLint zoffset, GLint width, GLint height, GLint depth, GLenum format, GLenum type, const GLvoid *pixels, const struct gl_pixelstore_attrib *packing, const char *caller) { const GLbitfield mapMode = get_read_write_mode(format, texImage->TexFormat); const GLenum target = texImage->TexObject->Target; GLboolean success = GL_FALSE; GLuint dims, slice, numSlices = 1, sliceOffset = 0; GLint srcImageStride = 0; const GLubyte *src; assert(xoffset + width <= texImage->Width); assert(yoffset + height <= texImage->Height); assert(zoffset + depth <= texImage->Depth); switch (target) { case GL_TEXTURE_1D: dims = 1; break; case GL_TEXTURE_2D_ARRAY: case GL_TEXTURE_CUBE_MAP_ARRAY: case GL_TEXTURE_3D: dims = 3; break; default: dims = 2; } /* get pointer to src pixels (may be in a pbo which we'll map here) */ src = (const GLubyte *) _mesa_validate_pbo_teximage(ctx, dims, width, height, depth, format, type, pixels, packing, caller); if (!src) return; /* compute slice info (and do some sanity checks) */ switch (target) { case GL_TEXTURE_2D: case GL_TEXTURE_RECTANGLE: case GL_TEXTURE_CUBE_MAP: case GL_TEXTURE_EXTERNAL_OES: /* one image slice, nothing special needs to be done */ break; case GL_TEXTURE_1D: assert(height == 1); assert(depth == 1); assert(yoffset == 0); assert(zoffset == 0); break; case GL_TEXTURE_1D_ARRAY: assert(depth == 1); assert(zoffset == 0); numSlices = height; sliceOffset = yoffset; height = 1; yoffset = 0; srcImageStride = _mesa_image_row_stride(packing, width, format, type); break; case GL_TEXTURE_2D_ARRAY: numSlices = depth; sliceOffset = zoffset; depth = 1; zoffset = 0; srcImageStride = _mesa_image_image_stride(packing, width, height, format, type); break; case GL_TEXTURE_3D: /* we'll store 3D images as a series of slices */ numSlices = depth; sliceOffset = zoffset; srcImageStride = _mesa_image_image_stride(packing, width, height, format, type); break; case GL_TEXTURE_CUBE_MAP_ARRAY: numSlices = depth; sliceOffset = zoffset; srcImageStride = _mesa_image_image_stride(packing, width, height, format, type); break; default: _mesa_warning(ctx, "Unexpected target 0x%x in store_texsubimage()", target); return; } assert(numSlices == 1 || srcImageStride != 0); for (slice = 0; slice < numSlices; slice++) { GLubyte *dstMap; GLint dstRowStride; ctx->Driver.MapTextureImage(ctx, texImage, slice + sliceOffset, xoffset, yoffset, width, height, mapMode, &dstMap, &dstRowStride); if (dstMap) { /* Note: we're only storing a 2D (or 1D) slice at a time but we need * to pass the right 'dims' value so that GL_UNPACK_SKIP_IMAGES is * used for 3D images. */ success = _mesa_texstore(ctx, dims, texImage->_BaseFormat, texImage->TexFormat, dstRowStride, &dstMap, width, height, 1, /* w, h, d */ format, type, src, packing); ctx->Driver.UnmapTextureImage(ctx, texImage, slice + sliceOffset); } src += srcImageStride; if (!success) break; } if (!success) _mesa_error(ctx, GL_OUT_OF_MEMORY, "%s", caller); _mesa_unmap_teximage_pbo(ctx, packing); }
/* 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; }
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; }
static struct gl_texture_image * create_texture_for_pbo(struct gl_context *ctx, bool create_pbo, GLenum pbo_target, int dims, int width, int height, int depth, GLenum format, GLenum type, const void *pixels, const struct gl_pixelstore_attrib *packing, struct gl_buffer_object **tmp_pbo, GLuint *tmp_tex) { uint32_t pbo_format; GLenum internal_format; unsigned row_stride; struct gl_buffer_object *buffer_obj; struct gl_texture_object *tex_obj; struct gl_texture_image *tex_image; bool read_only; if (packing->SwapBytes || packing->LsbFirst || packing->Invert) return NULL; pbo_format = _mesa_format_from_format_and_type(format, type); if (_mesa_format_is_mesa_array_format(pbo_format)) pbo_format = _mesa_format_from_array_format(pbo_format); if (!pbo_format || !ctx->TextureFormatSupported[pbo_format]) return NULL; /* Account for SKIP_PIXELS, SKIP_ROWS, ALIGNMENT, and SKIP_IMAGES */ uint32_t first_pixel = _mesa_image_offset(dims, packing, width, height, format, type, 0, 0, 0); uint32_t last_pixel = _mesa_image_offset(dims, packing, width, height, format, type, depth-1, height-1, width); row_stride = _mesa_image_row_stride(packing, width, format, type); if (_mesa_is_bufferobj(packing->BufferObj)) { *tmp_pbo = NULL; buffer_obj = packing->BufferObj; first_pixel += (intptr_t)pixels; } else { bool is_pixel_pack = pbo_target == GL_PIXEL_PACK_BUFFER; assert(create_pbo); *tmp_pbo = ctx->Driver.NewBufferObject(ctx, 0xDEADBEEF); if (*tmp_pbo == NULL) return NULL; /* In case of GL_PIXEL_PACK_BUFFER, pass null pointer for the pixel * data to avoid unnecessary data copying in _mesa_buffer_data. */ if (is_pixel_pack) _mesa_buffer_data(ctx, *tmp_pbo, GL_NONE, last_pixel - first_pixel, NULL, GL_STREAM_READ, __func__); else _mesa_buffer_data(ctx, *tmp_pbo, GL_NONE, last_pixel - first_pixel, (char *)pixels + first_pixel, GL_STREAM_DRAW, __func__); buffer_obj = *tmp_pbo; first_pixel = 0; } _mesa_GenTextures(1, tmp_tex); tex_obj = _mesa_lookup_texture(ctx, *tmp_tex); _mesa_initialize_texture_object(ctx, tex_obj, *tmp_tex, GL_TEXTURE_2D); /* This must be set after _mesa_initialize_texture_object, not before. */ tex_obj->Immutable = GL_TRUE; /* This is required for interactions with ARB_texture_view. */ tex_obj->NumLayers = 1; internal_format = _mesa_get_format_base_format(pbo_format); /* The texture is addressed as a single very-tall image, so we * need to pack the multiple image depths together taking the * inter-image padding into account. */ int image_height = packing->ImageHeight == 0 ? height : packing->ImageHeight; int full_height = image_height * (depth - 1) + height; tex_image = _mesa_get_tex_image(ctx, tex_obj, tex_obj->Target, 0); _mesa_init_teximage_fields(ctx, tex_image, width, full_height, 1, 0, internal_format, pbo_format); read_only = pbo_target == GL_PIXEL_UNPACK_BUFFER; if (!ctx->Driver.SetTextureStorageForBufferObject(ctx, tex_obj, buffer_obj, first_pixel, row_stride, read_only)) { _mesa_DeleteTextures(1, tmp_tex); _mesa_reference_buffer_object(ctx, tmp_pbo, NULL); return NULL; } return tex_image; }
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; }
static struct gl_texture_image * create_texture_for_pbo(struct gl_context *ctx, bool create_pbo, GLenum pbo_target, int width, int height, GLenum format, GLenum type, const void *pixels, const struct gl_pixelstore_attrib *packing, GLuint *tmp_pbo, GLuint *tmp_tex) { uint32_t pbo_format; GLenum internal_format; unsigned row_stride; struct gl_buffer_object *buffer_obj; struct gl_texture_object *tex_obj; struct gl_texture_image *tex_image; bool read_only; if (packing->SwapBytes || packing->LsbFirst || packing->Invert) return NULL; pbo_format = _mesa_format_from_format_and_type(format, type); if (_mesa_format_is_mesa_array_format(pbo_format)) pbo_format = _mesa_format_from_array_format(pbo_format); if (!pbo_format || !ctx->TextureFormatSupported[pbo_format]) return NULL; /* Account for SKIP_PIXELS, SKIP_ROWS, ALIGNMENT, and SKIP_IMAGES */ pixels = _mesa_image_address3d(packing, pixels, width, height, format, type, 0, 0, 0); row_stride = _mesa_image_row_stride(packing, width, format, type); if (_mesa_is_bufferobj(packing->BufferObj)) { *tmp_pbo = 0; buffer_obj = packing->BufferObj; } else { bool is_pixel_pack = pbo_target == GL_PIXEL_PACK_BUFFER; assert(create_pbo); _mesa_GenBuffers(1, tmp_pbo); /* We are not doing this inside meta_begin/end. However, we know the * client doesn't have the given target bound, so we can go ahead and * squash it. We'll set it back when we're done. */ _mesa_BindBuffer(pbo_target, *tmp_pbo); /* In case of GL_PIXEL_PACK_BUFFER, pass null pointer for the pixel * data to avoid unnecessary data copying in _mesa_BufferData(). */ if (is_pixel_pack) _mesa_BufferData(pbo_target, row_stride * height, NULL, GL_STREAM_READ); else _mesa_BufferData(pbo_target, row_stride * height, pixels, GL_STREAM_DRAW); buffer_obj = packing->BufferObj; pixels = NULL; _mesa_BindBuffer(pbo_target, 0); } _mesa_GenTextures(1, tmp_tex); tex_obj = _mesa_lookup_texture(ctx, *tmp_tex); _mesa_initialize_texture_object(ctx, tex_obj, *tmp_tex, GL_TEXTURE_2D); /* This must be set after _mesa_initialize_texture_object, not before. */ tex_obj->Immutable = GL_TRUE; /* This is required for interactions with ARB_texture_view. */ tex_obj->NumLayers = 1; internal_format = _mesa_get_format_base_format(pbo_format); tex_image = _mesa_get_tex_image(ctx, tex_obj, tex_obj->Target, 0); _mesa_init_teximage_fields(ctx, tex_image, width, height, 1, 0, internal_format, pbo_format); read_only = pbo_target == GL_PIXEL_UNPACK_BUFFER; if (!ctx->Driver.SetTextureStorageForBufferObject(ctx, tex_obj, buffer_obj, (intptr_t)pixels, row_stride, read_only)) { _mesa_DeleteTextures(1, tmp_tex); _mesa_DeleteBuffers(1, tmp_pbo); return NULL; } return tex_image; }
/** * Get a color texture image with decompression. */ static void get_tex_rgba_compressed(struct gl_context *ctx, GLuint dimensions, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage, GLbitfield transferOps) { /* don't want to apply sRGB -> RGB conversion here so override the format */ const mesa_format texFormat = _mesa_get_srgb_format_linear(texImage->TexFormat); const GLenum baseFormat = _mesa_get_format_base_format(texFormat); const GLuint width = texImage->Width; const GLuint height = texImage->Height; const GLuint depth = texImage->Depth; GLfloat *tempImage, *tempSlice; GLuint slice; int srcStride, dstStride; uint32_t dstFormat; bool needsRebase; uint8_t rebaseSwizzle[4]; /* Decompress into temp float buffer, then pack into user buffer */ tempImage = malloc(width * height * depth * 4 * sizeof(GLfloat)); if (!tempImage) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); return; } /* Decompress the texture image slices - results in 'tempImage' */ for (slice = 0; slice < depth; slice++) { GLubyte *srcMap; GLint srcRowStride; tempSlice = tempImage + slice * 4 * width * height; ctx->Driver.MapTextureImage(ctx, texImage, slice, 0, 0, width, height, GL_MAP_READ_BIT, &srcMap, &srcRowStride); if (srcMap) { _mesa_decompress_image(texFormat, width, height, srcMap, srcRowStride, tempSlice); ctx->Driver.UnmapTextureImage(ctx, texImage, slice); } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage"); free(tempImage); return; } } /* Depending on the base format involved we may need to apply a rebase * transform (for example: if we download to a Luminance format we want * G=0 and B=0). */ if (baseFormat == GL_LUMINANCE || baseFormat == GL_INTENSITY) { needsRebase = true; rebaseSwizzle[0] = MESA_FORMAT_SWIZZLE_X; rebaseSwizzle[1] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[2] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[3] = MESA_FORMAT_SWIZZLE_ONE; } else if (baseFormat == GL_LUMINANCE_ALPHA) { needsRebase = true; rebaseSwizzle[0] = MESA_FORMAT_SWIZZLE_X; rebaseSwizzle[1] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[2] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[3] = MESA_FORMAT_SWIZZLE_W; } else { needsRebase = false; } srcStride = 4 * width * sizeof(GLfloat); dstStride = _mesa_image_row_stride(&ctx->Pack, width, format, type); dstFormat = _mesa_format_from_format_and_type(format, type); tempSlice = tempImage; for (slice = 0; slice < depth; slice++) { void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels, width, height, format, type, slice, 0, 0); // _mesa_format_convert(dest, dstFormat, dstStride, // tempSlice, RGBA32_FLOAT, srcStride, // width, height, // needsRebase ? rebaseSwizzle : NULL); tempSlice += 4 * width * height; } free(tempImage); }
/** * Try to do glGetTexImage() with simple memcpy(). * \return GL_TRUE if done, GL_FALSE otherwise */ static GLboolean get_tex_memcpy(GLcontext *ctx, GLenum format, GLenum type, GLvoid *pixels, const struct gl_texture_object *texObj, const struct gl_texture_image *texImage) { GLboolean memCopy = GL_FALSE; /* Texture image should have been mapped already */ assert(texImage->Data); /* * Check if the src/dst formats are compatible. * Also note that GL's pixel transfer ops don't apply to glGetTexImage() * so we don't have to worry about those. * XXX more format combinations could be supported here. */ if ((texObj->Target == GL_TEXTURE_1D || texObj->Target == GL_TEXTURE_2D || texObj->Target == GL_TEXTURE_RECTANGLE || (texObj->Target >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && texObj->Target <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z))) { if (texImage->TexFormat == MESA_FORMAT_ARGB8888 && format == GL_BGRA && type == GL_UNSIGNED_BYTE && !ctx->Pack.SwapBytes && _mesa_little_endian()) { memCopy = GL_TRUE; } else if (texImage->TexFormat == MESA_FORMAT_AL88 && format == GL_LUMINANCE_ALPHA && type == GL_UNSIGNED_BYTE && !ctx->Pack.SwapBytes && _mesa_little_endian()) { memCopy = GL_TRUE; } else if (texImage->TexFormat == MESA_FORMAT_L8 && format == GL_LUMINANCE && type == GL_UNSIGNED_BYTE) { memCopy = GL_TRUE; } else if (texImage->TexFormat == MESA_FORMAT_A8 && format == GL_ALPHA && type == GL_UNSIGNED_BYTE) { memCopy = GL_TRUE; } } 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); const GLubyte *src = texImage->Data; const GLint srcRowStride = texImage->RowStride * bpp; GLuint row; if (bytesPerRow == dstRowStride && bytesPerRow == srcRowStride) { memcpy(dst, src, bytesPerRow * texImage->Height); } else { for (row = 0; row < texImage->Height; row++) { memcpy(dst, src, bytesPerRow); dst += dstRowStride; src += srcRowStride; } } } return memCopy; }
/** * 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) { const GLint imgX = x, imgY = y; struct gl_renderbuffer *rb = ctx->DrawBuffer->_ColorDrawBuffers[0]; GLenum rbType; SWcontext *swrast = SWRAST_CONTEXT(ctx); SWspan span; GLboolean simpleZoom; GLint yStep; /* +1 or -1 */ struct gl_pixelstore_attrib unpack; GLint destX, destY, drawWidth, drawHeight; /* post clipping */ if (!rb) return GL_TRUE; /* no-op */ rbType = rb->DataType; if ((swrast->_RasterMask & ~CLIP_BIT) || ctx->Texture._EnabledCoordUnits || userUnpack->SwapBytes || ctx->_ImageTransferState) { /* can't handle any of those conditions */ return GL_FALSE; } INIT_SPAN(span, GL_BITMAP); span.arrayMask = SPAN_RGBA; span.arrayAttribs = FRAG_BIT_COL0; _swrast_span_default_attribs(ctx, &span); /* copy input params since clipping may change them */ unpack = *userUnpack; destX = x; destY = y; drawWidth = width; drawHeight = height; /* check for simple zooming and clipping */ if (ctx->Pixel.ZoomX == 1.0F && (ctx->Pixel.ZoomY == 1.0F || ctx->Pixel.ZoomY == -1.0F)) { if (!_mesa_clip_drawpixels(ctx, &destX, &destY, &drawWidth, &drawHeight, &unpack)) { /* image was completely clipped: no-op, all done */ return GL_TRUE; } simpleZoom = GL_TRUE; yStep = (GLint) ctx->Pixel.ZoomY; ASSERT(yStep == 1 || yStep == -1); } else { /* non-simple zooming */ simpleZoom = GL_FALSE; yStep = 1; if (unpack.RowLength == 0) unpack.RowLength = width; } /* * Ready to draw! */ if (format == GL_RGBA && type == rbType) { const GLubyte *src = (const GLubyte *) _mesa_image_address2d(&unpack, pixels, width, height, format, type, 0, 0); const GLint srcStride = _mesa_image_row_stride(&unpack, width, format, type); if (simpleZoom) { GLint row; for (row = 0; row < drawHeight; row++) { rb->PutRow(ctx, rb, drawWidth, destX, destY, src, NULL); src += srcStride; destY += yStep; } } else { /* with zooming */ GLint row; for (row = 0; row < drawHeight; row++) { span.x = destX; span.y = destY + row; span.end = drawWidth; span.array->ChanType = rbType; _swrast_write_zoomed_rgba_span(ctx, imgX, imgY, &span, src); src += srcStride; } span.array->ChanType = CHAN_TYPE; } return GL_TRUE; } if (format == GL_RGB && type == rbType) { const GLubyte *src = (const GLubyte *) _mesa_image_address2d(&unpack, pixels, width, height, format, type, 0, 0); const GLint srcStride = _mesa_image_row_stride(&unpack, width, format, type); if (simpleZoom) { GLint row; for (row = 0; row < drawHeight; row++) { rb->PutRowRGB(ctx, rb, drawWidth, destX, destY, src, NULL); src += srcStride; destY += yStep; } } else { /* with zooming */ GLint row; for (row = 0; row < drawHeight; row++) { span.x = destX; span.y = destY; span.end = drawWidth; span.array->ChanType = rbType; _swrast_write_zoomed_rgb_span(ctx, imgX, imgY, &span, src); src += srcStride; destY++; } span.array->ChanType = CHAN_TYPE; } return GL_TRUE; } /* Remaining cases haven't been tested with alignment != 1 */ if (userUnpack->Alignment != 1) return GL_FALSE; if (format == GL_LUMINANCE && type == CHAN_TYPE && rbType == CHAN_TYPE) { const GLchan *src = (const GLchan *) pixels + (unpack.SkipRows * unpack.RowLength + unpack.SkipPixels); if (simpleZoom) { /* no zooming */ GLint row; ASSERT(drawWidth <= MAX_WIDTH); for (row = 0; row < drawHeight; row++) { GLchan rgb[MAX_WIDTH][3]; GLint i; for (i = 0;i<drawWidth;i++) { rgb[i][0] = src[i]; rgb[i][1] = src[i]; rgb[i][2] = src[i]; } rb->PutRowRGB(ctx, rb, drawWidth, destX, destY, rgb, NULL); src += unpack.RowLength; destY += yStep; } } else { /* with zooming */ GLint row; ASSERT(drawWidth <= MAX_WIDTH); for (row = 0; row < drawHeight; row++) { GLchan rgb[MAX_WIDTH][3]; GLint i; for (i = 0;i<drawWidth;i++) { rgb[i][0] = src[i]; rgb[i][1] = src[i]; rgb[i][2] = src[i]; } span.x = destX; span.y = destY; span.end = drawWidth; _swrast_write_zoomed_rgb_span(ctx, imgX, imgY, &span, rgb); src += unpack.RowLength; destY++; } } return GL_TRUE; } if (format == GL_LUMINANCE_ALPHA && type == CHAN_TYPE && rbType == CHAN_TYPE) { const GLchan *src = (const GLchan *) pixels + (unpack.SkipRows * unpack.RowLength + unpack.SkipPixels)*2; if (simpleZoom) { GLint row; ASSERT(drawWidth <= MAX_WIDTH); for (row = 0; row < drawHeight; row++) { GLint i; const GLchan *ptr = src; for (i = 0;i<drawWidth;i++) { span.array->rgba[i][0] = *ptr; span.array->rgba[i][1] = *ptr; span.array->rgba[i][2] = *ptr++; span.array->rgba[i][3] = *ptr++; } rb->PutRow(ctx, rb, drawWidth, destX, destY, span.array->rgba, NULL); src += unpack.RowLength*2; destY += yStep; } } else { /* with zooming */ GLint row; ASSERT(drawWidth <= MAX_WIDTH); for (row = 0; row < drawHeight; row++) { const GLchan *ptr = src; GLint i; for (i = 0;i<drawWidth;i++) { span.array->rgba[i][0] = *ptr; span.array->rgba[i][1] = *ptr; span.array->rgba[i][2] = *ptr++; span.array->rgba[i][3] = *ptr++; } span.x = destX; span.y = destY; span.end = drawWidth; _swrast_write_zoomed_rgba_span(ctx, imgX, imgY, &span, span.array->rgba); src += unpack.RowLength*2; destY++; } } return GL_TRUE; } if (format == GL_COLOR_INDEX && type == GL_UNSIGNED_BYTE) { const GLubyte *src = (const GLubyte *) pixels + unpack.SkipRows * unpack.RowLength + unpack.SkipPixels; if (rbType == GL_UNSIGNED_BYTE) { /* convert ubyte/CI data to ubyte/RGBA */ if (simpleZoom) { GLint row; for (row = 0; row < drawHeight; row++) { ASSERT(drawWidth <= MAX_WIDTH); _mesa_map_ci8_to_rgba8(ctx, drawWidth, src, span.array->rgba8); rb->PutRow(ctx, rb, drawWidth, destX, destY, span.array->rgba8, NULL); src += unpack.RowLength; destY += yStep; } } else { /* ubyte/CI to ubyte/RGBA with zooming */ GLint row; for (row = 0; row < drawHeight; row++) { ASSERT(drawWidth <= MAX_WIDTH); _mesa_map_ci8_to_rgba8(ctx, drawWidth, src, span.array->rgba8); span.x = destX; span.y = destY; span.end = drawWidth; _swrast_write_zoomed_rgba_span(ctx, imgX, imgY, &span, span.array->rgba8); src += unpack.RowLength; destY++; } } return GL_TRUE; } } /* can't handle this pixel format and/or data type */ return GL_FALSE; }
/** * \brief A fast path for glGetTexImage. * * \see intel_readpixels_tiled_memcpy() */ bool intel_gettexsubimage_tiled_memcpy(struct gl_context *ctx, struct gl_texture_image *texImage, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels, const struct gl_pixelstore_attrib *packing) { struct brw_context *brw = brw_context(ctx); struct intel_texture_image *image = intel_texture_image(texImage); int dst_pitch; /* The miptree's buffer. */ drm_intel_bo *bo; int error = 0; uint32_t cpp; mem_copy_fn mem_copy = NULL; /* This fastpath is restricted to specific texture types: * a 2D BGRA, RGBA, L8 or A8 texture. It could be generalized to support * more types. * * FINISHME: The restrictions below on packing alignment and packing row * length are likely unneeded now because we calculate the destination stride * with _mesa_image_row_stride. However, before removing the restrictions * we need tests. */ if (!brw->has_llc || !(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) || !(texImage->TexObject->Target == GL_TEXTURE_2D || texImage->TexObject->Target == GL_TEXTURE_RECTANGLE) || pixels == NULL || _mesa_is_bufferobj(packing->BufferObj) || packing->Alignment > 4 || packing->SkipPixels > 0 || packing->SkipRows > 0 || (packing->RowLength != 0 && packing->RowLength != width) || packing->SwapBytes || packing->LsbFirst || packing->Invert) return false; /* We can't handle copying from RGBX or BGRX because the tiled_memcpy * function doesn't set the last channel to 1. */ if (texImage->TexFormat == MESA_FORMAT_B8G8R8X8_UNORM || texImage->TexFormat == MESA_FORMAT_R8G8B8X8_UNORM) return false; if (!intel_get_memcpy(texImage->TexFormat, format, type, &mem_copy, &cpp, INTEL_DOWNLOAD)) return false; /* If this is a nontrivial texture view, let another path handle it instead. */ if (texImage->TexObject->MinLayer) return false; if (!image->mt || (image->mt->tiling != I915_TILING_X && image->mt->tiling != I915_TILING_Y)) { /* The algorithm is written only for X- or Y-tiled memory. */ return false; } /* Since we are going to write raw data to the miptree, we need to resolve * any pending fast color clears before we start. */ intel_miptree_resolve_color(brw, image->mt); bo = image->mt->bo; if (drm_intel_bo_references(brw->batch.bo, bo)) { perf_debug("Flushing before mapping a referenced bo.\n"); intel_batchbuffer_flush(brw); } error = brw_bo_map(brw, bo, false /* write enable */, "miptree"); if (error) { DBG("%s: failed to map bo\n", __func__); return false; } dst_pitch = _mesa_image_row_stride(packing, width, format, type); DBG("%s: level=%d x,y=(%d,%d) (w,h)=(%d,%d) format=0x%x type=0x%x " "mesa_format=0x%x tiling=%d " "packing=(alignment=%d row_length=%d skip_pixels=%d skip_rows=%d)\n", __func__, texImage->Level, xoffset, yoffset, width, height, format, type, texImage->TexFormat, image->mt->tiling, packing->Alignment, packing->RowLength, packing->SkipPixels, packing->SkipRows); int level = texImage->Level + texImage->TexObject->MinLevel; /* Adjust x and y offset based on miplevel */ xoffset += image->mt->level[level].level_x; yoffset += image->mt->level[level].level_y; tiled_to_linear( xoffset * cpp, (xoffset + width) * cpp, yoffset, yoffset + height, pixels - (ptrdiff_t) yoffset * dst_pitch - (ptrdiff_t) xoffset * cpp, bo->virtual, dst_pitch, image->mt->pitch, brw->has_swizzling, image->mt->tiling, mem_copy ); drm_intel_bo_unmap(bo); return true; }
/** * Store a combined depth/stencil texture image. */ static GLboolean _mesa_texstore_s8_z24(TEXSTORE_PARAMS) { const GLuint depthScale = 0xffffff; const GLint srcRowStride = _mesa_image_row_stride(srcPacking, srcWidth, srcFormat, srcType); GLint img, row; GLuint *depth; GLubyte *stencil; assert(dstFormat == MESA_FORMAT_Z24_UNORM_S8_UINT); assert(srcFormat == GL_DEPTH_STENCIL_EXT || srcFormat == GL_DEPTH_COMPONENT || srcFormat == GL_STENCIL_INDEX); assert(srcFormat != GL_DEPTH_STENCIL_EXT || srcType == GL_UNSIGNED_INT_24_8_EXT || srcType == GL_FLOAT_32_UNSIGNED_INT_24_8_REV); depth = malloc(srcWidth * sizeof(GLuint)); stencil = malloc(srcWidth * sizeof(GLubyte)); if (!depth || !stencil) { free(depth); free(stencil); return GL_FALSE; } for (img = 0; img < srcDepth; img++) { GLuint *dstRow = (GLuint *) dstSlices[img]; const GLubyte *src = (const GLubyte *) _mesa_image_address(dims, srcPacking, srcAddr, srcWidth, srcHeight, srcFormat, srcType, img, 0, 0); for (row = 0; row < srcHeight; row++) { GLint i; GLboolean keepdepth = GL_FALSE, keepstencil = GL_FALSE; if (srcFormat == GL_DEPTH_COMPONENT) { /* preserve stencil */ keepstencil = GL_TRUE; } else if (srcFormat == GL_STENCIL_INDEX) { /* preserve depth */ keepdepth = GL_TRUE; } if (keepdepth == GL_FALSE) /* the 24 depth bits will be in the low position: */ _mesa_unpack_depth_span(ctx, srcWidth, GL_UNSIGNED_INT, /* dst type */ keepstencil ? depth : dstRow, /* dst addr */ depthScale, srcType, src, srcPacking); if (keepstencil == GL_FALSE) /* get the 8-bit stencil values */ _mesa_unpack_stencil_span(ctx, srcWidth, GL_UNSIGNED_BYTE, /* dst type */ stencil, /* dst addr */ srcType, src, srcPacking, ctx->_ImageTransferState); /* merge stencil values into depth values */ for (i = 0; i < srcWidth; i++) { if (keepstencil) dstRow[i] = depth[i] | (dstRow[i] & 0xFF000000); else dstRow[i] = (dstRow[i] & 0xFFFFFF) | (stencil[i] << 24); } src += srcRowStride; dstRow += dstRowStride / sizeof(GLuint); } } free(depth); free(stencil); return GL_TRUE; }
/* * Read R, G, B, A, RGB, L, or LA pixels. */ static void read_rgba_pixels( 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; bool dst_is_integer, dst_is_luminance, needs_rebase; int dst_stride, src_stride, rb_stride; uint32_t dst_format, src_format; GLubyte *dst, *map; mesa_format rb_format; bool needs_rgba; void *rgba, *src; bool src_is_uint = false; uint8_t rebase_swizzle[4]; struct gl_framebuffer *fb = ctx->ReadBuffer; struct gl_renderbuffer *rb = fb->_ColorReadBuffer; if (!rb) return; transferOps = get_readpixels_transfer_ops(ctx, rb->Format, format, type, GL_FALSE); /* Describe the dst format */ dst_is_integer = _mesa_is_enum_format_integer(format); dst_stride = _mesa_image_row_stride(packing, width, format, type); dst_format = _mesa_format_from_format_and_type(format, type); dst_is_luminance = format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA || format == GL_LUMINANCE_INTEGER_EXT || format == GL_LUMINANCE_ALPHA_INTEGER_EXT; dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height, format, type, 0, 0); /* Map the source render buffer */ ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, &map, &rb_stride); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return; } rb_format = _mesa_get_srgb_format_linear(rb->Format); /* * Depending on the base formats involved in the conversion we might need to * rebase some values, so for these formats we compute a rebase swizzle. */ if (rb->_BaseFormat == GL_LUMINANCE || rb->_BaseFormat == GL_INTENSITY) { needs_rebase = true; rebase_swizzle[0] = MESA_FORMAT_SWIZZLE_X; rebase_swizzle[1] = MESA_FORMAT_SWIZZLE_ZERO; rebase_swizzle[2] = MESA_FORMAT_SWIZZLE_ZERO; rebase_swizzle[3] = MESA_FORMAT_SWIZZLE_ONE; } else if (rb->_BaseFormat == GL_LUMINANCE_ALPHA) { needs_rebase = true; rebase_swizzle[0] = MESA_FORMAT_SWIZZLE_X; rebase_swizzle[1] = MESA_FORMAT_SWIZZLE_ZERO; rebase_swizzle[2] = MESA_FORMAT_SWIZZLE_ZERO; rebase_swizzle[3] = MESA_FORMAT_SWIZZLE_W; } else if (_mesa_get_format_base_format(rb_format) != rb->_BaseFormat) { needs_rebase = _mesa_compute_rgba2base2rgba_component_mapping(rb->_BaseFormat, rebase_swizzle); } else { needs_rebase = false; } /* Since _mesa_format_convert does not handle transferOps we need to handle * them before we call the function. This requires to convert to RGBA float * first so we can call _mesa_apply_rgba_transfer_ops. If the dst format is * integer transferOps do not apply. * * Converting to luminance also requires converting to RGBA first, so we can * then compute luminance values as L=R+G+B. Notice that this is different * from GetTexImage, where we compute L=R. */ assert(!transferOps || (transferOps && !dst_is_integer)); needs_rgba = transferOps || dst_is_luminance; rgba = NULL; if (needs_rgba) { uint32_t rgba_format; int rgba_stride; bool need_convert; /* Convert to RGBA float or int/uint depending on the type of the src */ if (dst_is_integer) { src_is_uint = _mesa_is_format_unsigned(rb_format); if (src_is_uint) { rgba_format = RGBA32_UINT; rgba_stride = width * 4 * sizeof(GLuint); } else { rgba_format = RGBA32_INT; rgba_stride = width * 4 * sizeof(GLint); } } else { rgba_format = RGBA32_FLOAT; rgba_stride = width * 4 * sizeof(GLfloat); } /* If we are lucky and the dst format matches the RGBA format we need to * convert to, then we can convert directly into the dst buffer and avoid * the final conversion/copy from the rgba buffer to the dst buffer. */ if (dst_format == rgba_format) { need_convert = false; rgba = dst; } else { need_convert = true; rgba = malloc(height * rgba_stride); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); goto done_unmap; } } /* Convert to RGBA now */ _mesa_format_convert(rgba, rgba_format, rgba_stride, map, rb_format, rb_stride, width, height, needs_rebase ? rebase_swizzle : NULL); /* Handle transfer ops if necessary */ if (transferOps) _mesa_apply_rgba_transfer_ops(ctx, transferOps, width * height, rgba); /* If we had to rebase, we have already taken care of that */ needs_rebase = false; /* If we were lucky and our RGBA conversion matches the dst format, then * we are done. */ if (!need_convert) goto done_swap; /* Otherwise, we need to convert from RGBA to dst next */ src = rgba; src_format = rgba_format; src_stride = rgba_stride; } else { /* No RGBA conversion needed, convert directly to dst */ src = map; src_format = rb_format; src_stride = rb_stride; } /* Do the conversion. * * If the dst format is Luminance, we need to do the conversion by computing * L=R+G+B values. */ if (!dst_is_luminance) { _mesa_format_convert(dst, dst_format, dst_stride, src, src_format, src_stride, width, height, needs_rebase ? rebase_swizzle : NULL); } else if (!dst_is_integer) { /* Compute float Luminance values from RGBA float */ int luminance_stride, luminance_bytes; void *luminance; uint32_t luminance_format; luminance_stride = width * sizeof(GL_FLOAT); if (format == GL_LUMINANCE_ALPHA) luminance_stride *= 2; luminance_bytes = height * luminance_stride; luminance = malloc(luminance_bytes); if (!luminance) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); free(rgba); goto done_unmap; } _mesa_pack_luminance_from_rgba_float(width * height, src, luminance, format, transferOps); /* Convert from Luminance float to dst (this will hadle type conversion * from float to the type of dst if necessary) */ luminance_format = _mesa_format_from_format_and_type(format, GL_FLOAT); _mesa_format_convert(dst, dst_format, dst_stride, luminance, luminance_format, luminance_stride, width, height, NULL); } else { _mesa_pack_luminance_from_rgba_integer(width * height, src, !src_is_uint, dst, format, type); } if (rgba) free(rgba); done_swap: /* Handle byte swapping if required */ if (packing->SwapBytes) { int components = _mesa_components_in_format(format); GLint swapSize = _mesa_sizeof_packed_type(type); if (swapSize == 2) _mesa_swap2((GLushort *) dst, width * height * components); else if (swapSize == 4) _mesa_swap4((GLuint *) dst, width * height * components); } done_unmap: ctx->Driver.UnmapRenderbuffer(ctx, rb); }
static GLboolean r300ValidateClientStorage(GLcontext * ctx, GLenum target, GLint internalFormat, GLint srcWidth, GLint srcHeight, GLenum format, GLenum type, const void *pixels, const struct gl_pixelstore_attrib *packing, struct gl_texture_object *texObj, struct gl_texture_image *texImage) { r300ContextPtr rmesa = R300_CONTEXT(ctx); if (0) fprintf(stderr, "intformat %s format %s type %s\n", _mesa_lookup_enum_by_nr(internalFormat), _mesa_lookup_enum_by_nr(format), _mesa_lookup_enum_by_nr(type)); if (!ctx->Unpack.ClientStorage) return 0; if (ctx->_ImageTransferState || texImage->IsCompressed || texObj->GenerateMipmap) return 0; /* This list is incomplete, may be different on ppc??? */ switch (internalFormat) { case GL_RGBA: if (format == GL_BGRA && type == GL_UNSIGNED_INT_8_8_8_8_REV) { texImage->TexFormat = _dri_texformat_argb8888; } else return 0; break; case GL_RGB: if (format == GL_RGB && type == GL_UNSIGNED_SHORT_5_6_5) { texImage->TexFormat = _dri_texformat_rgb565; } else return 0; break; case GL_YCBCR_MESA: if (format == GL_YCBCR_MESA && type == GL_UNSIGNED_SHORT_8_8_REV_APPLE) { texImage->TexFormat = &_mesa_texformat_ycbcr_rev; } else if (format == GL_YCBCR_MESA && (type == GL_UNSIGNED_SHORT_8_8_APPLE || type == GL_UNSIGNED_BYTE)) { texImage->TexFormat = &_mesa_texformat_ycbcr; } else return 0; break; default: return 0; } /* Could deal with these packing issues, but currently don't: */ if (packing->SkipPixels || packing->SkipRows || packing->SwapBytes || packing->LsbFirst) { return 0; } { GLint srcRowStride = _mesa_image_row_stride(packing, srcWidth, format, type); if (0) fprintf(stderr, "%s: srcRowStride %d/%x\n", __FUNCTION__, srcRowStride, srcRowStride); /* Could check this later in upload, pitch restrictions could be * relaxed, but would need to store the image pitch somewhere, * as packing details might change before image is uploaded: */ if (!r300IsGartMemory(rmesa, pixels, srcHeight * srcRowStride) || (srcRowStride & 63)) return 0; /* Have validated that _mesa_transfer_teximage would be a straight * memcpy at this point. NOTE: future calls to TexSubImage will * overwrite the client data. This is explicitly mentioned in the * extension spec. */ texImage->Data = (void *)pixels; texImage->IsClientData = GL_TRUE; texImage->RowStride = srcRowStride / texImage->TexFormat->TexelBytes; return 1; } }
static GLboolean texstore_rgba(TEXSTORE_PARAMS) { void *tempImage = NULL, *tempRGBA = NULL; int srcRowStride, img; GLubyte *src, *dst; uint32_t srcMesaFormat; uint8_t rebaseSwizzle[4]; bool needRebase; bool transferOpsDone = false; /* We have to handle MESA_FORMAT_YCBCR manually because it is a special case * and _mesa_format_convert does not support it. In this case the we only * allow conversions between YCBCR formats and it is mostly a memcpy. */ if (dstFormat == MESA_FORMAT_YCBCR || dstFormat == MESA_FORMAT_YCBCR_REV) { return _mesa_texstore_ycbcr(ctx, dims, baseInternalFormat, dstFormat, dstRowStride, dstSlices, srcWidth, srcHeight, srcDepth, srcFormat, srcType, srcAddr, srcPacking); } /* We have to deal with GL_COLOR_INDEX manually because * _mesa_format_convert does not handle this format. So what we do here is * convert it to RGBA ubyte first and then convert from that to dst as usual. */ if (srcFormat == GL_COLOR_INDEX) { /* Notice that this will already handle byte swapping if necessary */ tempImage = _mesa_unpack_color_index_to_rgba_ubyte(ctx, dims, srcAddr, srcFormat, srcType, srcWidth, srcHeight, srcDepth, srcPacking, ctx->_ImageTransferState); if (!tempImage) return GL_FALSE; /* _mesa_unpack_color_index_to_rgba_ubyte has handled transferops * if needed. */ transferOpsDone = true; /* Now we only have to adjust our src info for a conversion from * the RGBA ubyte and then we continue as usual. */ srcAddr = tempImage; srcFormat = GL_RGBA; srcType = GL_UNSIGNED_BYTE; } else if (srcPacking->SwapBytes) { /* We have to handle byte-swapping scenarios before calling * _mesa_format_convert */ GLint swapSize = _mesa_sizeof_packed_type(srcType); if (swapSize == 2 || swapSize == 4) { int bytesPerPixel = _mesa_bytes_per_pixel(srcFormat, srcType); int swapsPerPixel = bytesPerPixel / swapSize; int elementCount = srcWidth * srcHeight * srcDepth; assert(bytesPerPixel % swapSize == 0); tempImage = malloc(elementCount * bytesPerPixel); if (!tempImage) return GL_FALSE; if (swapSize == 2) _mesa_swap2_copy(tempImage, (GLushort *) srcAddr, elementCount * swapsPerPixel); else _mesa_swap4_copy(tempImage, (GLuint *) srcAddr, elementCount * swapsPerPixel); srcAddr = tempImage; } } srcRowStride = _mesa_image_row_stride(srcPacking, srcWidth, srcFormat, srcType); srcMesaFormat = _mesa_format_from_format_and_type(srcFormat, srcType); dstFormat = _mesa_get_srgb_format_linear(dstFormat); /* If we have transferOps then we need to convert to RGBA float first, then apply transferOps, then do the conversion to dst */ if (!transferOpsDone && _mesa_texstore_needs_transfer_ops(ctx, baseInternalFormat, dstFormat)) { /* Allocate RGBA float image */ int elementCount = srcWidth * srcHeight * srcDepth; tempRGBA = malloc(4 * elementCount * sizeof(float)); if (!tempRGBA) { free(tempImage); free(tempRGBA); return GL_FALSE; } /* Convert from src to RGBA float */ src = (GLubyte *) srcAddr; dst = (GLubyte *) tempRGBA; for (img = 0; img < srcDepth; img++) { _mesa_format_convert(dst, RGBA32_FLOAT, 4 * srcWidth * sizeof(float), src, srcMesaFormat, srcRowStride, srcWidth, srcHeight, NULL); src += srcHeight * srcRowStride; dst += srcHeight * 4 * srcWidth * sizeof(float); } /* Apply transferOps */ _mesa_apply_rgba_transfer_ops(ctx, ctx->_ImageTransferState, elementCount, (float(*)[4]) tempRGBA); /* Now we have to adjust our src info for a conversion from * the RGBA float image and then we continue as usual. */ srcAddr = tempRGBA; srcFormat = GL_RGBA; srcType = GL_FLOAT; srcRowStride = srcWidth * 4 * sizeof(float); srcMesaFormat = RGBA32_FLOAT; } src = (GLubyte *) _mesa_image_address(dims, srcPacking, srcAddr, srcWidth, srcHeight, srcFormat, srcType, 0, 0, 0); if (_mesa_get_format_base_format(dstFormat) != baseInternalFormat) { needRebase = _mesa_compute_rgba2base2rgba_component_mapping(baseInternalFormat, rebaseSwizzle); } else { needRebase = false; } for (img = 0; img < srcDepth; img++) { _mesa_format_convert(dstSlices[img], dstFormat, dstRowStride, src, srcMesaFormat, srcRowStride, srcWidth, srcHeight, needRebase ? rebaseSwizzle : NULL); src += srcHeight * srcRowStride; } free(tempImage); free(tempRGBA); return GL_TRUE; }
/** * \brief A fast path for glReadPixels * * This fast path is taken when the source format is BGRA, RGBA, * A or L and when the texture memory is X- or Y-tiled. It downloads * the source data by directly mapping the memory without a GTT fence. * This then needs to be de-tiled on the CPU before presenting the data to * the user in the linear fasion. * * This is a performance win over the conventional texture download path. * In the conventional texture download path, the texture is either mapped * through the GTT or copied to a linear buffer with the blitter before * handing off to a software path. This allows us to avoid round-tripping * through the GPU (in the case where we would be blitting) and do only a * single copy operation. */ static bool intel_readpixels_tiled_memcpy(struct gl_context * ctx, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * pixels, const struct gl_pixelstore_attrib *pack) { struct brw_context *brw = brw_context(ctx); struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; /* This path supports reading from color buffers only */ if (rb == NULL) return false; struct intel_renderbuffer *irb = intel_renderbuffer(rb); int dst_pitch; /* The miptree's buffer. */ drm_intel_bo *bo; int error = 0; uint32_t cpp; mem_copy_fn mem_copy = NULL; /* This fastpath is restricted to specific renderbuffer types: * a 2D BGRA, RGBA, L8 or A8 texture. It could be generalized to support * more types. */ if (!brw->has_llc || !(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) || pixels == NULL || _mesa_is_bufferobj(pack->BufferObj) || pack->Alignment > 4 || pack->SkipPixels > 0 || pack->SkipRows > 0 || (pack->RowLength != 0 && pack->RowLength != width) || pack->SwapBytes || pack->LsbFirst || pack->Invert) return false; /* Only a simple blit, no scale, bias or other mapping. */ if (ctx->_ImageTransferState) return false; /* This renderbuffer can come from a texture. In this case, we impose * some of the same restrictions we have for textures and adjust for * miplevels. */ if (rb->TexImage) { if (rb->TexImage->TexObject->Target != GL_TEXTURE_2D && rb->TexImage->TexObject->Target != GL_TEXTURE_RECTANGLE) return false; int level = rb->TexImage->Level + rb->TexImage->TexObject->MinLevel; /* Adjust x and y offset based on miplevel */ xoffset += irb->mt->level[level].level_x; yoffset += irb->mt->level[level].level_y; } /* It is possible that the renderbuffer (or underlying texture) is * multisampled. Since ReadPixels from a multisampled buffer requires a * multisample resolve, we can't handle this here */ if (rb->NumSamples > 1) return false; /* We can't handle copying from RGBX or BGRX because the tiled_memcpy * function doesn't set the last channel to 1. Note this checks BaseFormat * rather than TexFormat in case the RGBX format is being simulated with an * RGBA format. */ if (rb->_BaseFormat == GL_RGB) return false; if (!intel_get_memcpy(rb->Format, format, type, &mem_copy, &cpp, INTEL_DOWNLOAD)) return false; if (!irb->mt || (irb->mt->tiling != I915_TILING_X && irb->mt->tiling != I915_TILING_Y)) { /* The algorithm is written only for X- or Y-tiled memory. */ return false; } /* Since we are going to read raw data to the miptree, we need to resolve * any pending fast color clears before we start. */ intel_miptree_resolve_color(brw, irb->mt); bo = irb->mt->bo; if (drm_intel_bo_references(brw->batch.bo, bo)) { perf_debug("Flushing before mapping a referenced bo.\n"); intel_batchbuffer_flush(brw); } error = brw_bo_map(brw, bo, false /* write enable */, "miptree"); if (error) { DBG("%s: failed to map bo\n", __func__); return false; } dst_pitch = _mesa_image_row_stride(pack, width, format, type); /* For a window-system renderbuffer, the buffer is actually flipped * vertically, so we need to handle that. Since the detiling function * can only really work in the forwards direction, we have to be a * little creative. First, we compute the Y-offset of the first row of * the renderbuffer (in renderbuffer coordinates). We then match that * with the last row of the client's data. Finally, we give * tiled_to_linear a negative pitch so that it walks through the * client's data backwards as it walks through the renderbufer forwards. */ if (rb->Name == 0) { yoffset = rb->Height - yoffset - height; pixels += (ptrdiff_t) (height - 1) * dst_pitch; dst_pitch = -dst_pitch; } /* We postponed printing this message until having committed to executing * the function. */ DBG("%s: x,y=(%d,%d) (w,h)=(%d,%d) format=0x%x type=0x%x " "mesa_format=0x%x tiling=%d " "pack=(alignment=%d row_length=%d skip_pixels=%d skip_rows=%d)\n", __func__, xoffset, yoffset, width, height, format, type, rb->Format, irb->mt->tiling, pack->Alignment, pack->RowLength, pack->SkipPixels, pack->SkipRows); tiled_to_linear( xoffset * cpp, (xoffset + width) * cpp, yoffset, yoffset + height, pixels - (ptrdiff_t) yoffset * dst_pitch - (ptrdiff_t) xoffset * cpp, bo->virtual, dst_pitch, irb->mt->pitch, brw->has_swizzling, irb->mt->tiling, mem_copy ); drm_intel_bo_unmap(bo); return true; }
/** * 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; }
/** * Get an uncompressed color texture image. */ static void get_tex_rgba_uncompressed(struct gl_context *ctx, GLuint dimensions, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage, GLbitfield transferOps) { /* don't want to apply sRGB -> RGB conversion here so override the format */ const mesa_format texFormat = _mesa_get_srgb_format_linear(texImage->TexFormat); const GLuint width = texImage->Width; GLuint height = texImage->Height; GLuint depth = texImage->Depth; GLuint img; GLboolean dst_is_integer; uint32_t dst_format; int dst_stride; uint8_t rebaseSwizzle[4]; bool needsRebase; void *rgba = NULL; if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) { depth = height; height = 1; } /* Depending on the base format involved we may need to apply a rebase * transform (for example: if we download to a Luminance format we want * G=0 and B=0). */ if (texImage->_BaseFormat == GL_LUMINANCE || texImage->_BaseFormat == GL_INTENSITY) { needsRebase = true; rebaseSwizzle[0] = MESA_FORMAT_SWIZZLE_X; rebaseSwizzle[1] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[2] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[3] = MESA_FORMAT_SWIZZLE_ONE; } else if (texImage->_BaseFormat == GL_LUMINANCE_ALPHA) { needsRebase = true; rebaseSwizzle[0] = MESA_FORMAT_SWIZZLE_X; rebaseSwizzle[1] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[2] = MESA_FORMAT_SWIZZLE_ZERO; rebaseSwizzle[3] = MESA_FORMAT_SWIZZLE_W; // } else if (texImage->_BaseFormat != _mesa_get_format_base_format(texFormat)) { // needsRebase = // _mesa_compute_rgba2base2rgba_component_mapping(texImage->_BaseFormat, // rebaseSwizzle); } else { needsRebase = false; } /* Describe the dst format */ dst_is_integer = _mesa_is_enum_format_integer(format); dst_format = _mesa_format_from_format_and_type(format, type); dst_stride = _mesa_image_row_stride(&ctx->Pack, width, format, type); /* Since _mesa_format_convert does not handle transferOps we need to handle * them before we call the function. This requires to convert to RGBA float * first so we can call _mesa_apply_rgba_transfer_ops. If the dst format is * integer then transferOps do not apply. */ assert(!transferOps || (transferOps && !dst_is_integer)); (void) dst_is_integer; /* silence unused var warning */ for (img = 0; img < depth; img++) { GLubyte *srcMap; GLint rowstride; GLubyte *img_src; void *dest; void *src; int src_stride; uint32_t src_format; /* map src texture buffer */ ctx->Driver.MapTextureImage(ctx, texImage, img, 0, 0, width, height, GL_MAP_READ_BIT, &srcMap, &rowstride); if (!srcMap) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage"); goto done; } img_src = srcMap; dest = _mesa_image_address(dimensions, &ctx->Pack, pixels, width, height, format, type, img, 0, 0); if (transferOps) { uint32_t rgba_format; int rgba_stride; bool need_convert = false; /* We will convert to RGBA float */ // rgba_format = RGBA32_FLOAT; rgba_stride = width * 4 * sizeof(GLfloat); /* If we are lucky and the dst format matches the RGBA format we need * to convert to, then we can convert directly into the dst buffer * and avoid the final conversion/copy from the rgba buffer to the dst * buffer. */ if (format == rgba_format) { rgba = dest; } else if (rgba == NULL) { /* Allocate the RGBA buffer only once */ need_convert = true; rgba = malloc(height * rgba_stride); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); ctx->Driver.UnmapTextureImage(ctx, texImage, img); return; } } // _mesa_format_convert(rgba, rgba_format, rgba_stride, // img_src, texFormat, rowstride, // width, height, // needsRebase ? rebaseSwizzle : NULL); /* Handle transfer ops now */ _mesa_apply_rgba_transfer_ops(ctx, transferOps, width * height, rgba); /* If we had to rebase, we have already handled that */ needsRebase = false; /* If we were lucky and our RGBA conversion matches the dst format, then * we are done. */ if (!need_convert) goto do_swap; /* Otherwise, we need to convert from RGBA to dst next */ src = rgba; src_format = rgba_format; src_stride = rgba_stride; } else { /* No RGBA conversion needed, convert directly to dst */ src = img_src; src_format = texFormat; src_stride = rowstride; } /* Do the conversion to destination format */ // _mesa_format_convert(dest, dst_format, dst_stride, // src, src_format, src_stride, // width, height, // needsRebase ? rebaseSwizzle : NULL); do_swap: /* Handle byte swapping if required */ if (ctx->Pack.SwapBytes) { GLint swapSize = _mesa_sizeof_packed_type(type); if (swapSize == 2 || swapSize == 4) { int swapsPerPixel = _mesa_bytes_per_pixel(format, type) / swapSize; assert(_mesa_bytes_per_pixel(format, type) % swapSize == 0); if (swapSize == 2) _mesa_swap2((GLushort *) dest, width * height * swapsPerPixel); else if (swapSize == 4) _mesa_swap4((GLuint *) dest, width * height * swapsPerPixel); } } /* Unmap the src texture buffer */ ctx->Driver.UnmapTextureImage(ctx, texImage, img); } done: if (rgba) free(rgba); }
/** * \brief A fast path for glReadPixels * * This fast path is taken when the source format is BGRA, RGBA, * A or L and when the texture memory is X- or Y-tiled. It downloads * the source data by directly mapping the memory without a GTT fence. * This then needs to be de-tiled on the CPU before presenting the data to * the user in the linear fasion. * * This is a performance win over the conventional texture download path. * In the conventional texture download path, the texture is either mapped * through the GTT or copied to a linear buffer with the blitter before * handing off to a software path. This allows us to avoid round-tripping * through the GPU (in the case where we would be blitting) and do only a * single copy operation. */ static bool intel_readpixels_tiled_memcpy(struct gl_context * ctx, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid * pixels, const struct gl_pixelstore_attrib *pack) { struct brw_context *brw = brw_context(ctx); struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; const struct gen_device_info *devinfo = &brw->screen->devinfo; /* This path supports reading from color buffers only */ if (rb == NULL) return false; struct intel_renderbuffer *irb = intel_renderbuffer(rb); int dst_pitch; /* The miptree's buffer. */ struct brw_bo *bo; uint32_t cpp; mem_copy_fn mem_copy = NULL; /* This fastpath is restricted to specific renderbuffer types: * a 2D BGRA, RGBA, L8 or A8 texture. It could be generalized to support * more types. */ if (!devinfo->has_llc || !(type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) || pixels == NULL || _mesa_is_bufferobj(pack->BufferObj) || pack->Alignment > 4 || pack->SkipPixels > 0 || pack->SkipRows > 0 || (pack->RowLength != 0 && pack->RowLength != width) || pack->SwapBytes || pack->LsbFirst || pack->Invert) return false; /* Only a simple blit, no scale, bias or other mapping. */ if (ctx->_ImageTransferState) return false; /* It is possible that the renderbuffer (or underlying texture) is * multisampled. Since ReadPixels from a multisampled buffer requires a * multisample resolve, we can't handle this here */ if (rb->NumSamples > 1) return false; /* We can't handle copying from RGBX or BGRX because the tiled_memcpy * function doesn't set the last channel to 1. Note this checks BaseFormat * rather than TexFormat in case the RGBX format is being simulated with an * RGBA format. */ if (rb->_BaseFormat == GL_RGB) return false; if (!intel_get_memcpy(rb->Format, format, type, &mem_copy, &cpp)) return false; if (!irb->mt || (irb->mt->surf.tiling != ISL_TILING_X && irb->mt->surf.tiling != ISL_TILING_Y0)) { /* The algorithm is written only for X- or Y-tiled memory. */ return false; } /* tiled_to_linear() assumes that if the object is swizzled, it is using * I915_BIT6_SWIZZLE_9_10 for X and I915_BIT6_SWIZZLE_9 for Y. This is only * true on gen5 and above. * * The killer on top is that some gen4 have an L-shaped swizzle mode, where * parts of the memory aren't swizzled at all. Userspace just can't handle * that. */ if (devinfo->gen < 5 && brw->has_swizzling) return false; /* Since we are going to read raw data to the miptree, we need to resolve * any pending fast color clears before we start. */ intel_miptree_access_raw(brw, irb->mt, irb->mt_level, irb->mt_layer, false); bo = irb->mt->bo; if (brw_batch_references(&brw->batch, bo)) { perf_debug("Flushing before mapping a referenced bo.\n"); intel_batchbuffer_flush(brw); } void *map = brw_bo_map(brw, bo, MAP_READ | MAP_RAW); if (map == NULL) { DBG("%s: failed to map bo\n", __func__); return false; } unsigned slice_offset_x, slice_offset_y; intel_miptree_get_image_offset(irb->mt, irb->mt_level, irb->mt_layer, &slice_offset_x, &slice_offset_y); xoffset += slice_offset_x; yoffset += slice_offset_y; dst_pitch = _mesa_image_row_stride(pack, width, format, type); /* For a window-system renderbuffer, the buffer is actually flipped * vertically, so we need to handle that. Since the detiling function * can only really work in the forwards direction, we have to be a * little creative. First, we compute the Y-offset of the first row of * the renderbuffer (in renderbuffer coordinates). We then match that * with the last row of the client's data. Finally, we give * tiled_to_linear a negative pitch so that it walks through the * client's data backwards as it walks through the renderbufer forwards. */ if (rb->Name == 0) { yoffset = rb->Height - yoffset - height; pixels += (ptrdiff_t) (height - 1) * dst_pitch; dst_pitch = -dst_pitch; } /* We postponed printing this message until having committed to executing * the function. */ DBG("%s: x,y=(%d,%d) (w,h)=(%d,%d) format=0x%x type=0x%x " "mesa_format=0x%x tiling=%d " "pack=(alignment=%d row_length=%d skip_pixels=%d skip_rows=%d)\n", __func__, xoffset, yoffset, width, height, format, type, rb->Format, irb->mt->surf.tiling, pack->Alignment, pack->RowLength, pack->SkipPixels, pack->SkipRows); tiled_to_linear( xoffset * cpp, (xoffset + width) * cpp, yoffset, yoffset + height, pixels - (ptrdiff_t) yoffset * dst_pitch - (ptrdiff_t) xoffset * cpp, map + irb->mt->offset, dst_pitch, irb->mt->surf.row_pitch, brw->has_swizzling, irb->mt->surf.tiling, mem_copy ); brw_bo_unmap(bo); return true; }
/** * 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 the src/dst formats are compatible. * Also note that GL's pixel transfer ops don't apply to glGetTexImage() * so we don't have to worry about those. * XXX more format combinations could be supported here. */ if (target == GL_TEXTURE_1D || target == GL_TEXTURE_2D || _mesa_is_cube_face(target)) { if ((texImage->TexFormat == MESA_FORMAT_ARGB8888) && format == GL_BGRA && (type == GL_UNSIGNED_BYTE || type == GL_UNSIGNED_INT_8_8_8_8_REV) && !ctx->Pack.SwapBytes && _mesa_little_endian()) { memCopy = GL_TRUE; } else if ((texImage->TexFormat == MESA_FORMAT_AL88) && format == GL_LUMINANCE_ALPHA && type == GL_UNSIGNED_BYTE && !ctx->Pack.SwapBytes && _mesa_little_endian()) { memCopy = GL_TRUE; } else if ((texImage->TexFormat == MESA_FORMAT_L8) && format == GL_LUMINANCE && type == GL_UNSIGNED_BYTE) { memCopy = GL_TRUE; } else if (texImage->TexFormat == MESA_FORMAT_L16 && format == GL_LUMINANCE && type == GL_UNSIGNED_SHORT) { memCopy = GL_TRUE; } else if (texImage->TexFormat == MESA_FORMAT_A8 && format == GL_ALPHA && type == GL_UNSIGNED_BYTE) { memCopy = GL_TRUE; } else if (texImage->TexFormat == MESA_FORMAT_A16 && format == GL_ALPHA && type == GL_UNSIGNED_SHORT) { memCopy = GL_TRUE; } } 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; }