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 "drm-uapi/drm_fourcc.h" 24 25#include "pipe/p_state.h" 26#include "pipe/p_defines.h" 27#include "frontend/drm_driver.h" 28#include "util/u_inlines.h" 29#include "util/format/u_format.h" 30 31#include "nvc0/nvc0_context.h" 32#include "nvc0/nvc0_resource.h" 33 34static uint32_t 35nvc0_tex_choose_tile_dims(unsigned nx, unsigned ny, unsigned nz, bool is_3d) 36{ 37 return nv50_tex_choose_tile_dims_helper(nx, ny, nz, is_3d); 38} 39 40static uint32_t 41tu102_choose_tiled_storage_type(enum pipe_format format, 42 unsigned ms, 43 bool compressed) 44 45{ 46 uint32_t kind; 47 48 switch (format) { 49 case PIPE_FORMAT_Z16_UNORM: 50 if (compressed) 51 kind = 0x0b; // NV_MMU_PTE_KIND_Z16_COMPRESSIBLE_DISABLE_PLC 52 else 53 kind = 0x01; // NV_MMU_PTE_KIND_Z16 54 break; 55 case PIPE_FORMAT_X8Z24_UNORM: 56 case PIPE_FORMAT_S8X24_UINT: 57 case PIPE_FORMAT_S8_UINT_Z24_UNORM: 58 if (compressed) 59 kind = 0x0e; // NV_MMU_PTE_KIND_Z24S8_COMPRESSIBLE_DISABLE_PLC 60 else 61 kind = 0x05; // NV_MMU_PTE_KIND_Z24S8 62 break; 63 case PIPE_FORMAT_X24S8_UINT: 64 case PIPE_FORMAT_Z24X8_UNORM: 65 case PIPE_FORMAT_Z24_UNORM_S8_UINT: 66 if (compressed) 67 kind = 0x0c; // NV_MMU_PTE_KIND_S8Z24_COMPRESSIBLE_DISABLE_PLC 68 else 69 kind = 0x03; // NV_MMU_PTE_KIND_S8Z24 70 break; 71 case PIPE_FORMAT_X32_S8X24_UINT: 72 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: 73 if (compressed) 74 kind = 0x0d; // NV_MMU_PTE_KIND_ZF32_X24S8_COMPRESSIBLE_DISABLE_PLC 75 else 76 kind = 0x04; // NV_MMU_PTE_KIND_ZF32_X24S8 77 break; 78 case PIPE_FORMAT_Z32_FLOAT: 79 default: 80 kind = 0x06; 81 break; 82 } 83 84 return kind; 85} 86 87uint32_t 88nvc0_choose_tiled_storage_type(struct pipe_screen *pscreen, 89 enum pipe_format format, 90 unsigned ms, 91 bool compressed) 92{ 93 uint32_t tile_flags; 94 95 if (nouveau_screen(pscreen)->device->chipset >= 0x160) 96 return tu102_choose_tiled_storage_type(format, ms, compressed); 97 98 switch (format) { 99 case PIPE_FORMAT_Z16_UNORM: 100 if (compressed) 101 tile_flags = 0x02 + ms; 102 else 103 tile_flags = 0x01; 104 break; 105 case PIPE_FORMAT_X8Z24_UNORM: 106 case PIPE_FORMAT_S8X24_UINT: 107 case PIPE_FORMAT_S8_UINT_Z24_UNORM: 108 if (compressed) 109 tile_flags = 0x51 + ms; 110 else 111 tile_flags = 0x46; 112 break; 113 case PIPE_FORMAT_X24S8_UINT: 114 case PIPE_FORMAT_Z24X8_UNORM: 115 case PIPE_FORMAT_Z24_UNORM_S8_UINT: 116 if (compressed) 117 tile_flags = 0x17 + ms; 118 else 119 tile_flags = 0x11; 120 break; 121 case PIPE_FORMAT_Z32_FLOAT: 122 if (compressed) 123 tile_flags = 0x86 + ms; 124 else 125 tile_flags = 0x7b; 126 break; 127 case PIPE_FORMAT_X32_S8X24_UINT: 128 case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: 129 if (compressed) 130 tile_flags = 0xce + ms; 131 else 132 tile_flags = 0xc3; 133 break; 134 default: 135 switch (util_format_get_blocksizebits(format)) { 136 case 128: 137 if (compressed) 138 tile_flags = 0xf4 + ms * 2; 139 else 140 tile_flags = 0xfe; 141 break; 142 case 64: 143 if (compressed) { 144 switch (ms) { 145 case 0: tile_flags = 0xe6; break; 146 case 1: tile_flags = 0xeb; break; 147 case 2: tile_flags = 0xed; break; 148 case 3: tile_flags = 0xf2; break; 149 default: 150 return 0; 151 } 152 } else { 153 tile_flags = 0xfe; 154 } 155 break; 156 case 32: 157 if (compressed && ms) { 158 switch (ms) { 159 /* This one makes things blurry: 160 case 0: tile_flags = 0xdb; break; 161 */ 162 case 1: tile_flags = 0xdd; break; 163 case 2: tile_flags = 0xdf; break; 164 case 3: tile_flags = 0xe4; break; 165 default: 166 return 0; 167 } 168 } else { 169 tile_flags = 0xfe; 170 } 171 break; 172 case 16: 173 case 8: 174 tile_flags = 0xfe; 175 break; 176 default: 177 return 0; 178 } 179 break; 180 } 181 182 return tile_flags; 183} 184 185static uint32_t 186nvc0_mt_choose_storage_type(struct pipe_screen *pscreen, 187 const struct nv50_miptree *mt, 188 bool compressed) 189{ 190 const unsigned ms = util_logbase2(mt->base.base.nr_samples); 191 192 if (unlikely(mt->base.base.bind & PIPE_BIND_CURSOR)) 193 return 0; 194 if (unlikely(mt->base.base.flags & NOUVEAU_RESOURCE_FLAG_LINEAR)) 195 return 0; 196 197 return nvc0_choose_tiled_storage_type(pscreen, mt->base.base.format, ms, compressed); 198} 199 200static inline bool 201nvc0_miptree_init_ms_mode(struct nv50_miptree *mt) 202{ 203 switch (mt->base.base.nr_samples) { 204 case 8: 205 mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS8; 206 mt->ms_x = 2; 207 mt->ms_y = 1; 208 break; 209 case 4: 210 mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS4; 211 mt->ms_x = 1; 212 mt->ms_y = 1; 213 break; 214 case 2: 215 mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS2; 216 mt->ms_x = 1; 217 break; 218 case 1: 219 case 0: 220 mt->ms_mode = NVC0_3D_MULTISAMPLE_MODE_MS1; 221 break; 222 default: 223 NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples); 224 return false; 225 } 226 return true; 227} 228 229static void 230nvc0_miptree_init_layout_video(struct nv50_miptree *mt) 231{ 232 const struct pipe_resource *pt = &mt->base.base; 233 const unsigned blocksize = util_format_get_blocksize(pt->format); 234 235 assert(pt->last_level == 0); 236 assert(mt->ms_x == 0 && mt->ms_y == 0); 237 assert(!util_format_is_compressed(pt->format)); 238 239 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 240 241 mt->level[0].tile_mode = 0x10; 242 mt->level[0].pitch = align(pt->width0 * blocksize, 64); 243 mt->total_size = align(pt->height0, 16) * mt->level[0].pitch * (mt->layout_3d ? pt->depth0 : 1); 244 245 if (pt->array_size > 1) { 246 mt->layer_stride = align(mt->total_size, NVC0_TILE_SIZE(0x10)); 247 mt->total_size = mt->layer_stride * pt->array_size; 248 } 249} 250 251static void 252nvc0_miptree_init_layout_tiled(struct nv50_miptree *mt, uint64_t modifier) 253{ 254 struct pipe_resource *pt = &mt->base.base; 255 unsigned w, h, d, l; 256 const unsigned blocksize = util_format_get_blocksize(pt->format); 257 258 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 259 260 w = pt->width0 << mt->ms_x; 261 h = pt->height0 << mt->ms_y; 262 263 /* For 3D textures, a mipmap is spanned by all the layers, for array 264 * textures and cube maps, each layer contains its own mipmaps. 265 */ 266 d = mt->layout_3d ? pt->depth0 : 1; 267 268 assert(!mt->ms_mode || !pt->last_level); 269 assert(modifier == DRM_FORMAT_MOD_INVALID || 270 (!pt->last_level && !mt->layout_3d)); 271 assert(modifier != DRM_FORMAT_MOD_LINEAR); 272 273 for (l = 0; l <= pt->last_level; ++l) { 274 struct nv50_miptree_level *lvl = &mt->level[l]; 275 unsigned tsx, tsy, tsz; 276 unsigned nbx = util_format_get_nblocksx(pt->format, w); 277 unsigned nby = util_format_get_nblocksy(pt->format, h); 278 279 lvl->offset = mt->total_size; 280 281 if (modifier != DRM_FORMAT_MOD_INVALID) 282 /* Extract the log2(block height) field from the modifier and pack it 283 * into tile_mode's y field. Other tile dimensions are always 1 284 * (represented using 0 here) for 2D surfaces, and non-2D surfaces are 285 * not supported by the current modifiers (asserted above). Note the 286 * modifier must be validated prior to calling this function. 287 */ 288 lvl->tile_mode = ((uint32_t)modifier & 0xf) << 4; 289 else 290 lvl->tile_mode = nvc0_tex_choose_tile_dims(nbx, nby, d, mt->layout_3d); 291 292 tsx = NVC0_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */ 293 tsy = NVC0_TILE_SIZE_Y(lvl->tile_mode); 294 tsz = NVC0_TILE_SIZE_Z(lvl->tile_mode); 295 296 lvl->pitch = align(nbx * blocksize, tsx); 297 298 mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz); 299 300 w = u_minify(w, 1); 301 h = u_minify(h, 1); 302 d = u_minify(d, 1); 303 } 304 305 if (pt->array_size > 1) { 306 mt->layer_stride = align(mt->total_size, 307 NVC0_TILE_SIZE(mt->level[0].tile_mode)); 308 mt->total_size = mt->layer_stride * pt->array_size; 309 } 310} 311 312static uint64_t 313nvc0_miptree_get_modifier(struct pipe_screen *pscreen, struct nv50_miptree *mt) 314{ 315 const union nouveau_bo_config *config = &mt->base.bo->config; 316 const uint32_t uc_kind = 317 nvc0_choose_tiled_storage_type(pscreen, 318 mt->base.base.format, 319 mt->base.base.nr_samples, 320 false); 321 const uint32_t kind_gen = nvc0_get_kind_generation(pscreen); 322 323 if (mt->layout_3d) 324 return DRM_FORMAT_MOD_INVALID; 325 if (mt->base.base.nr_samples > 1) 326 return DRM_FORMAT_MOD_INVALID; 327 if (config->nvc0.memtype == 0x00) 328 return DRM_FORMAT_MOD_LINEAR; 329 if (NVC0_TILE_MODE_Y(config->nvc0.tile_mode) > 5) 330 return DRM_FORMAT_MOD_INVALID; 331 if (config->nvc0.memtype != uc_kind) 332 return DRM_FORMAT_MOD_INVALID; 333 334 return DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D( 335 0, 336 nouveau_screen(pscreen)->tegra_sector_layout ? 0 : 1, 337 kind_gen, 338 config->nvc0.memtype, 339 NVC0_TILE_MODE_Y(config->nvc0.tile_mode)); 340} 341 342bool 343nvc0_miptree_get_handle(struct pipe_screen *pscreen, 344 struct pipe_context *context, 345 struct pipe_resource *pt, 346 struct winsys_handle *whandle, 347 unsigned usage) 348{ 349 struct nv50_miptree *mt = nv50_miptree(pt); 350 bool ret; 351 352 ret = nv50_miptree_get_handle(pscreen, context, pt, whandle, usage); 353 if (!ret) 354 return ret; 355 356 whandle->modifier = nvc0_miptree_get_modifier(pscreen, mt); 357 358 return true; 359} 360 361static uint64_t 362nvc0_miptree_select_best_modifier(struct pipe_screen *pscreen, 363 const struct nv50_miptree *mt, 364 const uint64_t *modifiers, 365 unsigned int count) 366{ 367 /* 368 * Supported block heights are 1,2,4,8,16,32, stored as log2() their 369 * value. Reserve one slot for each, as well as the linear modifier. 370 */ 371 uint64_t prio_supported_mods[] = { 372 DRM_FORMAT_MOD_INVALID, 373 DRM_FORMAT_MOD_INVALID, 374 DRM_FORMAT_MOD_INVALID, 375 DRM_FORMAT_MOD_INVALID, 376 DRM_FORMAT_MOD_INVALID, 377 DRM_FORMAT_MOD_INVALID, 378 DRM_FORMAT_MOD_LINEAR, 379 }; 380 const uint32_t uc_kind = nvc0_mt_choose_storage_type(pscreen, mt, false); 381 int top_mod_slot = ARRAY_SIZE(prio_supported_mods); 382 const uint32_t kind_gen = nvc0_get_kind_generation(pscreen); 383 unsigned int i; 384 int p; 385 386 if (uc_kind != 0u) { 387 const struct pipe_resource *pt = &mt->base.base; 388 const unsigned nbx = util_format_get_nblocksx(pt->format, pt->width0); 389 const unsigned nby = util_format_get_nblocksy(pt->format, pt->height0); 390 const uint32_t lbh_preferred = 391 NVC0_TILE_MODE_Y(nvc0_tex_choose_tile_dims(nbx, nby, 1u, false)); 392 uint32_t lbh = lbh_preferred; 393 bool dec_lbh = true; 394 const uint8_t s = nouveau_screen(pscreen)->tegra_sector_layout ? 0 : 1; 395 396 for (i = 0; i < ARRAY_SIZE(prio_supported_mods) - 1; i++) { 397 assert(lbh <= 5u); 398 prio_supported_mods[i] = 399 DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D(0, s, kind_gen, uc_kind, lbh); 400 401 /* 402 * The preferred block height is the largest block size that doesn't 403 * waste excessive space with unused padding bytes relative to the 404 * height of the image. Construct the priority array such that 405 * the preferred block height is highest priority, followed by 406 * progressively smaller block sizes down to a block height of one, 407 * followed by progressively larger (more wasteful) block sizes up 408 * to 5. 409 */ 410 if (lbh == 0u) { 411 lbh = lbh_preferred + 1u; 412 dec_lbh = false; 413 } else if (dec_lbh) { 414 lbh--; 415 } else { 416 lbh++; 417 } 418 } 419 } 420 421 assert(prio_supported_mods[ARRAY_SIZE(prio_supported_mods) - 1] == 422 DRM_FORMAT_MOD_LINEAR); 423 424 for (i = 0u; i < count; i++) { 425 for (p = 0; p < ARRAY_SIZE(prio_supported_mods); p++) { 426 if (prio_supported_mods[p] != DRM_FORMAT_MOD_INVALID) { 427 if (modifiers[i] == DRM_FORMAT_MOD_INVALID || 428 prio_supported_mods[p] == modifiers[i]) { 429 if (top_mod_slot > p) top_mod_slot = p; 430 break; 431 } 432 } 433 } 434 } 435 436 if (top_mod_slot >= ARRAY_SIZE(prio_supported_mods)) 437 return DRM_FORMAT_MOD_INVALID; 438 439 return prio_supported_mods[top_mod_slot]; 440} 441 442struct pipe_resource * 443nvc0_miptree_create(struct pipe_screen *pscreen, 444 const struct pipe_resource *templ, 445 const uint64_t *modifiers, unsigned int count) 446{ 447 struct nouveau_device *dev = nouveau_screen(pscreen)->device; 448 struct nouveau_drm *drm = nouveau_screen(pscreen)->drm; 449 struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree); 450 struct pipe_resource *pt = &mt->base.base; 451 bool compressed = drm->version >= 0x01000101; 452 int ret; 453 union nouveau_bo_config bo_config; 454 uint32_t bo_flags; 455 unsigned pitch_align; 456 uint64_t modifier = DRM_FORMAT_MOD_INVALID; 457 458 if (!mt) 459 return NULL; 460 461 *pt = *templ; 462 pipe_reference_init(&pt->reference, 1); 463 pt->screen = pscreen; 464 465 if (pt->usage == PIPE_USAGE_STAGING) { 466 /* PIPE_USAGE_STAGING, and usage in general, should not be specified when 467 * modifiers are used. */ 468 assert(count == 0); 469 switch (pt->target) { 470 case PIPE_TEXTURE_2D: 471 case PIPE_TEXTURE_RECT: 472 if (pt->last_level == 0 && 473 !util_format_is_depth_or_stencil(pt->format) && 474 pt->nr_samples <= 1) 475 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR; 476 break; 477 default: 478 break; 479 } 480 } 481 482 if (pt->bind & PIPE_BIND_LINEAR) 483 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR; 484 485 if (count > 0) { 486 modifier = nvc0_miptree_select_best_modifier(pscreen, mt, 487 modifiers, count); 488 489 if (modifier == DRM_FORMAT_MOD_INVALID) { 490 FREE(mt); 491 return NULL; 492 } 493 494 if (modifier == DRM_FORMAT_MOD_LINEAR) { 495 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR; 496 bo_config.nvc0.memtype = 0; 497 } else { 498 bo_config.nvc0.memtype = (modifier >> 12) & 0xff; 499 } 500 } else { 501 bo_config.nvc0.memtype = nvc0_mt_choose_storage_type(pscreen, mt, compressed); 502 } 503 504 if (!nvc0_miptree_init_ms_mode(mt)) { 505 FREE(mt); 506 return NULL; 507 } 508 509 if (unlikely(pt->flags & NVC0_RESOURCE_FLAG_VIDEO)) { 510 assert(modifier == DRM_FORMAT_MOD_INVALID); 511 nvc0_miptree_init_layout_video(mt); 512 } else 513 if (likely(bo_config.nvc0.memtype)) { 514 nvc0_miptree_init_layout_tiled(mt, modifier); 515 } else { 516 /* When modifiers are supplied, usage is zero. TODO: detect the 517 * modifiers+cursor case. */ 518 if (pt->usage & PIPE_BIND_CURSOR) 519 pitch_align = 1; 520 else if ((pt->usage & PIPE_BIND_SCANOUT) || count > 0) 521 pitch_align = 256; 522 else 523 pitch_align = 128; 524 if (!nv50_miptree_init_layout_linear(mt, pitch_align)) { 525 FREE(mt); 526 return NULL; 527 } 528 } 529 bo_config.nvc0.tile_mode = mt->level[0].tile_mode; 530 531 if (!bo_config.nvc0.memtype && (pt->usage == PIPE_USAGE_STAGING || pt->bind & PIPE_BIND_SHARED)) 532 mt->base.domain = NOUVEAU_BO_GART; 533 else 534 mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen)); 535 536 bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP; 537 538 if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET)) 539 bo_flags |= NOUVEAU_BO_CONTIG; 540 541 ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config, 542 &mt->base.bo); 543 if (ret) { 544 FREE(mt); 545 return NULL; 546 } 547 mt->base.address = mt->base.bo->offset; 548 549 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1); 550 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_bytes, 551 mt->total_size); 552 553 return pt; 554} 555 556/* Offset of zslice @z from start of level @l. */ 557inline unsigned 558nvc0_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z) 559{ 560 const struct pipe_resource *pt = &mt->base.base; 561 562 unsigned tds = NVC0_TILE_SHIFT_Z(mt->level[l].tile_mode); 563 unsigned ths = NVC0_TILE_SHIFT_Y(mt->level[l].tile_mode); 564 565 unsigned nby = util_format_get_nblocksy(pt->format, 566 u_minify(pt->height0, l)); 567 568 /* to next 2D tile slice within a 3D tile */ 569 unsigned stride_2d = NVC0_TILE_SIZE_2D(mt->level[l].tile_mode); 570 571 /* to slice in the next (in z direction) 3D tile */ 572 unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds; 573 574 return (z & (1 << (tds - 1))) * stride_2d + (z >> tds) * stride_3d; 575} 576 577/* Surface functions. 578 */ 579 580struct pipe_surface * 581nvc0_miptree_surface_new(struct pipe_context *pipe, 582 struct pipe_resource *pt, 583 const struct pipe_surface *templ) 584{ 585 struct nv50_surface *ns = nv50_surface_from_miptree(nv50_miptree(pt), templ); 586 if (!ns) 587 return NULL; 588 ns->base.context = pipe; 589 return &ns->base; 590} 591