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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * 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 "nouveau/nv_m2mf.xml.h"
31#include "nv30_screen.h"
32#include "nv30_context.h"
33#include "nv30_resource.h"
34#include "nv30_transfer.h"
35
36static INLINE unsigned
37layer_offset(struct pipe_resource *pt, unsigned level, unsigned layer)
38{
39   struct nv30_miptree *mt = nv30_miptree(pt);
40   struct nv30_miptree_level *lvl = &mt->level[level];
41
42   if (pt->target == PIPE_TEXTURE_CUBE)
43      return (layer * mt->layer_size) + lvl->offset;
44
45   return lvl->offset + (layer * lvl->zslice_size);
46}
47
48static boolean
49nv30_miptree_get_handle(struct pipe_screen *pscreen,
50                        struct pipe_resource *pt,
51                        struct winsys_handle *handle)
52{
53   struct nv30_miptree *mt = nv30_miptree(pt);
54   unsigned stride;
55
56   if (!mt || !mt->base.bo)
57      return FALSE;
58
59   stride = mt->level[0].pitch;
60
61   return nouveau_screen_bo_get_handle(pscreen, mt->base.bo, stride, handle);
62}
63
64static void
65nv30_miptree_destroy(struct pipe_screen *pscreen, struct pipe_resource *pt)
66{
67   struct nv30_miptree *mt = nv30_miptree(pt);
68
69   nouveau_bo_ref(NULL, &mt->base.bo);
70   FREE(mt);
71}
72
73struct nv30_transfer {
74   struct pipe_transfer base;
75   struct nv30_rect img;
76   struct nv30_rect tmp;
77   unsigned nblocksx;
78   unsigned nblocksy;
79};
80
81static INLINE struct nv30_transfer *
82nv30_transfer(struct pipe_transfer *ptx)
83{
84   return (struct nv30_transfer *)ptx;
85}
86
87static INLINE void
88define_rect(struct pipe_resource *pt, unsigned level, unsigned z,
89            unsigned x, unsigned y, unsigned w, unsigned h,
90            struct nv30_rect *rect)
91{
92   struct nv30_miptree *mt = nv30_miptree(pt);
93   struct nv30_miptree_level *lvl = &mt->level[level];
94
95   rect->w = u_minify(pt->width0, level) << mt->ms_x;
96   rect->w = util_format_get_nblocksx(pt->format, rect->w);
97   rect->h = u_minify(pt->height0, level) << mt->ms_y;
98   rect->h = util_format_get_nblocksy(pt->format, rect->h);
99   rect->d = 1;
100   rect->z = 0;
101   if (mt->swizzled) {
102      if (pt->target == PIPE_TEXTURE_3D) {
103         rect->d = u_minify(pt->depth0, level);
104         rect->z = z; z = 0;
105      }
106      rect->pitch = 0;
107   } else {
108      rect->pitch = lvl->pitch;
109   }
110
111   rect->bo     = mt->base.bo;
112   rect->domain = NOUVEAU_BO_VRAM;
113   rect->offset = layer_offset(pt, level, z);
114   rect->cpp    = util_format_get_blocksize(pt->format);
115
116   rect->x0     = util_format_get_nblocksx(pt->format, x) << mt->ms_x;
117   rect->y0     = util_format_get_nblocksy(pt->format, y) << mt->ms_y;
118   rect->x1     = rect->x0 + (w << mt->ms_x);
119   rect->y1     = rect->y0 + (h << mt->ms_y);
120}
121
122void
123nv30_resource_copy_region(struct pipe_context *pipe,
124                          struct pipe_resource *dstres, unsigned dst_level,
125                          unsigned dstx, unsigned dsty, unsigned dstz,
126                          struct pipe_resource *srcres, unsigned src_level,
127                          const struct pipe_box *src_box)
128{
129   struct nv30_context *nv30 = nv30_context(pipe);
130   struct nv30_rect src, dst;
131
132   if (dstres->target == PIPE_BUFFER && srcres->target == PIPE_BUFFER) {
133      util_resource_copy_region(pipe, dstres, dst_level, dstx, dsty, dstz,
134                                      srcres, src_level, src_box);
135      return;
136   }
137
138   define_rect(srcres, src_level, src_box->z, src_box->x, src_box->y,
139                       src_box->width, src_box->height, &src);
140   define_rect(dstres, dst_level, dstz, dstx, dsty,
141                       src_box->width, src_box->height, &dst);
142
143   nv30_transfer_rect(nv30, NEAREST, &src, &dst);
144}
145
146void
147nv30_resource_resolve(struct pipe_context *pipe,
148                      const struct pipe_resolve_info *info)
149{
150   struct nv30_context *nv30 = nv30_context(pipe);
151   struct nv30_rect src, dst;
152
153   define_rect(info->src.res, 0, 0, info->src.x0, info->src.y0,
154               info->src.x1 - info->src.x0, info->src.y1 - info->src.y0, &src);
155   define_rect(info->dst.res, info->dst.level, 0, info->dst.x0, info->dst.y0,
156               info->dst.x1 - info->dst.x0, info->dst.y1 - info->dst.y0, &dst);
157
158   nv30_transfer_rect(nv30, BILINEAR, &src, &dst);
159}
160
161static struct pipe_transfer *
162nv30_miptree_transfer_new(struct pipe_context *pipe, struct pipe_resource *pt,
163                          unsigned level, unsigned usage,
164                          const struct pipe_box *box)
165{
166   struct nv30_context *nv30 = nv30_context(pipe);
167   struct nouveau_device *dev = nv30->screen->base.device;
168   struct nv30_transfer *tx;
169   int ret;
170
171   tx = CALLOC_STRUCT(nv30_transfer);
172   if (!tx)
173      return NULL;
174   pipe_resource_reference(&tx->base.resource, pt);
175   tx->base.level = level;
176   tx->base.usage = usage;
177   tx->base.box = *box;
178   tx->base.stride = util_format_get_nblocksx(pt->format, box->width) *
179                     util_format_get_blocksize(pt->format);
180   tx->base.layer_stride = util_format_get_nblocksy(pt->format, box->height) *
181                           tx->base.stride;
182
183   tx->nblocksx = util_format_get_nblocksx(pt->format, box->width);
184   tx->nblocksy = util_format_get_nblocksy(pt->format, box->height);
185
186   define_rect(pt, level, box->z, box->x, box->y,
187                   tx->nblocksx, tx->nblocksy, &tx->img);
188
189   ret = nouveau_bo_new(dev, NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
190                        tx->base.layer_stride, NULL, &tx->tmp.bo);
191   if (ret) {
192      pipe_resource_reference(&tx->base.resource, NULL);
193      FREE(tx);
194      return NULL;
195   }
196
197   tx->tmp.domain = NOUVEAU_BO_GART;
198   tx->tmp.offset = 0;
199   tx->tmp.pitch  = tx->base.stride;
200   tx->tmp.cpp    = tx->img.cpp;
201   tx->tmp.w      = tx->nblocksx;
202   tx->tmp.h      = tx->nblocksy;
203   tx->tmp.d      = 1;
204   tx->tmp.x0     = 0;
205   tx->tmp.y0     = 0;
206   tx->tmp.x1     = tx->tmp.w;
207   tx->tmp.y1     = tx->tmp.h;
208   tx->tmp.z      = 0;
209
210   if (usage & PIPE_TRANSFER_READ)
211      nv30_transfer_rect(nv30, NEAREST, &tx->img, &tx->tmp);
212
213   return &tx->base;
214}
215
216static void
217nv30_miptree_transfer_del(struct pipe_context *pipe, struct pipe_transfer *ptx)
218{
219   struct nv30_context *nv30 = nv30_context(pipe);
220   struct nv30_transfer *tx = nv30_transfer(ptx);
221
222   if (ptx->usage & PIPE_TRANSFER_WRITE)
223      nv30_transfer_rect(nv30, NEAREST, &tx->tmp, &tx->img);
224
225   nouveau_bo_ref(NULL, &tx->tmp.bo);
226   pipe_resource_reference(&ptx->resource, NULL);
227   FREE(tx);
228}
229
230static void *
231nv30_miptree_transfer_map(struct pipe_context *pipe, struct pipe_transfer *ptx)
232{
233   struct nv30_context *nv30 = nv30_context(pipe);
234   struct nv30_transfer *tx = nv30_transfer(ptx);
235   unsigned access = 0;
236   int ret;
237
238   if (tx->tmp.bo->map)
239      return tx->tmp.bo->map;
240
241   if (ptx->usage & PIPE_TRANSFER_READ)
242      access |= NOUVEAU_BO_RD;
243   if (ptx->usage & PIPE_TRANSFER_WRITE)
244      access |= NOUVEAU_BO_WR;
245
246   ret = nouveau_bo_map(tx->tmp.bo, access, nv30->base.client);
247   if (ret)
248      return NULL;
249   return tx->tmp.bo->map;
250}
251
252static void
253nv30_miptree_transfer_unmap(struct pipe_context *pipe,
254                            struct pipe_transfer *ptx)
255{
256}
257
258const struct u_resource_vtbl nv30_miptree_vtbl = {
259   nv30_miptree_get_handle,
260   nv30_miptree_destroy,
261   nv30_miptree_transfer_new,
262   nv30_miptree_transfer_del,
263   nv30_miptree_transfer_map,
264   u_default_transfer_flush_region,
265   nv30_miptree_transfer_unmap,
266   u_default_transfer_inline_write
267};
268
269struct pipe_resource *
270nv30_miptree_create(struct pipe_screen *pscreen,
271                    const struct pipe_resource *tmpl)
272{
273   struct nouveau_device *dev = nouveau_screen(pscreen)->device;
274   struct nv30_miptree *mt = CALLOC_STRUCT(nv30_miptree);
275   struct pipe_resource *pt = &mt->base.base;
276   unsigned blocksz, size;
277   unsigned w, h, d, l;
278   int ret;
279
280   switch (tmpl->nr_samples) {
281   case 4:
282      mt->ms_mode = 0x00004000;
283      mt->ms_x = 1;
284      mt->ms_y = 1;
285      break;
286   case 2:
287      mt->ms_mode = 0x00003000;
288      mt->ms_x = 1;
289      mt->ms_y = 0;
290      break;
291   default:
292      mt->ms_mode = 0x00000000;
293      mt->ms_x = 0;
294      mt->ms_y = 0;
295      break;
296   }
297
298   mt->base.vtbl = &nv30_miptree_vtbl;
299   *pt = *tmpl;
300   pipe_reference_init(&pt->reference, 1);
301   pt->screen = pscreen;
302
303   w = pt->width0 << mt->ms_x;
304   h = pt->height0 << mt->ms_y;
305   d = (pt->target == PIPE_TEXTURE_3D) ? pt->depth0 : 1;
306   blocksz = util_format_get_blocksize(pt->format);
307
308   if ((pt->target == PIPE_TEXTURE_RECT) ||
309       !util_is_power_of_two(pt->width0) ||
310       !util_is_power_of_two(pt->height0) ||
311       !util_is_power_of_two(pt->depth0) ||
312       util_format_is_compressed(pt->format) ||
313       util_format_is_float(pt->format) || mt->ms_mode) {
314      mt->uniform_pitch = util_format_get_nblocksx(pt->format, w) * blocksz;
315      mt->uniform_pitch = align(mt->uniform_pitch, 64);
316   }
317
318   if (!mt->uniform_pitch)
319      mt->swizzled = TRUE;
320
321   size = 0;
322   for (l = 0; l <= pt->last_level; l++) {
323      struct nv30_miptree_level *lvl = &mt->level[l];
324      unsigned nbx = util_format_get_nblocksx(pt->format, w);
325      unsigned nby = util_format_get_nblocksx(pt->format, h);
326
327      lvl->offset = size;
328      lvl->pitch  = mt->uniform_pitch;
329      if (!lvl->pitch)
330         lvl->pitch = nbx * blocksz;
331
332      lvl->zslice_size = lvl->pitch * nby;
333      size += lvl->zslice_size * d;
334
335      w = u_minify(w, 1);
336      h = u_minify(h, 1);
337      d = u_minify(d, 1);
338   }
339
340   mt->layer_size = size;
341   if (pt->target == PIPE_TEXTURE_CUBE) {
342      if (!mt->uniform_pitch)
343         mt->layer_size = align(mt->layer_size, 128);
344      size = mt->layer_size * 6;
345   }
346
347   ret = nouveau_bo_new(dev, NOUVEAU_BO_VRAM, 256, size, NULL, &mt->base.bo);
348   if (ret) {
349      FREE(mt);
350      return NULL;
351   }
352
353   mt->base.domain = NOUVEAU_BO_VRAM;
354   return &mt->base.base;
355}
356
357struct pipe_resource *
358nv30_miptree_from_handle(struct pipe_screen *pscreen,
359                         const struct pipe_resource *tmpl,
360                         struct winsys_handle *handle)
361{
362   struct nv30_miptree *mt;
363   unsigned stride;
364
365   /* only supports 2D, non-mipmapped textures for the moment */
366   if ((tmpl->target != PIPE_TEXTURE_2D &&
367        tmpl->target != PIPE_TEXTURE_RECT) ||
368       tmpl->last_level != 0 ||
369       tmpl->depth0 != 1 ||
370       tmpl->array_size > 1)
371      return NULL;
372
373   mt = CALLOC_STRUCT(nv30_miptree);
374   if (!mt)
375      return NULL;
376
377   mt->base.bo = nouveau_screen_bo_from_handle(pscreen, handle, &stride);
378   if (mt->base.bo == NULL) {
379      FREE(mt);
380      return NULL;
381   }
382
383   mt->base.base = *tmpl;
384   mt->base.vtbl = &nv30_miptree_vtbl;
385   pipe_reference_init(&mt->base.base.reference, 1);
386   mt->base.base.screen = pscreen;
387   mt->uniform_pitch = stride;
388   mt->level[0].pitch = mt->uniform_pitch;
389   mt->level[0].offset = 0;
390
391   /* no need to adjust bo reference count */
392   return &mt->base.base;
393}
394
395struct pipe_surface *
396nv30_miptree_surface_new(struct pipe_context *pipe,
397                         struct pipe_resource *pt,
398                         const struct pipe_surface *tmpl)
399{
400   struct nv30_miptree *mt = nv30_miptree(pt); /* guaranteed */
401   struct nv30_surface *ns;
402   struct pipe_surface *ps;
403   struct nv30_miptree_level *lvl = &mt->level[tmpl->u.tex.level];
404
405   ns = CALLOC_STRUCT(nv30_surface);
406   if (!ns)
407      return NULL;
408   ps = &ns->base;
409
410   pipe_reference_init(&ps->reference, 1);
411   pipe_resource_reference(&ps->texture, pt);
412   ps->context = pipe;
413   ps->format = tmpl->format;
414   ps->usage = tmpl->usage;
415   ps->u.tex.level = tmpl->u.tex.level;
416   ps->u.tex.first_layer = tmpl->u.tex.first_layer;
417   ps->u.tex.last_layer = tmpl->u.tex.last_layer;
418
419   ns->width = u_minify(pt->width0, ps->u.tex.level);
420   ns->height = u_minify(pt->height0, ps->u.tex.level);
421   ns->depth = ps->u.tex.last_layer - ps->u.tex.first_layer + 1;
422   ns->offset = layer_offset(pt, ps->u.tex.level, ps->u.tex.first_layer);
423   if (mt->swizzled)
424      ns->pitch = 4096; /* random, just something the hw won't reject.. */
425   else
426      ns->pitch = lvl->pitch;
427
428   /* comment says there are going to be removed, but they're used by the st */
429   ps->width = ns->width;
430   ps->height = ns->height;
431   return ps;
432}
433
434void
435nv30_miptree_surface_del(struct pipe_context *pipe, struct pipe_surface *ps)
436{
437   struct nv30_surface *ns = nv30_surface(ps);
438
439   pipe_resource_reference(&ps->texture, NULL);
440   FREE(ns);
441}
442