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