NV40TEX: Switch to a big triangle and clip it to a quad.
[nouveau] / src / nv40_video_texture.c
1
2 /*
3  * Copyright 2007-2008 Maarten Maathuis
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include "xf86xv.h"
30 #include <X11/extensions/Xv.h>
31 #include "exa.h"
32 #include "damage.h"
33 #include "dixstruct.h"
34 #include "fourcc.h"
35
36 #include "nv_include.h"
37 #include "nv_dma.h"
38
39 #include "nv_shaders.h"
40
41 extern Atom xvSyncToVBlank, xvSetDefaults;
42
43 static nv_shader_t nv40_video = {
44         .card_priv.NV30VP.vp_in_reg  = 0x00000309,
45         .card_priv.NV30VP.vp_out_reg = 0x0000c001,
46         .size = (3*4),
47         .data = {
48                 /* MOV result.position, vertex.position */
49                 0x40041c6c, 0x0040000d, 0x8106c083, 0x6041ff80,
50                 /* MOV result.texcoord[0], vertex.texcoord[0] */
51                 0x401f9c6c, 0x0040080d, 0x8106c083, 0x6041ff9c,
52                 /* MOV result.texcoord[1], vertex.texcoord[1] */
53                 0x401f9c6c, 0x0040090d, 0x8106c083, 0x6041ffa1,
54         }
55 };
56
57 static nv_shader_t nv40_yv12 = {
58         .card_priv.NV30FP.num_regs = 2,
59         .size = (8*4),
60         .data = {
61                 /* INST 0: TEXR R0.x (TR0.xyzw), attrib.texcoord[0], abs(texture[0]) */
62                 0x17008200, 0x1c9dc801, 0x0001c800, 0x3fe1c800,
63                 /* INST 1: MADR R1.xyz (TR0.xyzw), R0.xxxx, { 1.16, -0.87, 0.53, -1.08 }.xxxx, { 1.16, -0.87, 0.53, -1.08 }.yzww */
64                 0x04000e02, 0x1c9c0000, 0x00000002, 0x0001f202,
65                 0x3f9507c8, 0xbf5ee393, 0x3f078fef, 0xbf8a6762,
66                 /* INST 2: TEXR R0.yz (TR0.xyzw), attrib.texcoord[1], abs(texture[1]) */
67                 0x1702ac80, 0x1c9dc801, 0x0001c800, 0x3fe1c800,
68                 /* INST 3: MADR R1.xyz (TR0.xyzw), R0.yyyy, { 0.00, -0.39, 2.02, 0.00 }, R1 */
69                 0x04000e02, 0x1c9cab00, 0x0001c802, 0x0001c804,
70                 0x00000000, 0xbec890d6, 0x40011687, 0x00000000,
71                 /* INST 4: MADR R0.xyz (TR0.xyzw), R0.zzzz, { 1.60, -0.81, 0.00, 0.00 }, R1 + END */
72                 0x04000e81, 0x1c9d5500, 0x0001c802, 0x0001c804,
73                 0x3fcc432d, 0xbf501a37, 0x00000000, 0x00000000,
74         }
75 };
76
77 #define SWIZZLE(ts0x,ts0y,ts0z,ts0w,ts1x,ts1y,ts1z,ts1w)                                                        \
78         (                                                                                                                                       \
79         NV40TCL_TEX_SWIZZLE_S0_X_##ts0x | NV40TCL_TEX_SWIZZLE_S0_Y_##ts0y               |       \
80         NV40TCL_TEX_SWIZZLE_S0_Z_##ts0z | NV40TCL_TEX_SWIZZLE_S0_W_##ts0w       |       \
81         NV40TCL_TEX_SWIZZLE_S1_X_##ts1x | NV40TCL_TEX_SWIZZLE_S1_Y_##ts1y       |       \
82         NV40TCL_TEX_SWIZZLE_S1_Z_##ts1z | NV40TCL_TEX_SWIZZLE_S1_W_##ts1w               \
83         )
84
85 static Bool
86 NV40VideoTexture(ScrnInfoPtr pScrn, int offset, uint16_t width, uint16_t height, uint16_t src_pitch, int unit)
87 {
88         NVPtr pNv = NVPTR(pScrn);
89
90         uint32_t card_fmt = 0;
91         uint32_t card_swz = 0;
92
93         if (unit == 0) {
94                 /* Pretend we've got a normal 8 bits format. */
95                 card_fmt = NV40TCL_TEX_FORMAT_FORMAT_L8;
96                 card_swz = SWIZZLE(S1, S1, S1, S1, X, X, X, X);
97         } else {
98                 /* Pretend we've got a normal 2x8 bits format. */
99                 card_fmt = NV40TCL_TEX_FORMAT_FORMAT_A8L8;
100                 card_swz = SWIZZLE(S1, S1, S1, S1, W, Z, Y, X); /* x = V, y = U */
101         }
102
103         BEGIN_RING(Nv3D, NV40TCL_TEX_OFFSET(unit), 8);
104         /* We get an obsolute offset, which needs to be corrected. */
105         OUT_RELOCl(pNv->FB, (uint32_t)(offset - pNv->FB->offset), NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
106         OUT_RELOCd(pNv->FB, card_fmt | NV40TCL_TEX_FORMAT_LINEAR |
107                         NV40TCL_TEX_FORMAT_DIMS_2D | NV40TCL_TEX_FORMAT_NO_BORDER |
108                         (0x8000) | (1 << NV40TCL_TEX_FORMAT_MIPMAP_COUNT_SHIFT),
109                         NOUVEAU_BO_VRAM | NOUVEAU_BO_RD,
110                         NV40TCL_TEX_FORMAT_DMA0, 0);
111
112         OUT_RING(NV40TCL_TEX_WRAP_S_CLAMP_TO_EDGE |
113                         NV40TCL_TEX_WRAP_T_CLAMP_TO_EDGE |
114                         NV40TCL_TEX_WRAP_R_CLAMP_TO_EDGE);
115         OUT_RING(NV40TCL_TEX_ENABLE_ENABLE);
116         OUT_RING(card_swz);
117         OUT_RING(NV40TCL_TEX_FILTER_MIN_LINEAR |
118                         NV40TCL_TEX_FILTER_MAG_LINEAR |
119                         0x3fd6);
120         OUT_RING((width << 16) | height);
121         OUT_RING(0); /* border ARGB */
122         BEGIN_RING(Nv3D, NV40TCL_TEX_SIZE1(unit), 1);
123         OUT_RING((1 << NV40TCL_TEX_SIZE1_DEPTH_SHIFT) |
124                         (uint16_t) src_pitch);
125
126         return TRUE;
127 }
128
129 Bool
130 NV40GetSurfaceFormat(PixmapPtr pPix, int *fmt_ret)
131 {
132         switch (pPix->drawable.bitsPerPixel) {
133                 case 32:
134                         *fmt_ret = NV40TCL_RT_FORMAT_COLOR_A8R8G8B8;
135                         break;
136                 case 24:
137                         *fmt_ret = NV40TCL_RT_FORMAT_COLOR_X8R8G8B8;
138                         break;
139                 case 16:
140                         *fmt_ret = NV40TCL_RT_FORMAT_COLOR_R5G6B5;
141                         break;
142                 case 8:
143                         *fmt_ret = NV40TCL_RT_FORMAT_COLOR_B8;
144                         break;
145                 default:
146                         return FALSE;
147         }
148
149         return TRUE;
150 }
151
152 void
153 NV40StopTexturedVideo(ScrnInfoPtr pScrn, pointer data, Bool Exit)
154 {
155 }
156
157 /* To support EXA 2.0, 2.1 has this in the header */
158 #ifndef exaMoveInPixmap
159 extern void exaMoveInPixmap(PixmapPtr pPixmap);
160 #endif
161
162 #define SF(bf) (NV40TCL_BLEND_FUNC_SRC_RGB_##bf |                              \
163                 NV40TCL_BLEND_FUNC_SRC_ALPHA_##bf)
164 #define DF(bf) (NV40TCL_BLEND_FUNC_DST_RGB_##bf |                              \
165                 NV40TCL_BLEND_FUNC_DST_ALPHA_##bf)
166
167 #define VERTEX_OUT(sx,sy,dx,dy) do {                                        \
168         BEGIN_RING(Nv3D, NV40TCL_VTX_ATTR_2F_X(8), 4);                         \
169         OUT_RINGf ((sx)); OUT_RINGf ((sy));                                    \
170         OUT_RINGf ((sx)); OUT_RINGf ((sy));                                    \
171         BEGIN_RING(Nv3D, NV40TCL_VTX_ATTR_2I(0), 1);                           \
172         OUT_RING  (((dy)<<16)|(dx));                                           \
173 } while(0)
174
175 #define GET_TEXTURED_PRIVATE(pNv) \
176         (NVPortPrivPtr)((pNv)->blitAdaptor->pPortPrivates[0].ptr)
177
178 int NV40PutTextureImage(ScrnInfoPtr pScrn, int src_offset,
179                 int src_offset2, int id,
180                 int src_pitch, BoxPtr dstBox,
181                 int x1, int y1, int x2, int y2,
182                 uint16_t width, uint16_t height,
183                 uint16_t src_w, uint16_t src_h,
184                 uint16_t drw_w, uint16_t drw_h,
185                 RegionPtr clipBoxes,
186                 DrawablePtr pDraw)
187 {
188         NVPtr pNv   = NVPTR(pScrn);
189         NVPortPrivPtr pPriv = GET_TEXTURED_PRIVATE(pNv);
190         Bool redirected = FALSE;
191
192         /* Remove some warnings. */
193         /* This has to be done better at some point. */
194         (void)nv40_vp_exa_render;
195         (void)nv30_fp_pass_col0;
196         (void)nv30_fp_pass_tex0;
197         (void)nv30_fp_composite_mask;
198         (void)nv30_fp_composite_mask_sa_ca;
199         (void)nv30_fp_composite_mask_ca;
200
201         if (drw_w > 4096 || drw_h > 4096) {
202                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
203                         "XV: Draw size too large.\n");
204                 return BadAlloc;
205         }
206
207         float X1, X2, Y1, Y2;
208         uint16_t p_width, p_height;
209         float pf_width, pf_height; /* fractions pbox/dstBox */
210         float pf_x1, pf_y1; /* fractions of x,y/pBox */
211         float df_x1, df_y1; /* fractions of x,y/dstBox */
212         float drf_x, drf_y; /* draw to src ratio. */
213         PixmapPtr pPix = NVGetDrawablePixmap(pDraw);
214         BoxPtr pbox;
215         int nbox;
216         int dst_format = 0;
217         if (!NV40GetSurfaceFormat(pPix, &dst_format)) {
218                 ErrorF("No surface format, bad.\n");
219         }
220
221         /* This has to be called always, since it does more than just migration. */
222         exaMoveInPixmap(pPix);
223         ExaOffscreenMarkUsed(pPix);
224
225 #ifdef COMPOSITE
226         /* Adjust coordinates if drawing to an offscreen pixmap */
227         if (pPix->screen_x || pPix->screen_y) {
228                 REGION_TRANSLATE(pScrn->pScreen, clipBoxes,
229                                                         -pPix->screen_x,
230                                                         -pPix->screen_y);
231                 dstBox->x1 -= pPix->screen_x;
232                 dstBox->x2 -= pPix->screen_x;
233                 dstBox->y1 -= pPix->screen_y;
234                 dstBox->y2 -= pPix->screen_y;
235         }
236
237         /* I suspect that pDraw itself is not offscreen, hence not suited for damage tracking. */
238         DamageDamageRegion(&pPix->drawable, clipBoxes);
239
240         /* This is test is unneeded for !COMPOSITE. */
241         if (!NVExaPixmapIsOnscreen(pPix))
242                 redirected = TRUE;
243 #endif
244
245         pbox = REGION_RECTS(clipBoxes);
246         nbox = REGION_NUM_RECTS(clipBoxes);
247
248         /* Disable blending */
249         BEGIN_RING(Nv3D, NV40TCL_BLEND_ENABLE, 1);
250         OUT_RING(0);
251
252         /* Setup surface */
253         BEGIN_RING(Nv3D, NV40TCL_RT_FORMAT, 3);
254         OUT_RING  (NV40TCL_RT_FORMAT_TYPE_LINEAR |
255                         NV40TCL_RT_FORMAT_ZETA_Z24S8 |
256                         dst_format);
257         OUT_RING  (exaGetPixmapPitch(pPix));
258         OUT_PIXMAPl(pPix, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
259
260         NV40VideoTexture(pScrn, src_offset, src_w, src_h, src_pitch, 0);
261         /* We've got NV12 format, which means half width and half height texture of chroma channels. */
262         NV40VideoTexture(pScrn, src_offset2, src_w/2, src_h/2, src_pitch, 1);
263
264         NV40_LoadVtxProg(pScrn, &nv40_video);
265         NV40_LoadFragProg(pScrn, &nv40_yv12);
266
267         /* Appears to be some kind of cache flush, needed here at least
268          * sometimes.. funky text rendering otherwise :)
269          */
270         BEGIN_RING(Nv3D, NV40TCL_TEX_CACHE_CTL, 1);
271         OUT_RING  (2);
272         BEGIN_RING(Nv3D, NV40TCL_TEX_CACHE_CTL, 1);
273         OUT_RING  (1);
274
275         /* These are fixed point values in the 16.16 format. */
276         x1 >>= 16;
277         x2 >>= 16;
278         y1 >>= 16;
279         y2 >>= 16;
280
281         X1 = (float)x1/(float)src_w;
282         Y1 = (float)y1/(float)src_h;
283         X2 = (float)x2/(float)src_w;
284         Y2 = (float)y2/(float)src_h;
285
286         /* The corrections here are emperical, i tried to explain them as best as possible. */
287
288         /* Fractions of the upperleft corner of the dstBox. */
289         /* Because all source coords are normalized. */
290         df_x1 = (float)(dstBox->x1)/(float)src_w;
291         df_y1 = (float)(dstBox->y1)/(float)src_h;
292
293         /* Draw ratio fractions. */
294         drf_x = (float)src_w/(float)drw_w;
295         drf_y = (float)src_h/(float)drw_h;
296
297         /* Just before rendering we wait for vblank in the non-composited case. */
298         if (pPriv->SyncToVBlank && !redirected) {
299                 uint8_t crtcs = nv_window_belongs_to_crtc(pScrn, dstBox->x1, dstBox->y1,
300                         dstBox->x2 - dstBox->x1, dstBox->y2 - dstBox->y1);
301
302                 FIRE_RING();
303                 if (crtcs & 0x1)
304                         NVWaitVSync(pScrn, 0);
305                 else if (crtcs & 0x2)
306                         NVWaitVSync(pScrn, 1);
307         }
308
309         BEGIN_RING(Nv3D, NV40TCL_BEGIN_END, 1);
310         OUT_RING (NV40TCL_BEGIN_END_TRIANGLES);
311
312         while(nbox--) {
313                 p_width = pbox->x2 - pbox->x1;
314                 p_height = pbox->y2 - pbox->y1;
315                 pf_width = (float)p_width/(float)src_w;
316                 pf_height = (float)p_height/(float)src_h;
317                 pf_x1 = (float)(pbox->x1)/(float)src_w;
318                 pf_y1 = (float)(pbox->y1)/(float)src_h;
319
320                 /* Let's use scissors, since we are drawing larger than our required drawing area. */
321                 /* Reminder: Even the pbox'es are drawn as triangles. */
322                 BEGIN_RING(Nv3D, NV40TCL_SCISSOR_HORIZ, 2);
323                 OUT_RING  ((p_width << 16) | pbox->x1);
324                 OUT_RING  ((p_height << 16) | pbox->y1);
325
326                 /* Submit the appropriate vertices. */
327                 /* This submits the same vertices for the Y and the UV texture. */
328                 /* All are screen coordinate references must be scaled to draw size. */
329                 /* I've included a little (svg) drawing for future reference. */
330                 VERTEX_OUT(X1 + (pf_x1 - df_x1) * drf_x, Y1 + (pf_y1 - df_y1 - pf_height) * drf_y, pbox->x1, pbox->y1 -  p_height);
331                 VERTEX_OUT(X1 + (pf_x1 - df_x1) * drf_x, Y1 + (pf_y1 - df_y1 + pf_height) * drf_y, pbox->x1, pbox->y2);
332                 VERTEX_OUT(X1 + (pf_x1 - df_x1 + 2*pf_width) * drf_x, Y1 + (pf_y1 - df_y1 + pf_height) * drf_y, pbox->x2 + p_width, pbox->y2);
333
334                 pbox++;
335         }
336
337         BEGIN_RING(Nv3D, NV40TCL_BEGIN_END, 1);
338         OUT_RING  (NV40TCL_BEGIN_END_STOP);
339
340         /* We can't leak state to exa. */
341         BEGIN_RING(Nv3D, NV40TCL_SCISSOR_HORIZ, 2);
342         OUT_RING  ((4096 << 16));
343         OUT_RING  ((4096 << 16));
344
345         FIRE_RING();
346
347         return Success;
348 }
349
350 /**
351  * NVSetTexturePortAttribute
352  * sets the attribute "attribute" of port "data" to value "value"
353  * supported attributes:
354  * Sync to vblank.
355  * 
356  * @param pScrenInfo
357  * @param attribute attribute to set
358  * @param value value to which attribute is to be set
359  * @param data port from which the attribute is to be set
360  * 
361  * @return Success, if setting is successful
362  * BadValue/BadMatch, if value/attribute are invalid
363  */
364 int
365 NVSetTexturePortAttribute(ScrnInfoPtr pScrn, Atom attribute,
366                        INT32 value, pointer data)
367 {
368         NVPortPrivPtr pPriv = (NVPortPrivPtr)data;
369         NVPtr           pNv = NVPTR(pScrn);
370
371         if ((attribute == xvSyncToVBlank) && pNv->WaitVSyncPossible) {
372                 if ((value < 0) || (value > 1))
373                         return BadValue;
374                 pPriv->SyncToVBlank = value;
375         } else
376         if (attribute == xvSetDefaults) {
377                 pPriv->SyncToVBlank = pNv->WaitVSyncPossible;
378         } else
379                 return BadMatch;
380
381         return Success;
382 }
383
384 /**
385  * NVGetTexturePortAttribute
386  * reads the value of attribute "attribute" from port "data" into INT32 "*value"
387  * Sync to vblank.
388  * 
389  * @param pScrn unused
390  * @param attribute attribute to be read
391  * @param value value of attribute will be stored here
392  * @param data port from which attribute will be read
393  * @return Success, if queried attribute exists
394  */
395 int
396 NVGetTexturePortAttribute(ScrnInfoPtr pScrn, Atom attribute,
397                        INT32 *value, pointer data)
398 {
399         NVPortPrivPtr pPriv = (NVPortPrivPtr)data;
400
401         if(attribute == xvSyncToVBlank)
402                 *value = (pPriv->SyncToVBlank) ? 1 : 0;
403         else
404                 return BadMatch;
405
406         return Success;
407 }
408