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; }
/** * Use direct map for the transfer request */ static void * svga_texture_transfer_map_direct(struct svga_context *svga, struct svga_transfer *st) { struct svga_winsys_screen *sws = svga_screen(svga->pipe.screen)->sws; struct pipe_transfer *transfer = &st->base; struct pipe_resource *texture = transfer->resource; struct svga_texture *tex = svga_texture(texture); struct svga_winsys_surface *surf = tex->handle; unsigned level = st->base.level; unsigned w, h, nblocksx, nblocksy, i; unsigned usage = st->base.usage; if (need_tex_readback(st)) { enum pipe_error ret; svga_surfaces_flush(svga); for (i = 0; i < st->box.d; i++) { if (svga_have_vgpu10(svga)) { ret = readback_image_vgpu10(svga, surf, st->slice + i, level, tex->b.b.last_level + 1); } else { ret = readback_image_vgpu9(svga, surf, st->slice + i, level); } } svga->hud.num_readbacks++; SVGA_STATS_COUNT_INC(sws, SVGA_STATS_COUNT_TEXREADBACK); 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, level); } else { assert(usage & PIPE_TRANSFER_WRITE); if ((usage & PIPE_TRANSFER_UNSYNCHRONIZED) == 0) { if (svga_is_texture_dirty(tex, st->slice, level)) { /* * do a surface flush if the subresource has been modified * in this command buffer. */ svga_surfaces_flush(svga); if (!sws->surface_is_flushed(sws, surf)) { svga->hud.surface_write_flushes++; SVGA_STATS_COUNT_INC(sws, SVGA_STATS_COUNT_SURFACEWRITEFLUSH); svga_context_flush(svga, NULL); } } } } /* we'll directly access the guest-backed surface */ w = u_minify(texture->width0, level); h = u_minify(texture->height0, level); nblocksx = util_format_get_nblocksx(texture->format, w); nblocksy = util_format_get_nblocksy(texture->format, h); st->hw_nblocksy = nblocksy; st->base.stride = nblocksx*util_format_get_blocksize(texture->format); st->base.layer_stride = st->base.stride * nblocksy; /* * Begin mapping code */ { SVGA3dSize baseLevelSize; uint8_t *map; boolean retry; unsigned offset, mip_width, mip_height; 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->hud.surface_write_flushes++; 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) { return NULL; } /** * 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; if ((tex->b.b.target == PIPE_TEXTURE_1D_ARRAY) || (tex->b.b.target == PIPE_TEXTURE_2D_ARRAY) || (tex->b.b.target == PIPE_TEXTURE_CUBE_ARRAY)) { st->base.layer_stride = svga3dsurface_get_image_offset(tex->key.format, baseLevelSize, tex->b.b.last_level + 1, 1, 0); } 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, st->box.x, st->box.y, st->box.z); return (void *) (map + offset); } }