MythTV master
mythrender_d3d9.cpp
Go to the documentation of this file.
1#define _WIN32_WINNT 0x500
2
3#include <algorithm>
4
5#include <QLibrary>
6#include <QRect>
7#include <QMap>
8#include <QMutex>
9
10#include "libmythbase/mythconfig.h"
12#include "mythrender_d3d9.h"
13
14#ifndef DXVA2_E_NEW_VIDEO_DEVICE
15#define DXVA2_E_NEW_VIDEO_DEVICE MAKE_HRESULT(1, 4, 4097)
16#endif
17
19{
20 FLOAT x;
21 FLOAT y;
22 FLOAT z;
23 FLOAT rhw;
24 D3DCOLOR diffuse;
25 FLOAT t1u;
26 FLOAT t1v;
27 FLOAT t2u;
28 FLOAT t2v;
29};
30
31struct VERTEX
32{
33 FLOAT x;
34 FLOAT y;
35 FLOAT z;
36 FLOAT rhw;
37 D3DCOLOR diffuse;
38};
39
40D3D9Image::D3D9Image(MythRenderD3D9 *render, QSize size, bool video)
41 : m_size(size), m_render(render)
42{
43 if (m_render)
44 {
48 }
50}
51
53{
54 if (!m_render)
55 return;
56
57 if (m_texture)
61 if (m_surface)
63}
64
66{
67 if (m_valid)
69 return false;
70}
71
72bool D3D9Image::UpdateImage(IDirect3DSurface9 *surface)
73{
74 if (m_valid)
75 return m_render->StretchRect(m_texture, surface, false);
76 return false;
77}
78
80{
81 bool result = true;
82 if (m_valid)
83 {
84 result &= m_render->UpdateSurface(m_surface, img);
86 }
87 return m_valid && result;
88}
89
90bool D3D9Image::UpdateVertices(const QRect &dvr, const QRect &vr, int alpha,
91 bool video)
92{
93 if (m_valid)
94 return m_render->UpdateVertexBuffer(m_vertexbuffer, dvr, vr, alpha, video);
95 return false;
96}
97
99{
100 if (m_valid)
102 return false;
103}
104
105uint8_t* D3D9Image::GetBuffer(bool &hardware_conv, uint &pitch)
106{
107 if (!m_valid)
108 return nullptr;
109
110 hardware_conv = m_render->HardwareYUVConversion();
111 return m_render->GetBuffer(m_surface, pitch);
112}
113
115{
116 if (!m_valid)
117 return;
120}
121
123{
124 if (!m_valid)
125 return QRect();
127}
128
129#define mD3DFMT_YV12 (D3DFORMAT)MAKEFOURCC('Y','V','1','2')
130#define mD3DFMT_IYUV (D3DFORMAT)MAKEFOURCC('I','Y','U','V')
131#define mD3DFMT_I420 (D3DFORMAT)MAKEFOURCC('I','4','2','0')
132#define mD3DFMT_YV16 (D3DFORMAT)MAKEFOURCC('Y','V','1','6')
133#define D3DFVF_TEXTUREVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1|D3DFVF_TEX2)
134#define D3DFVF_VERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
135#define D3DLOC QString("MythRenderD3D9: ")
136
138{
139 if (m_render)
141}
142
143IDirect3DDevice9* D3D9Locker::Acquire(void)
144{
145 IDirect3DDevice9* result = nullptr;
146 if (m_render)
147 result = m_render->AcquireDevice();
148 if (!result)
149 LOG(VB_GENERAL, LOG_ERR, "D3D9Locker: Failed to acquire device.");
150 return result;
151}
152
153void* MythRenderD3D9::ResolveAddress(const char* lib, const char* proc)
154{
155 return (void *)QLibrary::resolve(lib, proc);
156}
157
159{
160 QMutexLocker locker(&m_lock);
161
162 LOG(VB_GENERAL, LOG_INFO, D3DLOC + "Deleting D3D9 resources.");
163
165 m_rect_vertexbuffer->Release();
167 m_current_surface->Release();
169 m_default_surface->Release();
170
174
176
177 if (m_rootD3DDevice)
178 {
179 LOG(VB_GENERAL, LOG_INFO, D3DLOC + "Deleting D3D9 device.");
180 m_rootD3DDevice->Release();
181 }
182
183 if (m_d3d)
184 {
185 LOG(VB_GENERAL, LOG_INFO, D3DLOC + "Deleting D3D9.");
186 m_d3d->Release();
187 }
188}
189
190static const QString toString(D3DFORMAT fmt)
191{
192 switch (fmt)
193 {
194 case D3DFMT_A8:
195 return "A8";
196 case D3DFMT_A8R8G8B8:
197 return "A8R8G8B8";
198 case D3DFMT_X8R8G8B8:
199 return "X8R8G8B8";
200 case D3DFMT_A8B8G8R8:
201 return "A8B8G8R8";
202 case D3DFMT_X8B8G8R8:
203 return "X8B8G8R8";
204 case mD3DFMT_YV12:
205 return "YV12";
206 case D3DFMT_UYVY:
207 return "UYVY";
208 case D3DFMT_YUY2:
209 return "YUY2";
210 case mD3DFMT_IYUV:
211 return "IYUV";
212 case mD3DFMT_I420:
213 return "I420";
214 case mD3DFMT_YV16:
215 return "YV16";
216 default:
217 break;
218 }
219 return QString().setNum((ulong)fmt,16);
220}
221
222bool MythRenderD3D9::Create(QSize size, HWND window)
223{
224 QMutexLocker locker(&m_lock);
225
226 using LPFND3DC = LPDIRECT3D9 (WINAPI *)(UINT SDKVersion);
227 static LPFND3DC OurDirect3DCreate9 = nullptr;
228
229 OurDirect3DCreate9 = (LPFND3DC)ResolveAddress("D3D9","Direct3DCreate9");
230 if (!OurDirect3DCreate9)
231 {
232 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
233 "FATAL: Failed to find Direct3DCreate9.");
234 return false;
235 }
236
237 m_d3d = OurDirect3DCreate9(D3D_SDK_VERSION);
238 if (!m_d3d)
239 {
240 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
241 "Could not create Direct3D9 instance.");
242 return false;
243 }
244
245 D3DCAPS9 d3dCaps;
246 ZeroMemory(&d3dCaps, sizeof(d3dCaps));
247 if (D3D_OK != m_d3d->GetDeviceCaps(
248 D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))
249 {
250 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
251 "Could not read adapter capabilities.");
252 }
253
254 D3DDISPLAYMODE d3ddm;
255 if (D3D_OK != m_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm))
256 {
257 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
258 "Could not read adapter display mode.");
259 return false;
260 }
261
262 // Check the adaptor format is reasonable
263 static const D3DFORMAT bfmt[] =
264 {
265 D3DFMT_A8R8G8B8,
266 D3DFMT_A8B8G8R8,
267 D3DFMT_X8R8G8B8,
268 D3DFMT_X8B8G8R8,
269 D3DFMT_R8G8B8
270 };
271
272 m_adaptor_fmt = d3ddm.Format;
273 bool is_reasonable = false;
274 for (uint i = 0; i < sizeof(bfmt) / sizeof(bfmt[0]); i++)
275 if (bfmt[i] == m_adaptor_fmt)
276 is_reasonable = true;
277 LOG(VB_GENERAL, LOG_INFO, D3DLOC + QString("Default adaptor format %1.")
278 .arg(toString(m_adaptor_fmt)));
279 if (!is_reasonable)
280 {
281 LOG(VB_GENERAL, LOG_WARNING, D3DLOC +
282 "Warning: Default adaptor format may not work.");
283 }
284
285 // Choose a surface format
286 for (unsigned i = 0; i < sizeof bfmt / sizeof bfmt[0]; ++i)
287 {
288 if (SUCCEEDED(m_d3d->CheckDeviceType(D3DADAPTER_DEFAULT,
289 D3DDEVTYPE_HAL, m_adaptor_fmt, bfmt[i], TRUE)))
290 {
291 m_surface_fmt = bfmt[i];
292 break;
293 }
294 }
295
296 if (D3DFMT_UNKNOWN == m_surface_fmt)
297 {
298 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to choose surface format - "
299 "using default back buffer format.");
301 }
302
304 LOG(VB_GENERAL, LOG_INFO, D3DLOC +
305 QString("Chosen surface and texture format: %1")
306 .arg(toString(m_surface_fmt)));
307
308
309 // Test whether a YV12 video surface is available
310 if (FAILED(m_d3d->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT,
311 D3DDEVTYPE_HAL, D3DFMT_UNKNOWN, m_adaptor_fmt)) &&
312 SUCCEEDED(m_d3d->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT,
313 D3DDEVTYPE_HAL, mD3DFMT_YV12, m_surface_fmt)))
314 {
316 }
317 else
318 {
320 }
321
322 LOG(VB_GENERAL, LOG_INFO, D3DLOC +
323 QString("Chosen video surface format %1.")
325 LOG(VB_GENERAL, LOG_INFO, D3DLOC +
326 QString("Hardware YV12 to RGB conversion %1.")
328 "unavailable" : "available"));
329
330 D3DPRESENT_PARAMETERS d3dpp;
331 ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));
332 d3dpp.BackBufferFormat = m_adaptor_fmt;
333 d3dpp.hDeviceWindow = window;
334 d3dpp.Windowed = true;
335 d3dpp.BackBufferWidth = size.width();
336 d3dpp.BackBufferHeight = size.height();
337 d3dpp.BackBufferCount = 1;
338 d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
339 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
340 d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
341 d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
342
343 if (D3D_OK != m_d3d->CreateDevice(D3DADAPTER_DEFAULT,
344 D3DDEVTYPE_HAL, d3dpp.hDeviceWindow,
345 D3DCREATE_SOFTWARE_VERTEXPROCESSING |
346 D3DCREATE_MULTITHREADED,
347 &d3dpp, &m_rootD3DDevice))
348 {
349 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Could not create the D3D device.");
350 return false;
351 }
352
353 static bool debugged = false;
354 if (!debugged)
355 {
356 debugged = true;
357 D3DADAPTER_IDENTIFIER9 ident;
358 if (D3D_OK == m_d3d->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &ident))
359 {
360 LOG(VB_GENERAL, LOG_INFO, D3DLOC + QString("Device: %1")
361 .arg(ident.Description));
362 LOG(VB_GENERAL, LOG_INFO, D3DLOC +QString("Driver: %1.%2.%3.%4")
363 .arg(HIWORD(ident.DriverVersion.HighPart))
364 .arg(LOWORD(ident.DriverVersion.HighPart))
365 .arg(HIWORD(ident.DriverVersion.LowPart))
366 .arg(LOWORD(ident.DriverVersion.LowPart)));
367 }
368 }
369
371 Init2DState();
372 return true;
373}
374
376{
378}
379
380bool MythRenderD3D9::Test(bool &reset)
381{
382 D3D9Locker locker(this);
383 IDirect3DDevice9* dev = locker.Acquire();
384 if (!dev)
385 return false;
386
387 bool result = true;
388 HRESULT hr = dev->TestCooperativeLevel();
389 if (FAILED(hr))
390 {
391 switch (hr)
392 {
393 case D3DERR_DEVICENOTRESET:
394 LOG(VB_GENERAL, LOG_NOTICE, D3DLOC +
395 "The device was lost and needs to be reset.");
396 result = false;
397 reset = true;
398 break;
399
400 case D3DERR_DEVICELOST:
401 LOG(VB_GENERAL, LOG_NOTICE, D3DLOC +
402 "The device has been lost and cannot be reset "
403 "at this time.");
404 result = false;
405 break;
406
407 case D3DERR_DRIVERINTERNALERROR:
408 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
409 "Internal driver error. "
410 "Please shut down the application.");
411 result = false;
412 break;
413
414 default:
415 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
416 "TestCooperativeLevel() failed.");
417 result = false;
418 }
419 }
420 return result;
421}
422
424{
425 D3D9Locker locker(this);
426 IDirect3DDevice9* dev = locker.Acquire();
427 if (!dev)
428 return false;
429
430 HRESULT hr = dev->Clear(0, nullptr, D3DCLEAR_TARGET,
431 D3DCOLOR_ARGB(0, 0, 0, 0), 1.0F, 0);
432 if (FAILED(hr))
433 {
434 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Clear() failed.");
435 return false;
436 }
437 return true;
438}
439
441{
442 D3D9Locker locker(this);
443 IDirect3DDevice9* dev = locker.Acquire();
444 if (!dev)
445 return false;
446
447 HRESULT hr = dev->BeginScene();
448 if (FAILED(hr))
449 {
450 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "BeginScene() failed.");
451 return false;
452 }
453 return true;
454}
455
457{
458 D3D9Locker locker(this);
459 IDirect3DDevice9* dev = locker.Acquire();
460 if (!dev)
461 return false;
462
463 HRESULT hr = dev->EndScene();
464 if (FAILED(hr))
465 {
466 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "EndScene() failed.");
467 return false;
468 }
469 return true;
470}
471
472void MythRenderD3D9::CopyFrame(void* surface, D3D9Image *img)
473{
474 if (surface && img)
475 img->UpdateImage((IDirect3DSurface9*)surface);
476}
477
478bool MythRenderD3D9::StretchRect(IDirect3DTexture9 *texture,
479 IDirect3DSurface9 *surface,
480 bool known_surface)
481{
482 if (!m_textures.contains(texture) ||
483 (known_surface && !m_surfaces.contains(surface)))
484 return false;
485
486 D3D9Locker locker(this);
487 IDirect3DDevice9* dev = locker.Acquire();
488 if (!dev)
489 return false;
490
491 LPDIRECT3DSURFACE9 d3ddest;
492 HRESULT hr = texture->GetSurfaceLevel(0, &d3ddest);
493 if (FAILED(hr))
494 {
495 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "GetSurfaceLevel() failed");
496 return false;
497 }
498
499 hr = dev->StretchRect(surface, nullptr, d3ddest,
500 nullptr, D3DTEXF_POINT);
501 d3ddest->Release();
502 if (FAILED(hr))
503 {
504 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "StretchRect() failed");
505 return false;
506 }
507 return true;
508}
509
510bool MythRenderD3D9::DrawTexturedQuad(IDirect3DVertexBuffer9 *vertexbuffer)
511{
512 if (!m_vertexbuffers.contains(vertexbuffer))
513 return false;
514
515 D3D9Locker locker(this);
516 IDirect3DDevice9* dev = locker.Acquire();
517 if (!dev)
518 return false;
519
520 IDirect3DTexture9 *texture = m_vertexbuffers[vertexbuffer].m_texture;
521
522 if (texture && !SetTexture(dev, texture))
523 return false;
524
525 EnableBlending(dev, true);
526 SetTextureVertices(dev, true);
527 MultiTexturing(dev, false);
528
529 HRESULT hr = dev->SetStreamSource(0, vertexbuffer,
530 0, sizeof(TEXTUREVERTEX));
531 if (FAILED(hr))
532 {
533 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "SetStreamSource() failed");
534 return false;
535 }
536
537 hr = dev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
538 if (FAILED(hr))
539 {
540 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "DrawPrimitive() failed");
541 return false;
542 }
543
544 return true;
545}
546
547void MythRenderD3D9::DrawRect(const QRect &rect, const QColor &color, int alpha)
548{
549 D3D9Locker locker(this);
550 IDirect3DDevice9* dev = locker.Acquire();
551 if (!dev)
552 return;
553
555 {
556 HRESULT hr = dev->CreateVertexBuffer(
557 sizeof(VERTEX)*4, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
558 D3DFVF_VERTEX, D3DPOOL_DEFAULT,
559 &m_rect_vertexbuffer, nullptr);
560
561 if (FAILED(hr))
562 {
563 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to create vertex buffer");
564 return;
565 }
566 }
567
568 EnableBlending(dev, true);
569 SetTextureVertices(dev, false);
570 MultiTexturing(dev, false);
571 SetTexture(dev, nullptr, 0);
572
573 int alphamod = (int)(color.alpha() * (alpha / 255.0));
574 D3DCOLOR clr = D3DCOLOR_ARGB(alphamod, color.red(),
575 color.green(), color.blue());
576 VERTEX *p_vertices;
577 HRESULT hr = m_rect_vertexbuffer->Lock(0, 0, (VOID **)(&p_vertices),
578 D3DLOCK_DISCARD);
579 if (FAILED(hr))
580 {
581 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to lock vertex buffer.");
582 return;
583 }
584
585 p_vertices[0].x = (float)rect.left();
586 p_vertices[0].y = (float)rect.top();
587 p_vertices[0].z = 0.0F;
588 p_vertices[0].diffuse = clr;
589 p_vertices[0].rhw = 1.0F;
590 p_vertices[1].x = (float)(rect.left() + rect.width());
591 p_vertices[1].y = (float)rect.top();
592 p_vertices[1].z = 0.0F;
593 p_vertices[1].diffuse = clr;
594 p_vertices[1].rhw = 1.0F;
595 p_vertices[2].x = (float)(rect.left() + rect.width());
596 p_vertices[2].y = (float)(rect.top() + rect.height());
597 p_vertices[2].z = 0.0F;
598 p_vertices[2].diffuse = clr;
599 p_vertices[2].rhw = 1.0F;
600 p_vertices[3].x = (float)rect.left();
601 p_vertices[3].y = (float)(rect.top() + rect.height());
602 p_vertices[3].z = 0.0F;
603 p_vertices[3].diffuse = clr;
604 p_vertices[3].rhw = 1.0F;
605
606 hr = m_rect_vertexbuffer->Unlock();
607 if (FAILED(hr))
608 {
609 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to unlock vertex buffer");
610 return;
611 }
612
613 hr = dev->SetStreamSource(0, m_rect_vertexbuffer,
614 0, sizeof(VERTEX));
615 if (FAILED(hr))
616 {
617 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "SetStreamSource() failed");
618 return;
619 }
620
621 hr = dev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
622 if (FAILED(hr))
623 {
624 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "DrawPrimitive() failed");
625 return;
626 }
627}
628
629void MythRenderD3D9::MultiTexturing(IDirect3DDevice9* dev, bool enable,
630 IDirect3DTexture9 *texture)
631{
632 if (m_multi_texturing == enable)
633 return;
634
635 if (!dev)
636 return;
637
638 if (enable)
639 {
640 SetTexture(dev, texture, 1);
641 dev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
642 dev->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
643 dev->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
644 dev->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
645 dev->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
646 dev->SetTextureStageState(1, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
647 dev->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_DISABLE);
648 dev->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
649 }
650 else
651 {
652 dev->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
653 dev->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
654 dev->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
655 dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
656 dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
657 dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
658 dev->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
659 dev->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
660 SetTexture(dev, nullptr, 1);
661 }
662 m_multi_texturing = enable;
663}
664
666{
667 D3D9Locker locker(this);
668 IDirect3DDevice9* dev = locker.Acquire();
669 if (!dev)
670 return false;
671
672 HRESULT hr = dev->Present(nullptr, nullptr, win, nullptr);
673 if (FAILED(hr))
674 {
675 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Present() failed)");
676 return false;
677 }
678 SetThreadExecutionState(ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
679 return true;
680}
681
682QRect MythRenderD3D9::GetRect(IDirect3DVertexBuffer9 *vertexbuffer)
683{
684 if (!m_vertexbuffers.contains(vertexbuffer))
685 return QRect();
686 return m_vertexbuffers[vertexbuffer].m_dest;
687}
688
689bool MythRenderD3D9::SetRenderTarget(IDirect3DTexture9 *texture)
690{
691 D3D9Locker locker(this);
692 IDirect3DDevice9* dev = locker.Acquire();
693 if (!dev)
694 return false;
695
696 bool ret = true;
697 HRESULT hr;
698 if (texture && m_textures.contains(texture))
699 {
701 {
702 hr = dev->GetRenderTarget(0, &m_default_surface);
703 if (FAILED(hr))
704 {
705 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
706 "Failed to get default surface.");
707 return false;
708 }
709 }
710
711 IDirect3DSurface9 *new_surface = nullptr;
712 hr = texture->GetSurfaceLevel(0, &new_surface);
713 if (FAILED(hr))
714 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to get surface level.");
715 else
716 {
717 if (m_current_surface && m_current_surface != new_surface)
718 m_current_surface->Release();
719 m_current_surface = new_surface;
720 hr = dev->SetRenderTarget(0, m_current_surface);
721 if (FAILED(hr))
722 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
723 "Failed to set render target.");
724 }
725 }
726 else if (!texture)
727 {
729 {
730 hr = dev->SetRenderTarget(0, m_default_surface);
731 if (FAILED(hr))
732 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
733 "Failed to set render target.");
734 }
735 else
736 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
737 "No default surface for render target.");
738 }
739 else
740 ret = false;
741 return ret;
742}
743
744bool MythRenderD3D9::SetTexture(IDirect3DDevice9* dev,
745 IDirect3DTexture9 *texture, int num)
746{
747 if (!dev)
748 return false;
749
750 HRESULT hr = dev->SetTexture(num, (LPDIRECT3DBASETEXTURE9)texture);
751 if (FAILED(hr))
752 {
753 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "SetTexture() failed");
754 return false;
755 }
756 return true;
757}
758
759IDirect3DTexture9* MythRenderD3D9::CreateTexture(const QSize &size)
760{
761 D3D9Locker locker(this);
762 IDirect3DDevice9* dev = locker.Acquire();
763 if (!dev)
764 return nullptr;
765
766 IDirect3DTexture9* temp_texture = nullptr;
767
768 HRESULT hr = dev->CreateTexture(
769 size.width(), size.height(), 1, D3DUSAGE_RENDERTARGET,
770 m_texture_fmt, D3DPOOL_DEFAULT, &temp_texture, nullptr);
771
772 if (FAILED(hr) || !temp_texture)
773 {
774 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to create texture.");
775 return nullptr;
776 }
777
778 m_textures[temp_texture] = size;;
779 return temp_texture;
780}
781
783{
784 QMap<IDirect3DTexture9*,QSize>::iterator it;
785 for (it = m_textures.begin(); it != m_textures.end(); ++it)
786 it.key()->Release();
787 m_textures.clear();
788}
789
790void MythRenderD3D9::DeleteTexture(IDirect3DTexture9* texture)
791{
792 QMutexLocker locker(&m_lock);
793 if (m_textures.contains(texture))
794 {
795 texture->Release();
796 m_textures.remove(texture);
797 }
798}
799
800IDirect3DSurface9* MythRenderD3D9::CreateSurface(const QSize &size, bool video)
801{
802 D3D9Locker locker(this);
803 IDirect3DDevice9* dev = locker.Acquire();
804 if (!dev)
805 return nullptr;
806
807 IDirect3DSurface9* temp_surface = nullptr;
808
809 D3DFORMAT format = video ? m_videosurface_fmt : m_surface_fmt;
810
811 HRESULT hr = dev->CreateOffscreenPlainSurface(
812 size.width(), size.height(), format,
813 D3DPOOL_DEFAULT, &temp_surface, nullptr);
814
815 if (FAILED(hr)|| !temp_surface)
816 {
817 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to create surface.");
818 return nullptr;
819 }
820
821 m_surfaces[temp_surface] = MythD3DSurface(size, format);
822 dev->ColorFill(temp_surface, nullptr, D3DCOLOR_ARGB(0xFF, 0, 0, 0) );
823
824 return temp_surface;
825}
826
827bool MythRenderD3D9::UpdateSurface(IDirect3DSurface9 *surface,
828 const MythImage *image)
829{
830 if (!surface || !image || !m_surfaces.contains(surface))
831 return false;
832
833 if (m_surfaces[surface].m_size.width() != image->width() ||
834 m_surfaces[surface].m_size.height() != image->height())
835 {
836 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
837 "Frame size does not equal surface size.");
838 return false;
839 }
840
841 uint d3dpitch = 0;
842 uint8_t *buf = GetBuffer(surface, d3dpitch);
843
844 if (!(buf && d3dpitch))
845 return false;
846
847 D3DFORMAT format = m_surfaces[surface].m_fmt;
848 switch (format)
849 {
850 case D3DFMT_A8R8G8B8:
851 case D3DFMT_X8R8G8B8:
852 {
853 uint pitch = image->width() << 2;
854 uint8_t *dst = buf;
855 uint8_t *src = (uint8_t*)image->bits();
856 for (int i = 0; i < image->height(); i++)
857 {
858 memcpy(dst, src, pitch);
859 dst += d3dpitch;
860 src += pitch;
861 }
862 }
863 break;
864 default:
865 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Surface format not supported.");
866 break;
867 }
868
869 ReleaseBuffer(surface);
870 return true;
871}
872
874{
875 QMap<IDirect3DSurface9*, MythD3DSurface>::iterator it;
876 for (it = m_surfaces.begin(); it != m_surfaces.end(); ++it)
877 it.key()->Release();
878 m_surfaces.clear();
879}
880
881void MythRenderD3D9::DeleteSurface(IDirect3DSurface9 *surface)
882{
883 QMutexLocker locker(&m_lock);
884 if (m_surfaces.contains(surface))
885 {
886 surface->Release();
887 m_surfaces.remove(surface);
888 }
889}
890
891uint8_t* MythRenderD3D9::GetBuffer(IDirect3DSurface9* surface, uint &pitch)
892{
893 if (!m_surfaces.contains(surface))
894 return nullptr;
895
896 m_lock.lock(); // unlocked in release buffer
897 D3DLOCKED_RECT d3drect;
898 HRESULT hr = surface->LockRect(&d3drect, nullptr, 0);
899
900 if (FAILED(hr))
901 {
902 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to lock picture surface.");
903 m_lock.unlock();
904 return nullptr;
905 }
906
907 pitch = d3drect.Pitch;
908 return (uint8_t*)d3drect.pBits;
909}
910
911void MythRenderD3D9::ReleaseBuffer(IDirect3DSurface9* surface)
912{
913 if (!m_surfaces.contains(surface))
914 return;
915
916 HRESULT hr = surface->UnlockRect();
917 if (FAILED(hr))
918 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to unlock picture surface.");
919 m_lock.unlock();
920}
921
922IDirect3DVertexBuffer9* MythRenderD3D9::CreateVertexBuffer(IDirect3DTexture9* texture)
923{
924 D3D9Locker locker(this);
925 IDirect3DDevice9* dev = locker.Acquire();
926 if (!dev)
927 return nullptr;
928
929 if (texture && !m_textures.contains(texture))
930 return nullptr;
931
932 IDirect3DVertexBuffer9* temp_vbuf = nullptr;
933 HRESULT hr = dev->CreateVertexBuffer(
934 sizeof(TEXTUREVERTEX)*4, D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY,
935 D3DFVF_TEXTUREVERTEX, D3DPOOL_DEFAULT,
936 &temp_vbuf, nullptr);
937
938 if (FAILED(hr))
939 {
940 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to create vertex buffer");
941 return nullptr;
942 }
943
944 m_vertexbuffers[temp_vbuf] = MythD3DVertexBuffer(texture);
945 return temp_vbuf;
946}
947
949{
950 QMap<IDirect3DVertexBuffer9*,MythD3DVertexBuffer>::iterator it;
951 for (it = m_vertexbuffers.begin();
952 it != m_vertexbuffers.end(); ++it)
953 {
954 it.key()->Release();
955 }
956 m_vertexbuffers.clear();
957}
958
959void MythRenderD3D9::DeleteVertexBuffer(IDirect3DVertexBuffer9 *vertexbuffer)
960{
961 QMutexLocker locker(&m_lock);
962 if (m_vertexbuffers.contains(vertexbuffer))
963 {
964 vertexbuffer->Release();
965 m_vertexbuffers.remove(vertexbuffer);
966 }
967}
968
969bool MythRenderD3D9::UpdateVertexBuffer(IDirect3DVertexBuffer9* vertexbuffer,
970 const QRect &dst, const QRect &src,
971 int alpha, bool video)
972{
973 if (!m_vertexbuffers.contains(vertexbuffer))
974 return false;
975
976 MythD3DVertexBuffer mythvb = m_vertexbuffers[vertexbuffer];
977 uint32_t clr = (alpha << 24) + (255 << 16) + (255 << 8) + 255;
978
979 int width = dst.width();
980 int height = dst.height();
981 if (!video)
982 {
983 width = std::min(src.width(), width);
984 height = std::min(src.height(), height);
985 }
986 QRect dest(dst.left(), dst.top(), width, height);
987
988 // FIXME - with alpha pulse, this updates far more textures than necessary
989 if (dest == mythvb.m_dest &&
990 src == mythvb.m_src &&
991 clr == mythvb.m_color)
992 return true;
993
994 QSize norm = src.size();
995 if (video && mythvb.m_texture)
996 norm = m_textures[mythvb.m_texture];
997
998 QMutexLocker locker(&m_lock);
999 TEXTUREVERTEX *p_vertices;
1000 HRESULT hr = vertexbuffer->Lock(0, 0, (VOID **)(&p_vertices),
1001 D3DLOCK_DISCARD);
1002 D3DCOLOR color = D3DCOLOR_ARGB(alpha, 255, 255, 255);
1003 if (FAILED(hr))
1004 {
1005 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to lock vertex buffer.");
1006 return false;
1007 }
1008
1009 p_vertices[0].x = (float)dest.left();
1010 p_vertices[0].y = (float)dest.top();
1011 p_vertices[0].z = 0.0F;
1012 p_vertices[0].diffuse = color;
1013 p_vertices[0].rhw = 1.0F;
1014 p_vertices[0].t1u = ((float)src.left() - 0.5F) / (float)norm.width();
1015 p_vertices[0].t1v = ((float)src.top() - 0.5F) / (float)norm.height();
1016
1017 p_vertices[1].x = (float)(dest.left() + dest.width());
1018 p_vertices[1].y = (float)dest.top();
1019 p_vertices[1].z = 0.0F;
1020 p_vertices[1].diffuse = color;
1021 p_vertices[1].rhw = 1.0F;
1022 p_vertices[1].t1u = ((float)(src.left() + src.width()) - 0.5F) /
1023 (float)norm.width();
1024 p_vertices[1].t1v = ((float)src.top() - 0.5F) / (float)norm.height();
1025
1026 p_vertices[2].x = (float)(dest.left() + dest.width());
1027 p_vertices[2].y = (float)(dest.top() + dest.height());
1028 p_vertices[2].z = 0.0F;
1029 p_vertices[2].diffuse = color;
1030 p_vertices[2].rhw = 1.0F;
1031 p_vertices[2].t1u = ((float)(src.left() + src.width()) - 0.5F) /
1032 (float)norm.width();
1033 p_vertices[2].t1v = ((float)(src.top() + src.height()) - 0.5F) /
1034 (float)norm.height();
1035
1036 p_vertices[3].x = (float)dest.left();
1037 p_vertices[3].y = (float)(dest.top() + dest.height());
1038 p_vertices[3].z = 0.0F;
1039 p_vertices[3].diffuse = color;
1040 p_vertices[3].rhw = 1.0F;
1041 p_vertices[3].t1u = ((float)src.left() - 0.5F) / (float)norm.width();
1042 p_vertices[3].t1v = ((float)(src.top() + src.height()) - 0.5F) /
1043 (float)norm.height();
1044
1045 p_vertices[0].t2u = p_vertices[0].t1u;
1046 p_vertices[0].t2v = p_vertices[0].t1v;
1047 p_vertices[1].t2u = p_vertices[1].t1u;
1048 p_vertices[1].t2v = p_vertices[1].t1v;
1049 p_vertices[2].t2u = p_vertices[2].t1u;
1050 p_vertices[2].t2v = p_vertices[2].t1v;
1051 p_vertices[3].t2u = p_vertices[3].t1u;
1052 p_vertices[3].t2v = p_vertices[3].t1v;
1053
1054 hr = vertexbuffer->Unlock();
1055 if (FAILED(hr))
1056 {
1057 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to unlock vertex buffer");
1058 return false;
1059 }
1060
1061 m_vertexbuffers[vertexbuffer].m_dest = dest;
1062 m_vertexbuffers[vertexbuffer].m_src = src;
1063 m_vertexbuffers[vertexbuffer].m_color = clr;
1064 return true;
1065}
1066
1068{
1069 IDirect3DDevice9* dev = AcquireDevice();
1070 if (!dev)
1071 return;
1072
1073 dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1074 dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1075 dev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1076 dev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1077 dev->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
1078 dev->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
1079 dev->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1080 dev->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1081 dev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255));
1082 dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
1083 dev->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
1084 dev->SetRenderState(D3DRS_LIGHTING, FALSE);
1085 dev->SetRenderState(D3DRS_DITHERENABLE, TRUE);
1086 dev->SetRenderState(D3DRS_STENCILENABLE, FALSE);
1087 dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
1088 dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
1089 dev->SetVertexShader(nullptr);
1090 SetTextureVertices(dev, false);
1091 MultiTexturing(dev, false);
1092 EnableBlending(dev, false);
1093
1094 ReleaseDevice();
1095}
1096
1097void MythRenderD3D9::EnableBlending(IDirect3DDevice9* dev, bool enable)
1098{
1099 if (m_blend == enable)
1100 return;
1101 m_blend = enable;
1102
1103 if (dev)
1104 dev->SetRenderState(D3DRS_ALPHABLENDENABLE, enable);
1105}
1106
1107void MythRenderD3D9::SetTextureVertices(IDirect3DDevice9* dev, bool enable)
1108{
1109 if (m_texture_vertices == enable)
1110 return;
1111 m_texture_vertices = enable;
1112
1113 if (dev)
1114 dev->SetFVF(enable ? D3DFVF_TEXTUREVERTEX : D3DFVF_VERTEX);
1115}
1116
1117IDirect3DDevice9* MythRenderD3D9::AcquireDevice(void)
1118{
1119 m_lock.lock();
1120#if CONFIG_DXVA2
1121 if (m_deviceManager)
1122 {
1123 IDirect3DDevice9* result = nullptr;
1124
1125 HRESULT hr = IDirect3DDeviceManager9_LockDevice(m_deviceManager, m_deviceHandle, &result, true);
1126
1127 if (hr == DXVA2_E_NEW_VIDEO_DEVICE)
1128 {
1129 hr = IDirect3DDeviceManager9_CloseDeviceHandle(m_deviceManager, m_deviceHandle);
1130
1131 if (SUCCEEDED(hr))
1132 hr = IDirect3DDeviceManager9_OpenDeviceHandle(m_deviceManager, &m_deviceHandle);
1133
1134 if (SUCCEEDED(hr))
1135 hr = IDirect3DDeviceManager9_LockDevice(m_deviceManager, m_deviceHandle, &result, true);
1136 }
1137
1138 if (SUCCEEDED(hr))
1139 return result;
1140
1141 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to acquire D3D9 device.");
1142 m_lock.unlock();
1143 return nullptr;
1144 }
1145#endif
1146 return m_rootD3DDevice;
1147}
1148
1150{
1151#if CONFIG_DXVA2
1152 if (m_deviceManager)
1153 {
1154 HRESULT hr = IDirect3DDeviceManager9_UnlockDevice(m_deviceManager, m_deviceHandle, false);
1155 if (!SUCCEEDED(hr))
1156 LOG(VB_GENERAL, LOG_ERR, D3DLOC + "Failed to release D3D9 device.");
1157 }
1158#endif
1159 m_lock.unlock();
1160}
1161
1162#if CONFIG_DXVA2
1163using CreateDeviceManager9Ptr = HRESULT (WINAPI *)(UINT *pResetToken,
1165#endif
1166
1168{
1169#if CONFIG_DXVA2
1170 CreateDeviceManager9Ptr CreateDeviceManager9 =
1171 (CreateDeviceManager9Ptr)ResolveAddress("DXVA2",
1172 "DXVA2CreateDirect3DDeviceManager9");
1173 if (CreateDeviceManager9)
1174 {
1175 UINT resetToken = 0;
1176 HRESULT hr = CreateDeviceManager9(&resetToken, &m_deviceManager);
1177 if (SUCCEEDED(hr))
1178 {
1179 IDirect3DDeviceManager9_ResetDevice(m_deviceManager, m_rootD3DDevice, resetToken);
1180 IDirect3DDeviceManager9_AddRef(m_deviceManager);
1181 m_deviceManagerToken = resetToken;
1182 LOG(VB_GENERAL, LOG_INFO, D3DLOC + "Created DXVA2 device manager.");
1183 hr = IDirect3DDeviceManager9_OpenDeviceHandle(m_deviceManager, &m_deviceHandle);
1184 if (SUCCEEDED(hr))
1185 {
1186 LOG(VB_GENERAL, LOG_INFO, D3DLOC + "Retrieved device handle.");
1187 return;
1188 }
1189 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
1190 "Failed to retrieve device handle.");
1191 }
1192 else
1193 {
1194 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
1195 "Failed to create DXVA2 device manager.");
1196 }
1197 }
1198 else
1199 {
1200 LOG(VB_GENERAL, LOG_ERR, D3DLOC +
1201 "Failed to get DXVA2CreateDirect3DDeviceManager9 proc address.");
1202 }
1203#endif
1204 m_deviceManager = nullptr;
1206 LOG(VB_GENERAL, LOG_NOTICE, D3DLOC +
1207 "DXVA2 support not available - not using device manager");
1208}
1209
1211{
1212#if CONFIG_DXVA2
1214 IDirect3DDeviceManager9_CloseDeviceHandle(m_deviceManager, m_deviceHandle);
1215 if (m_deviceManager)
1216 IDirect3DDeviceManager9_Release(m_deviceManager);
1217#endif
1218 m_deviceHandle = nullptr;
1219 m_deviceManager = nullptr;
1220}
uint8_t * GetBuffer(bool &hardware_conv, uint &pitch)
IDirect3DSurface9 * m_surface
bool UpdateImage(IDirect3DSurface9 *surface)
bool UpdateVertices(const QRect &dvr, const QRect &vr, int alpha=255, bool video=false)
IDirect3DTexture9 * m_texture
void ReleaseBuffer(void)
MythRenderD3D9 * m_render
bool SetAsRenderTarget(void)
D3D9Image(MythRenderD3D9 *render, QSize size, bool video=false)
QRect GetRect(void)
bool Draw(void)
IDirect3DVertexBuffer9 * m_vertexbuffer
MythRenderD3D9 * m_render
IDirect3DDevice9 * Acquire(void)
IDirect3DTexture9 * m_texture
IDirect3DSurface9 * m_current_surface
QRecursiveMutex m_lock
D3DFORMAT m_videosurface_fmt
void DrawRect(const QRect &rect, const QColor &color, int alpha)
bool SetRenderTarget(IDirect3DTexture9 *texture)
QMap< IDirect3DSurface9 *, MythD3DSurface > m_surfaces
QMap< IDirect3DTexture9 *, QSize > m_textures
void MultiTexturing(IDirect3DDevice9 *dev, bool enable, IDirect3DTexture9 *texture=nullptr)
bool ClearBuffer(void)
virtual ~MythRenderD3D9()
void CreateDeviceManager(void)
QMap< IDirect3DVertexBuffer9 *, MythD3DVertexBuffer > m_vertexbuffers
void DeleteTextures(void)
bool HardwareYUVConversion(void)
bool SetTexture(IDirect3DDevice9 *dev, IDirect3DTexture9 *texture, int num=0)
IDirect3DTexture9 * CreateTexture(const QSize &size)
bool DrawTexturedQuad(IDirect3DVertexBuffer9 *vertexbuffer)
IDirect3DDevice9 * m_rootD3DDevice
IDirect3D9 * m_d3d
void DeleteSurface(IDirect3DSurface9 *surface)
D3DFORMAT m_surface_fmt
void Init2DState(void)
D3DFORMAT m_adaptor_fmt
void CopyFrame(void *surface, D3D9Image *img)
static void * ResolveAddress(const char *lib, const char *proc)
D3DFORMAT m_texture_fmt
void DeleteVertexBuffers(void)
bool Test(bool &reset)
void DeleteTexture(IDirect3DTexture9 *texture)
bool UpdateVertexBuffer(IDirect3DVertexBuffer9 *vertexbuffer, const QRect &dvr, const QRect &vr, int alpha=255, bool video=false)
bool StretchRect(IDirect3DTexture9 *texture, IDirect3DSurface9 *surface, bool known_surface=true)
void DeleteVertexBuffer(IDirect3DVertexBuffer9 *vertexbuffer)
IDirect3DDeviceManager9 * m_deviceManager
IDirect3DVertexBuffer9 * m_rect_vertexbuffer
QRect GetRect(IDirect3DVertexBuffer9 *vertexbuffer)
void ReleaseBuffer(IDirect3DSurface9 *surface)
void DeleteSurfaces(void)
void DestroyDeviceManager(void)
IDirect3DSurface9 * m_default_surface
bool Create(QSize size, HWND window)
bool Present(HWND win)
IDirect3DDevice9 * AcquireDevice(void)
IDirect3DSurface9 * CreateSurface(const QSize &size, bool video=false)
void SetTextureVertices(IDirect3DDevice9 *dev, bool enable)
IDirect3DVertexBuffer9 * CreateVertexBuffer(IDirect3DTexture9 *texture=nullptr)
uint8_t * GetBuffer(IDirect3DSurface9 *surface, uint &pitch)
bool UpdateSurface(IDirect3DSurface9 *surface, const MythImage *image)
void ReleaseDevice(void)
void EnableBlending(IDirect3DDevice9 *dev, bool enable)
unsigned int uint
Definition: compat.h:60
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define mD3DFMT_I420
#define D3DFVF_TEXTUREVERTEX
#define D3DFVF_VERTEX
#define mD3DFMT_YV12
#define D3DLOC
#define mD3DFMT_YV16
#define DXVA2_E_NEW_VIDEO_DEVICE
static const QString toString(D3DFORMAT fmt)
#define mD3DFMT_IYUV
void * IDirect3DDeviceManager9
D3DCOLOR diffuse