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/u_format.h" 24#include "util/u_inlines.h" 25#include "util/u_memory.h" 26 27#include "virgl_context.h" 28#include "virgl_resource.h" 29#include "virgl_screen.h" 30 31static void virgl_copy_region_with_blit(struct pipe_context *pipe, 32 struct pipe_resource *dst, 33 unsigned dst_level, 34 const struct pipe_box *dst_box, 35 struct pipe_resource *src, 36 unsigned src_level, 37 const struct pipe_box *src_box) 38{ 39 struct pipe_blit_info blit; 40 41 assert(src_box->width == dst_box->width); 42 assert(src_box->height == dst_box->height); 43 assert(src_box->depth == dst_box->depth); 44 45 memset(&blit, 0, sizeof(blit)); 46 blit.src.resource = src; 47 blit.src.format = src->format; 48 blit.src.level = src_level; 49 blit.src.box = *src_box; 50 blit.dst.resource = dst; 51 blit.dst.format = dst->format; 52 blit.dst.level = dst_level; 53 blit.dst.box.x = dst_box->x; 54 blit.dst.box.y = dst_box->y; 55 blit.dst.box.z = dst_box->z; 56 blit.dst.box.width = src_box->width; 57 blit.dst.box.height = src_box->height; 58 blit.dst.box.depth = src_box->depth; 59 blit.mask = util_format_get_mask(src->format) & 60 util_format_get_mask(dst->format); 61 blit.filter = PIPE_TEX_FILTER_NEAREST; 62 63 if (blit.mask) { 64 pipe->blit(pipe, &blit); 65 } 66} 67 68static unsigned temp_bind(unsigned orig) 69{ 70 unsigned warn = ~(PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL | 71 PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_DISPLAY_TARGET); 72 if (orig & warn) 73 debug_printf("VIRGL: Warning, possibly unhandled bind: %x\n", 74 orig & warn); 75 76 return orig & (PIPE_BIND_DEPTH_STENCIL | PIPE_BIND_RENDER_TARGET); 77} 78 79static void virgl_init_temp_resource_from_box(struct pipe_resource *res, 80 struct pipe_resource *orig, 81 const struct pipe_box *box, 82 unsigned level, unsigned flags, 83 enum pipe_format fmt) 84{ 85 memset(res, 0, sizeof(*res)); 86 res->bind = temp_bind(orig->bind); 87 res->format = fmt; 88 res->width0 = box->width; 89 res->height0 = box->height; 90 res->depth0 = 1; 91 res->array_size = 1; 92 res->usage = PIPE_USAGE_STAGING; 93 res->flags = flags; 94 95 /* We must set the correct texture target and dimensions for a 3D box. */ 96 if (box->depth > 1 && util_max_layer(orig, level) > 0) 97 res->target = orig->target; 98 else 99 res->target = PIPE_TEXTURE_2D; 100 101 if (res->target != PIPE_BUFFER) 102 res->bind = PIPE_BIND_RENDER_TARGET; 103 104 switch (res->target) { 105 case PIPE_TEXTURE_1D_ARRAY: 106 case PIPE_TEXTURE_2D_ARRAY: 107 case PIPE_TEXTURE_CUBE_ARRAY: 108 res->array_size = box->depth; 109 break; 110 case PIPE_TEXTURE_3D: 111 res->depth0 = box->depth; 112 break; 113 default: 114 break; 115 } 116} 117 118static void *texture_transfer_map_plain(struct pipe_context *ctx, 119 struct pipe_resource *resource, 120 unsigned level, 121 unsigned usage, 122 const struct pipe_box *box, 123 struct pipe_transfer **transfer) 124{ 125 struct virgl_context *vctx = virgl_context(ctx); 126 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws; 127 struct virgl_resource *vtex = virgl_resource(resource); 128 struct virgl_transfer *trans; 129 bool flush, readback; 130 131 trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource, 132 &vtex->metadata, level, usage, box); 133 trans->resolve_transfer = NULL; 134 135 assert(resource->nr_samples <= 1); 136 137 flush = virgl_res_needs_flush(vctx, trans); 138 if (flush) 139 ctx->flush(ctx, NULL, 0); 140 141 readback = virgl_res_needs_readback(vctx, vtex, usage, level); 142 if (readback) 143 vws->transfer_get(vws, vtex->hw_res, box, trans->base.stride, 144 trans->l_stride, trans->offset, level); 145 146 if (readback || flush) 147 vws->resource_wait(vws, vtex->hw_res); 148 149 trans->hw_res_map = vws->resource_map(vws, vtex->hw_res); 150 if (!trans->hw_res_map) { 151 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); 152 return NULL; 153 } 154 155 *transfer = &trans->base; 156 return trans->hw_res_map + trans->offset; 157} 158 159static void *texture_transfer_map_resolve(struct pipe_context *ctx, 160 struct pipe_resource *resource, 161 unsigned level, 162 unsigned usage, 163 const struct pipe_box *box, 164 struct pipe_transfer **transfer) 165{ 166 struct virgl_context *vctx = virgl_context(ctx); 167 struct virgl_resource *vtex = virgl_resource(resource); 168 struct pipe_resource templ, *resolve_tmp; 169 struct virgl_transfer *trans; 170 171 trans = virgl_resource_create_transfer(&vctx->transfer_pool, resource, 172 &vtex->metadata, level, usage, box); 173 if (!trans) 174 return NULL; 175 176 enum pipe_format fmt = resource->format; 177 if (!virgl_has_readback_format(ctx->screen, fmt)) { 178 if (util_format_fits_8unorm(util_format_description(fmt))) 179 fmt = PIPE_FORMAT_R8G8B8A8_UNORM; 180 else if (util_format_is_pure_sint(fmt)) 181 fmt = PIPE_FORMAT_R32G32B32A32_SINT; 182 else if (util_format_is_pure_uint(fmt)) 183 fmt = PIPE_FORMAT_R32G32B32A32_UINT; 184 else 185 fmt = PIPE_FORMAT_R32G32B32A32_FLOAT; 186 assert(virgl_has_readback_format(ctx->screen, fmt)); 187 } 188 189 virgl_init_temp_resource_from_box(&templ, resource, box, level, 0, fmt); 190 191 resolve_tmp = ctx->screen->resource_create(ctx->screen, &templ); 192 if (!resolve_tmp) 193 return NULL; 194 195 struct pipe_box dst_box = *box; 196 dst_box.x = dst_box.y = dst_box.z = 0; 197 198 if (usage & PIPE_TRANSFER_READ) { 199 virgl_copy_region_with_blit(ctx, resolve_tmp, 0, &dst_box, resource, 200 level, box); 201 ctx->flush(ctx, NULL, 0); 202 } 203 204 void *ptr = texture_transfer_map_plain(ctx, resolve_tmp, 0, usage, &dst_box, 205 &trans->resolve_transfer); 206 if (!ptr) 207 goto fail; 208 209 *transfer = &trans->base; 210 if (fmt == resource->format) { 211 trans->base.stride = trans->resolve_transfer->stride; 212 trans->base.layer_stride = trans->resolve_transfer->layer_stride; 213 return ptr; 214 } else { 215 if (usage & PIPE_TRANSFER_READ) { 216 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws; 217 void *src = ptr; 218 ptr = vws->resource_map(vws, vtex->hw_res); 219 if (!ptr) 220 goto fail; 221 222 if (!util_format_translate_3d(resource->format, 223 ptr, 224 trans->base.stride, 225 trans->base.layer_stride, 226 box->x, box->y, box->z, 227 fmt, 228 src, 229 trans->resolve_transfer->stride, 230 trans->resolve_transfer->layer_stride, 231 0, 0, 0, 232 box->width, box->height, box->depth)) { 233 debug_printf("failed to translate format %s to %s\n", 234 util_format_short_name(fmt), 235 util_format_short_name(resource->format)); 236 goto fail; 237 } 238 } 239 240 if ((usage & PIPE_TRANSFER_WRITE) == 0) 241 pipe_resource_reference(&trans->resolve_transfer->resource, NULL); 242 243 return ptr + trans->offset; 244 } 245 246fail: 247 pipe_resource_reference(&resolve_tmp, NULL); 248 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); 249 return NULL; 250} 251 252static bool needs_resolve(struct pipe_screen *screen, 253 struct pipe_resource *resource, unsigned usage) 254{ 255 if (resource->nr_samples > 1) 256 return true; 257 258 if (usage & PIPE_TRANSFER_READ) 259 return !util_format_is_depth_or_stencil(resource->format) && 260 !virgl_has_readback_format(screen, resource->format); 261 262 return false; 263} 264 265static void *virgl_texture_transfer_map(struct pipe_context *ctx, 266 struct pipe_resource *resource, 267 unsigned level, 268 unsigned usage, 269 const struct pipe_box *box, 270 struct pipe_transfer **transfer) 271{ 272 if (needs_resolve(ctx->screen, resource, usage)) 273 return texture_transfer_map_resolve(ctx, resource, level, usage, box, 274 transfer); 275 276 return texture_transfer_map_plain(ctx, resource, level, usage, box, transfer); 277} 278 279static void flush_data(struct pipe_context *ctx, 280 struct virgl_transfer *trans, 281 const struct pipe_box *box) 282{ 283 struct virgl_winsys *vws = virgl_screen(ctx->screen)->vws; 284 vws->transfer_put(vws, virgl_resource(trans->base.resource)->hw_res, box, 285 trans->base.stride, trans->l_stride, trans->offset, 286 trans->base.level); 287} 288 289static void virgl_texture_transfer_unmap(struct pipe_context *ctx, 290 struct pipe_transfer *transfer) 291{ 292 struct virgl_context *vctx = virgl_context(ctx); 293 struct virgl_transfer *trans = virgl_transfer(transfer); 294 bool queue_unmap = false; 295 296 if (transfer->usage & PIPE_TRANSFER_WRITE && 297 (transfer->usage & PIPE_TRANSFER_FLUSH_EXPLICIT) == 0) { 298 299 if (trans->resolve_transfer && (trans->base.resource->format == 300 trans->resolve_transfer->resource->format)) { 301 flush_data(ctx, virgl_transfer(trans->resolve_transfer), 302 &trans->resolve_transfer->box); 303 304 /* FINISHME: In case the destination format isn't renderable here, the 305 * blit here will currently fail. This could for instance happen if the 306 * mapped resource is of a compressed format, and it's mapped with both 307 * read and write usage. 308 */ 309 310 virgl_copy_region_with_blit(ctx, 311 trans->base.resource, trans->base.level, 312 &transfer->box, 313 trans->resolve_transfer->resource, 0, 314 &trans->resolve_transfer->box); 315 ctx->flush(ctx, NULL, 0); 316 } else 317 queue_unmap = true; 318 } 319 320 if (trans->resolve_transfer) { 321 pipe_resource_reference(&trans->resolve_transfer->resource, NULL); 322 virgl_resource_destroy_transfer(&vctx->transfer_pool, 323 virgl_transfer(trans->resolve_transfer)); 324 } 325 326 if (queue_unmap) 327 virgl_transfer_queue_unmap(&vctx->queue, trans); 328 else 329 virgl_resource_destroy_transfer(&vctx->transfer_pool, trans); 330} 331 332static const struct u_resource_vtbl virgl_texture_vtbl = 333{ 334 virgl_resource_get_handle, /* get_handle */ 335 virgl_resource_destroy, /* resource_destroy */ 336 virgl_texture_transfer_map, /* transfer_map */ 337 NULL, /* transfer_flush_region */ 338 virgl_texture_transfer_unmap, /* transfer_unmap */ 339}; 340 341void virgl_texture_init(struct virgl_resource *res) 342{ 343 res->u.vtbl = &virgl_texture_vtbl; 344} 345