2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
23 #include "WinRenderer.h"
25 #include "settings/Settings.h"
26 #include "settings/GUISettings.h"
27 #include "guilib/Texture.h"
28 #include "windowing/WindowingFactory.h"
29 #include "settings/AdvancedSettings.h"
30 #include "threads/SingleLock.h"
31 #include "utils/log.h"
32 #include "filesystem/File.h"
33 #include "utils/MathUtils.h"
34 #include "VideoShaders/WinVideoFilter.h"
35 #include "DllSwScale.h"
36 #include "guilib/LocalizeStrings.h"
37 #include "dialogs/GUIDialogKaiToast.h"
38 #include "win32/WIN32Util.h"
39 #include "cores/dvdplayer/DVDCodecs/Video/DVDVideoCodec.h"
46 static RenderMethodDetail RenderMethodDetails[] = {
47 { RENDER_SW , "Software" },
48 { RENDER_PS , "Pixel Shaders" },
49 { RENDER_DXVA , "DXVA" },
50 { RENDER_INVALID, NULL }
53 static RenderMethodDetail *FindRenderMethod(RenderMethod m)
55 for (unsigned i = 0; RenderMethodDetails[i].method != RENDER_INVALID; i++) {
56 if (RenderMethodDetails[i].method == m)
57 return &RenderMethodDetails[i];
62 CWinRenderer::CWinRenderer()
64 m_iYV12RenderBuffer = 0;
68 m_scalerShader = NULL;
69 m_extended_format = 0;
71 m_iRequestedMethod = RENDER_METHOD_AUTO;
73 m_renderMethod = RENDER_PS;
74 m_scalingMethod = VS_SCALINGMETHOD_LINEAR;
75 m_scalingMethodGui = (ESCALINGMETHOD)-1;
76 m_TextureFilter = D3DTEXF_POINT;
78 m_bUseHQScaler = false;
79 m_bFilterInitialized = false;
81 for (int i = 0; i<NUM_BUFFERS; i++)
82 m_VideoBuffers[i] = NULL;
84 m_sw_scale_ctx = NULL;
88 CWinRenderer::~CWinRenderer()
93 static enum PixelFormat PixelFormatFromFormat(ERenderFormat format)
95 if (format == RENDER_FMT_YUV420P) return PIX_FMT_YUV420P;
96 else if (format == RENDER_FMT_YUV420P10) return PIX_FMT_YUV420P10;
97 else if (format == RENDER_FMT_YUV420P10) return PIX_FMT_YUV420P16;
98 else if (format == RENDER_FMT_NV12) return PIX_FMT_NV12;
99 else if (format == RENDER_FMT_UYVY422) return PIX_FMT_UYVY422;
100 else if (format == RENDER_FMT_YUYV422) return PIX_FMT_YUYV422;
101 else return PIX_FMT_NONE;
104 void CWinRenderer::ManageTextures()
106 int neededbuffers = 2;
108 if( m_NumYV12Buffers < neededbuffers )
110 for(int i = m_NumYV12Buffers; i<neededbuffers;i++)
111 CreateYV12Texture(i);
113 m_NumYV12Buffers = neededbuffers;
115 else if( m_NumYV12Buffers > neededbuffers )
117 m_NumYV12Buffers = neededbuffers;
118 m_iYV12RenderBuffer = m_iYV12RenderBuffer % m_NumYV12Buffers;
120 for(int i = m_NumYV12Buffers-1; i>=neededbuffers;i--)
121 DeleteYV12Texture(i);
125 void CWinRenderer::SelectRenderMethod()
127 // Set rendering to dxva before trying it, in order to open the correct processor immediately, when deinterlacing method is auto.
129 // Force dxva renderer after dxva decoding: PS and SW renderers have performance issues after dxva decode.
130 if (g_advancedSettings.m_DXVAForceProcessorRenderer && m_format == RENDER_FMT_DXVA)
132 CLog::Log(LOGNOTICE, "D3D: rendering method forced to DXVA2 processor");
133 m_renderMethod = RENDER_DXVA;
134 if (!m_processor.Open(m_sourceWidth, m_sourceHeight, m_iFlags, m_format, m_extended_format))
136 CLog::Log(LOGNOTICE, "D3D: unable to open DXVA2 processor");
138 m_renderMethod = RENDER_INVALID;
143 CLog::Log(LOGDEBUG, __FUNCTION__": Requested render method: %d", m_iRequestedMethod);
145 switch(m_iRequestedMethod)
147 case RENDER_METHOD_DXVA:
148 m_renderMethod = RENDER_DXVA;
149 if (m_processor.Open(m_sourceWidth, m_sourceHeight, m_iFlags, m_format, m_extended_format))
153 CLog::Log(LOGNOTICE, "D3D: unable to open DXVA2 processor");
156 // Drop through to pixel shader
157 case RENDER_METHOD_AUTO:
158 case RENDER_METHOD_D3D_PS:
159 // Try the pixel shaders support
160 if (m_deviceCaps.PixelShaderVersion >= D3DPS_VERSION(2, 0))
165 m_renderMethod = RENDER_PS;
170 CLog::Log(LOGNOTICE, "D3D: unable to load test shader - D3D installation is most likely incomplete");
171 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, "DirectX", g_localizeStrings.Get(2101));
176 CLog::Log(LOGNOTICE, "D3D: graphics adapter does not support Pixel Shaders 2.0");
178 CLog::Log(LOGNOTICE, "D3D: falling back to SW mode");
179 // drop through to software
180 case RENDER_METHOD_SOFTWARE:
182 // So we'll do the color conversion in software.
183 m_renderMethod = RENDER_SW;
188 RenderMethodDetail *rmdet = FindRenderMethod(m_renderMethod);
189 CLog::Log(LOGDEBUG, __FUNCTION__": Selected render method %d: %s", m_renderMethod, rmdet != NULL ? rmdet->name : "unknown");
192 bool CWinRenderer::UpdateRenderMethod()
194 if (m_SWTarget.Get())
195 m_SWTarget.Release();
197 if (m_renderMethod == RENDER_SW)
199 m_dllSwScale = new DllSwScale();
201 if (!m_dllSwScale->Load())
202 CLog::Log(LOGERROR,"CDVDDemuxFFmpeg::Open - failed to load ffmpeg libraries");
204 if(!m_SWTarget.Create(m_sourceWidth, m_sourceHeight, 1, D3DUSAGE_DYNAMIC, D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT))
206 CLog::Log(LOGNOTICE, __FUNCTION__": Failed to create sw render target.");
213 bool CWinRenderer::Configure(unsigned int width, unsigned int height, unsigned int d_width, unsigned int d_height, float fps, unsigned flags, ERenderFormat format, unsigned extended_format, unsigned int orientation)
215 if(m_sourceWidth != width
216 || m_sourceHeight != height)
218 m_sourceWidth = width;
219 m_sourceHeight = height;
220 // need to recreate textures
221 m_NumYV12Buffers = 0;
222 m_iYV12RenderBuffer = 0;
223 // reinitialize the filters/shaders
224 m_bFilterInitialized = false;
230 m_extended_format = extended_format;
232 // calculate the input frame aspect ratio
233 CalculateFrameAspectRatio(d_width, d_height);
234 ChooseBestResolution(fps);
235 m_destWidth = g_settings.m_ResInfo[m_resolution].iWidth;
236 m_destHeight = g_settings.m_ResInfo[m_resolution].iHeight;
237 SetViewMode(g_settings.m_currentVideoSettings.m_ViewMode);
240 m_bConfigured = true;
242 SelectRenderMethod();
243 UpdateRenderMethod();
248 int CWinRenderer::NextYV12Texture()
251 return (m_iYV12RenderBuffer + 1) % m_NumYV12Buffers;
256 bool CWinRenderer::AddVideoPicture(DVDVideoPicture* picture)
258 if (m_renderMethod == RENDER_DXVA)
260 int source = NextYV12Texture();
264 DXVABuffer *buf = (DXVABuffer*)m_VideoBuffers[source];
265 buf->id = m_processor.Add(picture);
271 int CWinRenderer::GetImage(YV12Image *image, int source, bool readonly)
273 /* take next available buffer */
274 if( source == AUTOSOURCE )
275 source = NextYV12Texture();
280 YUVBuffer *buf = (YUVBuffer*)m_VideoBuffers[source];
284 image->height = m_sourceHeight;
285 image->width = m_sourceWidth;
287 if(m_format == RENDER_FMT_YUV420P10
288 || m_format == RENDER_FMT_YUV420P16)
295 image->stride[i] = buf->planes[i].rect.Pitch;
296 image->plane[i] = (BYTE*)buf->planes[i].rect.pBits;
302 void CWinRenderer::ReleaseImage(int source, bool preserve)
304 // no need to release anything here since we're using system memory
307 void CWinRenderer::Reset()
311 void CWinRenderer::Update(bool bPauseDrawing)
313 if (!m_bConfigured) return;
317 void CWinRenderer::RenderUpdate(bool clear, DWORD flags, DWORD alpha)
319 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
322 pD3DDevice->Clear( 0L, NULL, D3DCLEAR_TARGET, m_clearColour, 1.0f, 0L );
325 pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
327 pD3DDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
329 if (!m_bConfigured) return;
332 CSingleLock lock(g_graphicsContext);
339 void CWinRenderer::FlipPage(int source)
341 if(source == AUTOSOURCE)
342 source = NextYV12Texture();
344 if (m_VideoBuffers[m_iYV12RenderBuffer] != NULL)
345 m_VideoBuffers[m_iYV12RenderBuffer]->StartDecode();
347 if( source >= 0 && source < m_NumYV12Buffers )
348 m_iYV12RenderBuffer = source;
350 m_iYV12RenderBuffer = 0;
352 if (m_VideoBuffers[m_iYV12RenderBuffer] != NULL)
353 m_VideoBuffers[m_iYV12RenderBuffer]->StartRender();
355 #ifdef MP_DIRECTRENDERING
362 unsigned int CWinRenderer::PreInit()
364 CSingleLock lock(g_graphicsContext);
365 m_bConfigured = false;
367 m_resolution = g_guiSettings.m_LookAndFeelResolution;
368 if ( m_resolution == RES_WINDOW )
369 m_resolution = RES_DESKTOP;
371 // setup the background colour
372 m_clearColour = (g_advancedSettings.m_videoBlackBarColour & 0xff) * 0x010101;
374 g_Windowing.Get3DDevice()->GetDeviceCaps(&m_deviceCaps);
376 m_iRequestedMethod = g_guiSettings.GetInt("videoplayer.rendermethod");
378 if ((g_advancedSettings.m_DXVAForceProcessorRenderer || m_iRequestedMethod == RENDER_METHOD_DXVA) && !m_processor.PreInit())
379 CLog::Log(LOGNOTICE, "CWinRenderer::Preinit - could not init DXVA2 processor - skipping");
381 m_formats.push_back(RENDER_FMT_YUV420P);
382 if(g_Windowing.IsTextureFormatOk(D3DFMT_L16, 0))
384 m_formats.push_back(RENDER_FMT_YUV420P10);
385 m_formats.push_back(RENDER_FMT_YUV420P16);
387 m_formats.push_back(RENDER_FMT_NV12);
388 m_formats.push_back(RENDER_FMT_YUYV422);
389 m_formats.push_back(RENDER_FMT_UYVY422);
395 void CWinRenderer::UnInit()
397 CSingleLock lock(g_graphicsContext);
399 if (m_SWTarget.Get())
400 m_SWTarget.Release();
402 if (m_IntermediateTarget.Get())
403 m_IntermediateTarget.Release();
405 SAFE_DELETE(m_colorShader);
406 SAFE_DELETE(m_scalerShader);
408 m_bConfigured = false;
409 m_bFilterInitialized = false;
411 for(int i = 0; i < NUM_BUFFERS; i++)
412 DeleteYV12Texture(i);
414 m_NumYV12Buffers = 0;
418 m_dllSwScale->sws_freeContext(m_sw_scale_ctx);
419 m_sw_scale_ctx = NULL;
421 SAFE_DELETE(m_dllSwScale);
423 m_processor.UnInit();
426 bool CWinRenderer::CreateIntermediateRenderTarget()
428 // Initialize a render target for intermediate rendering - same size as the video source
429 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
430 D3DFORMAT format = D3DFMT_X8R8G8B8;
431 DWORD usage = D3DUSAGE_RENDERTARGET;
433 if (g_Windowing.IsTextureFormatOk(D3DFMT_A2R10G10B10, usage)) format = D3DFMT_A2R10G10B10;
434 else if (g_Windowing.IsTextureFormatOk(D3DFMT_A2B10G10R10, usage)) format = D3DFMT_A2B10G10R10;
435 else if (g_Windowing.IsTextureFormatOk(D3DFMT_A8R8G8B8, usage)) format = D3DFMT_A8R8G8B8;
436 else if (g_Windowing.IsTextureFormatOk(D3DFMT_A8B8G8R8, usage)) format = D3DFMT_A8B8G8R8;
437 else if (g_Windowing.IsTextureFormatOk(D3DFMT_X8R8G8B8, usage)) format = D3DFMT_X8R8G8B8;
438 else if (g_Windowing.IsTextureFormatOk(D3DFMT_X8B8G8R8, usage)) format = D3DFMT_X8B8G8R8;
439 else if (g_Windowing.IsTextureFormatOk(D3DFMT_R8G8B8, usage)) format = D3DFMT_R8G8B8;
441 CLog::Log(LOGDEBUG, __FUNCTION__": format %i", format);
443 if(!m_IntermediateTarget.Create(m_sourceWidth, m_sourceHeight, 1, usage, format, D3DPOOL_DEFAULT))
445 CLog::Log(LOGERROR, __FUNCTION__": render target creation failed. Going back to bilinear scaling.", format);
451 void CWinRenderer::SelectSWVideoFilter()
453 switch (m_scalingMethod)
455 case VS_SCALINGMETHOD_AUTO:
456 case VS_SCALINGMETHOD_LINEAR:
457 if (Supports(VS_SCALINGMETHOD_LINEAR))
459 m_TextureFilter = D3DTEXF_LINEAR;
462 // fall through for fallback
463 case VS_SCALINGMETHOD_NEAREST:
465 m_TextureFilter = D3DTEXF_POINT;
470 void CWinRenderer::SelectPSVideoFilter()
472 m_bUseHQScaler = false;
474 switch (m_scalingMethod)
476 case VS_SCALINGMETHOD_NEAREST:
477 case VS_SCALINGMETHOD_LINEAR:
480 case VS_SCALINGMETHOD_CUBIC:
481 case VS_SCALINGMETHOD_LANCZOS2:
482 case VS_SCALINGMETHOD_SPLINE36_FAST:
483 case VS_SCALINGMETHOD_LANCZOS3_FAST:
484 m_bUseHQScaler = true;
487 case VS_SCALINGMETHOD_SPLINE36:
488 case VS_SCALINGMETHOD_LANCZOS3:
489 m_bUseHQScaler = true;
492 case VS_SCALINGMETHOD_SINC8:
493 case VS_SCALINGMETHOD_NEDI:
494 CLog::Log(LOGERROR, "D3D: TODO: This scaler has not yet been implemented");
497 case VS_SCALINGMETHOD_BICUBIC_SOFTWARE:
498 case VS_SCALINGMETHOD_LANCZOS_SOFTWARE:
499 case VS_SCALINGMETHOD_SINC_SOFTWARE:
500 CLog::Log(LOGERROR, "D3D: TODO: Software scaling has not yet been implemented");
507 if (m_scalingMethod == VS_SCALINGMETHOD_AUTO)
509 bool scaleSD = m_sourceHeight < 720 && m_sourceWidth < 1280;
510 bool scaleUp = (int)m_sourceHeight < g_graphicsContext.GetHeight() && (int)m_sourceWidth < g_graphicsContext.GetWidth();
511 bool scaleFps = m_fps < (g_advancedSettings.m_videoAutoScaleMaxFps + 0.01f);
513 if (Supports(VS_SCALINGMETHOD_LANCZOS3_FAST) && scaleSD && scaleUp && scaleFps)
515 m_scalingMethod = VS_SCALINGMETHOD_LANCZOS3_FAST;
516 m_bUseHQScaler = true;
521 void CWinRenderer::UpdatePSVideoFilter()
523 SAFE_DELETE(m_scalerShader);
527 // First try the more efficient two pass convolution scaler
528 m_scalerShader = new CConvolutionShaderSeparable();
530 if (!m_scalerShader->Create(m_scalingMethod))
532 SAFE_DELETE(m_scalerShader);
533 CLog::Log(LOGNOTICE, __FUNCTION__": two pass convolution shader init problem, falling back to one pass.");
536 // Fallback on the one pass version
537 if (m_scalerShader == NULL)
539 m_scalerShader = new CConvolutionShader1Pass();
541 if (!m_scalerShader->Create(m_scalingMethod))
543 SAFE_DELETE(m_scalerShader);
544 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(34400), g_localizeStrings.Get(34401));
545 m_bUseHQScaler = false;
550 if(m_IntermediateTarget.Get())
551 m_IntermediateTarget.Release();
553 if (m_bUseHQScaler && !CreateIntermediateRenderTarget())
555 SAFE_DELETE(m_scalerShader);
556 m_bUseHQScaler = false;
559 SAFE_DELETE(m_colorShader);
563 m_colorShader = new CYUV2RGBShader();
564 if (!m_colorShader->Create(m_sourceWidth, m_sourceHeight, m_format))
566 // Try again after disabling the HQ scaler and freeing its resources
567 m_IntermediateTarget.Release();
568 SAFE_DELETE(m_scalerShader);
569 SAFE_DELETE(m_colorShader);
570 m_bUseHQScaler = false;
574 if (!m_bUseHQScaler) //fallback from HQ scalers and multipass creation above
576 m_colorShader = new CYUV2RGBShader();
577 if (!m_colorShader->Create(m_sourceWidth, m_sourceHeight, m_format))
578 SAFE_DELETE(m_colorShader);
579 // we're in big trouble - should fallback on D3D accelerated or sw method
583 void CWinRenderer::UpdateVideoFilter()
585 if (m_scalingMethodGui == g_settings.m_currentVideoSettings.m_ScalingMethod && m_bFilterInitialized)
588 m_bFilterInitialized = true;
590 m_scalingMethodGui = g_settings.m_currentVideoSettings.m_ScalingMethod;
591 m_scalingMethod = m_scalingMethodGui;
593 if (!Supports(m_scalingMethod))
595 CLog::Log(LOGWARNING, __FUNCTION__" - chosen scaling method %d is not supported by renderer", (int)m_scalingMethod);
596 m_scalingMethod = VS_SCALINGMETHOD_AUTO;
599 switch(m_renderMethod)
602 SelectSWVideoFilter();
606 SelectPSVideoFilter();
607 UpdatePSVideoFilter();
611 // Everything already setup, nothing to do.
619 void CWinRenderer::Render(DWORD flags)
621 if (m_renderMethod == RENDER_DXVA)
623 CWinRenderer::RenderProcessor(flags);
629 // Optimize later? we could get by with bilinear under some circumstances
631 || !g_graphicsContext.IsFullScreenVideo()
632 || g_graphicsContext.IsCalibrating()
633 || (m_destRect.Width() == m_sourceWidth && m_destRect.Height() == m_sourceHeight))
635 CSingleLock lock(g_graphicsContext);
637 // Don't need a stencil/depth buffer and a buffer smaller than the render target causes D3D complaints and nVidia issues
638 // Save & restore when we're done.
639 LPDIRECT3DSURFACE9 pZBuffer;
640 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
641 pD3DDevice->GetDepthStencilSurface(&pZBuffer);
642 pD3DDevice->SetDepthStencilSurface(NULL);
644 if (m_renderMethod == RENDER_SW)
646 else if (m_renderMethod == RENDER_PS)
649 pD3DDevice->SetDepthStencilSurface(pZBuffer);
653 void CWinRenderer::RenderSW()
655 enum PixelFormat format = PixelFormatFromFormat(m_format);
657 // 1. convert yuv to rgb
658 m_sw_scale_ctx = m_dllSwScale->sws_getCachedContext(m_sw_scale_ctx,
659 m_sourceWidth, m_sourceHeight, format,
660 m_sourceWidth, m_sourceHeight, PIX_FMT_BGRA,
661 SWS_FAST_BILINEAR | SwScaleCPUFlags(), NULL, NULL, NULL);
663 YUVBuffer* buf = (YUVBuffer*)m_VideoBuffers[m_iYV12RenderBuffer];
665 D3DLOCKED_RECT srclr[MAX_PLANES];
666 uint8_t *src[MAX_PLANES];
667 int srcStride[MAX_PLANES];
669 for (unsigned int idx = 0; idx < buf->GetActivePlanes(); idx++)
671 if(!(buf->planes[idx].texture.LockRect(0, &srclr[idx], NULL, D3DLOCK_READONLY)))
672 CLog::Log(LOGERROR, __FUNCTION__" - failed to lock yuv textures into memory");
675 src[idx] = (uint8_t*)srclr[idx].pBits;
676 srcStride[idx] = srclr[idx].Pitch;
680 D3DLOCKED_RECT destlr = {0,0};
681 if (!m_SWTarget.LockRect(0, &destlr, NULL, D3DLOCK_DISCARD))
682 CLog::Log(LOGERROR, __FUNCTION__" - failed to lock swtarget texture into memory");
684 uint8_t *dst[] = { (uint8_t*) destlr.pBits, 0, 0, 0 };
685 int dstStride[] = { destlr.Pitch, 0, 0, 0 };
687 m_dllSwScale->sws_scale(m_sw_scale_ctx, src, srcStride, 0, m_sourceHeight, dst, dstStride);
689 for (unsigned int idx = 0; idx < buf->GetActivePlanes(); idx++)
690 if(!(buf->planes[idx].texture.UnlockRect(0)))
691 CLog::Log(LOGERROR, __FUNCTION__" - failed to unlock yuv textures");
693 if (!m_SWTarget.UnlockRect(0))
694 CLog::Log(LOGERROR, __FUNCTION__" - failed to unlock swtarget texture");
696 // 2. scale to display
698 // Don't know where this martian comes from but it can happen in the initial frames of a video
699 if ((m_destRect.x1 < 0 && m_destRect.x2 < 0) || (m_destRect.y1 < 0 && m_destRect.y2 < 0))
702 ScaleFixedPipeline();
706 Code kept for reference, as a basis to re-enable StretchRect and
707 do the color conversion with it.
708 See IDirect3D9::CheckDeviceFormat() for support of non-standard fourcc textures
709 and IDirect3D9::CheckDeviceFormatConversion for color conversion support
711 void CWinRenderer::ScaleStretchRect()
713 // Test HW scaler support. StretchRect is slightly faster than drawing a quad.
714 // If linear filtering is not supported, drop back to quads, as most HW has linear filtering for quads.
715 //if(m_deviceCaps.DevCaps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES
716 //&& m_deviceCaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MINFLINEAR
717 //&& m_deviceCaps.StretchRectFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)
719 // m_StretchRectSupported = true;
722 CRect sourceRect = m_sourceRect;
723 CRect destRect = m_destRect;
725 D3DSURFACE_DESC desc;
726 if (FAILED(target->GetDesc(&desc)))
727 CLog::Log(LOGERROR, "CWinRenderer::Render - failed to get back buffer description");
728 CRect tgtRect(0, 0, desc.Width, desc.Height);
730 // Need to manipulate the coordinates since StretchRect doesn't accept off-screen coordinates.
731 CWIN32Util::CropSource(sourceRect, destRect, tgtRect);
733 RECT srcRect = { sourceRect.x1, sourceRect.y1, sourceRect.x2, sourceRect.y2 };
734 IDirect3DSurface9* source;
735 if(!m_SWTarget.GetSurfaceLevel(0, &source))
736 CLog::Log(LOGERROR, "CWinRenderer::Render - failed to get source");
738 RECT dstRect = { destRect.x1, destRect.y1, destRect.x2, destRect.y2 };
739 IDirect3DSurface9* target;
740 if(FAILED(g_Windowing.Get3DDevice()->GetRenderTarget(0, &target)))
741 CLog::Log(LOGERROR, "CWinRenderer::Render - failed to get back buffer");
744 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
746 if(FAILED(hr = pD3DDevice->StretchRect(source, &srcRect, target, &dstRect, m_TextureFilter)))
747 CLog::Log(LOGERROR, __FUNCTION__" - StretchRect failed (0x%08X)", hr);
754 void CWinRenderer::ScaleFixedPipeline()
757 IDirect3DDevice9 *pD3DDev = g_Windowing.Get3DDevice();
758 D3DSURFACE_DESC srcDesc;
759 if (FAILED(hr = m_SWTarget.Get()->GetLevelDesc(0, &srcDesc)))
760 CLog::Log(LOGERROR, __FUNCTION__": GetLevelDesc failed. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
762 float srcWidth = (float)srcDesc.Width;
763 float srcHeight = (float)srcDesc.Height;
765 bool cbcontrol = (g_settings.m_currentVideoSettings.m_Contrast != 50.0f || g_settings.m_currentVideoSettings.m_Brightness != 50.0f);
766 unsigned int contrast = (unsigned int)(g_settings.m_currentVideoSettings.m_Contrast *.01f * 255.0f); // we have to divide by two here/multiply by two later
767 unsigned int brightness = (unsigned int)(g_settings.m_currentVideoSettings.m_Brightness * .01f * 255.0f);
769 D3DCOLOR diffuse = D3DCOLOR_ARGB(255, contrast, contrast, contrast);
770 D3DCOLOR specular = D3DCOLOR_ARGB(255, brightness, brightness, brightness);
782 {m_destRect.x1, m_destRect.y1, 0.0f, 1.0f, diffuse, specular, m_sourceRect.x1 / srcWidth, m_sourceRect.y1 / srcHeight},
783 {m_destRect.x2, m_destRect.y1, 0.0f, 1.0f, diffuse, specular, m_sourceRect.x2 / srcWidth, m_sourceRect.y1 / srcHeight},
784 {m_destRect.x2, m_destRect.y2, 0.0f, 1.0f, diffuse, specular, m_sourceRect.x2 / srcWidth, m_sourceRect.y2 / srcHeight},
785 {m_destRect.x1, m_destRect.y2, 0.0f, 1.0f, diffuse, specular, m_sourceRect.x1 / srcWidth, m_sourceRect.y2 / srcHeight},
788 // Compensate for D3D coordinates system
789 for(int i = 0; i < sizeof(vertex)/sizeof(vertex[0]); i++)
795 pD3DDev->SetTexture(0, m_SWTarget.Get());
799 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
800 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
801 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
802 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
803 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE );
804 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
808 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE2X );
809 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
810 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
811 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
812 hr = pD3DDev->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
814 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_ADDSIGNED );
815 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_CURRENT );
816 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_SPECULAR );
817 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
818 hr = pD3DDev->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_CURRENT );
820 hr = pD3DDev->SetTextureStageState( 2, D3DTSS_COLOROP, D3DTOP_DISABLE );
821 hr = pD3DDev->SetTextureStageState( 2, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
824 hr = pD3DDev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
825 hr = pD3DDev->SetRenderState(D3DRS_LIGHTING, FALSE);
826 hr = pD3DDev->SetRenderState(D3DRS_ZENABLE, FALSE);
827 hr = pD3DDev->SetRenderState(D3DRS_STENCILENABLE, FALSE);
828 hr = pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
829 hr = pD3DDev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
830 hr = pD3DDev->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
831 hr = pD3DDev->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA|D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_RED);
833 hr = pD3DDev->SetSamplerState(0, D3DSAMP_MAGFILTER, m_TextureFilter);
834 hr = pD3DDev->SetSamplerState(0, D3DSAMP_MINFILTER, m_TextureFilter);
835 hr = pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
836 hr = pD3DDev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
838 hr = pD3DDev->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX1);
840 if (FAILED(hr = pD3DDev->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, vertex, sizeof(VERTEX))))
841 CLog::Log(LOGERROR, __FUNCTION__": DrawPrimitiveUP failed. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
843 pD3DDev->SetTexture(0, NULL);
846 void CWinRenderer::RenderPS()
859 void CWinRenderer::Stage1()
863 m_colorShader->Render(m_sourceRect, m_destRect,
864 g_settings.m_currentVideoSettings.m_Contrast,
865 g_settings.m_currentVideoSettings.m_Brightness,
867 (YUVBuffer*)m_VideoBuffers[m_iYV12RenderBuffer]);
871 // Switch the render target to the temporary destination
872 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
873 LPDIRECT3DSURFACE9 newRT, oldRT;
874 m_IntermediateTarget.GetSurfaceLevel(0, &newRT);
875 pD3DDevice->GetRenderTarget(0, &oldRT);
876 pD3DDevice->SetRenderTarget(0, newRT);
878 CRect srcRect(0.0f, 0.0f, m_sourceWidth, m_sourceHeight);
879 CRect rtRect(0.0f, 0.0f, m_sourceWidth, m_sourceHeight);
881 m_colorShader->Render(srcRect, rtRect,
882 g_settings.m_currentVideoSettings.m_Contrast,
883 g_settings.m_currentVideoSettings.m_Brightness,
885 (YUVBuffer*)m_VideoBuffers[m_iYV12RenderBuffer]);
887 // Restore the render target
888 pD3DDevice->SetRenderTarget(0, oldRT);
895 void CWinRenderer::Stage2()
897 m_scalerShader->Render(m_IntermediateTarget, m_sourceWidth, m_sourceHeight, m_destWidth, m_destHeight, m_sourceRect, m_destRect);
900 void CWinRenderer::RenderProcessor(DWORD flags)
902 CSingleLock lock(g_graphicsContext);
905 DXVABuffer *image = (DXVABuffer*)m_VideoBuffers[m_iYV12RenderBuffer];
907 IDirect3DSurface9* target;
908 if(FAILED(hr = g_Windowing.Get3DDevice()->GetRenderTarget(0, &target)))
910 CLog::Log(LOGERROR, "CWinRenderer::RenderSurface - failed to get render target. %s", CRenderSystemDX::GetErrorDescription(hr).c_str());
914 m_processor.Render(m_sourceRect, m_destRect, target, image->id, flags);
919 bool CWinRenderer::RenderCapture(CRenderCapture* capture)
921 if (!m_bConfigured || m_NumYV12Buffers == 0)
924 bool succeeded = false;
926 LPDIRECT3DDEVICE9 pD3DDevice = g_Windowing.Get3DDevice();
928 CRect saveSize = m_destRect;
929 saveRotatedCoords();//backup current m_rotatedDestCoords
931 m_destRect.SetRect(0, 0, (float)capture->GetWidth(), (float)capture->GetHeight());
932 syncDestRectToRotatedPoints();//syncs the changed destRect to m_rotatedDestCoords
934 LPDIRECT3DSURFACE9 oldSurface;
935 pD3DDevice->GetRenderTarget(0, &oldSurface);
937 capture->BeginRender();
938 if (capture->GetState() != CAPTURESTATE_FAILED)
940 pD3DDevice->BeginScene();
942 pD3DDevice->EndScene();
943 capture->EndRender();
947 pD3DDevice->SetRenderTarget(0, oldSurface);
948 oldSurface->Release();
950 m_destRect = saveSize;
951 restoreRotatedCoords();//restores the previous state of the rotated dest coords
956 //********************************************************************************************************
957 // YV12 Texture creation, deletion, copying + clearing
958 //********************************************************************************************************
959 void CWinRenderer::DeleteYV12Texture(int index)
961 CSingleLock lock(g_graphicsContext);
962 if (m_VideoBuffers[index] != NULL)
963 SAFE_DELETE(m_VideoBuffers[index]);
964 m_NumYV12Buffers = 0;
967 bool CWinRenderer::CreateYV12Texture(int index)
969 CSingleLock lock(g_graphicsContext);
970 DeleteYV12Texture(index);
972 if (m_renderMethod == RENDER_DXVA)
974 m_VideoBuffers[index] = new DXVABuffer();
978 YUVBuffer *buf = new YUVBuffer();
980 if (!buf->Create(m_format, m_sourceWidth, m_sourceHeight))
982 CLog::Log(LOGERROR, __FUNCTION__" - Unable to create YV12 video texture %i", index);
985 m_VideoBuffers[index] = buf;
988 SVideoBuffer *buf = m_VideoBuffers[index];
993 if(index == m_iYV12RenderBuffer)
996 CLog::Log(LOGDEBUG, "created video buffer %i", index);
1000 bool CWinRenderer::Supports(EDEINTERLACEMODE mode)
1002 if(mode == VS_DEINTERLACEMODE_OFF
1003 || mode == VS_DEINTERLACEMODE_AUTO
1004 || mode == VS_DEINTERLACEMODE_FORCE)
1010 bool CWinRenderer::Supports(EINTERLACEMETHOD method)
1012 if(method == VS_INTERLACEMETHOD_AUTO)
1015 if (m_renderMethod == RENDER_DXVA)
1017 if(method == VS_INTERLACEMETHOD_DXVA_BOB
1018 || method == VS_INTERLACEMETHOD_DXVA_BEST)
1022 if(m_format != RENDER_FMT_DXVA
1023 && ( method == VS_INTERLACEMETHOD_DEINTERLACE
1024 || method == VS_INTERLACEMETHOD_DEINTERLACE_HALF
1025 || method == VS_INTERLACEMETHOD_SW_BLEND))
1031 bool CWinRenderer::Supports(ERENDERFEATURE feature)
1033 if(feature == RENDERFEATURE_BRIGHTNESS)
1036 if(feature == RENDERFEATURE_CONTRAST)
1039 if (feature == RENDERFEATURE_STRETCH ||
1040 feature == RENDERFEATURE_NONLINSTRETCH ||
1041 feature == RENDERFEATURE_CROP ||
1042 feature == RENDERFEATURE_ZOOM ||
1043 feature == RENDERFEATURE_VERTICAL_SHIFT ||
1044 feature == RENDERFEATURE_PIXEL_RATIO ||
1045 feature == RENDERFEATURE_POSTPROCESS)
1052 bool CWinRenderer::Supports(ESCALINGMETHOD method)
1054 if (m_renderMethod == RENDER_DXVA)
1056 if(method == VS_SCALINGMETHOD_DXVA_HARDWARE)
1060 else if(m_renderMethod == RENDER_PS)
1062 if(m_deviceCaps.PixelShaderVersion >= D3DPS_VERSION(2, 0)
1063 && ( method == VS_SCALINGMETHOD_AUTO
1064 || method == VS_SCALINGMETHOD_LINEAR))
1067 if(m_deviceCaps.PixelShaderVersion >= D3DPS_VERSION(3, 0))
1069 if(method == VS_SCALINGMETHOD_CUBIC
1070 || method == VS_SCALINGMETHOD_LANCZOS2
1071 || method == VS_SCALINGMETHOD_SPLINE36_FAST
1072 || method == VS_SCALINGMETHOD_LANCZOS3_FAST
1073 || method == VS_SCALINGMETHOD_SPLINE36
1074 || method == VS_SCALINGMETHOD_LANCZOS3)
1078 else if(m_renderMethod == RENDER_SW)
1080 if(method == VS_SCALINGMETHOD_AUTO
1081 || method == VS_SCALINGMETHOD_NEAREST)
1083 if(method == VS_SCALINGMETHOD_LINEAR
1084 && m_deviceCaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR
1085 && m_deviceCaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR)
1091 EINTERLACEMETHOD CWinRenderer::AutoInterlaceMethod()
1093 if (m_renderMethod == RENDER_DXVA)
1094 return VS_INTERLACEMETHOD_DXVA_BOB;
1096 return VS_INTERLACEMETHOD_DEINTERLACE_HALF;
1099 //============================================
1101 YUVBuffer::~YUVBuffer()
1106 bool YUVBuffer::Create(ERenderFormat format, unsigned int width, unsigned int height)
1112 // Create the buffers in system memory and copy to D3DPOOL_DEFAULT:
1113 // - helps with lost devices. A buffer can be locked for dvdplayer without interfering.
1114 // - Dynamic + D3DPOOL_DEFAULT caused trouble for Intel i3 and some IGP. Bad sync/locking in the driver I suppose
1115 // and Present failed every second time for the second video played.
1116 // - this is what D3D9 does behind the scenes anyway
1119 case RENDER_FMT_YUV420P10:
1120 case RENDER_FMT_YUV420P16:
1122 if ( !planes[PLANE_Y].texture.Create(m_width , m_height , 1, 0, D3DFMT_L16, D3DPOOL_SYSTEMMEM)
1123 || !planes[PLANE_U].texture.Create(m_width / 2, m_height / 2, 1, 0, D3DFMT_L16, D3DPOOL_SYSTEMMEM)
1124 || !planes[PLANE_V].texture.Create(m_width / 2, m_height / 2, 1, 0, D3DFMT_L16, D3DPOOL_SYSTEMMEM))
1129 case RENDER_FMT_YUV420P:
1131 if ( !planes[PLANE_Y].texture.Create(m_width , m_height , 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM)
1132 || !planes[PLANE_U].texture.Create(m_width / 2, m_height / 2, 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM)
1133 || !planes[PLANE_V].texture.Create(m_width / 2, m_height / 2, 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM))
1138 case RENDER_FMT_NV12:
1140 if ( !planes[PLANE_Y].texture.Create(m_width , m_height , 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM)
1141 || !planes[PLANE_UV].texture.Create(m_width / 2, m_height / 2, 1, 0, D3DFMT_A8L8, D3DPOOL_SYSTEMMEM))
1146 case RENDER_FMT_YUYV422:
1148 if ( !planes[PLANE_Y].texture.Create(m_width >> 1 , m_height , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM))
1153 case RENDER_FMT_UYVY422:
1155 if ( !planes[PLANE_Y].texture.Create(m_width >> 1 , m_height , 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM))
1168 void YUVBuffer::Release()
1170 for(unsigned i = 0; i < m_activeplanes; i++)
1172 planes[i].texture.Release();
1173 memset(&planes[i].rect, 0, sizeof(planes[i].rect));
1177 void YUVBuffer::StartRender()
1179 for(unsigned i = 0; i < m_activeplanes; i++)
1181 if(planes[i].texture.Get() && planes[i].rect.pBits)
1182 if (!planes[i].texture.UnlockRect(0))
1183 CLog::Log(LOGERROR, __FUNCTION__" - failed to unlock texture %d", i);
1184 memset(&planes[i].rect, 0, sizeof(planes[i].rect));
1188 void YUVBuffer::StartDecode()
1190 for(unsigned i = 0; i < m_activeplanes; i++)
1192 if(planes[i].texture.Get()
1193 && planes[i].texture.LockRect(0, &planes[i].rect, NULL, D3DLOCK_DISCARD) == false)
1195 memset(&planes[i].rect, 0, sizeof(planes[i].rect));
1196 CLog::Log(LOGERROR, __FUNCTION__" - failed to lock texture %d into memory", i);
1201 void YUVBuffer::Clear()
1203 // Set Y to 0 and U,V to 128 (RGB 0,0,0) to avoid visual artifacts at the start of playback
1207 case RENDER_FMT_YUV420P16:
1209 wmemset((wchar_t*)planes[PLANE_Y].rect.pBits, 0, planes[PLANE_Y].rect.Pitch * m_height / 2);
1210 wmemset((wchar_t*)planes[PLANE_U].rect.pBits, 32768, planes[PLANE_U].rect.Pitch * (m_height/2) / 2);
1211 wmemset((wchar_t*)planes[PLANE_V].rect.pBits, 32768, planes[PLANE_V].rect.Pitch * (m_height/2) / 2);
1214 case RENDER_FMT_YUV420P10:
1216 wmemset((wchar_t*)planes[PLANE_Y].rect.pBits, 0, planes[PLANE_Y].rect.Pitch * m_height / 2);
1217 wmemset((wchar_t*)planes[PLANE_U].rect.pBits, 512, planes[PLANE_U].rect.Pitch * (m_height/2) / 2);
1218 wmemset((wchar_t*)planes[PLANE_V].rect.pBits, 512, planes[PLANE_V].rect.Pitch * (m_height/2) / 2);
1221 case RENDER_FMT_YUV420P:
1223 memset(planes[PLANE_Y].rect.pBits, 0, planes[PLANE_Y].rect.Pitch * m_height);
1224 memset(planes[PLANE_U].rect.pBits, 128, planes[PLANE_U].rect.Pitch * (m_height/2));
1225 memset(planes[PLANE_V].rect.pBits, 128, planes[PLANE_V].rect.Pitch * (m_height/2));
1228 case RENDER_FMT_NV12:
1230 memset(planes[PLANE_Y].rect.pBits, 0, planes[PLANE_Y].rect.Pitch * m_height);
1231 memset(planes[PLANE_UV].rect.pBits, 128, planes[PLANE_U].rect.Pitch * (m_height/2));
1234 // YUY2, UYVY: wmemset to set a 16bit pattern, byte-swapped because x86 is LE
1235 case RENDER_FMT_YUYV422:
1237 wmemset((wchar_t*)planes[PLANE_Y].rect.pBits, 0x8000, planes[PLANE_Y].rect.Pitch / 2 * m_height);
1240 case RENDER_FMT_UYVY422:
1242 wmemset((wchar_t*)planes[PLANE_Y].rect.pBits, 0x0080, planes[PLANE_Y].rect.Pitch / 2 * m_height);
1249 //==================================
1251 DXVABuffer::~DXVABuffer()
1256 void DXVABuffer::Release()
1261 void DXVABuffer::StartDecode()