static struct pipe_query *svga_create_query( struct pipe_context *pipe, unsigned query_type ) { struct svga_screen *svgascreen = svga_screen(pipe->screen); struct svga_winsys_screen *sws = svgascreen->sws; struct svga_query *sq; SVGA_DBG(DEBUG_QUERY, "%s\n", __FUNCTION__); sq = CALLOC_STRUCT(svga_query); if (!sq) goto no_sq; sq->type = SVGA3D_QUERYTYPE_OCCLUSION; sq->hwbuf = svga_winsys_buffer_create(svgascreen, 1, SVGA_BUFFER_USAGE_PINNED, sizeof *sq->queryResult); if(!sq->hwbuf) goto no_hwbuf; sq->queryResult = (SVGA3dQueryResult *)sws->buffer_map(sws, sq->hwbuf, PIPE_BUFFER_USAGE_CPU_WRITE); if(!sq->queryResult) goto no_query_result; sq->queryResult->totalSize = sizeof *sq->queryResult; sq->queryResult->state = SVGA3D_QUERYSTATE_NEW; /* * We request the buffer to be pinned and assume it is always mapped. * * The reason is that we don't want to wait for fences when checking the * query status. */ sws->buffer_unmap(sws, sq->hwbuf); return &sq->base; no_query_result: sws->buffer_destroy(sws, sq->hwbuf); no_hwbuf: FREE(sq); no_sq: return NULL; }
/** * Allocate DMA'ble storage for the buffer. * * Called before mapping a buffer. */ static INLINE enum pipe_error svga_buffer_create_hw_storage(struct svga_screen *ss, struct svga_buffer *sbuf) { assert(!sbuf->user); if(!sbuf->hwbuf) { unsigned alignment = sbuf->base.alignment; unsigned usage = 0; unsigned size = sbuf->base.size; sbuf->hwbuf = svga_winsys_buffer_create(ss, alignment, usage, size); if(!sbuf->hwbuf) return PIPE_ERROR_OUT_OF_MEMORY; assert(!sbuf->dma.pending); } return PIPE_OK; }
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 DMA for the transfer request */ static void * svga_texture_transfer_map_dma(struct svga_context *svga, struct svga_transfer *st) { struct svga_winsys_screen *sws = svga_screen(svga->pipe.screen)->sws; struct pipe_resource *texture = st->base.resource; unsigned nblocksx, nblocksy; unsigned d; unsigned usage = st->base.usage; /* we'll put the data into a tightly packed buffer */ nblocksx = util_format_get_nblocksx(texture->format, st->box.w); nblocksy = util_format_get_nblocksy(texture->format, st->box.h); d = st->box.d; st->base.stride = nblocksx*util_format_get_blocksize(texture->format); st->base.layer_stride = st->base.stride * nblocksy; 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) 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); return NULL; } } if (usage & PIPE_TRANSFER_READ) { SVGA3dSurfaceDMAFlags flags; memset(&flags, 0, sizeof flags); svga_transfer_dma(svga, st, SVGA3D_READ_HOST_VRAM, flags); } if (st->swbuf) { return st->swbuf; } else { return sws->buffer_map(sws, st->hwbuf, usage); } }
/* XXX: Still implementing this as if it was a screen function, but * can now modify it to queue transfers on the context. */ static struct pipe_transfer * svga_texture_get_transfer(struct pipe_context *pipe, struct pipe_resource *texture, unsigned level, unsigned usage, const struct pipe_box *box) { struct svga_context *svga = svga_context(pipe); struct svga_screen *ss = svga_screen(pipe->screen); struct svga_winsys_screen *sws = ss->sws; struct svga_transfer *st; unsigned nblocksx = util_format_get_nblocksx(texture->format, box->width); unsigned nblocksy = util_format_get_nblocksy(texture->format, box->height); /* We can't map texture storage directly */ if (usage & PIPE_TRANSFER_MAP_DIRECTLY) return NULL; assert(box->depth == 1); st = CALLOC_STRUCT(svga_transfer); if (!st) return NULL; 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 = 0; st->hw_nblocksy = nblocksy; st->hwbuf = svga_winsys_buffer_create(svga, 1, 0, st->hw_nblocksy*st->base.stride); while(!st->hwbuf && (st->hw_nblocksy /= 2)) { st->hwbuf = svga_winsys_buffer_create(svga, 1, 0, st->hw_nblocksy*st->base.stride); } if(!st->hwbuf) goto no_hwbuf; 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); if(!st->swbuf) goto no_swbuf; } if (usage & PIPE_TRANSFER_READ) { SVGA3dSurfaceDMAFlags flags; memset(&flags, 0, sizeof flags); svga_transfer_dma(svga, st, SVGA3D_READ_HOST_VRAM, flags); } return &st->base; no_swbuf: sws->buffer_destroy(sws, st->hwbuf); no_hwbuf: FREE(st); return NULL; }
/** * Upload the buffer to the host in a piecewise fashion. * * Used when the buffer is too big to fit in the GMR aperture. */ static INLINE enum pipe_error svga_buffer_upload_piecewise(struct svga_screen *ss, struct svga_context *svga, struct svga_buffer *sbuf) { struct svga_winsys_screen *sws = ss->sws; const unsigned alignment = sizeof(void *); const unsigned usage = 0; unsigned i; assert(sbuf->map.num_ranges); assert(!sbuf->dma.pending); SVGA_DBG(DEBUG_DMA, "dma to sid %p\n", sbuf->handle); for (i = 0; i < sbuf->map.num_ranges; ++i) { struct svga_buffer_range *range = &sbuf->map.ranges[i]; unsigned offset = range->start; unsigned size = range->end - range->start; while (offset < range->end) { struct svga_winsys_buffer *hwbuf; uint8_t *map; enum pipe_error ret; if (offset + size > range->end) size = range->end - offset; hwbuf = svga_winsys_buffer_create(ss, alignment, usage, size); while (!hwbuf) { size /= 2; if (!size) return PIPE_ERROR_OUT_OF_MEMORY; hwbuf = svga_winsys_buffer_create(ss, alignment, usage, size); } SVGA_DBG(DEBUG_DMA, " bytes %u - %u\n", offset, offset + size); map = sws->buffer_map(sws, hwbuf, PIPE_BUFFER_USAGE_CPU_WRITE | PIPE_BUFFER_USAGE_DISCARD); assert(map); if (map) { memcpy(map, sbuf->swbuf, size); sws->buffer_unmap(sws, hwbuf); } ret = SVGA3D_BufferDMA(svga->swc, hwbuf, sbuf->handle, SVGA3D_WRITE_HOST_VRAM, size, 0, offset, sbuf->dma.flags); if(ret != PIPE_OK) { svga_context_flush(svga, NULL); ret = SVGA3D_BufferDMA(svga->swc, hwbuf, sbuf->handle, SVGA3D_WRITE_HOST_VRAM, size, 0, offset, sbuf->dma.flags); assert(ret == PIPE_OK); } sbuf->dma.flags.discard = FALSE; sws->buffer_destroy(sws, hwbuf); offset += size; } } sbuf->map.num_ranges = 0; return PIPE_OK; }