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
34 static uint32_t
nvc0_tex_choose_tile_dims(unsigned nx,unsigned ny,unsigned nz,bool is_3d)35 nvc0_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
40 static uint32_t
tu102_choose_tiled_storage_type(enum pipe_format format,unsigned ms,bool compressed)41 tu102_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
87 uint32_t
nvc0_choose_tiled_storage_type(struct pipe_screen * pscreen,enum pipe_format format,unsigned ms,bool compressed)88 nvc0_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
185 static uint32_t
nvc0_mt_choose_storage_type(struct pipe_screen * pscreen,const struct nv50_miptree * mt,bool compressed)186 nvc0_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
200 static inline bool
nvc0_miptree_init_ms_mode(struct nv50_miptree * mt)201 nvc0_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
229 static void
nvc0_miptree_init_layout_video(struct nv50_miptree * mt)230 nvc0_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
251 static void
nvc0_miptree_init_layout_tiled(struct nv50_miptree * mt,uint64_t modifier)252 nvc0_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
312 static uint64_t
nvc0_miptree_get_modifier(struct pipe_screen * pscreen,struct nv50_miptree * mt)313 nvc0_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
342 bool
nvc0_miptree_get_handle(struct pipe_screen * pscreen,struct pipe_context * context,struct pipe_resource * pt,struct winsys_handle * whandle,unsigned usage)343 nvc0_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
361 static uint64_t
nvc0_miptree_select_best_modifier(struct pipe_screen * pscreen,const struct nv50_miptree * mt,const uint64_t * modifiers,unsigned int count)362 nvc0_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] == modifiers[i]) {
427 if (top_mod_slot > p) top_mod_slot = p;
428 break;
429 }
430 }
431 }
432
433 if (top_mod_slot >= ARRAY_SIZE(prio_supported_mods))
434 return DRM_FORMAT_MOD_INVALID;
435
436 return prio_supported_mods[top_mod_slot];
437 }
438
439 struct pipe_resource *
nvc0_miptree_create(struct pipe_screen * pscreen,const struct pipe_resource * templ,const uint64_t * modifiers,unsigned int count)440 nvc0_miptree_create(struct pipe_screen *pscreen,
441 const struct pipe_resource *templ,
442 const uint64_t *modifiers, unsigned int count)
443 {
444 struct nouveau_device *dev = nouveau_screen(pscreen)->device;
445 struct nouveau_drm *drm = nouveau_screen(pscreen)->drm;
446 struct nv50_miptree *mt = CALLOC_STRUCT(nv50_miptree);
447 struct pipe_resource *pt = &mt->base.base;
448 bool compressed = drm->version >= 0x01000101;
449 int ret;
450 union nouveau_bo_config bo_config;
451 uint32_t bo_flags;
452 unsigned pitch_align;
453 uint64_t modifier = DRM_FORMAT_MOD_INVALID;
454
455 if (!mt)
456 return NULL;
457
458 *pt = *templ;
459 pipe_reference_init(&pt->reference, 1);
460 pt->screen = pscreen;
461
462 if (pt->usage == PIPE_USAGE_STAGING) {
463 /* PIPE_USAGE_STAGING, and usage in general, should not be specified when
464 * modifiers are used. */
465 assert(count == 0);
466 switch (pt->target) {
467 case PIPE_TEXTURE_2D:
468 case PIPE_TEXTURE_RECT:
469 if (pt->last_level == 0 &&
470 !util_format_is_depth_or_stencil(pt->format) &&
471 pt->nr_samples <= 1)
472 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
473 break;
474 default:
475 break;
476 }
477 }
478
479 if (pt->bind & PIPE_BIND_LINEAR)
480 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
481
482 if (count > 0) {
483 modifier = nvc0_miptree_select_best_modifier(pscreen, mt,
484 modifiers, count);
485
486 if (modifier == DRM_FORMAT_MOD_INVALID) {
487 FREE(mt);
488 return NULL;
489 }
490
491 if (modifier == DRM_FORMAT_MOD_LINEAR) {
492 pt->flags |= NOUVEAU_RESOURCE_FLAG_LINEAR;
493 bo_config.nvc0.memtype = 0;
494 } else {
495 bo_config.nvc0.memtype = (modifier >> 12) & 0xff;
496 }
497 } else {
498 bo_config.nvc0.memtype = nvc0_mt_choose_storage_type(pscreen, mt, compressed);
499 }
500
501 if (!nvc0_miptree_init_ms_mode(mt)) {
502 FREE(mt);
503 return NULL;
504 }
505
506 if (unlikely(pt->flags & NVC0_RESOURCE_FLAG_VIDEO)) {
507 assert(modifier == DRM_FORMAT_MOD_INVALID);
508 nvc0_miptree_init_layout_video(mt);
509 } else
510 if (likely(bo_config.nvc0.memtype)) {
511 nvc0_miptree_init_layout_tiled(mt, modifier);
512 } else {
513 /* When modifiers are supplied, usage is zero. TODO: detect the
514 * modifiers+cursor case. */
515 if (pt->usage & PIPE_BIND_CURSOR)
516 pitch_align = 1;
517 else if ((pt->usage & PIPE_BIND_SCANOUT) || count > 0)
518 pitch_align = 256;
519 else
520 pitch_align = 128;
521 if (!nv50_miptree_init_layout_linear(mt, pitch_align)) {
522 FREE(mt);
523 return NULL;
524 }
525 }
526 bo_config.nvc0.tile_mode = mt->level[0].tile_mode;
527
528 if (!bo_config.nvc0.memtype && (pt->usage == PIPE_USAGE_STAGING || pt->bind & PIPE_BIND_SHARED))
529 mt->base.domain = NOUVEAU_BO_GART;
530 else
531 mt->base.domain = NV_VRAM_DOMAIN(nouveau_screen(pscreen));
532
533 bo_flags = mt->base.domain | NOUVEAU_BO_NOSNOOP;
534
535 if (mt->base.base.bind & (PIPE_BIND_CURSOR | PIPE_BIND_DISPLAY_TARGET))
536 bo_flags |= NOUVEAU_BO_CONTIG;
537
538 ret = nouveau_bo_new(dev, bo_flags, 4096, mt->total_size, &bo_config,
539 &mt->base.bo);
540 if (ret) {
541 FREE(mt);
542 return NULL;
543 }
544 mt->base.address = mt->base.bo->offset;
545
546 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_count, 1);
547 NOUVEAU_DRV_STAT(nouveau_screen(pscreen), tex_obj_current_bytes,
548 mt->total_size);
549
550 return pt;
551 }
552
553 /* Offset of zslice @z from start of level @l. */
554 inline unsigned
nvc0_mt_zslice_offset(const struct nv50_miptree * mt,unsigned l,unsigned z)555 nvc0_mt_zslice_offset(const struct nv50_miptree *mt, unsigned l, unsigned z)
556 {
557 const struct pipe_resource *pt = &mt->base.base;
558
559 unsigned tds = NVC0_TILE_SHIFT_Z(mt->level[l].tile_mode);
560 unsigned ths = NVC0_TILE_SHIFT_Y(mt->level[l].tile_mode);
561
562 unsigned nby = util_format_get_nblocksy(pt->format,
563 u_minify(pt->height0, l));
564
565 /* to next 2D tile slice within a 3D tile */
566 unsigned stride_2d = NVC0_TILE_SIZE_2D(mt->level[l].tile_mode);
567
568 /* to slice in the next (in z direction) 3D tile */
569 unsigned stride_3d = (align(nby, (1 << ths)) * mt->level[l].pitch) << tds;
570
571 return (z & (1 << (tds - 1))) * stride_2d + (z >> tds) * stride_3d;
572 }
573
574 /* Surface functions.
575 */
576
577 struct pipe_surface *
nvc0_miptree_surface_new(struct pipe_context * pipe,struct pipe_resource * pt,const struct pipe_surface * templ)578 nvc0_miptree_surface_new(struct pipe_context *pipe,
579 struct pipe_resource *pt,
580 const struct pipe_surface *templ)
581 {
582 struct nv50_surface *ns = nv50_surface_from_miptree(nv50_miptree(pt), templ);
583 if (!ns)
584 return NULL;
585 ns->base.context = pipe;
586 return &ns->base;
587 }
588