/* * Copyright 2012 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: Ben Skeggs * */ #include "util/u_format.h" #include "util/u_inlines.h" #include "util/u_surface.h" #include "nouveau/nv_m2mf.xml.h" #include "nv30_screen.h" #include "nv30_context.h" #include "nv30_resource.h" #include "nv30_transfer.h" static INLINE unsigned layer_offset(struct pipe_resource *pt, unsigned level, unsigned layer) { struct nv30_miptree *mt = nv30_miptree(pt); struct nv30_miptree_level *lvl = &mt->level[level]; if (pt->target == PIPE_TEXTURE_CUBE) return (layer * mt->layer_size) + lvl->offset; return lvl->offset + (layer * lvl->zslice_size); } static boolean nv30_miptree_get_handle(struct pipe_screen *pscreen, struct pipe_resource *pt, struct winsys_handle *handle) { struct nv30_miptree *mt = nv30_miptree(pt); unsigned stride; if (!mt || !mt->base.bo) return FALSE; stride = util_format_get_stride(mt->base.base.format, mt->base.base.width0); return nouveau_screen_bo_get_handle(pscreen, mt->base.bo, stride, handle); } static void nv30_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt) { struct nv30_miptree *mt = nv30_miptree(pt); nouveau_bo_ref(NULL, &mt->base.bo); FREE(mt); } struct nv30_transfer { struct pipe_transfer base; struct nv30_rect img; struct nv30_rect tmp; unsigned nblocksx; unsigned nblocksy; }; static INLINE struct nv30_transfer * nv30_transfer(struct pipe_transfer *ptx) { return (struct nv30_transfer *)ptx; } static INLINE void define_rect(struct pipe_resource *pt, unsigned level, unsigned z, unsigned x, unsigned y, unsigned w, unsigned h, struct nv30_rect *rect) { struct nv30_miptree *mt = nv30_miptree(pt); struct nv30_miptree_level *lvl = &mt->level[level]; rect->w = u_minify(pt->width0, level) << mt->ms_x; rect->w = util_format_get_nblocksx(pt->format, rect->w); rect->h = u_minify(pt->height0, level) << mt->ms_y; rect->h = util_format_get_nblocksy(pt->format, rect->h); rect->d = 1; rect->z = 0; if (mt->swizzled) { if (pt->target == PIPE_TEXTURE_3D) { rect->d = u_minify(pt->depth0, level); rect->z = z; z = 0; } rect->pitch = 0; } else { rect->pitch = lvl->pitch; } rect->bo = mt->base.bo; rect->domain = NOUVEAU_BO_VRAM; rect->offset = layer_offset(pt, level, z); rect->cpp = util_format_get_blocksize(pt->format); rect->x0 = util_format_get_nblocksx(pt->format, x) << mt->ms_x; rect->y0 = util_format_get_nblocksy(pt->format, y) << mt->ms_y; rect->x1 = rect->x0 + (w << mt->ms_x); rect->y1 = rect->y0 + (h << mt->ms_y); } void nv30_resource_copy_region(struct pipe_context *pipe, struct pipe_resource *dstres, unsigned dst_level, unsigned dstx, unsigned dsty, unsigned dstz, struct pipe_resource *srcres, unsigned src_level, const struct pipe_box *src_box) { struct nv30_context *nv30 = nv30_context(pipe); struct nv30_rect src, dst; if (dstres->target == PIPE_BUFFER && srcres->target == PIPE_BUFFER) { util_resource_copy_region(pipe, dstres, dst_level, dstx, dsty, dstz, srcres, src_level, src_box); return; } define_rect(srcres, src_level, src_box->z, src_box->x, src_box->y, src_box->width, src_box->height, &src); define_rect(dstres, dst_level, dstz, dstx, dsty, src_box->width, src_box->height, &dst); nv30_transfer_rect(nv30, NEAREST, &src, &dst); } void nv30_resource_resolve(struct pipe_context *pipe, const struct pipe_resolve_info *info) { struct nv30_context *nv30 = nv30_context(pipe); struct nv30_rect src, dst; define_rect(info->src.res, 0, 0, info->src.x0, info->src.y0, info->src.x1 - info->src.x0, info->src.y1 - info->src.y0, &src); define_rect(info->dst.surface->texture, info->dst.surface->u.tex.level, info->dst.surface->u.tex.first_layer, info->dst.x0, info->dst.y0, info->dst.x1 - info->dst.x0, info->dst.y1 - info->dst.y0, &dst); nv30_transfer_rect(nv30, BILINEAR, &src, &dst); } static struct pipe_transfer * nv30_miptree_transfer_new(struct pipe_context *pipe, struct pipe_resource *pt, unsigned level, unsigned usage, const struct pipe_box *box) { struct nv30_context *nv30 = nv30_context(pipe); struct nouveau_device *dev = nv30->screen->base.device; struct nv30_transfer *tx; int ret; tx = CALLOC_STRUCT(nv30_transfer); if (!tx) return NULL; pipe_resource_reference(&tx->base.resource, pt); tx->base.level = level; tx->base.usage = usage; tx->base.box = *box; tx->base.stride = util_format_get_nblocksx(pt->format, box->width) * util_format_get_blocksize(pt->format); tx->base.layer_stride = util_format_get_nblocksy(pt->format, box->height) * tx->base.stride; tx->nblocksx = util_format_get_nblocksx(pt->format, box->width); tx->nblocksy = util_format_get_nblocksy(pt->format, box->height); define_rect(pt, level, box->z, box->x, box->y, tx->nblocksx, tx->nblocksy, &tx->img); ret = nouveau_bo_new(dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0, tx->base.layer_stride, NULL, &tx->tmp.bo); if (ret) { pipe_resource_reference(&tx->base.resource, NULL); FREE(tx); return NULL; } tx->tmp.domain = NOUVEAU_BO_GART; tx->tmp.offset = 0; tx->tmp.pitch = tx->base.stride; tx->tmp.cpp = tx->img.cpp; tx->tmp.w = tx->nblocksx; tx->tmp.h = tx->nblocksy; tx->tmp.d = 1; tx->tmp.x0 = 0; tx->tmp.y0 = 0; tx->tmp.x1 = tx->tmp.w; tx->tmp.y1 = tx->tmp.h; tx->tmp.z = 0; if (usage & PIPE_TRANSFER_READ) nv30_transfer_rect(nv30, NEAREST, &tx->img, &tx->tmp); return &tx->base; } static void nv30_miptree_transfer_del(struct pipe_context *pipe, struct pipe_transfer *ptx) { struct nv30_context *nv30 = nv30_context(pipe); struct nv30_transfer *tx = nv30_transfer(ptx); if (ptx->usage & PIPE_TRANSFER_WRITE) nv30_transfer_rect(nv30, NEAREST, &tx->tmp, &tx->img); nouveau_bo_ref(NULL, &tx->tmp.bo); pipe_resource_reference(&ptx->resource, NULL); FREE(tx); } static void * nv30_miptree_transfer_map(struct pipe_context *pipe, struct pipe_transfer *ptx) { struct nv30_context *nv30 = nv30_context(pipe); struct nv30_transfer *tx = nv30_transfer(ptx); unsigned access = 0; int ret; if (tx->tmp.bo->map) return tx->tmp.bo->map; if (ptx->usage & PIPE_TRANSFER_READ) access |= NOUVEAU_BO_RD; if (ptx->usage & PIPE_TRANSFER_WRITE) access |= NOUVEAU_BO_WR; ret = nouveau_bo_map(tx->tmp.bo, access, nv30->base.client); if (ret) return NULL; return tx->tmp.bo->map; } static void nv30_miptree_transfer_unmap(struct pipe_context *pipe, struct pipe_transfer *ptx) { } const struct u_resource_vtbl nv30_miptree_vtbl = { nv30_miptree_get_handle, nv30_miptree_destroy, nv30_miptree_transfer_new, nv30_miptree_transfer_del, nv30_miptree_transfer_map, u_default_transfer_flush_region, nv30_miptree_transfer_unmap, u_default_transfer_inline_write }; struct pipe_resource * nv30_miptree_create(struct pipe_screen *pscreen, const struct pipe_resource *tmpl) { struct nouveau_device *dev = nouveau_screen(pscreen)->device; struct nv30_miptree *mt = CALLOC_STRUCT(nv30_miptree); struct pipe_resource *pt = &mt->base.base; unsigned blocksz, size; unsigned w, h, d, l; int ret; switch (tmpl->nr_samples) { case 4: mt->ms_mode = 0x00004000; mt->ms_x = 1; mt->ms_y = 1; break; case 2: mt->ms_mode = 0x00003000; mt->ms_x = 1; mt->ms_y = 0; break; default: mt->ms_mode = 0x00000000; mt->ms_x = 0; mt->ms_y = 0; break; } mt->base.vtbl = &nv30_miptree_vtbl; *pt = *tmpl; pipe_reference_init(&pt->reference, 1); pt->screen = pscreen; w = pt->width0 << mt->ms_x; h = pt->height0 << mt->ms_y; d = (pt->target == PIPE_TEXTURE_3D) ? pt->depth0 : 1; blocksz = util_format_get_blocksize(pt->format); if ((pt->target == PIPE_TEXTURE_RECT) || !util_is_power_of_two(pt->width0) || !util_is_power_of_two(pt->height0) || !util_is_power_of_two(pt->depth0) || util_format_is_compressed(pt->format) || util_format_is_float(pt->format) || mt->ms_mode) { mt->uniform_pitch = util_format_get_nblocksx(pt->format, w) * blocksz; mt->uniform_pitch = align(mt->uniform_pitch, 64); } if (!mt->uniform_pitch) mt->swizzled = TRUE; size = 0; for (l = 0; l <= pt->last_level; l++) { struct nv30_miptree_level *lvl = &mt->level[l]; unsigned nbx = util_format_get_nblocksx(pt->format, w); unsigned nby = util_format_get_nblocksx(pt->format, h); lvl->offset = size; lvl->pitch = mt->uniform_pitch; if (!lvl->pitch) lvl->pitch = nbx * blocksz; lvl->zslice_size = lvl->pitch * nby; size += lvl->zslice_size * d; w = u_minify(w, 1); h = u_minify(h, 1); d = u_minify(d, 1); } mt->layer_size = size; if (pt->target == PIPE_TEXTURE_CUBE) { if (!mt->uniform_pitch) mt->layer_size = align(mt->layer_size, 128); size = mt->layer_size * 6; } ret = nouveau_bo_new(dev, NOUVEAU_BO_VRAM, 256, size, NULL, &mt->base.bo); if (ret) { FREE(mt); return NULL; } mt->base.domain = NOUVEAU_BO_VRAM; return &mt->base.base; } struct pipe_resource * nv30_miptree_from_handle(struct pipe_screen *pscreen, const struct pipe_resource *tmpl, struct winsys_handle *handle) { struct nv30_miptree *mt; unsigned stride; /* only supports 2D, non-mipmapped textures for the moment */ if ((tmpl->target != PIPE_TEXTURE_2D && tmpl->target != PIPE_TEXTURE_RECT) || tmpl->last_level != 0 || tmpl->depth0 != 1 || tmpl->array_size > 1) return NULL; mt = CALLOC_STRUCT(nv30_miptree); if (!mt) return NULL; mt->base.bo = nouveau_screen_bo_from_handle(pscreen, handle, &stride); if (mt->base.bo == NULL) { FREE(mt); return NULL; } mt->base.base = *tmpl; mt->base.vtbl = &nv30_miptree_vtbl; pipe_reference_init(&mt->base.base.reference, 1); mt->base.base.screen = pscreen; mt->uniform_pitch = stride; mt->level[0].pitch = mt->uniform_pitch; mt->level[0].offset = 0; /* no need to adjust bo reference count */ return &mt->base.base; } struct pipe_surface * nv30_miptree_surface_new(struct pipe_context *pipe, struct pipe_resource *pt, const struct pipe_surface *tmpl) { struct nv30_miptree *mt = nv30_miptree(pt); /* guaranteed */ struct nv30_surface *ns; struct pipe_surface *ps; struct nv30_miptree_level *lvl = &mt->level[tmpl->u.tex.level]; ns = CALLOC_STRUCT(nv30_surface); if (!ns) return NULL; ps = &ns->base; pipe_reference_init(&ps->reference, 1); pipe_resource_reference(&ps->texture, pt); ps->context = pipe; ps->format = tmpl->format; ps->usage = tmpl->usage; ps->u.tex.level = tmpl->u.tex.level; ps->u.tex.first_layer = tmpl->u.tex.first_layer; ps->u.tex.last_layer = tmpl->u.tex.last_layer; ns->width = u_minify(pt->width0, ps->u.tex.level); ns->height = u_minify(pt->height0, ps->u.tex.level); ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1; ns->offset = layer_offset(pt, ps->u.tex.level, ps->u.tex.first_layer); if (mt->swizzled) ns->pitch = 4096; /* random, just something the hw won't reject.. */ else ns->pitch = lvl->pitch; /* comment says there are going to be removed, but they're used by the st */ ps->width = ns->width; ps->height = ns->height; return ps; } void nv30_miptree_surface_del(struct pipe_context *pipe, struct pipe_surface *ps) { struct nv30_surface *ns = nv30_surface(ps); pipe_resource_reference(&ps->texture, NULL); FREE(ns); }