nv50_miptree.c revision cad17554c4b121c03e188dd0281718a52d603a15
1/* 2 * Copyright 2008 Ben Skeggs 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 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 */ 22 23#include "pipe/p_state.h" 24#include "pipe/p_defines.h" 25#include "util/u_inlines.h" 26#include "util/u_format.h" 27 28#include "nv50_context.h" 29#include "nv50_resource.h" 30#include "nv50_transfer.h" 31 32static INLINE uint32_t 33nv50_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz) 34{ 35 return nvc0_tex_choose_tile_dims(nx, ny * 2, nz) >> 4; 36} 37 38static uint32_t 39nv50_mt_choose_storage_type(struct nv50_miptree *mt, boolean compressed) 40{ 41 const unsigned ms = util_logbase2(mt->base.base.nr_samples); 42 43 uint32_t tile_flags; 44 45 if (mt->base.base.bind & PIPE_BIND_CURSOR) 46 return NOUVEAU_BO_TILE_SCANOUT; 47 48 switch (mt->base.base.format) { 49 case PIPE_FORMAT_Z16_UNORM: 50 tile_flags = 0x6c00 + (ms << 8); 51 break; 52 case PIPE_FORMAT_S8_USCALED_Z24_UNORM: 53 tile_flags = 0x1800 + (ms << 8); 54 break; 55 case PIPE_FORMAT_Z24X8_UNORM: 56 case PIPE_FORMAT_Z24_UNORM_S8_USCALED: 57 tile_flags = 0x22800 + (ms << 8); 58 break; 59 case PIPE_FORMAT_Z32_FLOAT_S8X24_USCALED: 60 tile_flags = 0x6000 + (ms << 8); 61 break; 62 default: 63 switch (util_format_get_blocksizebits(mt->base.base.format)) { 64 case 128: 65 assert(ms < 3); 66 tile_flags = 0x7400; 67 break; 68 case 64: 69 switch (ms) { 70 case 2: tile_flags = 0x17c00; break; 71 case 3: tile_flags = 0x17d00; break; 72 default: 73 tile_flags = 0x7000; 74 break; 75 } 76 break; 77 case 32: 78 if (mt->base.base.bind & PIPE_BIND_SCANOUT) { 79 assert(ms == 0); 80 tile_flags = 0x7a00; 81 } else { 82 switch (ms) { 83 case 2: tile_flags = 0x17800; break; 84 case 3: tile_flags = 0x17900; break; 85 default: 86 tile_flags = 0x7000; 87 break; 88 } 89 } 90 break; 91 case 16: 92 case 8: 93 tile_flags = 0x7000; 94 break; 95 default: 96 return 0; 97 } 98 if (mt->base.base.bind & PIPE_BIND_CURSOR) 99 tile_flags = 0; 100 } 101 102 if (mt->base.base.bind & (PIPE_BIND_SCANOUT | PIPE_BIND_CURSOR)) 103 tile_flags |= NOUVEAU_BO_TILE_SCANOUT; 104 105 if (!compressed) 106 tile_flags &= ~0x30000; 107 108 return tile_flags; 109} 110 111void 112nv50_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt) 113{ 114 struct nv50_miptree *mt = nv50_miptree(pt); 115 116 nouveau_screen_bo_release(pscreen, mt->base.bo); 117 118 FREE(mt); 119} 120 121boolean 122nv50_miptree_get_handle(struct pipe_screen *pscreen, 123 struct pipe_resource *pt, 124 struct winsys_handle *whandle) 125{ 126 struct nv50_miptree *mt = nv50_miptree(pt); 127 unsigned stride; 128 129 if (!mt || !mt->base.bo) 130 return FALSE; 131 132 stride = util_format_get_stride(mt->base.base.format, 133 mt->base.base.width0); 134 135 return nouveau_screen_bo_get_handle(pscreen, 136 mt->base.bo, 137 stride, 138 whandle); 139} 140 141const struct u_resource_vtbl nv50_miptree_vtbl = 142{ 143 nv50_miptree_get_handle, /* get_handle */ 144 nv50_miptree_destroy, /* resource_destroy */ 145 nv50_miptree_transfer_new, /* get_transfer */ 146 nv50_miptree_transfer_del, /* transfer_destroy */ 147 nv50_miptree_transfer_map, /* transfer_map */ 148 u_default_transfer_flush_region, /* transfer_flush_region */ 149 nv50_miptree_transfer_unmap, /* transfer_unmap */ 150 u_default_transfer_inline_write /* transfer_inline_write */ 151}; 152 153static INLINE boolean 154nv50_miptree_init_ms_mode(struct nv50_miptree *mt) 155{ 156 switch (mt->base.base.nr_samples) { 157 case 8: 158 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS8; 159 mt->ms_x = 2; 160 mt->ms_y = 1; 161 break; 162 case 4: 163 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS4; 164 mt->ms_x = 1; 165 mt->ms_y = 1; 166 break; 167 case 2: 168 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS2; 169 mt->ms_x = 1; 170 break; 171 case 1: 172 case 0: 173 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS1; 174 break; 175 default: 176 NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples); 177 return FALSE; 178 } 179 return TRUE; 180} 181 182boolean 183nv50_miptree_init_layout_linear(struct nv50_miptree *mt) 184{ 185 struct pipe_resource *pt = &mt->base.base; 186 187 if (util_format_is_depth_or_stencil(pt->format)) 188 return FALSE; 189 190 if ((pt->last_level > 0) || (pt->depth0 > 1) || (pt->array_size > 1)) 191 return FALSE; 192 if (mt->ms_x | mt->ms_y) 193 return FALSE; 194 195 mt->level[0].pitch = align(pt->width0, 64); 196 197 mt->total_size = mt->level[0].pitch * pt->height0; 198 199 return TRUE; 200} 201 202static void 203nv50_miptree_init_layout_tiled(struct nv50_miptree *mt) 204{ 205 struct pipe_resource *pt = &mt->base.base; 206 unsigned w, h, d, l; 207 const unsigned blocksize = util_format_get_blocksize(pt->format); 208 209 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 210 211 w = pt->width0 << mt->ms_x; 212 h = pt->height0 << mt->ms_y; 213 214 /* For 3D textures, a mipmap is spanned by all the layers, for array 215 * textures and cube maps, each layer contains its own mipmaps. 216 */ 217 d = mt->layout_3d ? pt->depth0 : 1; 218 219 for (l = 0; l <= pt->last_level; ++l) { 220 struct nv50_miptree_level *lvl = &mt->level[l]; 221 unsigned tsx, tsy, tsz; 222 unsigned nbx = util_format_get_nblocksx(pt->format, w); 223 unsigned nby = util_format_get_nblocksy(pt->format, h); 224 225 lvl->offset = mt->total_size; 226 227 lvl->tile_mode = nv50_tex_choose_tile_dims(nbx, nby, d); 228 229 tsx = NV50_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */ 230 tsy = NV50_TILE_SIZE_Y(lvl->tile_mode); 231 tsz = NV50_TILE_SIZE_Z(lvl->tile_mode); 232 233 lvl->pitch = align(nbx * blocksize, tsx); 234 235 mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz); 236 237 w = u_minify(w, 1); 238 h = u_minify(h, 1); 239 d = u_minify(d, 1); 240 } 241 242 if (pt->array_size > 1) { 243 mt->layer_stride = align(mt->total_size, 244 NV50_TILE_SIZE(mt->level[0].tile_mode)); 245 mt->total_size = mt->layer_stride * pt->array_size; 246 } 247} 248 249struct pipe_resource * 250nv50_miptree_create(struct pipe_screen *pscreen, 251 const struct pipe_resource *templ) 252{ 253 struct nouveau_device *dev = nouveau_screen(pscreen)->device; 254 struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree); 255 struct pipe_resource *pt = &mt->base.base; 256 int ret; 257 uint32_t tile_flags; 258 259 if (!mt) 260 return NULL; 261 262 mt->base.vtbl = &nv50_miptree_vtbl; 263 *pt = *templ; 264 pipe_reference_init(&pt->reference, 1); 265 pt->screen = pscreen; 266 267 tile_flags = nv50_mt_choose_storage_type(mt, TRUE); 268 269 if (!nv50_miptree_init_ms_mode(mt)) { 270 FREE(mt); 271 return NULL; 272 } 273 274 if (tile_flags & NOUVEAU_BO_TILE_LAYOUT_MASK) { 275 nv50_miptree_init_layout_tiled(mt); 276 } else 277 if (!nv50_miptree_init_layout_linear(mt)) { 278 FREE(mt); 279 return NULL; 280 } 281 282 ret = nouveau_bo_new_tile(dev, NOUVEAU_BO_VRAM, 4096, 283 mt->total_size, 284 mt->level[0].tile_mode, tile_flags, 285 &mt->base.bo); 286 if (ret) { 287 FREE(mt); 288 return NULL; 289 } 290 mt->base.domain = NOUVEAU_BO_VRAM; 291 292 return pt; 293} 294 295struct pipe_resource * 296nv50_miptree_from_handle(struct pipe_screen *pscreen, 297 const struct pipe_resource *templ, 298 struct winsys_handle *whandle) 299{ 300 struct nv50_miptree *mt; 301 unsigned stride; 302 303 /* only supports 2D, non-mipmapped textures for the moment */ 304 if ((templ->target != PIPE_TEXTURE_2D && 305 templ->target != PIPE_TEXTURE_RECT) || 306 templ->last_level != 0 || 307 templ->depth0 != 1 || 308 templ->array_size > 1) 309 return NULL; 310 311 mt = CALLOC_STRUCT(nv50_miptree); 312 if (!mt) 313 return NULL; 314 315 mt->base.bo = nouveau_screen_bo_from_handle(pscreen, whandle, &stride); 316 if (mt->base.bo == NULL) { 317 FREE(mt); 318 return NULL; 319 } 320 321 mt->base.base = *templ; 322 mt->base.vtbl = &nv50_miptree_vtbl; 323 pipe_reference_init(&mt->base.base.reference, 1); 324 mt->base.base.screen = pscreen; 325 mt->level[0].pitch = stride; 326 mt->level[0].offset = 0; 327 mt->level[0].tile_mode = mt->base.bo->tile_mode; 328 329 /* no need to adjust bo reference count */ 330 return &mt->base.base; 331} 332 333 334/* Offset of zslice @z from start of level @l. */ 335INLINE unsigned 336nv50_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z) 337{ 338 const struct pipe_resource *pt = &mt->base.base; 339 340 unsigned tds = NV50_TILE_SHIFT_Z(mt->level[l].tile_mode); 341 unsigned ths = NV50_TILE_SHIFT_Y(mt->level[l].tile_mode); 342 343 unsigned nby = util_format_get_nblocksy(pt->format, 344 u_minify(pt->height0, l)); 345 346 /* to next 2D tile slice within a 3D tile */ 347 unsigned stride_2d = NV50_TILE_SIZE_2D(mt->level[l].tile_mode); 348 349 /* to slice in the next (in z direction) 3D tile */ 350 unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds; 351 352 return (z & ((1 << tds) - 1)) * stride_2d + (z >> tds) * stride_3d; 353} 354 355/* Surface functions. 356 */ 357 358struct nv50_surface * 359nv50_surface_from_miptree(struct nv50_miptree *mt, 360 const struct pipe_surface *templ) 361{ 362 struct pipe_surface *ps; 363 struct nv50_surface *ns = CALLOC_STRUCT(nv50_surface); 364 if (!ns) 365 return NULL; 366 ps = &ns->base; 367 368 pipe_reference_init(&ps->reference, 1); 369 pipe_resource_reference(&ps->texture, &mt->base.base); 370 371 ps->format = templ->format; 372 ps->usage = templ->usage; 373 ps->u.tex.level = templ->u.tex.level; 374 ps->u.tex.first_layer = templ->u.tex.first_layer; 375 ps->u.tex.last_layer = templ->u.tex.last_layer; 376 377 ns->width = u_minify(mt->base.base.width0, ps->u.tex.level); 378 ns->height = u_minify(mt->base.base.height0, ps->u.tex.level); 379 ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1; 380 ns->offset = mt->level[templ->u.tex.level].offset; 381 382 /* comment says there are going to be removed, but they're used by the st */ 383 ps->width = ns->width; 384 ps->height = ns->height; 385 386 ns->width <<= mt->ms_x; 387 ns->height <<= mt->ms_y; 388 389 return ns; 390} 391 392struct pipe_surface * 393nv50_miptree_surface_new(struct pipe_context *pipe, 394 struct pipe_resource *pt, 395 const struct pipe_surface *templ) 396{ 397 struct nv50_miptree *mt = nv50_miptree(pt); 398 struct nv50_surface *ns = nv50_surface_from_miptree(mt, templ); 399 if (!ns) 400 return NULL; 401 ns->base.context = pipe; 402 403 if (ns->base.u.tex.first_layer) { 404 const unsigned l = ns->base.u.tex.level; 405 const unsigned z = ns->base.u.tex.first_layer; 406 407 if (mt->layout_3d) { 408 ns->offset += nv50_mt_zslice_offset(mt, l, z); 409 410 if (z & (NV50_TILE_SIZE_Z(mt->level[l].tile_mode) - 1)) 411 NOUVEAU_ERR("Creating unsupported 3D surface !\n"); 412 } else { 413 ns->offset += mt->layer_stride * z; 414 } 415 } 416 417 return &ns->base; 418} 419 420void 421nv50_miptree_surface_del(struct pipe_context *pipe, struct pipe_surface *ps) 422{ 423 struct nv50_surface *s = nv50_surface(ps); 424 425 pipe_resource_reference(&ps->texture, NULL); 426 427 FREE(s); 428} 429