static bool compare_textures(struct pipe_context *ctx, struct pipe_resource *tex, struct cpu_texture *cpu, int bpp) { struct pipe_transfer *t; uint8_t *map; int y,z; bool pass = true; map = pipe_transfer_map_3d(ctx, tex, 0, PIPE_TRANSFER_READ, 0, 0, 0, tex->width0, tex->height0, tex->array_size, &t); assert(map); for (z = 0; z < tex->array_size; z++) { for (y = 0; y < tex->height0; y++) { uint8_t *ptr = map + t->layer_stride*z + t->stride*y; uint8_t *cpu_ptr = cpu->ptr + cpu->layer_stride*z + cpu->stride*y; if (memcmp(ptr, cpu_ptr, tex->width0 * bpp)) { pass = false; goto done; } } } done: pipe_transfer_unmap(ctx, t); return pass; }
static void set_random_pixels(struct pipe_context *ctx, struct pipe_resource *tex, struct cpu_texture *cpu) { struct pipe_transfer *t; uint8_t *map; int x,y,z; map = pipe_transfer_map_3d(ctx, tex, 0, PIPE_TRANSFER_WRITE, 0, 0, 0, tex->width0, tex->height0, tex->array_size, &t); assert(map); for (z = 0; z < tex->array_size; z++) { for (y = 0; y < tex->height0; y++) { uint64_t *ptr = (uint64_t*) (map + t->layer_stride*z + t->stride*y); uint64_t *ptr_cpu = (uint64_t*) (cpu->ptr + cpu->layer_stride*z + cpu->stride*y); unsigned size = cpu->stride / RAND_NUM_SIZE; assert(t->stride % RAND_NUM_SIZE == 0); assert(cpu->stride % RAND_NUM_SIZE == 0); for (x = 0; x < size; x++) { *ptr++ = *ptr_cpu++ = rand_xorshift128plus(seed_xorshift128plus); } } } pipe_transfer_unmap(ctx, t); }
/** * Map a texture image and return the address for a particular 2D face/slice/ * layer. The stImage indicates the cube face and mipmap level. The slice * of the 3D texture is passed in 'zoffset'. * \param usage one of the PIPE_TRANSFER_x values * \param x, y, w, h the region of interest of the 2D image. * \return address of mapping or NULL if any error */ GLubyte * st_texture_image_map(struct st_context *st, struct st_texture_image *stImage, enum pipe_transfer_usage usage, GLuint x, GLuint y, GLuint z, GLuint w, GLuint h, GLuint d, struct pipe_transfer **transfer) { struct st_texture_object *stObj = st_texture_object(stImage->base.TexObject); GLuint level; void *map; DBG("%s \n", __func__); if (!stImage->pt) return NULL; if (stObj->pt != stImage->pt) level = 0; else level = stImage->base.Level; if (stObj->base.Immutable) { level += stObj->base.MinLevel; z += stObj->base.MinLayer; if (stObj->pt->array_size > 1) d = MIN2(d, stObj->base.NumLayers); } z += stImage->base.Face; map = pipe_transfer_map_3d(st->pipe, stImage->pt, level, usage, x, y, z, w, h, d, transfer); if (map) { /* Enlarge the transfer array if it's not large enough. */ if (z >= stImage->num_transfers) { unsigned new_size = z + 1; stImage->transfer = realloc(stImage->transfer, new_size * sizeof(struct st_texture_image_transfer)); memset(&stImage->transfer[stImage->num_transfers], 0, (new_size - stImage->num_transfers) * sizeof(struct st_texture_image_transfer)); stImage->num_transfers = new_size; } assert(!stImage->transfer[z].transfer); stImage->transfer[z].transfer = *transfer; } return map; }
/** * Map a texture image and return the address for a particular 2D face/slice/ * layer. The stImage indicates the cube face and mipmap level. The slice * of the 3D texture is passed in 'zoffset'. * \param usage one of the PIPE_TRANSFER_x values * \param x, y, w, h the region of interest of the 2D image. * \return address of mapping or NULL if any error */ GLubyte * st_texture_image_map(struct st_context *st, struct st_texture_image *stImage, enum pipe_transfer_usage usage, GLuint x, GLuint y, GLuint z, GLuint w, GLuint h, GLuint d) { struct st_texture_object *stObj = st_texture_object(stImage->base.TexObject); GLuint level; DBG("%s \n", __FUNCTION__); if (!stImage->pt) return NULL; if (stObj->pt != stImage->pt) level = 0; else level = stImage->base.Level; return pipe_transfer_map_3d(st->pipe, stImage->pt, level, usage, x, y, z + stImage->base.Face, w, h, d, &stImage->transfer); }
/** * 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); }
/** * Fallback for pipe->clear_stencil() function. * sw fallback doesn't look terribly useful here. * Plus can't use these transfer fallbacks when clearing * multisampled surfaces for instance. * Clears all bound layers. */ void util_clear_depth_stencil(struct pipe_context *pipe, struct pipe_surface *dst, unsigned clear_flags, double depth, unsigned stencil, unsigned dstx, unsigned dsty, unsigned width, unsigned height) { enum pipe_format format = dst->format; struct pipe_transfer *dst_trans; ubyte *dst_map; boolean need_rmw = FALSE; unsigned max_layer, layer; if ((clear_flags & PIPE_CLEAR_DEPTHSTENCIL) && ((clear_flags & PIPE_CLEAR_DEPTHSTENCIL) != PIPE_CLEAR_DEPTHSTENCIL) && util_format_is_depth_and_stencil(format)) need_rmw = TRUE; assert(dst->texture); if (!dst->texture) return; max_layer = dst->u.tex.last_layer - dst->u.tex.first_layer; dst_map = pipe_transfer_map_3d(pipe, dst->texture, dst->u.tex.level, (need_rmw ? PIPE_TRANSFER_READ_WRITE : PIPE_TRANSFER_WRITE), dstx, dsty, dst->u.tex.first_layer, width, height, max_layer + 1, &dst_trans); assert(dst_map); if (dst_map) { unsigned dst_stride = dst_trans->stride; uint64_t zstencil = util_pack64_z_stencil(format, depth, stencil); ubyte *dst_layer = dst_map; unsigned i, j; assert(dst_trans->stride > 0); for (layer = 0; layer <= max_layer; layer++) { dst_map = dst_layer; switch (util_format_get_blocksize(format)) { case 1: assert(format == PIPE_FORMAT_S8_UINT); if(dst_stride == width) memset(dst_map, (uint8_t) zstencil, height * width); else { for (i = 0; i < height; i++) { memset(dst_map, (uint8_t) zstencil, width); dst_map += dst_stride; } } break; case 2: assert(format == PIPE_FORMAT_Z16_UNORM); for (i = 0; i < height; i++) { uint16_t *row = (uint16_t *)dst_map; for (j = 0; j < width; j++) *row++ = (uint16_t) zstencil; dst_map += dst_stride; } break; case 4: if (!need_rmw) { for (i = 0; i < height; i++) { uint32_t *row = (uint32_t *)dst_map; for (j = 0; j < width; j++) *row++ = (uint32_t) zstencil; dst_map += dst_stride; } } else { uint32_t dst_mask; if (format == PIPE_FORMAT_Z24_UNORM_S8_UINT) dst_mask = 0x00ffffff; else { assert(format == PIPE_FORMAT_S8_UINT_Z24_UNORM); dst_mask = 0xffffff00; } if (clear_flags & PIPE_CLEAR_DEPTH) dst_mask = ~dst_mask; for (i = 0; i < height; i++) { uint32_t *row = (uint32_t *)dst_map; for (j = 0; j < width; j++) { uint32_t tmp = *row & dst_mask; *row++ = tmp | ((uint32_t) zstencil & ~dst_mask); } dst_map += dst_stride; } } break; case 8: if (!need_rmw) { for (i = 0; i < height; i++) { uint64_t *row = (uint64_t *)dst_map; for (j = 0; j < width; j++) *row++ = zstencil; dst_map += dst_stride; } } else { uint64_t src_mask; if (clear_flags & PIPE_CLEAR_DEPTH) src_mask = 0x00000000ffffffffull; else src_mask = 0x000000ff00000000ull; for (i = 0; i < height; i++) { uint64_t *row = (uint64_t *)dst_map; for (j = 0; j < width; j++) { uint64_t tmp = *row & ~src_mask; *row++ = tmp | (zstencil & src_mask); } dst_map += dst_stride; } } break; default: assert(0); break; } dst_layer += dst_trans->layer_stride; } pipe->transfer_unmap(pipe, dst_trans); } }
/** * Fallback for pipe->clear_render_target() function. * XXX this looks too hackish to be really useful. * cpp > 4 looks like a gross hack at best... * Plus can't use these transfer fallbacks when clearing * multisampled surfaces for instance. * Clears all bound layers. */ void util_clear_render_target(struct pipe_context *pipe, struct pipe_surface *dst, const union pipe_color_union *color, unsigned dstx, unsigned dsty, unsigned width, unsigned height) { struct pipe_transfer *dst_trans; ubyte *dst_map; union util_color uc; unsigned max_layer; assert(dst->texture); if (!dst->texture) return; if (dst->texture->target == PIPE_BUFFER) { /* * The fill naturally works on the surface format, however * the transfer uses resource format which is just bytes for buffers. */ unsigned dx, w; unsigned pixstride = util_format_get_blocksize(dst->format); dx = (dst->u.buf.first_element + dstx) * pixstride; w = width * pixstride; max_layer = 0; dst_map = pipe_transfer_map(pipe, dst->texture, 0, 0, PIPE_TRANSFER_WRITE, dx, 0, w, 1, &dst_trans); } else { max_layer = dst->u.tex.last_layer - dst->u.tex.first_layer; dst_map = pipe_transfer_map_3d(pipe, dst->texture, dst->u.tex.level, PIPE_TRANSFER_WRITE, dstx, dsty, dst->u.tex.first_layer, width, height, max_layer + 1, &dst_trans); } assert(dst_map); if (dst_map) { enum pipe_format format = dst->format; assert(dst_trans->stride > 0); if (util_format_is_pure_integer(format)) { /* * We expect int/uint clear values here, though some APIs * might disagree (but in any case util_pack_color() * couldn't handle it)... */ if (util_format_is_pure_sint(format)) { util_format_write_4i(format, color->i, 0, &uc, 0, 0, 0, 1, 1); } else { assert(util_format_is_pure_uint(format)); util_format_write_4ui(format, color->ui, 0, &uc, 0, 0, 0, 1, 1); } } else { util_pack_color(color->f, format, &uc); } util_fill_box(dst_map, dst->format, dst_trans->stride, dst_trans->layer_stride, 0, 0, 0, width, height, max_layer + 1, &uc); pipe->transfer_unmap(pipe, dst_trans); } }