/**************************************************************************
*
- * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * Copyright 2006 VMware, Inc.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**************************************************************************/
/*
* Authors:
- * Keith Whitwell <keith@tungstengraphics.com>
- * Michel Dänzer <michel@tungstengraphics.com>
+ * Keith Whitwell <keithw@vmware.com>
+ * Michel Dänzer <daenzer@vmware.com>
*/
#include <stdio.h>
#include "util/u_format.h"
#include "util/u_math.h"
#include "util/u_memory.h"
-#include "util/u_simple_list.h"
+#include "util/simple_list.h"
#include "util/u_transfer.h"
#include "lp_context.h"
/**
* Conventional allocation path for non-display textures:
- * Just compute row strides here. Storage is allocated on demand later.
+ * Compute strides and allocate data (unless asked not to).
*/
static boolean
llvmpipe_texture_layout(struct llvmpipe_screen *screen,
- struct llvmpipe_resource *lpr)
+ struct llvmpipe_resource *lpr,
+ boolean allocate)
{
struct pipe_resource *pt = &lpr->base;
unsigned level;
unsigned depth = pt->depth0;
uint64_t total_size = 0;
unsigned layers = pt->array_size;
+ /* XXX:
+ * This alignment here (same for displaytarget) was added for the purpose of
+ * ARB_map_buffer_alignment. I am not convinced it's needed for non-buffer
+ * resources. Otherwise we'd want the max of cacheline size and 16 (max size
+ * of a block for all formats) though this should not be strictly necessary
+ * neither. In any case it can only affect compressed or 1d textures.
+ */
+ unsigned mip_align = MAX2(64, util_cpu_caps.cacheline);
assert(LP_MAX_TEXTURE_2D_LEVELS <= LP_MAX_TEXTURE_LEVELS);
assert(LP_MAX_TEXTURE_3D_LEVELS <= LP_MAX_TEXTURE_LEVELS);
for (level = 0; level <= pt->last_level; level++) {
+ uint64_t mipsize;
+ unsigned align_x, align_y, nblocksx, nblocksy, block_size, num_slices;
/* Row stride and image stride */
- {
- unsigned alignment, nblocksx, nblocksy, block_size;
- /* For non-compressed formats we need 4x4 pixel alignment
- * (for now). We also want cache line size in x direction,
- * otherwise same cache line could end up in multiple threads.
- * XXX this blows up 1d/1d array textures by a factor of 4.
- */
- if (util_format_is_compressed(pt->format))
- alignment = 1;
+ /* For non-compressed formats we need 4x4 pixel alignment
+ * so we can read/write LP_RASTER_BLOCK_SIZE when rendering to them.
+ * We also want cache line size in x direction,
+ * otherwise same cache line could end up in multiple threads.
+ * For explicit 1d resources however we reduce this to 4x1 and
+ * handle specially in render output code (as we need to do special
+ * handling there for buffers in any case).
+ */
+ if (util_format_is_compressed(pt->format))
+ align_x = align_y = 1;
+ else {
+ align_x = LP_RASTER_BLOCK_SIZE;
+ if (llvmpipe_resource_is_1d(&lpr->base))
+ align_y = 1;
else
- alignment = LP_RASTER_BLOCK_SIZE;
-
- nblocksx = util_format_get_nblocksx(pt->format,
- align(width, alignment));
- nblocksy = util_format_get_nblocksy(pt->format,
- align(height, alignment));
- block_size = util_format_get_blocksize(pt->format);
+ align_y = LP_RASTER_BLOCK_SIZE;
+ }
- if (util_format_is_compressed(pt->format))
- lpr->row_stride[level] = nblocksx * block_size;
- else
- lpr->row_stride[level] = align(nblocksx * block_size, util_cpu_caps.cacheline);
+ nblocksx = util_format_get_nblocksx(pt->format,
+ align(width, align_x));
+ nblocksy = util_format_get_nblocksy(pt->format,
+ align(height, align_y));
+ block_size = util_format_get_blocksize(pt->format);
- /* if row_stride * height > LP_MAX_TEXTURE_SIZE */
- if (lpr->row_stride[level] > LP_MAX_TEXTURE_SIZE / nblocksy) {
- /* image too large */
- goto fail;
- }
+ if (util_format_is_compressed(pt->format))
+ lpr->row_stride[level] = nblocksx * block_size;
+ else
+ lpr->row_stride[level] = align(nblocksx * block_size, util_cpu_caps.cacheline);
- lpr->img_stride[level] = lpr->row_stride[level] * nblocksy;
+ /* if row_stride * height > LP_MAX_TEXTURE_SIZE */
+ if ((uint64_t)lpr->row_stride[level] * nblocksy > LP_MAX_TEXTURE_SIZE) {
+ /* image too large */
+ goto fail;
}
- /* Number of 3D image slices, cube faces or texture array layers */
- {
- unsigned num_slices;
-
- if (lpr->base.target == PIPE_TEXTURE_CUBE)
- num_slices = 6;
- else if (lpr->base.target == PIPE_TEXTURE_3D)
- num_slices = depth;
- else if (lpr->base.target == PIPE_TEXTURE_1D_ARRAY ||
- lpr->base.target == PIPE_TEXTURE_2D_ARRAY)
- num_slices = layers;
- else
- num_slices = 1;
+ lpr->img_stride[level] = lpr->row_stride[level] * nblocksy;
- lpr->num_slices_faces[level] = num_slices;
+ /* Number of 3D image slices, cube faces or texture array layers */
+ if (lpr->base.target == PIPE_TEXTURE_CUBE) {
+ assert(layers == 6);
}
+ if (lpr->base.target == PIPE_TEXTURE_3D)
+ num_slices = depth;
+ else if (lpr->base.target == PIPE_TEXTURE_1D_ARRAY ||
+ lpr->base.target == PIPE_TEXTURE_2D_ARRAY ||
+ lpr->base.target == PIPE_TEXTURE_CUBE ||
+ lpr->base.target == PIPE_TEXTURE_CUBE_ARRAY)
+ num_slices = layers;
+ else
+ num_slices = 1;
+
/* if img_stride * num_slices_faces > LP_MAX_TEXTURE_SIZE */
- if (lpr->img_stride[level] >
- LP_MAX_TEXTURE_SIZE / lpr->num_slices_faces[level]) {
+ mipsize = (uint64_t)lpr->img_stride[level] * num_slices;
+ if (mipsize > LP_MAX_TEXTURE_SIZE) {
/* volume too large */
goto fail;
}
- total_size += (uint64_t) lpr->num_slices_faces[level]
- * (uint64_t) lpr->img_stride[level];
+ lpr->mip_offsets[level] = total_size;
+
+ total_size += align((unsigned)mipsize, mip_align);
if (total_size > LP_MAX_TEXTURE_SIZE) {
goto fail;
}
depth = u_minify(depth, 1);
}
+ if (allocate) {
+ lpr->tex_data = align_malloc(total_size, mip_align);
+ if (!lpr->tex_data) {
+ return FALSE;
+ }
+ else {
+ memset(lpr->tex_data, 0, total_size);
+ }
+ }
+
return TRUE;
fail:
struct llvmpipe_resource lpr;
memset(&lpr, 0, sizeof(lpr));
lpr.base = *res;
- return llvmpipe_texture_layout(llvmpipe_screen(screen), &lpr);
+ return llvmpipe_texture_layout(llvmpipe_screen(screen), &lpr, false);
}
static boolean
llvmpipe_displaytarget_layout(struct llvmpipe_screen *screen,
- struct llvmpipe_resource *lpr)
+ struct llvmpipe_resource *lpr,
+ const void *map_front_private)
{
struct sw_winsys *winsys = screen->winsys;
const unsigned width = MAX2(1, align(lpr->base.width0, TILE_SIZE));
const unsigned height = MAX2(1, align(lpr->base.height0, TILE_SIZE));
- lpr->num_slices_faces[0] = 1;
- lpr->img_stride[0] = 0;
-
lpr->dt = winsys->displaytarget_create(winsys,
lpr->base.bind,
lpr->base.format,
width, height,
- 16,
+ 64,
+ map_front_private,
&lpr->row_stride[0] );
if (lpr->dt == NULL)
return FALSE;
- {
+ if (!map_front_private) {
void *map = winsys->displaytarget_map(winsys, lpr->dt,
PIPE_TRANSFER_WRITE);
static struct pipe_resource *
-llvmpipe_resource_create(struct pipe_screen *_screen,
- const struct pipe_resource *templat)
+llvmpipe_resource_create_front(struct pipe_screen *_screen,
+ const struct pipe_resource *templat,
+ const void *map_front_private)
{
struct llvmpipe_screen *screen = llvmpipe_screen(_screen);
struct llvmpipe_resource *lpr = CALLOC_STRUCT(llvmpipe_resource);
PIPE_BIND_SCANOUT |
PIPE_BIND_SHARED)) {
/* displayable surface */
- if (!llvmpipe_displaytarget_layout(screen, lpr))
+ if (!llvmpipe_displaytarget_layout(screen, lpr, map_front_private))
goto fail;
}
else {
/* texture map */
- if (!llvmpipe_texture_layout(screen, lpr))
+ if (!llvmpipe_texture_layout(screen, lpr, true))
goto fail;
}
}
* read/write always LP_RASTER_BLOCK_SIZE pixels, but the element
* offset doesn't need to be aligned to LP_RASTER_BLOCK_SIZE.
*/
- lpr->data = align_malloc(bytes + (LP_RASTER_BLOCK_SIZE - 1) * 4 * sizeof(float), 16);
+ lpr->data = align_malloc(bytes + (LP_RASTER_BLOCK_SIZE - 1) * 4 * sizeof(float), 64);
+
/*
* buffers don't really have stride but it's probably safer
* (for code doing same calculations for buffers and textures)
FREE(lpr);
return NULL;
}
-
+static struct pipe_resource *
+llvmpipe_resource_create(struct pipe_screen *_screen,
+ const struct pipe_resource *templat)
+{
+ return llvmpipe_resource_create_front(_screen, templat, NULL);
+}
static void
llvmpipe_resource_destroy(struct pipe_screen *pscreen,
}
else if (llvmpipe_resource_is_texture(pt)) {
/* free linear image data */
- if (lpr->linear_img.data) {
- align_free(lpr->linear_img.data);
- lpr->linear_img.data = NULL;
+ if (lpr->tex_data) {
+ align_free(lpr->tex_data);
+ lpr->tex_data = NULL;
}
}
else if (!lpr->userBuffer) {
map = winsys->displaytarget_map(winsys, lpr->dt, dt_usage);
/* install this linear image in texture data structure */
- lpr->linear_img.data = map;
+ lpr->tex_data = map;
return map;
}
else if (llvmpipe_resource_is_texture(resource)) {
- map = llvmpipe_get_texture_image(lpr, layer, level, tex_usage);
+ map = llvmpipe_get_texture_image_address(lpr, layer, level);
return map;
}
else {
static struct pipe_resource *
llvmpipe_resource_from_handle(struct pipe_screen *screen,
const struct pipe_resource *template,
- struct winsys_handle *whandle)
+ struct winsys_handle *whandle,
+ unsigned usage)
{
struct sw_winsys *winsys = llvmpipe_screen(screen)->winsys;
struct llvmpipe_resource *lpr;
assert(lpr->base.height0 == height);
#endif
- lpr->num_slices_faces[0] = 1;
- lpr->img_stride[0] = 0;
-
lpr->dt = winsys->displaytarget_from_handle(winsys,
template,
whandle,
static boolean
llvmpipe_resource_get_handle(struct pipe_screen *screen,
+ struct pipe_context *ctx,
struct pipe_resource *pt,
- struct winsys_handle *whandle)
+ struct winsys_handle *whandle,
+ unsigned usage)
{
struct sw_winsys *winsys = llvmpipe_screen(screen)->winsys;
struct llvmpipe_resource *lpr = llvmpipe_resource(pt);
}
-static struct pipe_surface *
-llvmpipe_create_surface(struct pipe_context *pipe,
- struct pipe_resource *pt,
- const struct pipe_surface *surf_tmpl)
-{
- struct pipe_surface *ps;
-
- if (!(pt->bind & (PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_RENDER_TARGET)))
- debug_printf("Illegal surface creation without bind flag\n");
-
- ps = CALLOC_STRUCT(pipe_surface);
- if (ps) {
- pipe_reference_init(&ps->reference, 1);
- pipe_resource_reference(&ps->texture, pt);
- ps->context = pipe;
- ps->format = surf_tmpl->format;
- if (llvmpipe_resource_is_texture(pt)) {
- assert(surf_tmpl->u.tex.level <= pt->last_level);
- ps->width = u_minify(pt->width0, surf_tmpl->u.tex.level);
- ps->height = u_minify(pt->height0, surf_tmpl->u.tex.level);
- ps->u.tex.level = surf_tmpl->u.tex.level;
- ps->u.tex.first_layer = surf_tmpl->u.tex.first_layer;
- ps->u.tex.last_layer = surf_tmpl->u.tex.last_layer;
- if (ps->u.tex.first_layer != ps->u.tex.last_layer) {
- debug_printf("creating surface with multiple layers, rendering to first layer only\n");
- }
- }
- else {
- /* setting width as number of elements should get us correct renderbuffer width */
- ps->width = surf_tmpl->u.buf.last_element - surf_tmpl->u.buf.first_element + 1;
- ps->height = pt->height0;
- ps->u.buf.first_element = surf_tmpl->u.buf.first_element;
- ps->u.buf.last_element = surf_tmpl->u.buf.last_element;
- assert(ps->u.buf.first_element <= ps->u.buf.last_element);
- assert(util_format_get_blocksize(surf_tmpl->format) *
- (ps->u.buf.last_element + 1) <= pt->width0);
- }
- }
- return ps;
-}
-
-
-static void
-llvmpipe_surface_destroy(struct pipe_context *pipe,
- struct pipe_surface *surf)
-{
- /* Effectively do the texture_update work here - if texture images
- * needed post-processing to put them into hardware layout, this is
- * where it would happen. For llvmpipe, nothing to do.
- */
- assert(surf->texture);
- pipe_resource_reference(&surf->texture, NULL);
- FREE(surf);
-}
-
-
static void *
llvmpipe_transfer_map( struct pipe_context *pipe,
struct pipe_resource *resource,
}
}
- /* Check if we're mapping the current constant buffer */
+ /* Check if we're mapping a current constant buffer */
if ((usage & PIPE_TRANSFER_WRITE) &&
(resource->bind & PIPE_BIND_CONSTANT_BUFFER)) {
unsigned i;
- for (i = 0; i < Elements(llvmpipe->constants[PIPE_SHADER_FRAGMENT]); ++i) {
+ for (i = 0; i < ARRAY_SIZE(llvmpipe->constants[PIPE_SHADER_FRAGMENT]); ++i) {
if (resource == llvmpipe->constants[PIPE_SHADER_FRAGMENT][i].buffer) {
/* constants may have changed */
- llvmpipe->dirty |= LP_NEW_CONSTANTS;
+ llvmpipe->dirty |= LP_NEW_FS_CONSTANTS;
break;
}
}
unsigned level)
{
struct llvmpipe_context *llvmpipe = llvmpipe_context( pipe );
-
- /*
- * XXX checking only resources with the right bind flags
- * is unsafe since with opengl state tracker we can end up
- * with resources bound to places they weren't supposed to be
- * (buffers bound as sampler views is one possibility here).
- */
if (!(presource->bind & (PIPE_BIND_DEPTH_STENCIL |
PIPE_BIND_RENDER_TARGET |
PIPE_BIND_SAMPLER_VIEW)))
/**
* Create buffer which wraps user-space data.
+ * XXX unreachable.
*/
struct pipe_resource *
llvmpipe_user_buffer_create(struct pipe_screen *screen,
void *ptr,
unsigned bytes,
- unsigned bind_flags)
+ unsigned bind_flags)
{
struct llvmpipe_resource *buffer;
buffer = CALLOC_STRUCT(llvmpipe_resource);
- if(!buffer)
+ if (!buffer)
return NULL;
pipe_reference_init(&buffer->base.reference, 1);
}
-/**
- * Compute size (in bytes) need to store a texture image / mipmap level,
- * including all cube faces or 3D image slices
- */
-static unsigned
-tex_image_size(const struct llvmpipe_resource *lpr, unsigned level)
-{
- const unsigned buf_size = tex_image_face_size(lpr, level);
- return buf_size * lpr->num_slices_faces[level];
-}
-
-
/**
* Return pointer to a 2D texture image/face/slice.
* No tiled/linear conversion is done.
llvmpipe_get_texture_image_address(struct llvmpipe_resource *lpr,
unsigned face_slice, unsigned level)
{
- struct llvmpipe_texture_image *img;
unsigned offset;
- img = &lpr->linear_img;
- offset = lpr->linear_mip_offsets[level];
-
- if (face_slice > 0)
- offset += face_slice * tex_image_face_size(lpr, level);
-
- return (ubyte *) img->data + offset;
-}
-
-
-/**
- * Allocate storage for a linear image
- * (all cube faces and all 3D slices, all levels).
- */
-static void
-alloc_image_data(struct llvmpipe_resource *lpr)
-{
- uint alignment = MAX2(16, util_cpu_caps.cacheline);
- uint level;
- uint offset = 0;
-
- if (lpr->dt) {
- /* we get the linear memory from the winsys, and it has
- * already been zeroed
- */
- struct llvmpipe_screen *screen = llvmpipe_screen(lpr->base.screen);
- struct sw_winsys *winsys = screen->winsys;
-
- assert(lpr->base.last_level == 0);
-
- lpr->linear_img.data =
- winsys->displaytarget_map(winsys, lpr->dt,
- PIPE_TRANSFER_READ_WRITE);
- }
- else {
- /* not a display target - allocate regular memory */
- /*
- * Offset calculation for start of a specific mip/layer is always
- * offset = lpr->linear_mip_offsets[level] + lpr->img_stride[level] * layer
- */
- for (level = 0; level <= lpr->base.last_level; level++) {
- uint buffer_size = tex_image_size(lpr, level);
- lpr->linear_mip_offsets[level] = offset;
- offset += align(buffer_size, alignment);
- }
- lpr->linear_img.data = align_malloc(offset, alignment);
- if (lpr->linear_img.data) {
- memset(lpr->linear_img.data, 0, offset);
- }
- }
-}
-
-
-
-/**
- * Return pointer to texture image data
- * for a particular cube face or 3D texture slice.
- *
- * \param face_slice the cube face or 3D slice of interest
- * \param usage one of LP_TEX_USAGE_READ/WRITE_ALL/READ_WRITE
- */
-void *
-llvmpipe_get_texture_image(struct llvmpipe_resource *lpr,
- unsigned face_slice, unsigned level,
- enum lp_texture_usage usage)
-{
- struct llvmpipe_texture_image *target_img;
- void *target_data;
- unsigned target_offset;
- unsigned *target_off_ptr;
-
- assert(usage == LP_TEX_USAGE_READ ||
- usage == LP_TEX_USAGE_READ_WRITE ||
- usage == LP_TEX_USAGE_WRITE_ALL);
-
- if (lpr->dt) {
- assert(lpr->linear_img.data);
- }
-
- target_img = &lpr->linear_img;
- target_off_ptr = lpr->linear_mip_offsets;
- target_data = target_img->data;
-
- if (!target_data) {
- /* allocate memory for the target image now */
- alloc_image_data(lpr);
- target_data = target_img->data;
- }
-
- target_offset = target_off_ptr[level];
-
- if (face_slice > 0) {
- target_offset += face_slice * tex_image_face_size(lpr, level);
- }
-
- if (target_data) {
- target_data = (uint8_t *) target_data + target_offset;
- }
-
- return target_data;
-}
-
-
-/**
- * Return pointer to start of a texture image (1D, 2D, 3D, CUBE).
- * This is typically used when we're about to sample from a texture.
- */
-void *
-llvmpipe_get_texture_image_all(struct llvmpipe_resource *lpr,
- unsigned level,
- enum lp_texture_usage usage)
-{
- const int slices = lpr->num_slices_faces[level];
- int slice;
- void *map = NULL;
-
- assert(slices > 0);
-
- for (slice = slices - 1; slice >= 0; slice--) {
- map = llvmpipe_get_texture_image(lpr, slice, level, usage);
- }
-
- return map;
-}
-
-
-/**
- * Get pointer to a linear image (not the tile!) at tile (x,y).
- * \return pointer to start of image/face (not the tile)
- */
-ubyte *
-llvmpipe_get_texture_tile_linear(struct llvmpipe_resource *lpr,
- unsigned face_slice, unsigned level,
- enum lp_texture_usage usage,
- unsigned x, unsigned y)
-{
- struct llvmpipe_texture_image *linear_img = &lpr->linear_img;
- uint8_t *linear_image;
-
assert(llvmpipe_resource_is_texture(&lpr->base));
- assert(x % TILE_SIZE == 0);
- assert(y % TILE_SIZE == 0);
- if (!linear_img->data) {
- /* allocate memory for the linear image now */
- /* XXX should probably not do that here? */
- alloc_image_data(lpr);
- }
- assert(linear_img->data);
+ offset = lpr->mip_offsets[level];
- /* compute address of the slice/face of the image that contains the tile */
- linear_image = llvmpipe_get_texture_image_address(lpr, face_slice, level);
+ if (face_slice > 0)
+ offset += face_slice * tex_image_face_size(lpr, level);
- return linear_image;
+ return (ubyte *) lpr->tex_data + offset;
}
llvmpipe_resource_size(const struct pipe_resource *resource)
{
const struct llvmpipe_resource *lpr = llvmpipe_resource_const(resource);
- unsigned lvl, size = 0;
+ unsigned size = 0;
if (llvmpipe_resource_is_texture(resource)) {
- for (lvl = 0; lvl <= lpr->base.last_level; lvl++) {
- if (lpr->linear_img.data)
- size += tex_image_size(lpr, lvl);
- }
+ /* Note this will always return 0 for displaytarget resources */
+ size = lpr->total_alloc_size;
}
else {
size = resource->width0;
}
-
return size;
}
#endif
screen->resource_create = llvmpipe_resource_create;
+/* screen->resource_create_front = llvmpipe_resource_create_front; */
screen->resource_destroy = llvmpipe_resource_destroy;
screen->resource_from_handle = llvmpipe_resource_from_handle;
screen->resource_get_handle = llvmpipe_resource_get_handle;
{
pipe->transfer_map = llvmpipe_transfer_map;
pipe->transfer_unmap = llvmpipe_transfer_unmap;
-
- pipe->transfer_flush_region = u_default_transfer_flush_region;
- pipe->transfer_inline_write = u_default_transfer_inline_write;
- pipe->create_surface = llvmpipe_create_surface;
- pipe->surface_destroy = llvmpipe_surface_destroy;
+ pipe->transfer_flush_region = u_default_transfer_flush_region;
+ pipe->buffer_subdata = u_default_buffer_subdata;
+ pipe->texture_subdata = u_default_texture_subdata;
}