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/format/u_format.h" 27 28#include "nv50/nv50_context.h" 29#include "nv50/nv50_resource.h" 30 31uint32_t 32nv50_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 61static uint32_t 62nv50_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 67static uint32_t 68nv50_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 161void 162nv50_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 181bool 182nv50_miptree_get_handle(struct pipe_screen *pscreen, 183 struct pipe_context *context, 184 struct pipe_resource *pt, 185 struct winsys_handle *whandle, 186 unsigned usage) 187{ 188 if (pt->target == PIPE_BUFFER) 189 return false; 190 191 struct nv50_miptree *mt = nv50_miptree(pt); 192 unsigned stride; 193 194 if (!mt || !mt->base.bo) 195 return false; 196 197 stride = mt->level[0].pitch; 198 199 return nouveau_screen_bo_get_handle(pscreen, 200 mt->base.bo, 201 stride, 202 whandle); 203} 204 205static inline bool 206nv50_miptree_init_ms_mode(struct nv50_miptree *mt) 207{ 208 switch (mt->base.base.nr_samples) { 209 case 8: 210 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS8; 211 mt->ms_x = 2; 212 mt->ms_y = 1; 213 break; 214 case 4: 215 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS4; 216 mt->ms_x = 1; 217 mt->ms_y = 1; 218 break; 219 case 2: 220 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS2; 221 mt->ms_x = 1; 222 break; 223 case 1: 224 case 0: 225 mt->ms_mode = NV50_3D_MULTISAMPLE_MODE_MS1; 226 break; 227 default: 228 NOUVEAU_ERR("invalid nr_samples: %u\n", mt->base.base.nr_samples); 229 return false; 230 } 231 return true; 232} 233 234bool 235nv50_miptree_init_layout_linear(struct nv50_miptree *mt, unsigned pitch_align) 236{ 237 struct pipe_resource *pt = &mt->base.base; 238 const unsigned blocksize = util_format_get_blocksize(pt->format); 239 unsigned h = pt->height0; 240 241 if (util_format_is_depth_or_stencil(pt->format)) 242 return false; 243 244 if ((pt->last_level > 0) || (pt->depth0 > 1) || (pt->array_size > 1)) 245 return false; 246 if (mt->ms_x | mt->ms_y) 247 return false; 248 249 mt->level[0].pitch = align(pt->width0 * blocksize, pitch_align); 250 251 /* Account for very generous prefetch (allocate size as if tiled). */ 252 h = MAX2(h, 8); 253 h = util_next_power_of_two(h); 254 255 mt->total_size = mt->level[0].pitch * h; 256 257 return true; 258} 259 260static void 261nv50_miptree_init_layout_video(struct nv50_miptree *mt) 262{ 263 const struct pipe_resource *pt = &mt->base.base; 264 const unsigned blocksize = util_format_get_blocksize(pt->format); 265 266 assert(pt->last_level == 0); 267 assert(mt->ms_x == 0 && mt->ms_y == 0); 268 assert(!util_format_is_compressed(pt->format)); 269 270 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 271 272 mt->level[0].tile_mode = 0x20; 273 mt->level[0].pitch = align(pt->width0 * blocksize, 64); 274 mt->total_size = align(pt->height0, 16) * mt->level[0].pitch * (mt->layout_3d ? pt->depth0 : 1); 275 276 if (pt->array_size > 1) { 277 mt->layer_stride = align(mt->total_size, NV50_TILE_SIZE(0x20)); 278 mt->total_size = mt->layer_stride * pt->array_size; 279 } 280} 281 282static void 283nv50_miptree_init_layout_tiled(struct nv50_miptree *mt) 284{ 285 struct pipe_resource *pt = &mt->base.base; 286 unsigned w, h, d, l; 287 const unsigned blocksize = util_format_get_blocksize(pt->format); 288 289 mt->layout_3d = pt->target == PIPE_TEXTURE_3D; 290 291 w = pt->width0 << mt->ms_x; 292 h = pt->height0 << mt->ms_y; 293 294 /* For 3D textures, a mipmap is spanned by all the layers, for array 295 * textures and cube maps, each layer contains its own mipmaps. 296 */ 297 d = mt->layout_3d ? pt->depth0 : 1; 298 299 for (l = 0; l <= pt->last_level; ++l) { 300 struct nv50_miptree_level *lvl = &mt->level[l]; 301 unsigned tsx, tsy, tsz; 302 unsigned nbx = util_format_get_nblocksx(pt->format, w); 303 unsigned nby = util_format_get_nblocksy(pt->format, h); 304 305 lvl->offset = mt->total_size; 306 307 lvl->tile_mode = nv50_tex_choose_tile_dims(nbx, nby, d, mt->layout_3d); 308 309 tsx = NV50_TILE_SIZE_X(lvl->tile_mode); /* x is tile row pitch in bytes */ 310 tsy = NV50_TILE_SIZE_Y(lvl->tile_mode); 311 tsz = NV50_TILE_SIZE_Z(lvl->tile_mode); 312 313 lvl->pitch = align(nbx * blocksize, tsx); 314 315 mt->total_size += lvl->pitch * align(nby, tsy) * align(d, tsz); 316 317 w = u_minify(w, 1); 318 h = u_minify(h, 1); 319 d = u_minify(d, 1); 320 } 321 322 if (pt->array_size > 1) { 323 mt->layer_stride = align(mt->total_size, 324 NV50_TILE_SIZE(mt->level[0].tile_mode)); 325 mt->total_size = mt->layer_stride * pt->array_size; 326 } 327} 328 329struct pipe_resource * 330nv50_miptree_create(struct pipe_screen *pscreen, 331 const struct pipe_resource *templ) 332{ 333 struct nouveau_device *dev = nouveau_screen(pscreen)->device; 334 struct nouveau_drm *drm = nouveau_screen(pscreen)->drm; 335 struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree); 336 struct pipe_resource *pt = &mt->base.base; 337 bool compressed = drm->version >= 0x01000101; 338 int ret; 339 union nouveau_bo_config bo_config; 340 uint32_t bo_flags; 341 unsigned pitch_align; 342 343 if (!mt) 344 return NULL; 345 346 *pt = *templ; 347 pipe_reference_init(&pt->reference, 1); 348 pt->screen = pscreen; 349 350 if (pt->bind & PIPE_BIND_LINEAR) 351 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR; 352 353 bo_config.nv50.memtype = nv50_mt_choose_storage_type(mt, compressed); 354 355 if (!nv50_miptree_init_ms_mode(mt)) { 356 FREE(mt); 357 return NULL; 358 } 359 360 if (unlikely(pt->flags & NV50_RESOURCE_FLAG_VIDEO)) { 361 nv50_miptree_init_layout_video(mt); 362 if (pt->flags & NV50_RESOURCE_FLAG_NOALLOC) { 363 /* BO allocation done by client */ 364 return pt; 365 } 366 } else 367 if (bo_config.nv50.memtype != 0) { 368 nv50_miptree_init_layout_tiled(mt); 369 } else { 370 if (pt->usage & PIPE_BIND_CURSOR) 371 pitch_align = 1; 372 else if (pt->usage & PIPE_BIND_SCANOUT) 373 pitch_align = 256; 374 else 375 pitch_align = 64; 376 if (!nv50_miptree_init_layout_linear(mt, pitch_align)) { 377 FREE(mt); 378 return NULL; 379 } 380 } 381 bo_config.nv50.tile_mode = mt->level[0].tile_mode; 382 383 if (!bo_config.nv50.memtype && (pt->bind & PIPE_BIND_SHARED)) 384 mt->base.domain = NOUVEAU_BO_GART; 385 else 386 mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen)); 387 388 bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP; 389 if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET)) 390 bo_flags |= NOUVEAU_BO_CONTIG; 391 392 ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config, 393 &mt->base.bo); 394 if (ret) { 395 FREE(mt); 396 return NULL; 397 } 398 mt->base.address = mt->base.bo->offset; 399 400 return pt; 401} 402 403struct pipe_resource * 404nv50_miptree_from_handle(struct pipe_screen *pscreen, 405 const struct pipe_resource *templ, 406 struct winsys_handle *whandle) 407{ 408 struct nv50_miptree *mt; 409 unsigned stride; 410 411 /* only supports 2D, non-mipmapped textures for the moment */ 412 if ((templ->target != PIPE_TEXTURE_2D && 413 templ->target != PIPE_TEXTURE_RECT) || 414 templ->last_level != 0 || 415 templ->depth0 != 1 || 416 templ->array_size > 1) 417 return NULL; 418 419 mt = CALLOC_STRUCT(nv50_miptree); 420 if (!mt) 421 return NULL; 422 423 mt->base.bo = nouveau_screen_bo_from_handle(pscreen, whandle, &stride); 424 if (mt->base.bo == NULL) { 425 FREE(mt); 426 return NULL; 427 } 428 mt->base.domain = mt->base.bo->flags & NOUVEAU_BO_APER; 429 mt->base.address = mt->base.bo->offset; 430 431 mt->base.base = *templ; 432 pipe_reference_init(&mt->base.base.reference, 1); 433 mt->base.base.screen = pscreen; 434 mt->level[0].pitch = stride; 435 mt->level[0].offset = 0; 436 mt->level[0].tile_mode = mt->base.bo->config.nv50.tile_mode; 437 438 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1); 439 440 /* no need to adjust bo reference count */ 441 return &mt->base.base; 442} 443 444 445/* Offset of zslice @z from start of level @l. */ 446inline unsigned 447nv50_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z) 448{ 449 const struct pipe_resource *pt = &mt->base.base; 450 451 unsigned tds = NV50_TILE_SHIFT_Z(mt->level[l].tile_mode); 452 unsigned ths = NV50_TILE_SHIFT_Y(mt->level[l].tile_mode); 453 454 unsigned nby = util_format_get_nblocksy(pt->format, 455 u_minify(pt->height0, l)); 456 457 /* to next 2D tile slice within a 3D tile */ 458 unsigned stride_2d = NV50_TILE_SIZE_2D(mt->level[l].tile_mode); 459 460 /* to slice in the next (in z direction) 3D tile */ 461 unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds; 462 463 return (z & ((1 << tds) - 1)) * stride_2d + (z >> tds) * stride_3d; 464} 465 466/* Surface functions. 467 */ 468 469struct nv50_surface * 470nv50_surface_from_miptree(struct nv50_miptree *mt, 471 const struct pipe_surface *templ) 472{ 473 struct pipe_surface *ps; 474 struct nv50_surface *ns = CALLOC_STRUCT(nv50_surface); 475 if (!ns) 476 return NULL; 477 ps = &ns->base; 478 479 pipe_reference_init(&ps->reference, 1); 480 pipe_resource_reference(&ps->texture, &mt->base.base); 481 482 ps->format = templ->format; 483 ps->writable = templ->writable; 484 ps->u.tex.level = templ->u.tex.level; 485 ps->u.tex.first_layer = templ->u.tex.first_layer; 486 ps->u.tex.last_layer = templ->u.tex.last_layer; 487 488 ns->width = u_minify(mt->base.base.width0, ps->u.tex.level); 489 ns->height = u_minify(mt->base.base.height0, ps->u.tex.level); 490 ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1; 491 ns->offset = mt->level[templ->u.tex.level].offset; 492 493 /* comment says there are going to be removed, but they're used by the st */ 494 ps->width = ns->width; 495 ps->height = ns->height; 496 497 ns->width <<= mt->ms_x; 498 ns->height <<= mt->ms_y; 499 500 return ns; 501} 502 503struct pipe_surface * 504nv50_miptree_surface_new(struct pipe_context *pipe, 505 struct pipe_resource *pt, 506 const struct pipe_surface *templ) 507{ 508 struct nv50_miptree *mt = nv50_miptree(pt); 509 struct nv50_surface *ns = nv50_surface_from_miptree(mt, templ); 510 if (!ns) 511 return NULL; 512 ns->base.context = pipe; 513 514 if (ns->base.u.tex.first_layer) { 515 const unsigned l = ns->base.u.tex.level; 516 const unsigned z = ns->base.u.tex.first_layer; 517 518 if (mt->layout_3d) { 519 ns->offset += nv50_mt_zslice_offset(mt, l, z); 520 521 /* TODO: switch to depth 1 tiles; but actually this shouldn't happen */ 522 if (ns->depth > 1 && 523 (z & (NV50_TILE_SIZE_Z(mt->level[l].tile_mode) - 1))) 524 NOUVEAU_ERR("Creating unsupported 3D surface !\n"); 525 } else { 526 ns->offset += mt->layer_stride * z; 527 } 528 } 529 530 return &ns->base; 531} 532