[WIN32] add yuy2/uyvy rendering (software and ps)
authorCrystalP <CrystalP@xbmc.org>
Tue, 1 Feb 2011 08:14:28 +0000 (03:14 -0500)
committerCrystalP <CrystalP@xbmc.org>
Mon, 7 Feb 2011 01:45:17 +0000 (20:45 -0500)
system/shaders/yuv2rgb_d3d.fx
xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.cpp
xbmc/cores/VideoRenderers/VideoShaders/WinVideoFilter.h
xbmc/cores/VideoRenderers/WinRenderer.cpp

index 984c032..610e12b 100644 (file)
@@ -23,6 +23,7 @@ texture g_YTexture;
 texture g_UTexture;
 texture g_VTexture;
 float4x4 g_ColorMatrix;
+float2  g_StepXY;
 
 sampler YSampler =
   sampler_state {
@@ -77,7 +78,35 @@ PS_OUTPUT YUV2RGB( VS_OUTPUT In)
   float4 YUV = float4(tex2D (YSampler, In.TextureY).x
                     , tex2D (USampler, In.TextureU).ra
                     , 1.0);
+#elif defined(XBMC_YUY2) || defined(XBMC_UYVY)
+  // The HLSL compiler is smart enough to optimize away these redundant assignments.
+  // That way the code is almost identical to the OGL shader.
+  float2 stepxy = g_StepXY;
+  float2 pos    = In.TextureY;
+  pos           = float2(pos.x - (stepxy.x * 0.25), pos.y);
+  float2 f      = frac(pos / stepxy);
+
+  //y axis will be correctly interpolated by opengl
+  //x axis will not, so we grab two pixels at the center of two columns and interpolate ourselves
+  float4 c1 = tex2D(YSampler, float2(pos.x + ((0.5 - f.x) * stepxy.x), pos.y));
+  float4 c2 = tex2D(YSampler, float2(pos.x + ((1.5 - f.x) * stepxy.x), pos.y));
+
+  /* each pixel has two Y subpixels and one UV subpixel
+      YUV  Y  YUV
+      check if we're left or right of the middle Y subpixel and interpolate accordingly*/
+  #if defined(XBMC_YUY2) // BGRA = YUYV
+    float  leftY  = lerp(c1.b, c1.r, f.x * 2.0);
+    float  rightY = lerp(c1.r, c2.b, f.x * 2.0 - 1.0);
+    float2 outUV  = lerp(c1.ga, c2.ga, f.x);
+  #elif defined(XBMC_UYVY) // BGRA = UYVY
+    float  leftY  = lerp(c1.g, c1.a, f.x * 2.0);
+    float  rightY = lerp(c1.a, c2.g, f.x * 2.0 - 1.0);
+    float2 outUV  = lerp(c1.br, c2.br, f.x);
+  #endif
+    float  outY   = lerp(leftY, rightY, step(0.5, f.x));
+    float4 YUV    = float4(outY, outUV, 1.0);
 #endif
+
   OUT.RGBColor = mul(YUV, g_ColorMatrix);
   OUT.RGBColor.a = 1.0;
   return OUT;
index 255b8a3..f5542fa 100644 (file)
@@ -198,45 +198,73 @@ bool CYUV2RGBShader::Create(unsigned int sourceWidth, unsigned int sourceHeight,
 
   CWinShader::CreateVertexBuffer(D3DFVF_XYZRHW | D3DFVF_TEX3, 4, sizeof(CUSTOMVERTEX), 2);
 
+  m_sourceWidth = sourceWidth;
+  m_sourceHeight = sourceHeight;
+
+  unsigned int texWidth;
+
   DefinesMap defines;
 
   if (fmt == YV12)
+  {
     defines["XBMC_YV12"] = "";
+    texWidth = sourceWidth;
+
+    if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
+    || !m_YUVPlanes[1].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
+    || !m_YUVPlanes[2].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT))
+    {
+      CLog::Log(LOGERROR, __FUNCTION__": Failed to create YV12 planes.");
+      return false;
+    }
+  }
   else if (fmt == NV12)
+  {
     defines["XBMC_NV12"] = "";
-  else
-    return false;
+    texWidth = sourceWidth;
 
-  CStdString effectString = "special://xbmc/system/shaders/yuv2rgb_d3d.fx";
-
-  if(!LoadEffect(effectString, &defines))
-  {
-    CLog::Log(LOGERROR, __FUNCTION__": Failed to load shader %s.", effectString.c_str());
-    return false;
+    if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
+    || !m_YUVPlanes[1].Create(texWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_A8L8, D3DPOOL_DEFAULT))
+    {
+      CLog::Log(LOGERROR, __FUNCTION__": Failed to create NV12 planes.");
+      return false;
+    }
   }
-
-  m_sourceWidth = sourceWidth;
-  m_sourceHeight = sourceHeight;
-
-  if (fmt == YV12)
+  else if (fmt == YUY2)
   {
-    if(!m_YUVPlanes[0].Create(m_sourceWidth    , m_sourceHeight    , 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
-    || !m_YUVPlanes[1].Create(m_sourceWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
-    || !m_YUVPlanes[2].Create(m_sourceWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT))
+    defines["XBMC_YUY2"] = "";
+    texWidth = sourceWidth >> 1;
+
+    if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT))
     {
-      CLog::Log(LOGERROR, __FUNCTION__": Failed to create YUV planes.");
+      CLog::Log(LOGERROR, __FUNCTION__": Failed to create YUY2 planes.");
       return false;
     }
   }
-  else if (fmt == NV12)
+  else if (fmt == UYVY)
   {
-    if(!m_YUVPlanes[0].Create(m_sourceWidth    , m_sourceHeight    , 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT)
-    || !m_YUVPlanes[1].Create(m_sourceWidth / 2, m_sourceHeight / 2, 1, 0, D3DFMT_A8L8, D3DPOOL_DEFAULT))
+    defines["XBMC_UYVY"] = "";
+    texWidth = sourceWidth >> 1;
+
+    if(!m_YUVPlanes[0].Create(texWidth    , m_sourceHeight    , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT))
     {
-      CLog::Log(LOGERROR, __FUNCTION__": Failed to create YUV planes.");
+      CLog::Log(LOGERROR, __FUNCTION__": Failed to create UYVY planes.");
       return false;
     }
   }
+  else
+    return false;
+
+  m_texSteps[0] = 1.0f/(float)texWidth;
+  m_texSteps[1] = 1.0f/(float)sourceHeight;
+
+  CStdString effectString = "special://xbmc/system/shaders/yuv2rgb_d3d.fx";
+
+  if(!LoadEffect(effectString, &defines))
+  {
+    CLog::Log(LOGERROR, __FUNCTION__": Failed to load shader %s.", effectString.c_str());
+    return false;
+  }
 
   return true;
 }
@@ -318,12 +346,14 @@ void CYUV2RGBShader::PrepareParameters(CRect sourceRect,
 
 void CYUV2RGBShader::SetShaderParameters(YUVBuffer* YUVbuf)
 {
-  m_effect.SetMatrix( "g_ColorMatrix", m_matrix.Matrix());
-  m_effect.SetTechnique( "YUV2RGB_T" );
-  m_effect.SetTexture( "g_YTexture",  m_YUVPlanes[0] ) ;
-  m_effect.SetTexture( "g_UTexture",  m_YUVPlanes[1] ) ;
-  if (YUVbuf->GetActivePlanes() == 3)
-    m_effect.SetTexture( "g_VTexture",  m_YUVPlanes[2] ) ;
+  m_effect.SetMatrix("g_ColorMatrix", m_matrix.Matrix());
+  m_effect.SetTechnique("YUV2RGB_T");
+  m_effect.SetTexture("g_YTexture", m_YUVPlanes[0]);
+  if (YUVbuf->GetActivePlanes() > 1)
+    m_effect.SetTexture("g_UTexture", m_YUVPlanes[1]);
+  if (YUVbuf->GetActivePlanes() > 2)
+    m_effect.SetTexture("g_VTexture", m_YUVPlanes[2]);
+  m_effect.SetFloatArray("g_StepXY", m_texSteps, sizeof(m_texSteps)/sizeof(m_texSteps[0]));
 }
 
 void CYUV2RGBShader::ReleaseInternal()
@@ -337,19 +367,18 @@ void CYUV2RGBShader::ReleaseInternal()
 
 bool CYUV2RGBShader::UploadToGPU(YUVBuffer* YUVbuf)
 {
-  const RECT rect = { 0, 0, m_sourceWidth, m_sourceHeight };
-  const RECT recthalf = { 0, 0, m_sourceWidth / 2, m_sourceHeight / 2};
   const POINT point = { 0, 0 };
 
   for (unsigned int i = 0; i<YUVbuf->GetActivePlanes(); i++)
   {
+    const RECT rect = { 0, 0, YUVbuf->planes[i].texture.GetWidth(), YUVbuf->planes[i].texture.GetHeight() };
     IDirect3DSurface9 *src, *dest;
     if(FAILED(YUVbuf->planes[i].texture.Get()->GetSurfaceLevel(0, &src)))
       CLog::Log(LOGERROR, __FUNCTION__": Failed to retrieve level 0 surface for source YUV plane %d", i);
     if (FAILED(m_YUVPlanes[i].Get()->GetSurfaceLevel(0, &dest)))
       CLog::Log(LOGERROR, __FUNCTION__": Failed to retrieve level 0 surface for destination YUV plane %d", i);
 
-    if (FAILED(g_Windowing.Get3DDevice()->UpdateSurface(src, i == 0 ? &rect : &recthalf, dest, &point)))
+    if (FAILED(g_Windowing.Get3DDevice()->UpdateSurface(src, &rect, dest, &point)))
     {
       CLog::Log(LOGERROR, __FUNCTION__": Failed to copy plane %d from sysmem to vidmem.", i);
       src->Release();
index c785ea4..046acba 100644 (file)
@@ -99,6 +99,7 @@ private:
   unsigned int   m_sourceWidth, m_sourceHeight;
   CRect          m_sourceRect, m_destRect;
   CD3DTexture    m_YUVPlanes[3];
+  float          m_texSteps[2];
 
   struct CUSTOMVERTEX {
       FLOAT x, y, z;
index 220c9be..356ac4b 100644 (file)
@@ -545,8 +545,6 @@ void CWinRenderer::UpdatePSVideoFilter()
 {
   SAFE_RELEASE(m_scalerShader)
 
-  BufferFormat format = BufferFormatFromFlags(m_flags);
-
   if (m_bUseHQScaler)
   {
     m_scalerShader = new CConvolutionShader();
@@ -569,6 +567,8 @@ void CWinRenderer::UpdatePSVideoFilter()
 
   SAFE_RELEASE(m_colorShader)
 
+  BufferFormat format = BufferFormatFromFlags(m_flags);
+
   if (m_bUseHQScaler)
   {
     m_colorShader = new CYUV2RGBShader();
@@ -1146,6 +1146,20 @@ bool YUVBuffer::Create(BufferFormat format, unsigned int width, unsigned int hei
       m_activeplanes = 2;
       break;
     }
+  case YUY2:
+    {
+      if ( !planes[PLANE_Y].texture.Create(m_width >> 1    , m_height    , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM))
+        return false;
+      m_activeplanes = 1;
+      break;
+    }
+  case UYVY:
+    {
+      if ( !planes[PLANE_Y].texture.Create(m_width >> 1    , m_height    , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM))
+        return false;
+      m_activeplanes = 1;
+      break;
+    }
   default:
     m_activeplanes = 0;
     return false;
@@ -1189,6 +1203,8 @@ void YUVBuffer::StartDecode()
 
 void YUVBuffer::Clear()
 {
+  // Set Y to 0 and U,V to 128 (RGB 0,0,0) to avoid visual artifacts at the start of playback
+
   switch(m_format)
   {
   case YV12:
@@ -1204,6 +1220,18 @@ void YUVBuffer::Clear()
       memset(planes[PLANE_UV].rect.pBits, 128, planes[PLANE_U].rect.Pitch * (m_height/2));
       break;
     }
+  // YUY2, UYVY: wmemset to set a 16bit pattern, byte-swapped because x86 is LE
+  case YUY2:
+    {
+      wmemset((wchar_t*)planes[PLANE_Y].rect.pBits, 0x8000, planes[PLANE_Y].rect.Pitch / 2 * m_height);
+      break;
+    }
+  case UYVY:
+    {
+      wmemset((wchar_t*)planes[PLANE_Y].rect.pBits, 0x0080, planes[PLANE_Y].rect.Pitch / 2 * m_height);
+      break;
+    }
+
   }
 }