1/*
2 * Copyright 2012 Red Hat Inc.
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 * Authors: Ben Skeggs
23 *
24 */
25
26#include "util/u_format.h"
27#include "util/u_inlines.h"
28#include "util/u_surface.h"
29
30#include "nv_m2mf.xml.h"
31#include "nv_object.xml.h"
32#include "nv30/nv30_screen.h"
33#include "nv30/nv30_context.h"
34#include "nv30/nv30_resource.h"
35#include "nv30/nv30_transfer.h"
36
37static inline unsigned
38layer_offset(struct pipe_resource *pt, unsigned level, unsigned layer)
39{
40   struct nv30_miptree *mt = nv30_miptree(pt);
41   struct nv30_miptree_level *lvl = &mt->level[level];
42
43   if (pt->target == PIPE_TEXTURE_CUBE)
44      return (layer * mt->layer_size) + lvl->offset;
45
46   return lvl->offset + (layer * lvl->zslice_size);
47}
48
49static boolean
50nv30_miptree_get_handle(struct pipe_screen *pscreen,
51                        struct pipe_resource *pt,
52                        struct winsys_handle *handle)
53{
54   struct nv30_miptree *mt = nv30_miptree(pt);
55   unsigned stride;
56
57   if (!mt || !mt->base.bo)
58      return false;
59
60   stride = mt->level[0].pitch;
61
62   return nouveau_screen_bo_get_handle(pscreen, mt->base.bo, stride, handle);
63}
64
65static void
66nv30_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt)
67{
68   struct nv30_miptree *mt = nv30_miptree(pt);
69
70   nouveau_bo_ref(NULL, &mt->base.bo);
71   FREE(mt);
72}
73
74struct nv30_transfer {
75   struct pipe_transfer base;
76   struct nv30_rect img;
77   struct nv30_rect tmp;
78   unsigned nblocksx;
79   unsigned nblocksy;
80};
81
82static inline struct nv30_transfer *
83nv30_transfer(struct pipe_transfer *ptx)
84{
85   return (struct nv30_transfer *)ptx;
86}
87
88static inline void
89define_rect(struct pipe_resource *pt, unsigned level, unsigned z,
90            unsigned x, unsigned y, unsigned w, unsigned h,
91            struct nv30_rect *rect)
92{
93   struct nv30_miptree *mt = nv30_miptree(pt);
94   struct nv30_miptree_level *lvl = &mt->level[level];
95
96   rect->w = u_minify(pt->width0, level) << mt->ms_x;
97   rect->w = util_format_get_nblocksx(pt->format, rect->w);
98   rect->h = u_minify(pt->height0, level) << mt->ms_y;
99   rect->h = util_format_get_nblocksy(pt->format, rect->h);
100   rect->d = 1;
101   rect->z = 0;
102   if (mt->swizzled) {
103      if (pt->target == PIPE_TEXTURE_3D) {
104         rect->d = u_minify(pt->depth0, level);
105         rect->z = z; z = 0;
106      }
107      rect->pitch = 0;
108   } else {
109      rect->pitch = lvl->pitch;
110   }
111
112   rect->bo     = mt->base.bo;
113   rect->domain = NOUVEAU_BO_VRAM;
114   rect->offset = layer_offset(pt, level, z);
115   rect->cpp    = util_format_get_blocksize(pt->format);
116
117   rect->x0     = util_format_get_nblocksx(pt->format, x) << mt->ms_x;
118   rect->y0     = util_format_get_nblocksy(pt->format, y) << mt->ms_y;
119   rect->x1     = rect->x0 + (util_format_get_nblocksx(pt->format, w) << mt->ms_x);
120   rect->y1     = rect->y0 + (util_format_get_nblocksy(pt->format, h) << mt->ms_y);
121
122   /* XXX There's some indication that swizzled formats > 4 bytes are treated
123    * differently. However that only applies to RGBA16_FLOAT, RGBA32_FLOAT,
124    * and the DXT* formats. The former aren't properly supported yet, and the
125    * latter avoid swizzled layouts.
126
127   if (mt->swizzled && rect->cpp > 4) {
128      unsigned scale = rect->cpp / 4;
129      rect->w *= scale;
130      rect->x0 *= scale;
131      rect->x1 *= scale;
132      rect->cpp = 4;
133   }
134   */
135}
136
137void
138nv30_resource_copy_region(struct pipe_context *pipe,
139                          struct pipe_resource *dstres, unsigned dst_level,
140                          unsigned dstx, unsigned dsty, unsigned dstz,
141                          struct pipe_resource *srcres, unsigned src_level,
142                          const struct pipe_box *src_box)
143{
144   struct nv30_context *nv30 = nv30_context(pipe);
145   struct nv30_rect src, dst;
146
147   if (dstres->target == PIPE_BUFFER && srcres->target == PIPE_BUFFER) {
148      nouveau_copy_buffer(&nv30->base,
149                          nv04_resource(dstres), dstx,
150                          nv04_resource(srcres), src_box->x, src_box->width);
151      return;
152   }
153
154   define_rect(srcres, src_level, src_box->z, src_box->x, src_box->y,
155                       src_box->width, src_box->height, &src);
156   define_rect(dstres, dst_level, dstz, dstx, dsty,
157                       src_box->width, src_box->height, &dst);
158
159   nv30_transfer_rect(nv30, NEAREST, &src, &dst);
160}
161
162static void
163nv30_resource_resolve(struct nv30_context *nv30,
164                      const struct pipe_blit_info *info)
165{
166   struct nv30_miptree *src_mt = nv30_miptree(info->src.resource);
167   struct nv30_rect src, dst;
168   unsigned x, x0, x1, y, y1, w, h;
169
170   define_rect(info->src.resource, 0, info->src.box.z, info->src.box.x,
171      info->src.box.y, info->src.box.width, info->src.box.height, &src);
172   define_rect(info->dst.resource, 0, info->dst.box.z, info->dst.box.x,
173      info->dst.box.y, info->dst.box.width, info->dst.box.height, &dst);
174
175   x0 = src.x0;
176   x1 = src.x1;
177   y1 = src.y1;
178
179   /* On nv3x we must use sifm which is restricted to 1024x1024 tiles */
180   for (y = src.y0; y < y1; y += h) {
181      h = y1 - y;
182      if (h > 1024)
183         h = 1024;
184
185      src.y0 = 0;
186      src.y1 = h;
187      src.h = h;
188
189      dst.y1 = dst.y0 + (h >> src_mt->ms_y);
190      dst.h = h >> src_mt->ms_y;
191
192      for (x = x0; x < x1; x += w) {
193         w = x1 - x;
194         if (w > 1024)
195            w = 1024;
196
197         src.offset = y * src.pitch + x * src.cpp;
198         src.x0 = 0;
199         src.x1 = w;
200         src.w = w;
201
202         dst.offset = (y >> src_mt->ms_y) * dst.pitch +
203                      (x >> src_mt->ms_x) * dst.cpp;
204         dst.x1 = dst.x0 + (w >> src_mt->ms_x);
205         dst.w = w >> src_mt->ms_x;
206
207         nv30_transfer_rect(nv30, BILINEAR, &src, &dst);
208      }
209   }
210}
211
212void
213nv30_blit(struct pipe_context *pipe,
214          const struct pipe_blit_info *blit_info)
215{
216   struct nv30_context *nv30 = nv30_context(pipe);
217   struct pipe_blit_info info = *blit_info;
218
219   if (info.src.resource->nr_samples > 1 &&
220       info.dst.resource->nr_samples <= 1 &&
221       !util_format_is_depth_or_stencil(info.src.resource->format) &&
222       !util_format_is_pure_integer(info.src.resource->format)) {
223      nv30_resource_resolve(nv30, blit_info);
224      return;
225   }
226
227   if (util_try_blit_via_copy_region(pipe, &info)) {
228      return; /* done */
229   }
230
231   if (info.mask & PIPE_MASK_S) {
232      debug_printf("nv30: cannot blit stencil, skipping\n");
233      info.mask &= ~PIPE_MASK_S;
234   }
235
236   if (!util_blitter_is_blit_supported(nv30->blitter, &info)) {
237      debug_printf("nv30: blit unsupported %s -> %s\n",
238                   util_format_short_name(info.src.resource->format),
239                   util_format_short_name(info.dst.resource->format));
240      return;
241   }
242
243   /* XXX turn off occlusion queries */
244
245   util_blitter_save_vertex_buffer_slot(nv30->blitter, nv30->vtxbuf);
246   util_blitter_save_vertex_elements(nv30->blitter, nv30->vertex);
247   util_blitter_save_vertex_shader(nv30->blitter, nv30->vertprog.program);
248   util_blitter_save_rasterizer(nv30->blitter, nv30->rast);
249   util_blitter_save_viewport(nv30->blitter, &nv30->viewport);
250   util_blitter_save_scissor(nv30->blitter, &nv30->scissor);
251   util_blitter_save_fragment_shader(nv30->blitter, nv30->fragprog.program);
252   util_blitter_save_blend(nv30->blitter, nv30->blend);
253   util_blitter_save_depth_stencil_alpha(nv30->blitter,
254                                         nv30->zsa);
255   util_blitter_save_stencil_ref(nv30->blitter, &nv30->stencil_ref);
256   util_blitter_save_sample_mask(nv30->blitter, nv30->sample_mask);
257   util_blitter_save_framebuffer(nv30->blitter, &nv30->framebuffer);
258   util_blitter_save_fragment_sampler_states(nv30->blitter,
259                     nv30->fragprog.num_samplers,
260                     (void**)nv30->fragprog.samplers);
261   util_blitter_save_fragment_sampler_views(nv30->blitter,
262                     nv30->fragprog.num_textures, nv30->fragprog.textures);
263   util_blitter_save_render_condition(nv30->blitter, nv30->render_cond_query,
264                                      nv30->render_cond_cond, nv30->render_cond_mode);
265   util_blitter_blit(nv30->blitter, &info);
266}
267
268void
269nv30_flush_resource(struct pipe_context *pipe,
270                    struct pipe_resource *resource)
271{
272}
273
274static void *
275nv30_miptree_transfer_map(struct pipe_context *pipe, struct pipe_resource *pt,
276                          unsigned level, unsigned usage,
277                          const struct pipe_box *box,
278                          struct pipe_transfer **ptransfer)
279{
280   struct nv30_context *nv30 = nv30_context(pipe);
281   struct nouveau_device *dev = nv30->screen->base.device;
282   struct nv30_miptree *mt = nv30_miptree(pt);
283   struct nv30_transfer *tx;
284   unsigned access = 0;
285   int ret;
286
287   tx = CALLOC_STRUCT(nv30_transfer);
288   if (!tx)
289      return NULL;
290   pipe_resource_reference(&tx->base.resource, pt);
291   tx->base.level = level;
292   tx->base.usage = usage;
293   tx->base.box = *box;
294   tx->base.stride = align(util_format_get_nblocksx(pt->format, box->width) *
295                           util_format_get_blocksize(pt->format), 64);
296   tx->base.layer_stride = util_format_get_nblocksy(pt->format, box->height) *
297                           tx->base.stride;
298
299   tx->nblocksx = util_format_get_nblocksx(pt->format, box->width);
300   tx->nblocksy = util_format_get_nblocksy(pt->format, box->height);
301
302   define_rect(pt, level, box->z, box->x, box->y,
303               box->width, box->height, &tx->img);
304
305   ret = nouveau_bo_new(dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
306                        tx->base.layer_stride * tx->base.box.depth, NULL,
307                        &tx->tmp.bo);
308   if (ret) {
309      pipe_resource_reference(&tx->base.resource, NULL);
310      FREE(tx);
311      return NULL;
312   }
313
314   tx->tmp.domain = NOUVEAU_BO_GART;
315   tx->tmp.offset = 0;
316   tx->tmp.pitch  = tx->base.stride;
317   tx->tmp.cpp    = tx->img.cpp;
318   tx->tmp.w      = tx->nblocksx;
319   tx->tmp.h      = tx->nblocksy;
320   tx->tmp.d      = 1;
321   tx->tmp.x0     = 0;
322   tx->tmp.y0     = 0;
323   tx->tmp.x1     = tx->tmp.w;
324   tx->tmp.y1     = tx->tmp.h;
325   tx->tmp.z      = 0;
326
327   if (usage & PIPE_TRANSFER_READ) {
328      bool is_3d = mt->base.base.target == PIPE_TEXTURE_3D;
329      unsigned offset = tx->img.offset;
330      unsigned z = tx->img.z;
331      unsigned i;
332      for (i = 0; i < box->depth; ++i) {
333         nv30_transfer_rect(nv30, NEAREST, &tx->img, &tx->tmp);
334         if (is_3d && mt->swizzled)
335            tx->img.z++;
336         else if (is_3d)
337            tx->img.offset += mt->level[level].zslice_size;
338         else
339            tx->img.offset += mt->layer_size;
340         tx->tmp.offset += tx->base.layer_stride;
341      }
342      tx->img.z = z;
343      tx->img.offset = offset;
344      tx->tmp.offset = 0;
345   }
346
347   if (tx->tmp.bo->map) {
348      *ptransfer = &tx->base;
349      return tx->tmp.bo->map;
350   }
351
352   if (usage & PIPE_TRANSFER_READ)
353      access |= NOUVEAU_BO_RD;
354   if (usage & PIPE_TRANSFER_WRITE)
355      access |= NOUVEAU_BO_WR;
356
357   ret = nouveau_bo_map(tx->tmp.bo, access, nv30->base.client);
358   if (ret) {
359      pipe_resource_reference(&tx->base.resource, NULL);
360      FREE(tx);
361      return NULL;
362   }
363
364   *ptransfer = &tx->base;
365   return tx->tmp.bo->map;
366}
367
368static void
369nv30_miptree_transfer_unmap(struct pipe_context *pipe,
370                            struct pipe_transfer *ptx)
371{
372   struct nv30_context *nv30 = nv30_context(pipe);
373   struct nv30_transfer *tx = nv30_transfer(ptx);
374   struct nv30_miptree *mt = nv30_miptree(tx->base.resource);
375   unsigned i;
376
377   if (ptx->usage & PIPE_TRANSFER_WRITE) {
378      bool is_3d = mt->base.base.target == PIPE_TEXTURE_3D;
379      for (i = 0; i < tx->base.box.depth; ++i) {
380         nv30_transfer_rect(nv30, NEAREST, &tx->tmp, &tx->img);
381         if (is_3d && mt->swizzled)
382            tx->img.z++;
383         else if (is_3d)
384            tx->img.offset += mt->level[tx->base.level].zslice_size;
385         else
386            tx->img.offset += mt->layer_size;
387         tx->tmp.offset += tx->base.layer_stride;
388      }
389
390      /* Allow the copies above to finish executing before freeing the source */
391      nouveau_fence_work(nv30->screen->base.fence.current,
392                         nouveau_fence_unref_bo, tx->tmp.bo);
393   } else {
394      nouveau_bo_ref(NULL, &tx->tmp.bo);
395   }
396   pipe_resource_reference(&ptx->resource, NULL);
397   FREE(tx);
398}
399
400const struct u_resource_vtbl nv30_miptree_vtbl = {
401   nv30_miptree_get_handle,
402   nv30_miptree_destroy,
403   nv30_miptree_transfer_map,
404   u_default_transfer_flush_region,
405   nv30_miptree_transfer_unmap,
406};
407
408struct pipe_resource *
409nv30_miptree_create(struct pipe_screen *pscreen,
410                    const struct pipe_resource *tmpl)
411{
412   struct nouveau_device *dev = nouveau_screen(pscreen)->device;
413   struct nv30_miptree *mt = CALLOC_STRUCT(nv30_miptree);
414   struct pipe_resource *pt = &mt->base.base;
415   unsigned blocksz, size;
416   unsigned w, h, d, l;
417   int ret;
418
419   switch (tmpl->nr_samples) {
420   case 4:
421      mt->ms_mode = 0x00004000;
422      mt->ms_x = 1;
423      mt->ms_y = 1;
424      break;
425   case 2:
426      mt->ms_mode = 0x00003000;
427      mt->ms_x = 1;
428      mt->ms_y = 0;
429      break;
430   default:
431      mt->ms_mode = 0x00000000;
432      mt->ms_x = 0;
433      mt->ms_y = 0;
434      break;
435   }
436
437   mt->base.vtbl = &nv30_miptree_vtbl;
438   *pt = *tmpl;
439   pipe_reference_init(&pt->reference, 1);
440   pt->screen = pscreen;
441
442   w = pt->width0 << mt->ms_x;
443   h = pt->height0 << mt->ms_y;
444   d = (pt->target == PIPE_TEXTURE_3D) ? pt->depth0 : 1;
445   blocksz = util_format_get_blocksize(pt->format);
446
447   if ((pt->target == PIPE_TEXTURE_RECT) ||
448       (pt->bind & PIPE_BIND_SCANOUT) ||
449       !util_is_power_of_two_or_zero(pt->width0) ||
450       !util_is_power_of_two_or_zero(pt->height0) ||
451       !util_is_power_of_two_or_zero(pt->depth0) ||
452       mt->ms_mode) {
453      mt->uniform_pitch = util_format_get_nblocksx(pt->format, w) * blocksz;
454      mt->uniform_pitch = align(mt->uniform_pitch, 64);
455      if (pt->bind & PIPE_BIND_SCANOUT) {
456         struct nv30_screen *screen = nv30_screen(pscreen);
457         int pitch_align = MAX2(
458               screen->eng3d->oclass >= NV40_3D_CLASS ? 1024 : 256,
459               /* round_down_pow2(mt->uniform_pitch / 4) */
460               1 << (util_last_bit(mt->uniform_pitch / 4) - 1));
461         mt->uniform_pitch = align(mt->uniform_pitch, pitch_align);
462      }
463   }
464
465   if (util_format_is_compressed(pt->format)) {
466      // Compressed (DXT) formats are packed tightly. We don't mark them as
467      // swizzled, since their layout is largely linear. However we do end up
468      // omitting the LINEAR flag when texturing them, as the levels are not
469      // uniformly sized (for POT sizes).
470   } else if (!mt->uniform_pitch) {
471      mt->swizzled = true;
472   }
473
474   size = 0;
475   for (l = 0; l <= pt->last_level; l++) {
476      struct nv30_miptree_level *lvl = &mt->level[l];
477      unsigned nbx = util_format_get_nblocksx(pt->format, w);
478      unsigned nby = util_format_get_nblocksy(pt->format, h);
479
480      lvl->offset = size;
481      lvl->pitch  = mt->uniform_pitch;
482      if (!lvl->pitch)
483         lvl->pitch = nbx * blocksz;
484
485      lvl->zslice_size = lvl->pitch * nby;
486      size += lvl->zslice_size * d;
487
488      w = u_minify(w, 1);
489      h = u_minify(h, 1);
490      d = u_minify(d, 1);
491   }
492
493   mt->layer_size = size;
494   if (pt->target == PIPE_TEXTURE_CUBE) {
495      if (!mt->uniform_pitch)
496         mt->layer_size = align(mt->layer_size, 128);
497      size = mt->layer_size * 6;
498   }
499
500   ret = nouveau_bo_new(dev, NOUVEAU_BO_VRAM, 256, size, NULL, &mt->base.bo);
501   if (ret) {
502      FREE(mt);
503      return NULL;
504   }
505
506   mt->base.domain = NOUVEAU_BO_VRAM;
507   return &mt->base.base;
508}
509
510struct pipe_resource *
511nv30_miptree_from_handle(struct pipe_screen *pscreen,
512                         const struct pipe_resource *tmpl,
513                         struct winsys_handle *handle)
514{
515   struct nv30_miptree *mt;
516   unsigned stride;
517
518   /* only supports 2D, non-mipmapped textures for the moment */
519   if ((tmpl->target != PIPE_TEXTURE_2D &&
520        tmpl->target != PIPE_TEXTURE_RECT) ||
521       tmpl->last_level != 0 ||
522       tmpl->depth0 != 1 ||
523       tmpl->array_size > 1)
524      return NULL;
525
526   mt = CALLOC_STRUCT(nv30_miptree);
527   if (!mt)
528      return NULL;
529
530   mt->base.bo = nouveau_screen_bo_from_handle(pscreen, handle, &stride);
531   if (mt->base.bo == NULL) {
532      FREE(mt);
533      return NULL;
534   }
535
536   mt->base.base = *tmpl;
537   mt->base.vtbl = &nv30_miptree_vtbl;
538   pipe_reference_init(&mt->base.base.reference, 1);
539   mt->base.base.screen = pscreen;
540   mt->uniform_pitch = stride;
541   mt->level[0].pitch = mt->uniform_pitch;
542   mt->level[0].offset = 0;
543
544   /* no need to adjust bo reference count */
545   return &mt->base.base;
546}
547
548struct pipe_surface *
549nv30_miptree_surface_new(struct pipe_context *pipe,
550                         struct pipe_resource *pt,
551                         const struct pipe_surface *tmpl)
552{
553   struct nv30_miptree *mt = nv30_miptree(pt); /* guaranteed */
554   struct nv30_surface *ns;
555   struct pipe_surface *ps;
556   struct nv30_miptree_level *lvl = &mt->level[tmpl->u.tex.level];
557
558   ns = CALLOC_STRUCT(nv30_surface);
559   if (!ns)
560      return NULL;
561   ps = &ns->base;
562
563   pipe_reference_init(&ps->reference, 1);
564   pipe_resource_reference(&ps->texture, pt);
565   ps->context = pipe;
566   ps->format = tmpl->format;
567   ps->u.tex.level = tmpl->u.tex.level;
568   ps->u.tex.first_layer = tmpl->u.tex.first_layer;
569   ps->u.tex.last_layer = tmpl->u.tex.last_layer;
570
571   ns->width = u_minify(pt->width0, ps->u.tex.level);
572   ns->height = u_minify(pt->height0, ps->u.tex.level);
573   ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1;
574   ns->offset = layer_offset(pt, ps->u.tex.level, ps->u.tex.first_layer);
575   if (mt->swizzled)
576      ns->pitch = 4096; /* random, just something the hw won't reject.. */
577   else
578      ns->pitch = lvl->pitch;
579
580   /* comment says there are going to be removed, but they're used by the st */
581   ps->width = ns->width;
582   ps->height = ns->height;
583   return ps;
584}
585
586void
587nv30_miptree_surface_del(struct pipe_context *pipe, struct pipe_surface *ps)
588{
589   struct nv30_surface *ns = nv30_surface(ps);
590
591   pipe_resource_reference(&ps->texture, NULL);
592   FREE(ns);
593}
594