f619cba69d27358553d61cfacf0bcfd5439fc1bf
[vuplus_xbmc] / xbmc / cores / VideoRenderers / VideoShaders / WinVideoFilter.cpp
1 /*
2  *      Copyright (C) 2007-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #ifdef HAS_DX
22
23 #include "WinVideoFilter.h"
24 #include "windowing/WindowingFactory.h"
25 #include "../../../utils/log.h"
26 #include "../../../FileSystem/File.h"
27 #include <map>
28 #include "ConvolutionKernels.h"
29 #include "YUV2RGBShader.h"
30 #include "win32/WIN32Util.h"
31 #include "cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h"
32
33 CYUV2RGBMatrix::CYUV2RGBMatrix()
34 {
35   m_NeedRecalc  = true;
36   m_blacklevel  = 0.0f;
37   m_contrast    = 0.0f;
38   m_flags       = 0;
39   m_format      = RENDER_FMT_NONE;
40 }
41
42 void CYUV2RGBMatrix::SetParameters(float contrast, float blacklevel, unsigned int flags, ERenderFormat format)
43 {
44   if (m_contrast != contrast)
45   {
46     m_NeedRecalc = true;
47     m_contrast = contrast;
48   }
49   if (m_blacklevel != blacklevel)
50   {
51     m_NeedRecalc = true;
52     m_blacklevel = blacklevel;
53   }
54   if (m_flags != flags)
55   {
56     m_NeedRecalc = true;
57     m_flags = flags;
58   }
59   if (m_format != format)
60   {
61     m_NeedRecalc = true;
62     m_format = format;
63   }
64 }
65
66 D3DXMATRIX* CYUV2RGBMatrix::Matrix()
67 {
68   if (m_NeedRecalc)
69   {
70     TransformMatrix matrix;
71     CalculateYUVMatrix(matrix, m_flags, m_format, m_blacklevel, m_contrast);
72
73     m_mat._11 = matrix.m[0][0];
74     m_mat._12 = matrix.m[1][0];
75     m_mat._13 = matrix.m[2][0];
76     m_mat._14 = 0.0f;
77     m_mat._21 = matrix.m[0][1];
78     m_mat._22 = matrix.m[1][1];
79     m_mat._23 = matrix.m[2][1];
80     m_mat._24 = 0.0f;
81     m_mat._31 = matrix.m[0][2];
82     m_mat._32 = matrix.m[1][2];
83     m_mat._33 = matrix.m[2][2];
84     m_mat._34 = 0.0f;
85     m_mat._41 = matrix.m[0][3];
86     m_mat._42 = matrix.m[1][3];
87     m_mat._43 = matrix.m[2][3];
88     m_mat._44 = 1.0f;
89
90     m_NeedRecalc = false;
91   }
92   return &m_mat;
93 }
94
95 //===================================================================
96
97 CWinShader::~CWinShader()
98 {
99   if (m_effect.Get())
100     m_effect.Release();
101
102   if (m_vb.Get())
103     m_vb.Release();
104 }
105
106 bool CWinShader::CreateVertexBuffer(DWORD FVF, unsigned int vertCount, unsigned int vertSize, unsigned int primitivesCount)
107 {
108   if (!m_vb.Create(vertCount * vertSize, D3DUSAGE_WRITEONLY, FVF, g_Windowing.DefaultD3DPool()))
109     return false;
110   m_vbsize = vertCount * vertSize;
111   m_FVF = FVF;
112   m_vertsize = vertSize;
113   m_primitivesCount = primitivesCount;
114   return true;
115 }
116
117 bool CWinShader::LockVertexBuffer(void **data)
118 {
119   if (!m_vb.Lock(0, m_vbsize, data, 0))
120   {
121     CLog::Log(LOGERROR, __FUNCTION__" - failed to lock vertex buffer");
122     return false;
123   }
124   return true;
125 }
126
127 bool CWinShader::UnlockVertexBuffer()
128 {
129   if (!m_vb.Unlock())
130   {
131     CLog::Log(LOGERROR, __FUNCTION__" - failed to unlock vertex buffer");
132     return false;
133   }
134   return true;
135 }
136
137 bool CWinShader::LoadEffect(CStdString filename, DefinesMap* defines)
138 {
139   CLog::Log(LOGDEBUG, __FUNCTION__" - loading shader %s", filename.c_str());
140
141   XFILE::CFileStream file;
142   if(!file.Open(filename))
143   {
144     CLog::Log(LOGERROR, __FUNCTION__" - failed to open file %s", filename.c_str());
145     return false;
146   }
147
148   CStdString pStrEffect;
149   getline(file, pStrEffect, '\0');
150
151   if (!m_effect.Create(pStrEffect, defines))
152   {
153     CLog::Log(LOGERROR, __FUNCTION__" %s failed", pStrEffect.c_str());
154     return false;
155   }
156
157   return true;
158 }
159
160 bool CWinShader::Execute(std::vector<LPDIRECT3DSURFACE9> *vecRT, unsigned int vertexIndexStep)
161 {
162   LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
163
164   LPDIRECT3DSURFACE9 oldRT = 0;
165   // The render target will be overriden: save the caller's original RT
166   if (vecRT != NULL && !vecRT->empty())
167     pD3DDevice->GetRenderTarget(0, &oldRT);
168
169   pD3DDevice->SetFVF(m_FVF);
170   pD3DDevice->SetStreamSource(0, m_vb.Get(), 0, m_vertsize);
171
172   UINT cPasses, iPass;
173   if (!m_effect.Begin( &cPasses, 0 ))
174   {
175     CLog::Log(LOGERROR, __FUNCTION__" - failed to begin d3d effect");
176     return false;
177   }
178
179   for( iPass = 0; iPass < cPasses; iPass++ )
180   {
181     if (!m_effect.BeginPass( iPass ))
182     {
183       CLog::Log(LOGERROR, __FUNCTION__" - failed to begin d3d effect pass");
184       break;
185     }
186
187     if (vecRT != NULL && vecRT->size() > iPass)
188       pD3DDevice->SetRenderTarget(0, (*vecRT)[iPass]);
189
190     HRESULT hr = pD3DDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, iPass * vertexIndexStep, m_primitivesCount);
191     if (FAILED(hr))
192       CLog::Log(LOGERROR, __FUNCTION__" - failed DrawPrimitive %08X", hr);
193
194     if (!m_effect.EndPass())
195       CLog::Log(LOGERROR, __FUNCTION__" - failed to end d3d effect pass");
196   }
197   if (!m_effect.End())
198     CLog::Log(LOGERROR, __FUNCTION__" - failed to end d3d effect");
199
200   if (oldRT != 0)
201   {
202     pD3DDevice->SetRenderTarget(0, oldRT);
203     oldRT->Release();
204   }
205
206   return true;
207 }
208
209 //==================================================================================
210
211 bool CYUV2RGBShader::Create(unsigned int sourceWidth, unsigned int sourceHeight, ERenderFormat fmt)
212 {
213   CWinShader::CreateVertexBuffer(D3DFVF_XYZRHW | D3DFVF_TEX3, 4, sizeof(CUSTOMVERTEX), 2);
214
215   m_sourceWidth = sourceWidth;
216   m_sourceHeight = sourceHeight;
217   m_format = fmt;
218
219   unsigned int texWidth;
220
221   DefinesMap defines;
222
223   if (fmt == RENDER_FMT_YUV420P16)
224   {
225     defines["XBMC_YV12"] = "";
226     texWidth = sourceWidth;
227
228     if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_L16, D3DPOOL_DEFAULT)
229     || !m_YUVPlanes[1].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L16, D3DPOOL_DEFAULT)
230     || !m_YUVPlanes[2].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L16, D3DPOOL_DEFAULT))
231     {
232       CLog::Log(LOGERROR, __FUNCTION__": Failed to create 16 bit YV12 planes.");
233       return false;
234     }
235   }
236   else if (fmt == RENDER_FMT_YUV420P10)
237   {
238     defines["XBMC_YV12"] = "";
239     texWidth = sourceWidth;
240
241     if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_L16, D3DPOOL_DEFAULT)
242     || !m_YUVPlanes[1].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L16, D3DPOOL_DEFAULT)
243     || !m_YUVPlanes[2].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L16, D3DPOOL_DEFAULT))
244     {
245       CLog::Log(LOGERROR, __FUNCTION__": Failed to create 10 bit YV12 planes.");
246       return false;
247     }
248   }
249   else if (fmt == RENDER_FMT_YUV420P)
250   {
251     defines["XBMC_YV12"] = "";
252     texWidth = sourceWidth;
253
254     if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
255     || !m_YUVPlanes[1].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
256     || !m_YUVPlanes[2].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT))
257     {
258       CLog::Log(LOGERROR, __FUNCTION__": Failed to create YV12 planes.");
259       return false;
260     }
261   }
262   else if (fmt == RENDER_FMT_NV12)
263   {
264     defines["XBMC_NV12"] = "";
265     texWidth = sourceWidth;
266
267     if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
268     || !m_YUVPlanes[1].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_A8L8, D3DPOOL_DEFAULT))
269     {
270       CLog::Log(LOGERROR, __FUNCTION__": Failed to create NV12 planes.");
271       return false;
272     }
273   }
274   else if (fmt == RENDER_FMT_YUYV422)
275   {
276     defines["XBMC_YUY2"] = "";
277     texWidth = sourceWidth >> 1;
278
279     if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT))
280     {
281       CLog::Log(LOGERROR, __FUNCTION__": Failed to create YUY2 planes.");
282       return false;
283     }
284   }
285   else if (fmt == RENDER_FMT_UYVY422)
286   {
287     defines["XBMC_UYVY"] = "";
288     texWidth = sourceWidth >> 1;
289
290     if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT))
291     {
292       CLog::Log(LOGERROR, __FUNCTION__": Failed to create UYVY planes.");
293       return false;
294     }
295   }
296   else
297     return false;
298
299   m_texSteps[0] = 1.0f/(float)texWidth;
300   m_texSteps[1] = 1.0f/(float)sourceHeight;
301
302   CStdString effectString = "special://xbmc/system/shaders/yuv2rgb_d3d.fx";
303
304   if(!LoadEffect(effectString, &defines))
305   {
306     CLog::Log(LOGERROR, __FUNCTION__": Failed to load shader %s.", effectString.c_str());
307     return false;
308   }
309
310   return true;
311 }
312
313 void CYUV2RGBShader::Render(CRect sourceRect, CRect destRect,
314                             float contrast,
315                             float brightness,
316                             unsigned int flags,
317                             YUVBuffer* YUVbuf)
318 {
319   PrepareParameters(sourceRect, destRect,
320                     contrast, brightness, flags);
321   UploadToGPU(YUVbuf);
322   SetShaderParameters(YUVbuf);
323   Execute(NULL,4);
324 }
325
326 CYUV2RGBShader::~CYUV2RGBShader()
327 {
328   for(unsigned i = 0; i < MAX_PLANES; i++)
329   {
330     if (m_YUVPlanes[i].Get())
331       m_YUVPlanes[i].Release();
332   }
333 }
334
335 void CYUV2RGBShader::PrepareParameters(CRect sourceRect,
336                                        CRect destRect,
337                                        float contrast,
338                                        float brightness,
339                                        unsigned int flags)
340 {
341   //See RGB renderer for comment on this
342   #define CHROMAOFFSET_HORIZ 0.25f
343
344   if (m_sourceRect != sourceRect || m_destRect != destRect)
345   {
346     m_sourceRect = sourceRect;
347     m_destRect = destRect;
348
349     CUSTOMVERTEX* v;
350     CWinShader::LockVertexBuffer((void**)&v);
351
352     v[0].x = destRect.x1;
353     v[0].y = destRect.y1;
354     v[0].tu = sourceRect.x1 / m_sourceWidth;
355     v[0].tv = sourceRect.y1 / m_sourceHeight;
356     v[0].tu2 = v[0].tu3 = (sourceRect.x1 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceWidth>>1);
357     v[0].tv2 = v[0].tv3 = (sourceRect.y1 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceHeight>>1);
358
359     v[1].x = destRect.x2;
360     v[1].y = destRect.y1;
361     v[1].tu = sourceRect.x2 / m_sourceWidth;
362     v[1].tv = sourceRect.y1 / m_sourceHeight;
363     v[1].tu2 = v[1].tu3 = (sourceRect.x2 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceWidth>>1);
364     v[1].tv2 = v[1].tv3 = (sourceRect.y1 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceHeight>>1);
365
366     v[2].x = destRect.x2;
367     v[2].y = destRect.y2;
368     v[2].tu = sourceRect.x2 / m_sourceWidth;
369     v[2].tv = sourceRect.y2 / m_sourceHeight;
370     v[2].tu2 = v[2].tu3 = (sourceRect.x2 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceWidth>>1);
371     v[2].tv2 = v[2].tv3 = (sourceRect.y2 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceHeight>>1);
372
373     v[3].x = destRect.x1;
374     v[3].y = destRect.y2;
375     v[3].tu = sourceRect.x1 / m_sourceWidth;
376     v[3].tv = sourceRect.y2 / m_sourceHeight;
377     v[3].tu2 = v[3].tu3 = (sourceRect.x1 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceWidth>>1);
378     v[3].tv2 = v[3].tv3 = (sourceRect.y2 / 2.0f + CHROMAOFFSET_HORIZ) / (m_sourceHeight>>1);
379
380     // -0.5 offset to compensate for D3D rasterization
381     // set z and rhw
382     for(int i = 0; i < 4; i++)
383     {
384       v[i].x -= 0.5;
385       v[i].y -= 0.5;
386       v[i].z = 0.0f;
387       v[i].rhw = 1.0f;
388     }
389     CWinShader::UnlockVertexBuffer();
390   }
391
392   m_matrix.SetParameters(contrast * 0.02f,
393                          brightness * 0.01f - 0.5f,
394                          flags,
395                          m_format);
396 }
397
398 void CYUV2RGBShader::SetShaderParameters(YUVBuffer* YUVbuf)
399 {
400   m_effect.SetMatrix("g_ColorMatrix", m_matrix.Matrix());
401   m_effect.SetTechnique("YUV2RGB_T");
402   m_effect.SetTexture("g_YTexture", m_YUVPlanes[0]);
403   if (YUVbuf->GetActivePlanes() > 1)
404     m_effect.SetTexture("g_UTexture", m_YUVPlanes[1]);
405   if (YUVbuf->GetActivePlanes() > 2)
406     m_effect.SetTexture("g_VTexture", m_YUVPlanes[2]);
407   m_effect.SetFloatArray("g_StepXY", m_texSteps, sizeof(m_texSteps)/sizeof(m_texSteps[0]));
408 }
409
410 bool CYUV2RGBShader::UploadToGPU(YUVBuffer* YUVbuf)
411 {
412   const POINT point = { 0, 0 };
413
414   for (unsigned int i = 0; i<YUVbuf->GetActivePlanes(); i++)
415   {
416     const RECT rect = { 0, 0, YUVbuf->planes[i].texture.GetWidth(), YUVbuf->planes[i].texture.GetHeight() };
417     IDirect3DSurface9 *src, *dest;
418     if(FAILED(YUVbuf->planes[i].texture.Get()->GetSurfaceLevel(0, &src)))
419       CLog::Log(LOGERROR, __FUNCTION__": Failed to retrieve level 0 surface for source YUV plane %d", i);
420     if (FAILED(m_YUVPlanes[i].Get()->GetSurfaceLevel(0, &dest)))
421       CLog::Log(LOGERROR, __FUNCTION__": Failed to retrieve level 0 surface for destination YUV plane %d", i);
422
423     if (FAILED(g_Windowing.Get3DDevice()->UpdateSurface(src, &rect, dest, &point)))
424     {
425       CLog::Log(LOGERROR, __FUNCTION__": Failed to copy plane %d from sysmem to vidmem.", i);
426       src->Release();
427       dest->Release();
428       return false;
429     }
430     src->Release();
431     dest->Release();
432   }
433   return true;
434 }
435
436 //==================================================================================
437
438 CConvolutionShader::~CConvolutionShader()
439 {
440   if(m_HQKernelTexture.Get())
441     m_HQKernelTexture.Release();
442 }
443
444 bool CConvolutionShader::ChooseKernelD3DFormat()
445 {
446   if (g_Windowing.IsTextureFormatOk(D3DFMT_A16B16G16R16F, 0))
447   {
448     m_KernelFormat = D3DFMT_A16B16G16R16F;
449     m_floattex = true;
450     m_rgba = true;
451   }
452   else if (g_Windowing.IsTextureFormatOk(D3DFMT_A8B8G8R8, 0))
453   {
454     m_KernelFormat = D3DFMT_A8B8G8R8;
455     m_floattex = false;
456     m_rgba = true;
457   }
458   else if (g_Windowing.IsTextureFormatOk(D3DFMT_A8R8G8B8, 0))
459   {
460     m_KernelFormat = D3DFMT_A8R8G8B8;
461     m_floattex = false;
462     m_rgba = false;
463   }
464   else
465     return false;
466
467   return true;
468 }
469
470 bool CConvolutionShader::CreateHQKernel(ESCALINGMETHOD method)
471 {
472   CConvolutionKernel kern(method, 256);
473
474   if (!m_HQKernelTexture.Create(kern.GetSize(), 1, 1, g_Windowing.DefaultD3DUsage(), m_KernelFormat, g_Windowing.DefaultD3DPool()))
475   {
476     CLog::Log(LOGERROR, __FUNCTION__": Failed to create kernel texture.");
477     return false;
478   }
479
480   void *kernelVals;
481   int kernelValsSize;
482
483   if (m_floattex)
484   {
485     float *rawVals = kern.GetFloatPixels();
486     D3DXFLOAT16* float16Vals = new D3DXFLOAT16[kern.GetSize()*4];
487
488     for(int i = 0; i < kern.GetSize()*4; i++)
489       float16Vals[i] = rawVals[i];
490     kernelVals = float16Vals;
491     kernelValsSize = sizeof(D3DXFLOAT16)*kern.GetSize()*4;
492   }
493   else
494   {
495     kernelVals = kern.GetUint8Pixels();
496     kernelValsSize = sizeof(uint8_t)*kern.GetSize()*4;
497   }
498
499   D3DLOCKED_RECT lr;
500   if (!m_HQKernelTexture.LockRect(0, &lr, NULL, D3DLOCK_DISCARD))
501     CLog::Log(LOGERROR, __FUNCTION__": Failed to lock kernel texture.");
502   memcpy(lr.pBits, kernelVals, kernelValsSize);
503   if (!m_HQKernelTexture.UnlockRect(0))
504     CLog::Log(LOGERROR, __FUNCTION__": Failed to unlock kernel texture.");
505
506   if (m_floattex)
507     delete[] kernelVals;
508
509   return true;
510 }
511 //==================================================================================
512 bool CConvolutionShader1Pass::Create(ESCALINGMETHOD method)
513 {
514   CStdString effectString;
515   switch(method)
516   {
517     case VS_SCALINGMETHOD_CUBIC:
518     case VS_SCALINGMETHOD_LANCZOS2:
519     case VS_SCALINGMETHOD_SPLINE36_FAST:
520     case VS_SCALINGMETHOD_LANCZOS3_FAST:
521       effectString = "special://xbmc/system/shaders/convolution-4x4_d3d.fx";
522       break;
523     case VS_SCALINGMETHOD_SPLINE36:
524     case VS_SCALINGMETHOD_LANCZOS3:
525       effectString = "special://xbmc/system/shaders/convolution-6x6_d3d.fx";
526       break;
527     default:
528       CLog::Log(LOGERROR, __FUNCTION__": scaling method %d not supported.", method);
529       return false;
530   }
531
532   if (!ChooseKernelD3DFormat())
533   {
534     CLog::Log(LOGERROR, __FUNCTION__": failed to find a compatible texture format for the kernel.");
535     return false;
536   }
537
538   CWinShader::CreateVertexBuffer(D3DFVF_XYZRHW | D3DFVF_TEX1, 4, sizeof(CUSTOMVERTEX), 2);
539
540   DefinesMap defines;
541   if (m_floattex)
542     defines["HAS_FLOAT_TEXTURE"] = "";
543   if (m_rgba)
544     defines["HAS_RGBA"] = "";
545
546   if(!LoadEffect(effectString, &defines))
547   {
548     CLog::Log(LOGERROR, __FUNCTION__": Failed to load shader %s.", effectString.c_str());
549     return false;
550   }
551
552   if (!CreateHQKernel(method))
553     return false;
554
555   return true;
556 }
557
558 void CConvolutionShader1Pass::Render(CD3DTexture &sourceTexture,
559                                 unsigned int sourceWidth, unsigned int sourceHeight,
560                                 unsigned int destWidth, unsigned int destHeight,
561                                 CRect sourceRect,
562                                 CRect destRect)
563 {
564   PrepareParameters(sourceWidth, sourceHeight, sourceRect, destRect);
565   float texSteps[] = { 1.0f/(float)sourceWidth, 1.0f/(float)sourceHeight};
566   SetShaderParameters(sourceTexture, &texSteps[0], sizeof(texSteps)/sizeof(texSteps[0]));
567   Execute(NULL,4);
568 }
569
570 void CConvolutionShader1Pass::PrepareParameters(unsigned int sourceWidth, unsigned int sourceHeight,
571                                            CRect sourceRect,
572                                            CRect destRect)
573 {
574   if(m_sourceWidth != sourceWidth || m_sourceHeight != sourceHeight
575   || m_sourceRect != sourceRect || m_destRect != destRect)
576   {
577     m_sourceWidth = sourceWidth;
578     m_sourceHeight = sourceHeight;
579     m_sourceRect = sourceRect;
580     m_destRect = destRect;
581
582     CUSTOMVERTEX* v;
583     CWinShader::LockVertexBuffer((void**)&v);
584
585     v[0].x = destRect.x1;
586     v[0].y = destRect.y1;
587     v[0].tu = sourceRect.x1 / sourceWidth;
588     v[0].tv = sourceRect.y1 / sourceHeight;
589
590     v[1].x = destRect.x2;
591     v[1].y = destRect.y1;
592     v[1].tu = sourceRect.x2 / sourceWidth;
593     v[1].tv = sourceRect.y1 / sourceHeight;
594
595     v[2].x = destRect.x2;
596     v[2].y = destRect.y2;
597     v[2].tu = sourceRect.x2 / sourceWidth;
598     v[2].tv = sourceRect.y2 / sourceHeight;
599
600     v[3].x = destRect.x1;
601     v[3].y = destRect.y2;
602     v[3].tu = sourceRect.x1 / sourceWidth;
603     v[3].tv = sourceRect.y2 / sourceHeight;
604
605     // -0.5 offset to compensate for D3D rasterization
606     // set z and rhw
607     for(int i = 0; i < 4; i++)
608     {
609       v[i].x -= 0.5;
610       v[i].y -= 0.5;
611       v[i].z = 0.0f;
612       v[i].rhw = 1.0f;
613     }
614
615     CWinShader::UnlockVertexBuffer();
616   }
617 }
618
619 void CConvolutionShader1Pass::SetShaderParameters(CD3DTexture &sourceTexture, float* texSteps, int texStepsCount)
620 {
621   m_effect.SetTechnique( "SCALER_T" );
622   m_effect.SetTexture( "g_Texture",  sourceTexture ) ;
623   m_effect.SetTexture( "g_KernelTexture", m_HQKernelTexture );
624   m_effect.SetFloatArray("g_StepXY", texSteps, texStepsCount);
625 }
626
627 //==================================================================================
628
629 CConvolutionShaderSeparable::CConvolutionShaderSeparable()
630 {
631   m_sourceWidth = -1;
632   m_sourceHeight = -1;
633   m_destWidth = -1;
634   m_destHeight = -1;
635 }
636
637 bool CConvolutionShaderSeparable::Create(ESCALINGMETHOD method)
638 {
639   CStdString effectString;
640   switch(method)
641   {
642     case VS_SCALINGMETHOD_CUBIC:
643     case VS_SCALINGMETHOD_LANCZOS2:
644     case VS_SCALINGMETHOD_SPLINE36_FAST:
645     case VS_SCALINGMETHOD_LANCZOS3_FAST:
646       effectString = "special://xbmc/system/shaders/convolutionsep-4x4_d3d.fx";
647       break;
648     case VS_SCALINGMETHOD_SPLINE36:
649     case VS_SCALINGMETHOD_LANCZOS3:
650       effectString = "special://xbmc/system/shaders/convolutionsep-6x6_d3d.fx";
651       break;
652     default:
653       CLog::Log(LOGERROR, __FUNCTION__": scaling method %d not supported.", method);
654       return false;
655   }
656
657   if (!ChooseIntermediateD3DFormat())
658   {
659     CLog::Log(LOGERROR, __FUNCTION__": failed to find a compatible texture format for the intermediate render target.");
660     return false;
661   }
662
663   if (!ChooseKernelD3DFormat())
664   {
665     CLog::Log(LOGERROR, __FUNCTION__": failed to find a compatible texture format for the kernel.");
666     return false;
667   }
668
669   CWinShader::CreateVertexBuffer(D3DFVF_XYZRHW | D3DFVF_TEX1, 8, sizeof(CUSTOMVERTEX), 2);
670
671   DefinesMap defines;
672   if (m_floattex)
673     defines["HAS_FLOAT_TEXTURE"] = "";
674   if (m_rgba)
675     defines["HAS_RGBA"] = "";
676
677   if(!LoadEffect(effectString, &defines))
678   {
679     CLog::Log(LOGERROR, __FUNCTION__": Failed to load shader %s.", effectString.c_str());
680     return false;
681   }
682
683   if (!CreateHQKernel(method))
684     return false;
685
686   return true;
687 }
688
689 void CConvolutionShaderSeparable::Render(CD3DTexture &sourceTexture,
690                                 unsigned int sourceWidth, unsigned int sourceHeight,
691                                 unsigned int destWidth, unsigned int destHeight,
692                                 CRect sourceRect,
693                                 CRect destRect)
694 {
695   LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
696
697   if(m_destWidth != destWidth || m_sourceHeight != sourceHeight)
698     CreateIntermediateRenderTarget(destWidth, sourceHeight);
699
700   PrepareParameters(sourceWidth, sourceHeight, destWidth, destHeight, sourceRect, destRect);
701   float texSteps1[] = { 1.0f/(float)sourceWidth, 1.0f/(float)sourceHeight};
702   float texSteps2[] = { 1.0f/(float)destWidth, 1.0f/(float)(sourceHeight)};
703   SetShaderParameters(sourceTexture, &texSteps1[0], sizeof(texSteps1)/sizeof(texSteps1[0]), &texSteps2[0], sizeof(texSteps2)/sizeof(texSteps2[0]));
704
705   // This part should be cleaned up, but how?
706   std::vector<LPDIRECT3DSURFACE9> rts;
707   LPDIRECT3DSURFACE9 intRT, currentRT;
708   m_IntermediateTarget.GetSurfaceLevel(0, &intRT);
709   pD3DDevice->GetRenderTarget(0, &currentRT);
710   rts.push_back(intRT);
711   rts.push_back(currentRT);
712   Execute(&rts, 4);
713   intRT->Release();
714   currentRT->Release();
715 }
716
717 CConvolutionShaderSeparable::~CConvolutionShaderSeparable()
718 {
719   if (m_IntermediateTarget.Get())
720     m_IntermediateTarget.Release();
721 }
722
723 bool CConvolutionShaderSeparable::ChooseIntermediateD3DFormat()
724 {
725   DWORD usage = D3DUSAGE_RENDERTARGET;
726
727   // Need a float texture, as the output of the first pass can contain negative values.
728   if      (g_Windowing.IsTextureFormatOk(D3DFMT_A16B16G16R16F, usage)) m_IntermediateFormat = D3DFMT_A16B16G16R16F;
729   else if (g_Windowing.IsTextureFormatOk(D3DFMT_A32B32G32R32F, usage)) m_IntermediateFormat = D3DFMT_A32B32G32R32F;
730   else
731   {
732     CLog::Log(LOGNOTICE, __FUNCTION__": no float format available for the intermediate render target");
733     return false;
734   }
735
736   CLog::Log(LOGDEBUG, __FUNCTION__": format %i", m_IntermediateFormat);
737
738   return true;
739 }
740
741 bool CConvolutionShaderSeparable::CreateIntermediateRenderTarget(unsigned int width, unsigned int height)
742 {
743   if (m_IntermediateTarget.Get())
744     m_IntermediateTarget.Release();
745
746   if(!m_IntermediateTarget.Create(width, height, 1, D3DUSAGE_RENDERTARGET, m_IntermediateFormat, D3DPOOL_DEFAULT))
747   {
748     CLog::Log(LOGERROR, __FUNCTION__": render target creation failed.");
749     return false;
750   }
751   return true;
752 }
753
754 bool CConvolutionShaderSeparable::ClearIntermediateRenderTarget()
755 {
756   LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
757
758   LPDIRECT3DSURFACE9 currentRT;
759   pD3DDevice->GetRenderTarget(0, &currentRT);
760
761   LPDIRECT3DSURFACE9 intermediateRT;
762   m_IntermediateTarget.GetSurfaceLevel(0, &intermediateRT);
763
764   pD3DDevice->SetRenderTarget(0, intermediateRT);
765
766   pD3DDevice->Clear(0L, NULL, D3DCLEAR_TARGET, 0L, 1.0f, 0L);
767
768   pD3DDevice->SetRenderTarget(0, currentRT);
769   currentRT->Release();
770   intermediateRT->Release();
771
772   return true;
773 }
774
775 void CConvolutionShaderSeparable::PrepareParameters(unsigned int sourceWidth, unsigned int sourceHeight,
776                                            unsigned int destWidth, unsigned int destHeight,
777                                            CRect sourceRect,
778                                            CRect destRect)
779 {
780   if(m_sourceWidth != sourceWidth || m_sourceHeight != sourceHeight
781   || m_destWidth != destWidth || m_destHeight != destHeight
782   || m_sourceRect != sourceRect || m_destRect != destRect)
783   {
784     // fixme better: clearing the whole render target when changing the source/dest rect is not optimal.
785     // Problem is that the edges of the final picture may retain content when the rects change.
786     // For example when changing zoom value, the edges can retain content from the previous zoom value.
787     // Playing with coordinates was unsuccessful so far, this is a quick fix for release.
788     ClearIntermediateRenderTarget();
789
790     m_sourceWidth = sourceWidth;
791     m_sourceHeight = sourceHeight;
792     m_destWidth = destWidth;
793     m_destHeight = destHeight;
794     m_sourceRect = sourceRect;
795     m_destRect = destRect;
796
797     CUSTOMVERTEX* v;
798     CWinShader::LockVertexBuffer((void**)&v);
799
800     // Alter rectangles the destination rectangle exceeds the intermediate target width when zooming and causes artifacts.
801     // Work on the parameters rather than the members to avoid disturbing the parameter change detection the next time the function is called
802     CRect tgtRect(0, 0, destWidth, destHeight);
803     CWIN32Util::CropSource(sourceRect, destRect, tgtRect);
804
805     // Manipulate the coordinates to work only on the active parts of the textures,
806     // and therefore avoid the need to clear surfaces/render targets
807
808     // Pass 1:
809     // Horizontal dimension: crop/zoom, so that it is completely done with the convolution shader. Scaling to display width in pass1 and
810     // cropping/zooming in pass 2 would use bilinear in pass2, which we don't want.
811     // Vertical dimension: crop using sourceRect to save memory bandwidth for high zoom values, but don't stretch/shrink in any way in this pass.
812     
813     v[0].x = 0;
814     v[0].y = 0;
815     v[0].tu = sourceRect.x1 / sourceWidth;
816     v[0].tv = sourceRect.y1 / sourceHeight;
817
818     v[1].x = destRect.x2 - destRect.x1;
819     v[1].y = 0;
820     v[1].tu = sourceRect.x2 / sourceWidth;
821     v[1].tv = sourceRect.y1 / sourceHeight;
822
823     v[2].x = destRect.x2 - destRect.x1;
824     v[2].y = sourceRect.y2 - sourceRect.y1;
825     v[2].tu = sourceRect.x2 / sourceWidth;
826     v[2].tv = sourceRect.y2 / sourceHeight;
827
828     v[3].x = 0;
829     v[3].y = sourceRect.y2 - sourceRect.y1;
830     v[3].tu = sourceRect.x1 / sourceWidth;
831     v[3].tv = sourceRect.y2 / sourceHeight;
832
833     // Pass 2: pass the horizontal data untouched, resize vertical dimension for final result.
834
835     v[4].x = destRect.x1;
836     v[4].y = destRect.y1;
837     v[4].tu = 0;
838     v[4].tv = 0;
839
840     v[5].x = destRect.x2;
841     v[5].y = destRect.y1;
842     v[5].tu = (destRect.x2 - destRect.x1) / destWidth;
843     v[5].tv = 0;
844
845     v[6].x = destRect.x2;
846     v[6].y = destRect.y2;
847     v[6].tu = (destRect.x2 - destRect.x1) / destWidth;
848     v[6].tv = (sourceRect.y2 - sourceRect.y1) / sourceHeight;
849
850     v[7].x = destRect.x1;
851     v[7].y = destRect.y2;
852     v[7].tu = 0;
853     v[7].tv = (sourceRect.y2 - sourceRect.y1) / sourceHeight;
854
855     // -0.5 offset to compensate for D3D rasterization
856     // set z and rhw
857     for(int i = 0; i < 8; i++)
858     {
859       v[i].x -= 0.5;
860       v[i].y -= 0.5;
861       v[i].z = 0.0f;
862       v[i].rhw = 1.0f;
863     }
864
865     CWinShader::UnlockVertexBuffer();
866   }
867 }
868
869 void CConvolutionShaderSeparable::SetShaderParameters(CD3DTexture &sourceTexture, float* texSteps1, int texStepsCount1, float* texSteps2, int texStepsCount2)
870 {
871   m_effect.SetTechnique( "SCALER_T" );
872   m_effect.SetTexture( "g_Texture",  sourceTexture ) ;
873   m_effect.SetTexture( "g_KernelTexture", m_HQKernelTexture );
874   m_effect.SetTexture( "g_IntermediateTexture",  m_IntermediateTarget ) ;
875   m_effect.SetFloatArray("g_StepXY_P0", texSteps1, texStepsCount1);
876   m_effect.SetFloatArray("g_StepXY_P1", texSteps2, texStepsCount2);
877 }
878
879
880 //==========================================================
881
882 bool CTestShader::Create()
883 {
884   CStdString effectString = "special://xbmc/system/shaders/testshader.fx";
885
886   if(!LoadEffect(effectString, NULL))
887   {
888     CLog::Log(LOGERROR, __FUNCTION__": Failed to load shader %s.", effectString.c_str());
889     return false;
890   }
891   return true;
892 }
893
894 #endif