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, 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 */ 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 = 0; buffer_obj = packing->BufferObj; first_pixel += (intptr_t)pixels; } 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, last_pixel - first_pixel, NULL, GL_STREAM_READ); else _mesa_BufferData(pbo_target, last_pixel - first_pixel, (char *)pixels + first_pixel, GL_STREAM_DRAW); buffer_obj = packing->BufferObj; first_pixel = 0; _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); /* 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_DeleteBuffers(1, tmp_pbo); return NULL; } return tex_image; }
/** * When we're about to read pixel data out of a PBO (via glDrawPixels, * glTexImage, etc) or write data into a PBO (via glReadPixels, * glGetTexImage, etc) we call this function to check that we're not * going to read/write out of bounds. * * XXX This would also be a convenient time to check that the PBO isn't * currently mapped. Whoever calls this function should check for that. * Remember, we can't use a PBO when it's mapped! * * If we're not using a PBO, this is a no-op. * * \param width width of image to read/write * \param height height of image to read/write * \param depth depth of image to read/write * \param format format of image to read/write * \param type datatype of image to read/write * \param clientMemSize the maximum number of bytes to read/write * \param ptr the user-provided pointer/offset * \return GL_TRUE if the buffer access is OK, GL_FALSE if the access would * go out of bounds. */ GLboolean _mesa_validate_pbo_access(GLuint dimensions, const struct gl_pixelstore_attrib *pack, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei clientMemSize, const GLvoid *ptr) { /* unsigned, to detect overflow/wrap-around */ uintptr_t start, end, offset, size; /* If no PBO is bound, 'ptr' is a pointer to client memory containing 'clientMemSize' bytes. If a PBO is bound, 'ptr' is an offset into the bound PBO. In that case 'clientMemSize' is ignored: we just use the PBO's size. */ if (!_mesa_is_bufferobj(pack->BufferObj)) { offset = 0; size = (clientMemSize == INT_MAX) ? UINTPTR_MAX : clientMemSize; } else { offset = (uintptr_t)ptr; size = pack->BufferObj->Size; /* The ARB_pixel_buffer_object spec says: * "INVALID_OPERATION is generated by ColorTable, ColorSubTable, * ConvolutionFilter2D, ConvolutionFilter1D, SeparableFilter2D, * TexImage1D, TexImage2D, TexImage3D, TexSubImage1D, * TexSubImage2D, TexSubImage3D, and DrawPixels if the current * PIXEL_UNPACK_BUFFER_BINDING_ARB value is non-zero and the data * parameter is not evenly divisible into the number of basic machine * units needed to store in memory a datum indicated by the type * parameter." */ if (type != GL_BITMAP && (offset % _mesa_sizeof_packed_type(type))) return GL_FALSE; } if (size == 0) /* no buffer! */ return GL_FALSE; /* If the size of the image is zero then no pixels are accessed so we * don't need to check anything else. */ if (width == 0 || height == 0 || depth == 0) return GL_TRUE; /* get the offset to the first pixel we'll read/write */ start = _mesa_image_offset(dimensions, pack, width, height, format, type, 0, 0, 0); /* get the offset to just past the last pixel we'll read/write */ end = _mesa_image_offset(dimensions, pack, width, height, format, type, depth-1, height-1, width); start += offset; end += offset; if (start > size) { /* This will catch negative values / wrap-around */ return GL_FALSE; } if (end > size) { /* Image read/write goes beyond end of buffer */ return GL_FALSE; } /* OK! */ return GL_TRUE; }
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; }