static void * svga_texture_transfer_map(struct pipe_context *pipe, struct pipe_resource *texture, unsigned level, unsigned usage, const struct pipe_box *box, struct pipe_transfer **ptransfer) { struct svga_context *svga = svga_context(pipe); struct svga_screen *ss = svga_screen(pipe->screen); struct svga_winsys_screen *sws = ss->sws; struct svga_texture *tex = svga_texture(texture); struct svga_transfer *st; unsigned nblocksx, nblocksy; boolean use_direct_map = svga_have_gb_objects(svga) && !svga_have_gb_dma(svga); unsigned d; void *returnVal; int64_t begin = os_time_get(); /* We can't map texture storage directly unless we have GB objects */ if (usage & PIPE_TRANSFER_MAP_DIRECTLY) { if (svga_have_gb_objects(svga)) use_direct_map = TRUE; else return NULL; } st = CALLOC_STRUCT(svga_transfer); if (!st) return NULL; { unsigned w, h; if (use_direct_map) { /* we'll directly access the guest-backed surface */ w = u_minify(texture->width0, level); h = u_minify(texture->height0, level); d = u_minify(texture->depth0, level); } else { /* we'll put the data into a tightly packed buffer */ w = box->width; h = box->height; d = box->depth; } nblocksx = util_format_get_nblocksx(texture->format, w); nblocksy = util_format_get_nblocksy(texture->format, h); } pipe_resource_reference(&st->base.resource, texture); st->base.level = level; st->base.usage = usage; st->base.box = *box; st->base.stride = nblocksx*util_format_get_blocksize(texture->format); st->base.layer_stride = st->base.stride * nblocksy; switch (tex->b.b.target) { case PIPE_TEXTURE_CUBE: case PIPE_TEXTURE_2D_ARRAY: case PIPE_TEXTURE_1D_ARRAY: st->slice = st->base.box.z; st->base.box.z = 0; /* so we don't apply double offsets below */ break; default: st->slice = 0; break; } if (usage & PIPE_TRANSFER_WRITE) { /* record texture upload for HUD */ svga->hud.num_bytes_uploaded += nblocksx * nblocksy * d * util_format_get_blocksize(texture->format); } if (!use_direct_map) { /* Use a DMA buffer */ st->hw_nblocksy = nblocksy; st->hwbuf = svga_winsys_buffer_create(svga, 1, 0, st->hw_nblocksy * st->base.stride * d); while(!st->hwbuf && (st->hw_nblocksy /= 2)) { st->hwbuf = svga_winsys_buffer_create(svga, 1, 0, st->hw_nblocksy * st->base.stride * d); } if (!st->hwbuf) { FREE(st); return NULL; } if (st->hw_nblocksy < nblocksy) { /* We couldn't allocate a hardware buffer big enough for the transfer, * so allocate regular malloc memory instead */ if (0) { debug_printf("%s: failed to allocate %u KB of DMA, " "splitting into %u x %u KB DMA transfers\n", __FUNCTION__, (nblocksy*st->base.stride + 1023)/1024, (nblocksy + st->hw_nblocksy - 1)/st->hw_nblocksy, (st->hw_nblocksy*st->base.stride + 1023)/1024); } st->swbuf = MALLOC(nblocksy * st->base.stride * d); if (!st->swbuf) { sws->buffer_destroy(sws, st->hwbuf); FREE(st); return NULL; } } if (usage & PIPE_TRANSFER_READ) { SVGA3dSurfaceDMAFlags flags; memset(&flags, 0, sizeof flags); svga_transfer_dma(svga, st, SVGA3D_READ_HOST_VRAM, flags); } } else { struct pipe_transfer *transfer = &st->base; struct svga_winsys_surface *surf = tex->handle; if (!surf) { FREE(st); return NULL; } if (need_tex_readback(transfer)) { enum pipe_error ret; svga_surfaces_flush(svga); if (svga_have_vgpu10(svga)) { ret = readback_image_vgpu10(svga, surf, st->slice, transfer->level, tex->b.b.last_level + 1); } else { ret = readback_image_vgpu9(svga, surf, st->slice, transfer->level); } assert(ret == PIPE_OK); (void) ret; svga_context_flush(svga, NULL); /* * Note: if PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE were specified * we could potentially clear the flag for all faces/layers/mips. */ svga_clear_texture_rendered_to(tex, st->slice, transfer->level); } else { assert(transfer->usage & PIPE_TRANSFER_WRITE); if ((transfer->usage & PIPE_TRANSFER_UNSYNCHRONIZED) == 0) { svga_surfaces_flush(svga); if (!sws->surface_is_flushed(sws, surf)) svga_context_flush(svga, NULL); } } } st->use_direct_map = use_direct_map; *ptransfer = &st->base; /* * Begin mapping code */ if (st->swbuf) { returnVal = st->swbuf; } else if (!st->use_direct_map) { returnVal = sws->buffer_map(sws, st->hwbuf, usage); } else { SVGA3dSize baseLevelSize; struct svga_texture *tex = svga_texture(texture); struct svga_winsys_surface *surf = tex->handle; uint8_t *map; boolean retry; unsigned offset, mip_width, mip_height; unsigned xoffset = st->base.box.x; unsigned yoffset = st->base.box.y; unsigned zoffset = st->base.box.z; map = svga->swc->surface_map(svga->swc, surf, usage, &retry); if (map == NULL && retry) { /* * At this point, the svga_surfaces_flush() should already have * called in svga_texture_get_transfer(). */ svga_context_flush(svga, NULL); map = svga->swc->surface_map(svga->swc, surf, usage, &retry); } /* * Make sure we return NULL if the map fails */ if (!map) { FREE(st); return map; } /** * Compute the offset to the specific texture slice in the buffer. */ baseLevelSize.width = tex->b.b.width0; baseLevelSize.height = tex->b.b.height0; baseLevelSize.depth = tex->b.b.depth0; offset = svga3dsurface_get_image_offset(tex->key.format, baseLevelSize, tex->b.b.last_level + 1, /* numMips */ st->slice, level); if (level > 0) { assert(offset > 0); } mip_width = u_minify(tex->b.b.width0, level); mip_height = u_minify(tex->b.b.height0, level); offset += svga3dsurface_get_pixel_offset(tex->key.format, mip_width, mip_height, xoffset, yoffset, zoffset); returnVal = (void *) (map + offset); } svga->hud.map_buffer_time += (os_time_get() - begin); svga->hud.num_resources_mapped++; return returnVal; }
/** * Request a transfer map to the texture resource */ static void * svga_texture_transfer_map(struct pipe_context *pipe, struct pipe_resource *texture, unsigned level, unsigned usage, const struct pipe_box *box, struct pipe_transfer **ptransfer) { struct svga_context *svga = svga_context(pipe); struct svga_winsys_screen *sws = svga_screen(pipe->screen)->sws; struct svga_texture *tex = svga_texture(texture); struct svga_transfer *st; struct svga_winsys_surface *surf = tex->handle; boolean use_direct_map = svga_have_gb_objects(svga) && !svga_have_gb_dma(svga); void *map = NULL; int64_t begin = svga_get_time(svga); SVGA_STATS_TIME_PUSH(sws, SVGA_STATS_TIME_TEXTRANSFERMAP); if (!surf) goto done; /* We can't map texture storage directly unless we have GB objects */ if (usage & PIPE_TRANSFER_MAP_DIRECTLY) { if (svga_have_gb_objects(svga)) use_direct_map = TRUE; else goto done; } st = CALLOC_STRUCT(svga_transfer); if (!st) goto done; st->base.level = level; st->base.usage = usage; st->base.box = *box; /* The modified transfer map box with the array index removed from z. * The array index is specified in slice. */ st->box.x = box->x; st->box.y = box->y; st->box.z = box->z; st->box.w = box->width; st->box.h = box->height; st->box.d = box->depth; switch (tex->b.b.target) { case PIPE_TEXTURE_CUBE: st->slice = st->base.box.z; st->box.z = 0; /* so we don't apply double offsets below */ break; case PIPE_TEXTURE_1D_ARRAY: case PIPE_TEXTURE_2D_ARRAY: case PIPE_TEXTURE_CUBE_ARRAY: st->slice = st->base.box.z; st->box.z = 0; /* so we don't apply double offsets below */ /* Force direct map for transfering multiple slices */ if (st->base.box.depth > 1) use_direct_map = svga_have_gb_objects(svga); break; default: st->slice = 0; break; } /* Force direct map for multisample surface */ if (texture->nr_samples > 1) { assert(svga_have_gb_objects(svga)); assert(sws->have_sm4_1); use_direct_map = TRUE; } st->use_direct_map = use_direct_map; pipe_resource_reference(&st->base.resource, texture); /* If this is the first time mapping to the surface in this * command buffer, clear the dirty masks of this surface. */ if (sws->surface_is_flushed(sws, surf)) { svga_clear_texture_dirty(tex); } if (!use_direct_map) { /* upload to the DMA buffer */ map = svga_texture_transfer_map_dma(svga, st); } else { boolean can_use_upload = tex->can_use_upload && !(st->base.usage & PIPE_TRANSFER_READ); boolean was_rendered_to = svga_was_texture_rendered_to(svga_texture(texture), st->slice, st->base.level); /* If the texture was already rendered to and upload buffer * is supported, then we will use upload buffer to * avoid the need to read back the texture content; otherwise, * we'll first try to map directly to the GB surface, if it is blocked, * then we'll try the upload buffer. */ if (was_rendered_to && can_use_upload) { map = svga_texture_transfer_map_upload(svga, st); } else { unsigned orig_usage = st->base.usage; /* First try directly map to the GB surface */ if (can_use_upload) st->base.usage |= PIPE_TRANSFER_DONTBLOCK; map = svga_texture_transfer_map_direct(svga, st); st->base.usage = orig_usage; if (!map && can_use_upload) { /* if direct map with DONTBLOCK fails, then try upload to the * texture upload buffer. */ map = svga_texture_transfer_map_upload(svga, st); } } /* If upload fails, then try direct map again without forcing it * to DONTBLOCK. */ if (!map) { map = svga_texture_transfer_map_direct(svga, st); } } if (!map) { FREE(st); } else { *ptransfer = &st->base; svga->hud.num_textures_mapped++; if (usage & PIPE_TRANSFER_WRITE) { /* record texture upload for HUD */ svga->hud.num_bytes_uploaded += st->base.layer_stride * st->box.d; /* mark this texture level as dirty */ svga_set_texture_dirty(tex, st->slice, level); } } done: svga->hud.map_buffer_time += (svga_get_time(svga) - begin); SVGA_STATS_TIME_POP(sws); (void) sws; return map; }