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 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE 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/nv50_context.h" 29 #include "nv50/nv50_resource.h" 30 31 uint32_t 32 nv50_tex_choose_tile_dims_helper(unsigned nx, unsigned ny, unsigned nz, 33 bool is_3d) 34 { 35 uint32_t tile_mode = 0x000; 36 37 if (ny > 64) tile_mode = 0x040; /* height 128 tiles */ 38 else 39 if (ny > 32) tile_mode = 0x030; /* height 64 tiles */ 40 else 41 if (ny > 16) tile_mode = 0x020; /* height 32 tiles */ 42 else 43 if (ny > 8) tile_mode = 0x010; /* height 16 tiles */ 44 45 if (!is_3d) 46 return tile_mode; 47 else 48 if (tile_mode > 0x020) 49 tile_mode = 0x020; 50 51 if (nz > 16 && tile_mode < 0x020) 52 return tile_mode | 0x500; /* depth 32 tiles */ 53 if (nz > 8) return tile_mode | 0x400; /* depth 16 tiles */ 54 if (nz > 4) return tile_mode | 0x300; /* depth 8 tiles */ 55 if (nz > 2) return tile_mode | 0x200; /* depth 4 tiles */ 56 if (nz > 1) return tile_mode | 0x100; /* depth 2 tiles */ 57 58 return tile_mode; 59 } 60 61 static uint32_t 62 nv50_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz, bool is_3d) 63 { 64 return nv50_tex_choose_tile_dims_helper(nx, ny * 2, nz, is_3d); 65 } 66 67 static uint32_t 68 nv50_mt_choose_storage_type(struct nv50_miptree *mt, bool compressed) 69 { 70 const unsigned ms = util_logbase2(mt->base.base.nr_samples); 71 uint32_t tile_flags; 72 73 if (unlikely(mt->base.base.flags & NOUVEAU_RESOURCE_FLAG_LINEAR)) 74 return 0; 75 if (unlikely(mt->base.base.bind & PIPE_BIND_CURSOR)) 76 return 0; 77 78 switch (mt->base.base.format) { 79 case PIPE_FORMAT_Z16_UNORM: 80 tile_flags = 0x6c + ms; 81 break; 82 case PIPE_FORMAT_X8Z24_UNORM: 83 case PIPE_FORMAT_S8X24_UINT: 84 case PIPE_FORMAT_S8_UINT_Z24_UNORM: 85 tile_flags = 0x18 + ms; 86 break; 87 case PIPE_FORMAT_X24S8_UINT: 88 case PIPE_FORMAT_Z24X8_UNORM: 89 case PIPE_FORMAT_Z24_UNORM_S8_UINT: 90 tile_flags = 0x128 + ms; 91 break; 92 case PIPE_FORMAT_Z32_FLOAT: 93 tile_flags = 0x40 + ms; 94 break; 95 case PIPE_FORMAT_X32_S8X24_UINT: 96 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: 97 tile_flags = 0x60 + ms; 98 break; 99 default: 100 /* Most color formats don't work with compression. */ 101 compressed = false; 102 /* fallthrough */ 103 case PIPE_FORMAT_R8G8B8A8_UNORM: 104 case PIPE_FORMAT_R8G8B8A8_SRGB: 105 case PIPE_FORMAT_R8G8B8X8_UNORM: 106 case PIPE_FORMAT_R8G8B8X8_SRGB: 107 case PIPE_FORMAT_B8G8R8A8_UNORM: 108 case PIPE_FORMAT_B8G8R8A8_SRGB: 109 case PIPE_FORMAT_B8G8R8X8_UNORM: 110 case PIPE_FORMAT_B8G8R8X8_SRGB: 111 case PIPE_FORMAT_R10G10B10A2_UNORM: 112 case PIPE_FORMAT_B10G10R10A2_UNORM: 113 case PIPE_FORMAT_R16G16B16A16_FLOAT: 114 case PIPE_FORMAT_R16G16B16X16_FLOAT: 115 case PIPE_FORMAT_R11G11B10_FLOAT: 116 switch (util_format_get_blocksizebits(mt->base.base.format)) { 117 case 128: 118 assert(ms < 3); 119 tile_flags = 0x74; 120 break; 121 case 64: 122 switch (ms) { 123 case 2: tile_flags = 0xfc; break; 124 case 3: tile_flags = 0xfd; break; 125 default: 126 tile_flags = 0x70; 127 break; 128 } 129 break; 130 case 32: 131 if (mt->base.base.bind & PIPE_BIND_SCANOUT) { 132 assert(ms == 0); 133 tile_flags = 0x7a; 134 } else { 135 switch (ms) { 136 case 2: tile_flags = 0xf8; break; 137 case 3: tile_flags = 0xf9; break; 138 default: 139 tile_flags = 0x70; 140 break; 141 } 142 } 143 break; 144 case 16: 145 case 8: 146 tile_flags = 0x70; 147 break; 148 default: 149 return 0; 150 } 151 if (mt->base.base.bind & PIPE_BIND_CURSOR) 152 tile_flags = 0; 153 } 154 155 if (!compressed) 156 tile_flags &= ~0x180; 157 158 return tile_flags; 159 } 160 161 void 162 nv50_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt) 163 { 164 struct nv50_miptree *mt = nv50_miptree(pt); 165 166 if (mt->base.fence && mt->base.fence->state < NOUVEAU_FENCE_STATE_FLUSHED) 167 nouveau_fence_work(mt->base.fence, nouveau_fence_unref_bo, mt->base.bo); 168 else 169 nouveau_bo_ref(NULL, &mt->base.bo); 170 171 nouveau_fence_ref(NULL, &mt->base.fence); 172 nouveau_fence_ref(NULL, &mt->base.fence_wr); 173 174 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, -1); 175 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_bytes, 176 -(uint64_t)mt->total_size); 177 178 FREE(mt); 179 } 180 181 boolean 182 nv50_miptree_get_handle(struct pipe_screen *pscreen, 183 struct pipe_resource *pt, 184 struct winsys_handle *whandle) 185 { 186 struct nv50_miptree *mt = nv50_miptree(pt); 187 unsigned stride; 188 189 if (!mt || !mt->base.bo) 190 return false; 191 192 stride = mt->level[0].pitch; 193 194 return nouveau_screen_bo_get_handle(pscreen, 195 mt->base.bo, 196 stride, 197 whandle); 198 } 199 200 const struct u_resource_vtbl nv50_miptree_vtbl = 201 { 202 nv50_miptree_get_handle, /* get_handle */ 203 nv50_miptree_destroy, /* resource_destroy */ 204 nv50_miptree_transfer_map, /* transfer_map */ 205 u_default_transfer_flush_region, /* transfer_flush_region */ 206 nv50_miptree_transfer_unmap, /* transfer_unmap */ 207 }; 208 209 static inline bool 210 nv50_miptree_init_ms_mode(struct nv50_miptree *mt) 211 { 212 switch (mt->base.base.nr_samples) { 213 case 8: 214 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS8; 215 mt->ms_x = 2; 216 mt->ms_y = 1; 217 break; 218 case 4: 219 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS4; 220 mt->ms_x = 1; 221 mt->ms_y = 1; 222 break; 223 case 2: 224 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS2; 225 mt->ms_x = 1; 226 break; 227 case 1: 228 case 0: 229 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS1; 230 break; 231 default: 232 NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples); 233 return false; 234 } 235 return true; 236 } 237 238 bool 239 nv50_miptree_init_layout_linear(struct nv50_miptree *mt, unsigned pitch_align) 240 { 241 struct pipe_resource *pt = &mt->base.base; 242 const unsigned blocksize = util_format_get_blocksize(pt->format); 243 unsigned h = pt->height0; 244 245 if (util_format_is_depth_or_stencil(pt->format)) 246 return false; 247 248 if ((pt->last_level > 0) || (pt->depth0 > 1) || (pt->array_size > 1)) 249 return false; 250 if (mt->ms_x | mt->ms_y) 251 return false; 252 253 mt->level[0].pitch = align(pt->width0 * blocksize, pitch_align); 254 255 /* Account for very generous prefetch (allocate size as if tiled). */ 256 h = MAX2(h, 8); 257 h = util_next_power_of_two(h); 258 259 mt->total_size = mt->level[0].pitch * h; 260 261 return true; 262 } 263 264 static void 265 nv50_miptree_init_layout_video(struct nv50_miptree *mt) 266 { 267 const struct pipe_resource *pt = &mt->base.base; 268 const unsigned blocksize = util_format_get_blocksize(pt->format); 269 270 assert(pt->last_level == 0); 271 assert(mt->ms_x == 0 && mt->ms_y == 0); 272 assert(!util_format_is_compressed(pt->format)); 273 274 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 275 276 mt->level[0].tile_mode = 0x20; 277 mt->level[0].pitch = align(pt->width0 * blocksize, 64); 278 mt->total_size = align(pt->height0, 16) * mt->level[0].pitch * (mt->layout_3d ? pt->depth0 : 1); 279 280 if (pt->array_size > 1) { 281 mt->layer_stride = align(mt->total_size, NV50_TILE_SIZE(0x20)); 282 mt->total_size = mt->layer_stride * pt->array_size; 283 } 284 } 285 286 static void 287 nv50_miptree_init_layout_tiled(struct nv50_miptree *mt) 288 { 289 struct pipe_resource *pt = &mt->base.base; 290 unsigned w, h, d, l; 291 const unsigned blocksize = util_format_get_blocksize(pt->format); 292 293 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 294 295 w = pt->width0 << mt->ms_x; 296 h = pt->height0 << mt->ms_y; 297 298 /* For 3D textures, a mipmap is spanned by all the layers, for array 299 * textures and cube maps, each layer contains its own mipmaps. 300 */ 301 d = mt->layout_3d ? pt->depth0 : 1; 302 303 for (l = 0; l <= pt->last_level; ++l) { 304 struct nv50_miptree_level *lvl = &mt->level[l]; 305 unsigned tsx, tsy, tsz; 306 unsigned nbx = util_format_get_nblocksx(pt->format, w); 307 unsigned nby = util_format_get_nblocksy(pt->format, h); 308 309 lvl->offset = mt->total_size; 310 311 lvl->tile_mode = nv50_tex_choose_tile_dims(nbx, nby, d, mt->layout_3d); 312 313 tsx = NV50_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */ 314 tsy = NV50_TILE_SIZE_Y(lvl->tile_mode); 315 tsz = NV50_TILE_SIZE_Z(lvl->tile_mode); 316 317 lvl->pitch = align(nbx * blocksize, tsx); 318 319 mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz); 320 321 w = u_minify(w, 1); 322 h = u_minify(h, 1); 323 d = u_minify(d, 1); 324 } 325 326 if (pt->array_size > 1) { 327 mt->layer_stride = align(mt->total_size, 328 NV50_TILE_SIZE(mt->level[0].tile_mode)); 329 mt->total_size = mt->layer_stride * pt->array_size; 330 } 331 } 332 333 struct pipe_resource * 334 nv50_miptree_create(struct pipe_screen *pscreen, 335 const struct pipe_resource *templ) 336 { 337 struct nouveau_device *dev = nouveau_screen(pscreen)->device; 338 struct nouveau_drm *drm = nouveau_screen(pscreen)->drm; 339 struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree); 340 struct pipe_resource *pt = &mt->base.base; 341 bool compressed = drm->version >= 0x01000101; 342 int ret; 343 union nouveau_bo_config bo_config; 344 uint32_t bo_flags; 345 346 if (!mt) 347 return NULL; 348 349 mt->base.vtbl = &nv50_miptree_vtbl; 350 *pt = *templ; 351 pipe_reference_init(&pt->reference, 1); 352 pt->screen = pscreen; 353 354 if (pt->bind & PIPE_BIND_LINEAR) 355 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR; 356 357 bo_config.nv50.memtype = nv50_mt_choose_storage_type(mt, compressed); 358 359 if (!nv50_miptree_init_ms_mode(mt)) { 360 FREE(mt); 361 return NULL; 362 } 363 364 if (unlikely(pt->flags & NV50_RESOURCE_FLAG_VIDEO)) { 365 nv50_miptree_init_layout_video(mt); 366 if (pt->flags & NV50_RESOURCE_FLAG_NOALLOC) { 367 /* BO allocation done by client */ 368 return pt; 369 } 370 } else 371 if (bo_config.nv50.memtype != 0) { 372 nv50_miptree_init_layout_tiled(mt); 373 } else 374 if (!nv50_miptree_init_layout_linear(mt, 64)) { 375 FREE(mt); 376 return NULL; 377 } 378 bo_config.nv50.tile_mode = mt->level[0].tile_mode; 379 380 if (!bo_config.nv50.memtype && (pt->bind & PIPE_BIND_SHARED)) 381 mt->base.domain = NOUVEAU_BO_GART; 382 else 383 mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen)); 384 385 bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP; 386 if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET)) 387 bo_flags |= NOUVEAU_BO_CONTIG; 388 389 ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config, 390 &mt->base.bo); 391 if (ret) { 392 FREE(mt); 393 return NULL; 394 } 395 mt->base.address = mt->base.bo->offset; 396 397 return pt; 398 } 399 400 struct pipe_resource * 401 nv50_miptree_from_handle(struct pipe_screen *pscreen, 402 const struct pipe_resource *templ, 403 struct winsys_handle *whandle) 404 { 405 struct nv50_miptree *mt; 406 unsigned stride; 407 408 /* only supports 2D, non-mipmapped textures for the moment */ 409 if ((templ->target != PIPE_TEXTURE_2D && 410 templ->target != PIPE_TEXTURE_RECT) || 411 templ->last_level != 0 || 412 templ->depth0 != 1 || 413 templ->array_size > 1) 414 return NULL; 415 416 mt = CALLOC_STRUCT(nv50_miptree); 417 if (!mt) 418 return NULL; 419 420 mt->base.bo = nouveau_screen_bo_from_handle(pscreen, whandle, &stride); 421 if (mt->base.bo == NULL) { 422 FREE(mt); 423 return NULL; 424 } 425 mt->base.domain = mt->base.bo->flags & NOUVEAU_BO_APER; 426 mt->base.address = mt->base.bo->offset; 427 428 mt->base.base = *templ; 429 mt->base.vtbl = &nv50_miptree_vtbl; 430 pipe_reference_init(&mt->base.base.reference, 1); 431 mt->base.base.screen = pscreen; 432 mt->level[0].pitch = stride; 433 mt->level[0].offset = 0; 434 mt->level[0].tile_mode = mt->base.bo->config.nv50.tile_mode; 435 436 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1); 437 438 /* no need to adjust bo reference count */ 439 return &mt->base.base; 440 } 441 442 443 /* Offset of zslice @z from start of level @l. */ 444 inline unsigned 445 nv50_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z) 446 { 447 const struct pipe_resource *pt = &mt->base.base; 448 449 unsigned tds = NV50_TILE_SHIFT_Z(mt->level[l].tile_mode); 450 unsigned ths = NV50_TILE_SHIFT_Y(mt->level[l].tile_mode); 451 452 unsigned nby = util_format_get_nblocksy(pt->format, 453 u_minify(pt->height0, l)); 454 455 /* to next 2D tile slice within a 3D tile */ 456 unsigned stride_2d = NV50_TILE_SIZE_2D(mt->level[l].tile_mode); 457 458 /* to slice in the next (in z direction) 3D tile */ 459 unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds; 460 461 return (z & ((1 << tds) - 1)) * stride_2d + (z >> tds) * stride_3d; 462 } 463 464 /* Surface functions. 465 */ 466 467 struct nv50_surface * 468 nv50_surface_from_miptree(struct nv50_miptree *mt, 469 const struct pipe_surface *templ) 470 { 471 struct pipe_surface *ps; 472 struct nv50_surface *ns = CALLOC_STRUCT(nv50_surface); 473 if (!ns) 474 return NULL; 475 ps = &ns->base; 476 477 pipe_reference_init(&ps->reference, 1); 478 pipe_resource_reference(&ps->texture, &mt->base.base); 479 480 ps->format = templ->format; 481 ps->writable = templ->writable; 482 ps->u.tex.level = templ->u.tex.level; 483 ps->u.tex.first_layer = templ->u.tex.first_layer; 484 ps->u.tex.last_layer = templ->u.tex.last_layer; 485 486 ns->width = u_minify(mt->base.base.width0, ps->u.tex.level); 487 ns->height = u_minify(mt->base.base.height0, ps->u.tex.level); 488 ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1; 489 ns->offset = mt->level[templ->u.tex.level].offset; 490 491 /* comment says there are going to be removed, but they're used by the st */ 492 ps->width = ns->width; 493 ps->height = ns->height; 494 495 ns->width <<= mt->ms_x; 496 ns->height <<= mt->ms_y; 497 498 return ns; 499 } 500 501 struct pipe_surface * 502 nv50_miptree_surface_new(struct pipe_context *pipe, 503 struct pipe_resource *pt, 504 const struct pipe_surface *templ) 505 { 506 struct nv50_miptree *mt = nv50_miptree(pt); 507 struct nv50_surface *ns = nv50_surface_from_miptree(mt, templ); 508 if (!ns) 509 return NULL; 510 ns->base.context = pipe; 511 512 if (ns->base.u.tex.first_layer) { 513 const unsigned l = ns->base.u.tex.level; 514 const unsigned z = ns->base.u.tex.first_layer; 515 516 if (mt->layout_3d) { 517 ns->offset += nv50_mt_zslice_offset(mt, l, z); 518 519 /* TODO: switch to depth 1 tiles; but actually this shouldn't happen */ 520 if (ns->depth > 1 && 521 (z & (NV50_TILE_SIZE_Z(mt->level[l].tile_mode) - 1))) 522 NOUVEAU_ERR("Creating unsupported 3D surface !\n"); 523 } else { 524 ns->offset += mt->layer_stride * z; 525 } 526 } 527 528 return &ns->base; 529 } 530