1/* 2 * Copyright 2014, 2015 Red Hat. 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 AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 * USE OR OTHER DEALINGS IN THE SOFTWARE. 22 */ 23#include "util/format/u_format.h" 24#include "util/u_inlines.h" 25#include "util/u_memory.h" 26 27#include "virgl_context.h" 28#include "virgl_encode.h" 29#include "virgl_resource.h" 30#include "virgl_screen.h" 31 32static void virgl_copy_region_with_blit(struct pipe_context *pipe, 33 struct pipe_resource *dst, 34 unsigned dst_level, 35 const struct pipe_box *dst_box, 36 struct pipe_resource *src, 37 unsigned src_level, 38 const struct pipe_box *src_box) 39{ 40 struct pipe_blit_info blit; 41 42 memset(&blit, 0, sizeof(blit)); 43 blit.src.resource = src; 44 blit.src.format = src->format; 45 blit.src.level = src_level; 46 blit.src.box = *src_box; 47 blit.dst.resource = dst; 48 blit.dst.format = dst->format; 49 blit.dst.level = dst_level; 50 blit.dst.box.x = dst_box->x; 51 blit.dst.box.y = dst_box->y; 52 blit.dst.box.z = dst_box->z; 53 blit.dst.box.width = dst_box->width; 54 blit.dst.box.height = dst_box->height; 55 blit.dst.box.depth = dst_box->depth; 56 blit.mask = util_format_get_mask(src->format) & 57 util_format_get_mask(dst->format); 58 blit.filter = PIPE_TEX_FILTER_NEAREST; 59 60 if (blit.mask) { 61 pipe->blit(pipe, &blit); 62 } 63} 64 65static unsigned temp_bind(unsigned orig) 66{ 67 unsigned warn = ~(PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL | 68 PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_DISPLAY_TARGET); 69 if (orig & warn) 70 debug_printf("VIRGL: Warning, possibly unhandled bind: %x\n", 71 orig & warn); 72 73 return orig & (PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_RENDER_TARGET); 74} 75 76static void virgl_init_temp_resource_from_box(struct pipe_resource *res, 77 struct pipe_resource *orig, 78 const struct pipe_box *box, 79 unsigned level, unsigned flags, 80 enum pipe_format fmt) 81{ 82 memset(res, 0, sizeof(*res)); 83 res->bind = temp_bind(orig->bind); 84 res->format = fmt; 85 res->width0 = box->width; 86 res->height0 = box->height; 87 res->depth0 = 1; 88 res->array_size = 1; 89 res->usage = PIPE_USAGE_STAGING; 90 res->flags = flags; 91 92 /* We must set the correct texture target and dimensions for a 3D box. */ 93 if (box->depth > 1 && util_max_layer(orig, level) > 0) 94 res->target = orig->target; 95 else 96 res->target = PIPE_TEXTURE_2D; 97 98 if (res->target != PIPE_BUFFER) 99 res->bind = PIPE_BIND_RENDER_TARGET; 100 101 switch (res->target) { 102 case PIPE_TEXTURE_1D_ARRAY: 103 case PIPE_TEXTURE_2D_ARRAY: 104 case PIPE_TEXTURE_CUBE_ARRAY: 105 res->array_size = box->depth; 106 break; 107 case PIPE_TEXTURE_3D: 108 res->depth0 = box->depth; 109 break; 110 default: 111 break; 112 } 113} 114 115static void *texture_transfer_map_resolve(struct pipe_context *ctx, 116 struct pipe_resource *resource, 117 unsigned level, 118 unsigned usage, 119 const struct pipe_box *box, 120 struct pipe_transfer **transfer) 121{ 122 struct virgl_context *vctx = virgl_context(ctx); 123 struct virgl_resource *vtex = virgl_resource(resource); 124 struct pipe_resource templ, *resolve_tmp; 125 struct virgl_transfer *trans; 126 127 trans = virgl_resource_create_transfer(vctx, resource, 128 &vtex->metadata, level, usage, box); 129 if (!trans) 130 return NULL; 131 132 enum pipe_format fmt = resource->format; 133 if (!virgl_has_readback_format(ctx->screen, pipe_to_virgl_format(fmt), true)) { 134 if (util_format_fits_8unorm(util_format_description(fmt))) 135 fmt = PIPE_FORMAT_R8G8B8A8_UNORM; 136 else if (util_format_is_pure_sint(fmt)) 137 fmt = PIPE_FORMAT_R32G32B32A32_SINT; 138 else if (util_format_is_pure_uint(fmt)) 139 fmt = PIPE_FORMAT_R32G32B32A32_UINT; 140 else 141 fmt = PIPE_FORMAT_R32G32B32A32_FLOAT; 142 assert(virgl_has_readback_format(ctx->screen, pipe_to_virgl_format(fmt), true)); 143 } 144 145 struct pipe_box dst_box = *box; 146 dst_box.x = dst_box.y = dst_box.z = 0; 147 if (usage & PIPE_MAP_READ) { 148 /* readback should scale to the block size */ 149 dst_box.width = align(dst_box.width, 150 util_format_get_blockwidth(resource->format)); 151 dst_box.height = align(dst_box.height, 152 util_format_get_blockheight(resource->format)); 153 } 154 155 virgl_init_temp_resource_from_box(&templ, resource, &dst_box, level, 0, fmt); 156 157 resolve_tmp = ctx->screen->resource_create(ctx->screen, &templ); 158 if (!resolve_tmp) 159 return NULL; 160 161 if (usage & PIPE_MAP_READ) { 162 virgl_copy_region_with_blit(ctx, resolve_tmp, 0, &dst_box, resource, 163 level, box); 164 ctx->flush(ctx, NULL, 0); 165 } 166 167 void *ptr = virgl_resource_transfer_map(ctx, resolve_tmp, 0, usage, &dst_box, 168 &trans->resolve_transfer); 169 if (!ptr) 170 goto fail; 171 172 /* trans->resolve_transfer owns resolve_tmp now */ 173 pipe_resource_reference(&resolve_tmp, NULL); 174 175 *transfer = &trans->base; 176 if (fmt == resource->format) { 177 trans->base.stride = trans->resolve_transfer->stride; 178 trans->base.layer_stride = trans->resolve_transfer->layer_stride; 179 return ptr; 180 } else { 181 if (usage & PIPE_MAP_READ) { 182 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws; 183 void *src = ptr; 184 ptr = vws->resource_map(vws, vtex->hw_res); 185 if (!ptr) 186 goto fail; 187 188 if (!util_format_translate_3d(resource->format, 189 ptr + vtex->metadata.level_offset[level], 190 trans->base.stride, 191 trans->base.layer_stride, 192 box->x, box->y, box->z, 193 fmt, 194 src, 195 trans->resolve_transfer->stride, 196 trans->resolve_transfer->layer_stride, 197 0, 0, 0, 198 dst_box.width, 199 dst_box.height, 200 dst_box.depth)) { 201 debug_printf("failed to translate format %s to %s\n", 202 util_format_short_name(fmt), 203 util_format_short_name(resource->format)); 204 goto fail; 205 } 206 } 207 208 if ((usage & PIPE_MAP_WRITE) == 0) 209 pipe_resource_reference(&trans->resolve_transfer->resource, NULL); 210 211 return ptr + trans->offset; 212 } 213 214fail: 215 pipe_resource_reference(&resolve_tmp, NULL); 216 virgl_resource_destroy_transfer(vctx, trans); 217 return NULL; 218} 219 220static bool needs_resolve(struct pipe_screen *screen, 221 struct pipe_resource *resource, unsigned usage) 222{ 223 if (resource->nr_samples > 1) 224 return true; 225 226 if (usage & PIPE_MAP_READ) 227 return !util_format_is_depth_or_stencil(resource->format) && 228 !virgl_has_readback_format(screen, pipe_to_virgl_format(resource->format), true); 229 230 return false; 231} 232 233void *virgl_texture_transfer_map(struct pipe_context *ctx, 234 struct pipe_resource *resource, 235 unsigned level, 236 unsigned usage, 237 const struct pipe_box *box, 238 struct pipe_transfer **transfer) 239{ 240 if (needs_resolve(ctx->screen, resource, usage)) 241 return texture_transfer_map_resolve(ctx, resource, level, usage, box, 242 transfer); 243 244 return virgl_resource_transfer_map(ctx, resource, level, usage, box, transfer); 245} 246 247static void flush_data(struct pipe_context *ctx, 248 struct virgl_transfer *trans, 249 const struct pipe_box *box) 250{ 251 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws; 252 vws->transfer_put(vws, trans->hw_res, box, 253 trans->base.stride, trans->l_stride, trans->offset, 254 trans->base.level); 255} 256 257void virgl_texture_transfer_unmap(struct pipe_context *ctx, 258 struct pipe_transfer *transfer) 259{ 260 struct virgl_context *vctx = virgl_context(ctx); 261 struct virgl_transfer *trans = virgl_transfer(transfer); 262 bool queue_unmap = false; 263 264 if (transfer->usage & PIPE_MAP_WRITE && 265 (transfer->usage & PIPE_MAP_FLUSH_EXPLICIT) == 0) { 266 267 if (trans->resolve_transfer && (trans->base.resource->format == 268 trans->resolve_transfer->resource->format)) { 269 flush_data(ctx, virgl_transfer(trans->resolve_transfer), 270 &trans->resolve_transfer->box); 271 272 /* FINISHME: In case the destination format isn't renderable here, the 273 * blit here will currently fail. This could for instance happen if the 274 * mapped resource is of a compressed format, and it's mapped with both 275 * read and write usage. 276 */ 277 278 virgl_copy_region_with_blit(ctx, 279 trans->base.resource, trans->base.level, 280 &transfer->box, 281 trans->resolve_transfer->resource, 0, 282 &trans->resolve_transfer->box); 283 ctx->flush(ctx, NULL, 0); 284 } else 285 queue_unmap = true; 286 } 287 288 if (trans->resolve_transfer) { 289 virgl_resource_destroy_transfer(vctx, 290 virgl_transfer(trans->resolve_transfer)); 291 } 292 293 if (queue_unmap) { 294 if (trans->copy_src_hw_res && trans->direction == VIRGL_TRANSFER_TO_HOST) { 295 virgl_encode_copy_transfer(vctx, trans); 296 virgl_resource_destroy_transfer(vctx, trans); 297 } else if (trans->copy_src_hw_res && trans->direction == VIRGL_TRANSFER_FROM_HOST) { 298 // if it is readback, then we have already encoded transfer 299 virgl_resource_destroy_transfer(vctx, trans); 300 } else { 301 virgl_transfer_queue_unmap(&vctx->queue, trans); 302 } 303 } else { 304 virgl_resource_destroy_transfer(vctx, trans); 305 } 306} 307 308void virgl_texture_init(struct virgl_resource *res) 309{ 310} 311