Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright 2009, 2010 Red Hat, Inc.
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * on the rights to use, copy, modify, merge, publish, distribute, sub
      8  * license, and/or sell copies of the Software, and to permit persons to whom
      9  * the Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     21  *
     22  */
     23 /** \file QXLImage.c
     24  * \author Sren Sandmann <sandmann (at) redhat.com>
     25  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 #include "config.h"
     29 #endif
     30 
     31 #include <string.h>
     32 #include <assert.h>
     33 #include <stdlib.h>
     34 
     35 #include <spice/macros.h>
     36 
     37 #include "qxl.h"
     38 #include "murmurhash3.h"
     39 
     40 static unsigned int
     41 hash_and_copy (const uint8_t *src, int src_stride,
     42 	       uint8_t *dest, int dest_stride,
     43 	       int bytes_per_pixel, int width, int height,
     44 	       uint32_t hash)
     45 {
     46     int i;
     47 
     48     for (i = 0; i < height; ++i)
     49     {
     50 	const uint8_t *src_line = src + i * src_stride;
     51 	uint8_t *dest_line = dest + i * dest_stride;
     52 	int n_bytes = width * bytes_per_pixel;
     53 	if (n_bytes > src_stride)
     54 	    n_bytes = src_stride;
     55 
     56 	if (dest)
     57 	    memcpy (dest_line, src_line, n_bytes);
     58 
     59 	MurmurHash3_x86_32 (src_line, n_bytes, hash, &hash);
     60     }
     61 
     62     return hash;
     63 }
     64 
     65 struct qxl_bo *
     66 qxl_image_create (qxl_screen_t *qxl, const uint8_t *data,
     67 		  int x, int y, int width, int height,
     68 		  int stride, int Bpp, Bool fallback)
     69 {
     70 	uint32_t hash;
     71 	struct QXLImage *image;
     72 	struct qxl_bo *head_bo, *tail_bo;
     73 	struct qxl_bo *image_bo;
     74 	int dest_stride = (width * Bpp + 3) & (~3);
     75 	int h;
     76 	int chunk_size;
     77 
     78 	data += y * stride + x * Bpp;
     79 
     80 #if 0
     81 	ErrorF ("Must create new image of size %d %d\n", width, height);
     82 #endif
     83 
     84 	/* Chunk */
     85 
     86 	/* FIXME: Check integer overflow */
     87 
     88 	head_bo = tail_bo = NULL;
     89 
     90 	hash = 0;
     91 	h = height;
     92 
     93 	chunk_size = MAX (512 * 512, dest_stride);
     94 
     95 #ifdef XF86DRM_MODE
     96 	/* ensure we will not create too many pieces and overflow
     97 	 * the command buffer (MAX_RELOCS).  if so, increase the chunk_size.
     98 	 * each loop creates at least 2 cmd buffer entries, and
     99 	 * we have to leave room when we're done.
    100 	 */
    101 	if (height / (chunk_size / dest_stride) > (MAX_RELOCS / 4)) {
    102 		chunk_size = height / (MAX_RELOCS/4) * dest_stride;
    103 #if 0
    104 		ErrorF ("adjusted chunk_size to %d\n", chunk_size);
    105 #endif
    106 	}
    107 #endif
    108 
    109 	while (h)
    110 	{
    111 	    int n_lines = MIN ((chunk_size / dest_stride), h);
    112 	    struct qxl_bo *bo = qxl->bo_funcs->bo_alloc (qxl, sizeof (QXLDataChunk) + n_lines * dest_stride, "image data");
    113 
    114 	    QXLDataChunk *chunk = qxl->bo_funcs->bo_map(bo);
    115 	    chunk->data_size = n_lines * dest_stride;
    116 	    hash = hash_and_copy (data, stride,
    117 				  chunk->data, dest_stride,
    118 				  Bpp, width, n_lines, hash);
    119 
    120 	    if (tail_bo)
    121 	    {
    122 		qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDataChunk, next_chunk),
    123 					       tail_bo, bo);
    124 		qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLDataChunk, prev_chunk),
    125 					       bo, tail_bo);
    126 
    127 		chunk->next_chunk = 0;
    128 
    129 		tail_bo = bo;
    130 	    }
    131 	    else
    132 	    {
    133 		head_bo = tail_bo = bo;
    134 		chunk->next_chunk = 0;
    135 		chunk->prev_chunk = 0;
    136 	    }
    137 
    138 	    qxl->bo_funcs->bo_unmap(bo);
    139 	    if (bo != head_bo)
    140 		qxl->bo_funcs->bo_decref(qxl, bo);
    141 	    data += n_lines * stride;
    142 	    h -= n_lines;
    143 	}
    144 
    145 	/* Image */
    146 	image_bo = qxl->bo_funcs->bo_alloc (qxl, sizeof *image, "image struct");
    147 	image = qxl->bo_funcs->bo_map(image_bo);
    148 
    149 	image->descriptor.id = 0;
    150 	image->descriptor.type = SPICE_IMAGE_TYPE_BITMAP;
    151 
    152 	image->descriptor.flags = 0;
    153 	image->descriptor.width = width;
    154 	image->descriptor.height = height;
    155 
    156 	if (Bpp == 2)
    157 	{
    158 	    image->bitmap.format = SPICE_BITMAP_FMT_16BIT;
    159 	}
    160 	else if (Bpp == 1)
    161 	{
    162 	    image->bitmap.format = SPICE_BITMAP_FMT_8BIT_A;
    163 	}
    164 	else if (Bpp == 4)
    165 	{
    166 	    image->bitmap.format = SPICE_BITMAP_FMT_RGBA;
    167 	}
    168 	else
    169 	{
    170 	    abort();
    171 	}
    172 
    173 	image->bitmap.flags = SPICE_BITMAP_FLAGS_TOP_DOWN;
    174 	image->bitmap.x = width;
    175 	image->bitmap.y = height;
    176 	image->bitmap.stride = dest_stride;
    177 	image->bitmap.palette = 0;
    178 	qxl->bo_funcs->bo_output_bo_reloc(qxl, offsetof(QXLImage, bitmap.data),
    179 				       image_bo, head_bo);
    180 
    181 	qxl->bo_funcs->bo_decref(qxl, head_bo);
    182 	/* Add to hash table if caching is enabled */
    183 	if ((fallback && qxl->enable_fallback_cache)	||
    184 	    (!fallback && qxl->enable_image_cache))
    185 	{
    186             image->descriptor.id = hash;
    187             image->descriptor.flags = QXL_IMAGE_CACHE;
    188 #if 0
    189             ErrorF ("added with hash %u\n", hash);
    190 #endif
    191 	}
    192 
    193 	qxl->bo_funcs->bo_unmap(image_bo);
    194 	return image_bo;
    195 }
    196 
    197 void
    198 qxl_image_destroy (qxl_screen_t *qxl,
    199 		   struct qxl_bo *image_bo)
    200 {
    201     struct QXLImage *image;
    202     uint64_t chunk, prev_chunk;
    203 
    204     image = qxl->bo_funcs->bo_map(image_bo);
    205     qxl->bo_funcs->bo_unmap(image_bo);
    206 
    207     image = qxl->bo_funcs->bo_map(image_bo);
    208     chunk = image->bitmap.data;
    209     while (chunk)
    210     {
    211 	struct qxl_bo *bo;
    212 	struct QXLDataChunk *virtual;
    213 
    214 	bo = qxl_ums_lookup_phy_addr(qxl, chunk);
    215 	assert(bo);
    216 	virtual = qxl->bo_funcs->bo_map(bo);
    217 	chunk = virtual->next_chunk;
    218 	prev_chunk = virtual->prev_chunk;
    219 
    220 	qxl->bo_funcs->bo_unmap(bo);
    221 	qxl->bo_funcs->bo_decref (qxl, bo);
    222 	if (prev_chunk) {
    223 	    bo = qxl_ums_lookup_phy_addr(qxl, prev_chunk);
    224 	    assert(bo);
    225 	    qxl->bo_funcs->bo_decref (qxl, bo);
    226 	}
    227     }
    228     qxl->bo_funcs->bo_unmap(image_bo);
    229     qxl->bo_funcs->bo_decref (qxl, image_bo);
    230 }
    231