RenderAPI_D3D11.cpp 15.2 KB
Newer Older
Martin Finkel's avatar
Martin Finkel committed
1
2
3
4
5
#include "RenderAPI.h"
#include "PlatformBase.h"

// Direct3D 11 implementation of RenderAPI.

Martin Finkel's avatar
WIP    
Martin Finkel committed
6
// #if SUPPORT_D3D11
Martin Finkel's avatar
Martin Finkel committed
7
8

#include <assert.h>
9
#include <tchar.h>
10
#include <windows.h>
11
#include <d3d11_1.h>
12
#include <d3dcompiler.h>
Martin Finkel's avatar
Martin Finkel committed
13
#include "Unity/IUnityGraphicsD3D11.h"
14
#include "Log.h"
Martin Finkel's avatar
Martin Finkel committed
15

Martin Finkel's avatar
WIP-2    
Martin Finkel committed
16
#include <algorithm>
17
#include <dxgi1_2.h>
18
#include <comdef.h>
19

Martin Finkel's avatar
WIP    
Martin Finkel committed
20
21
#define SCREEN_WIDTH  100
#define SCREEN_HEIGHT  100
22

23
24
25
26
27
28
29
30
31
class ReadWriteTexture
{
public:
    ID3D11Texture2D          *m_textureUnity        = nullptr;
    ID3D11ShaderResourceView *m_textureShaderInput  = nullptr;
    HANDLE                   m_sharedHandle         = nullptr; // handle of the texture used by VLC and the app
    ID3D11RenderTargetView   *m_textureRenderTarget = nullptr;

    void Cleanup();
32
    void Update(unsigned width, unsigned height, ID3D11Device *m_d3deviceUnity, ID3D11Device *m_d3deviceVLC);
33
34
};

35
class RenderAPI_D3D11 : public RenderAPI
36
{
37
public:
38
39
40
    RenderAPI_D3D11();
    ~RenderAPI_D3D11();

41
    virtual void setVlcContext(libvlc_media_player_t *mp) override;
42
    virtual void unsetVlcContext(libvlc_media_player_t *mp) override;
Martin Finkel's avatar
Martin Finkel committed
43
    virtual void ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces) override;
44
    void* getVideoFrame(bool* out_updated) override;
45
    void setColorSpace(int color_space) override;
46

47
    /* VLC callbacks */
48
    bool UpdateOutput( const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out );
49
    void Swap();
50
    bool StartRendering(bool enter );
51
    bool SelectPlane(size_t plane, void *output);
52
    bool Setup(const libvlc_video_setup_device_cfg_t *cfg, libvlc_video_setup_device_info_t *out );
53
    void Resize(void (*report_size_change)(void *report_opaque, unsigned width, unsigned height), void *report_opaque );
54
55
56

    ReadWriteTexture        *read_write[2];
    bool                    write_on_first = false;
57

58
private:
59
    void CreateResources();
Martin Finkel's avatar
Martin Finkel committed
60
    void ReleaseResources();
61
62
    void DebugInUnity(LPCSTR message);
    void Update(UINT width, UINT height);
63

64
    /* Unity side resources */
Martin Finkel's avatar
Martin Finkel committed
65
66
    ID3D11Device             *m_d3deviceUnity       = nullptr;
    ID3D11DeviceContext      *m_d3dctxUnity         = nullptr;
67

68
69
70
    /* VLC side resources */
    ID3D11Device            *m_d3deviceVLC          = nullptr;
    ID3D11DeviceContext     *m_d3dctxVLC            = nullptr;
Martin Finkel's avatar
WIP-2    
Martin Finkel committed
71

72
73
74
75
76
    CRITICAL_SECTION m_sizeLock; // the ReportSize callback cannot be called during/after the Cleanup_cb is called
    unsigned m_width = 0;
    unsigned m_height = 0;
    void (*m_ReportSize)(void *ReportOpaque, unsigned width, unsigned height) = nullptr;
    void *m_reportOpaque = nullptr;
77

78
79
80
81
82
    bool m_updated = false;

    CRITICAL_SECTION m_outputLock; // the ReportSize callback cannot be called during/after the Cleanup_cb is called

    bool m_initialized = false;
83
    bool m_linear = false;
84
85
};

86
// VLC C-style callbacks
87
bool UpdateOutput_cb( void *opaque, const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
Martin Finkel's avatar
Martin Finkel committed
88
{
89
90
    RenderAPI_D3D11 *me = reinterpret_cast<RenderAPI_D3D11*>(opaque);
    return me->UpdateOutput(cfg, out);
91
}
Martin Finkel's avatar
Martin Finkel committed
92

93
94
void Swap_cb( void* opaque )
{
95
96
    RenderAPI_D3D11 *me = reinterpret_cast<RenderAPI_D3D11*>(opaque);
    me->Swap();
97
}
Martin Finkel's avatar
Martin Finkel committed
98

99
bool StartRendering_cb( void *opaque, bool enter )
100
{
101
102
    RenderAPI_D3D11 *me = reinterpret_cast<RenderAPI_D3D11*>(opaque);
    return me->StartRendering(enter);
103
104
}

105
bool SelectPlane_cb( void *opaque, size_t plane, void *output )
106
{
107
    return ((RenderAPI_D3D11*)opaque)->SelectPlane(plane, output);
108
109
}

110
bool Setup_cb( void **opaque, const libvlc_video_setup_device_cfg_t *cfg, libvlc_video_setup_device_info_t *out )
111
{
Martin Finkel's avatar
Martin Finkel committed
112
    RenderAPI_D3D11 *me = reinterpret_cast<RenderAPI_D3D11*>(*opaque);
Martin Finkel's avatar
Martin Finkel committed
113
    return me->Setup(cfg, out);
114
115
116
117
}

void Cleanup_cb( void *opaque )
{
118
    RenderAPI_D3D11 *me = reinterpret_cast<RenderAPI_D3D11*>(opaque);
119
120
    me->read_write[0]->Cleanup();
    me->read_write[1]->Cleanup();
121
}
Martin Finkel's avatar
Martin Finkel committed
122

123
124
void Resize_cb( void *opaque,
                    void (*report_size_change)(void *report_opaque, unsigned width, unsigned height),
125
                    void *report_opaque )
Martin Finkel's avatar
Martin Finkel committed
126
{
127
128
    RenderAPI_D3D11 *me = reinterpret_cast<RenderAPI_D3D11*>(opaque);
    me->Resize(report_size_change, report_opaque);
Martin Finkel's avatar
Martin Finkel committed
129
130
}

131
RenderAPI* CreateRenderAPI_D3D11()
Martin Finkel's avatar
Martin Finkel committed
132
{
Martin Finkel's avatar
Martin Finkel committed
133
    return new RenderAPI_D3D11();
Martin Finkel's avatar
Martin Finkel committed
134
135
}

136
137
RenderAPI_D3D11::RenderAPI_D3D11()
{
138
139
    read_write[0] = new ReadWriteTexture();
    read_write[1] = new ReadWriteTexture();
140
141
142
143
}

RenderAPI_D3D11::~RenderAPI_D3D11()
{
144
145
    delete read_write[0];
    delete read_write[1];
146
147
}

148
149
void RenderAPI_D3D11::setVlcContext(libvlc_media_player_t *mp)
{
150
    DEBUG("[D3D11] Subscribing output callbacks %p \n", this);
151

Martin Finkel's avatar
Martin Finkel committed
152
    libvlc_video_set_output_callbacks( mp, libvlc_video_engine_d3d11,
153
                                    Setup_cb, Cleanup_cb, Resize_cb, UpdateOutput_cb,
154
                                    Swap_cb, StartRendering_cb, nullptr, nullptr, SelectPlane_cb,
155
                                    this);
156
157

    CreateResources();
158
159
}

160
void RenderAPI_D3D11::unsetVlcContext(libvlc_media_player_t *mp)
161
{
162
    DEBUG("[D3D11] Unsubscribing to output callbacks %p \n", this);
163
164
165
166
167

    libvlc_video_set_output_callbacks(mp, libvlc_video_engine_disable,
                                    Setup_cb, Cleanup_cb, Resize_cb, UpdateOutput_cb,
                                    Swap_cb, StartRendering_cb, nullptr, nullptr, SelectPlane_cb,
                                    this);
168
169

    ReleaseResources();
170
171
}

Martin Finkel's avatar
Martin Finkel committed
172
173
void RenderAPI_D3D11::ProcessDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces* interfaces)
{
174
175
    DEBUG("Entering ProcessDeviceEvent \n");

Martin Finkel's avatar
Martin Finkel committed
176
177
    switch (type)
    {
178
179
180
        case kUnityGfxDeviceEventInitialize:
        {
            IUnityGraphicsD3D11* d3d = interfaces->Get<IUnityGraphicsD3D11>();
181
182
183
184
185
            if(d3d == NULL)
            {
                DEBUG("Could not retrieve IUnityGraphicsD3D11 \n");
                return;
            }
186
187
            m_d3deviceUnity = d3d->GetDevice();
            if(m_d3deviceUnity == NULL)
188
            {
189
                DEBUG("Could not retrieve d3device \n");
190
191
                return;
            }
192
193
            m_d3deviceUnity->GetImmediateContext(&m_d3dctxUnity);
            if(m_d3dctxUnity == NULL)
194
195
196
197
            {
                DEBUG("Could not retrieve d3dctx \n");
                return;
            }
198
199
200
201
            break;
        }
        case kUnityGfxDeviceEventShutdown:
        {
202
            ReleaseResources();
203
204
205
206
207
208
209
210
211
212
213
            break;
        }
        case kUnityGfxDeviceEventAfterReset:
        {
            break;
        }
        case kUnityGfxDeviceEventBeforeReset:
        {
            break;
        }
    }
Martin Finkel's avatar
Martin Finkel committed
214
215
}

216
void RenderAPI_D3D11::Update(UINT width, UINT height)
Martin Finkel's avatar
Martin Finkel committed
217
{
218
    EnterCriticalSection(&m_outputLock);
Martin Finkel's avatar
Martin Finkel committed
219

220
221
    m_width = width;
    m_height = height;
222
223
    read_write[0]->Update(m_width, m_height, m_d3deviceUnity, m_d3deviceVLC);
    read_write[1]->Update(m_width, m_height, m_d3deviceUnity, m_d3deviceVLC);
224
225
226
227
228
229
230

    LeaveCriticalSection(&m_outputLock);
}

void ReadWriteTexture::Update(UINT width, UINT height, ID3D11Device *m_d3deviceUnity, ID3D11Device *m_d3deviceVLC)
{
    Cleanup();
231

Martin Finkel's avatar
Martin Finkel committed
232
233
    DEBUG("Done releasing d3d objects.\n");

234
235
236
237
238
239
240
241
    /* interim texture */
    D3D11_TEXTURE2D_DESC texDesc = { 0 };
    texDesc.MipLevels = 1;
    texDesc.SampleDesc.Count = 1;
    texDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
    texDesc.Usage = D3D11_USAGE_DEFAULT;
    texDesc.CPUAccessFlags = 0;
    texDesc.ArraySize = 1;
242
    texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
243
244
    texDesc.Height = height;
    texDesc.Width  = width;
245
    
Martin Finkel's avatar
Martin Finkel committed
246
    texDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE;
247
    
248
249
    HRESULT hr;
    hr = m_d3deviceUnity->CreateTexture2D( &texDesc, NULL, &m_textureUnity );
Martin Finkel's avatar
Martin Finkel committed
250
251
252
253
254
255
256
257
    if (FAILED(hr))
    {
        DEBUG("CreateTexture2D FAILED \n");
    }
    else
    {
        DEBUG("CreateTexture2D SUCCEEDED.\n");
    }
258

259
    IDXGIResource1* sharedResource = NULL;
260

261
    hr = m_textureUnity->QueryInterface(&sharedResource);
262
263
264
265
    if(FAILED(hr))
    {
        DEBUG("get IDXGIResource1 FAILED \n");
    }
266

267
    hr = sharedResource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, NULL, &m_sharedHandle);
268
269
    if(FAILED(hr))
    {
270
271
        _com_error error(hr);
        DEBUG("sharedResource->CreateSharedHandle FAILED %s \n", error.ErrorMessage());
272
273
274
    }

    sharedResource->Release();
Martin Finkel's avatar
Martin Finkel committed
275
    sharedResource = NULL;
276

277
    ID3D11Device1* d3d11VLC1;
278
    hr = m_d3deviceVLC->QueryInterface(&d3d11VLC1);
279
280
281
282
283
    if(FAILED(hr))
    {
        _com_error error(hr);
        DEBUG("QueryInterface ID3D11Device1 FAILED %s \n", error.ErrorMessage());
    }
284
    
285
    ID3D11Texture2D* textureVLC;
286
    hr = d3d11VLC1->OpenSharedResource1(m_sharedHandle, __uuidof(ID3D11Texture2D), (void**)&textureVLC);
287
288
289
290
291
292
    if(FAILED(hr))
    {
        _com_error error(hr);
        DEBUG("ctx->d3device->OpenSharedResource FAILED %s \n", error.ErrorMessage());
    }

293
    d3d11VLC1->Release();
Martin Finkel's avatar
Martin Finkel committed
294
    d3d11VLC1 = NULL;
295

296
297
298
299
300
    D3D11_SHADER_RESOURCE_VIEW_DESC resviewDesc;
    ZeroMemory(&resviewDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
    resviewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    resviewDesc.Texture2D.MipLevels = 1;
    resviewDesc.Format = texDesc.Format;
301
    hr = m_d3deviceUnity->CreateShaderResourceView(m_textureUnity, &resviewDesc, &m_textureShaderInput);
302
    if (FAILED(hr))
Martin Finkel's avatar
Martin Finkel committed
303
304
305
306
307
308
309
    {
        DEBUG("CreateShaderResourceView FAILED \n");
    }
    else
    {
        DEBUG("CreateShaderResourceView SUCCEEDED.\n");
    }
310

Martin Finkel's avatar
Martin Finkel committed
311
312
    D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
    ZeroMemory(&renderTargetViewDesc, sizeof(D3D11_RENDER_TARGET_VIEW_DESC));
313
314
315
    renderTargetViewDesc.Format = texDesc.Format;
    renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;

316
    hr = m_d3deviceVLC->CreateRenderTargetView(textureVLC, &renderTargetViewDesc, &m_textureRenderTarget);
Martin Finkel's avatar
Martin Finkel committed
317
318
319
320
    if (FAILED(hr))
    {
        DEBUG("CreateRenderTargetView FAILED \n");
    }
321
322

    textureVLC->Release();// No need to keep a reference to that, VLC only writes to the renderTarget
Martin Finkel's avatar
Martin Finkel committed
323
    textureVLC = NULL;
324
325
}

326
void RenderAPI_D3D11::CreateResources()
327
328
329
{
    DEBUG("Entering CreateResources \n");

330
331
332
333
334
335
    if(m_d3deviceUnity == NULL || m_d3dctxUnity == NULL)
    {
        DEBUG("Could not retrieve unity d3device %p or d3dctx %p, aborting... \n", m_d3deviceUnity, m_d3dctxUnity);
        return;
    }

Martin Finkel's avatar
Martin Finkel committed
336
    HRESULT hr;
337

338
339
340
341
    ZeroMemory(&m_sizeLock, sizeof(CRITICAL_SECTION));
    InitializeCriticalSection(&m_sizeLock);
    ZeroMemory(&m_outputLock, sizeof(CRITICAL_SECTION));
    InitializeCriticalSection(&m_outputLock);
342
343

    UINT creationFlags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT; /* needed for hardware decoding */
344
345
346
#ifndef NDEBUG
    creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
347
348
349
350
351
352
353
354

    hr = D3D11CreateDevice(NULL,
                        D3D_DRIVER_TYPE_HARDWARE,
                        NULL,
                        creationFlags,
                        NULL,
                        NULL,
                        D3D11_SDK_VERSION,
355
                        &m_d3deviceVLC,
356
                        NULL,
357
                        &m_d3dctxVLC);
Martin Finkel's avatar
Martin Finkel committed
358
    DEBUG("CreateResources m_d3dctxVLC = %p this = %p \n", m_d3dctxVLC, this);
359
360
361

    if(FAILED(hr))
    {
Martin Finkel's avatar
Add CI    
Martin Finkel committed
362
363
        _com_error error(hr);
        DEBUG("FAILED to create d3d11 device and context %s \n", error.ErrorMessage());
364
365
366
367
368
369
    }

    DEBUG("Configuring multithread \n");

    /* The ID3D11Device must have multithread protection */
    ID3D10Multithread *pMultithread;
370
    hr = m_d3deviceUnity->QueryInterface(&pMultithread);
371
372
373
374
375
376
377
378
    if (SUCCEEDED(hr)) {
        pMultithread->SetMultithreadProtected(TRUE);
        pMultithread->Release();
    }

    DEBUG("Exiting CreateResources.\n");
}

379
void ReadWriteTexture::Cleanup()
380
381
{
    DEBUG("Entering ReleaseResources.\n");
382

383
384
385
386
387
    if(m_textureRenderTarget)
    {
        m_textureRenderTarget->Release();
        m_textureRenderTarget = nullptr;
    }
388
        
389
390
391
392
393
    if(m_textureShaderInput)
    {
        m_textureShaderInput->Release();
        m_textureShaderInput = nullptr;
    }
394

395
396
397
398
399
    if(m_textureUnity)
    {
        m_textureUnity->Release();
        m_textureUnity = nullptr;
    }
400
401
402
403
404
405

    if(m_sharedHandle)
    {
        CloseHandle(m_sharedHandle);
        m_sharedHandle = nullptr;
    }
406
407
}

408
409
void RenderAPI_D3D11::ReleaseResources()
{
Martin Finkel's avatar
Martin Finkel committed
410
411
    DEBUG("ReleaseResources called \n");

412
413
414
415
416
417
418
419
420
    if(m_d3deviceVLC)
    {
        m_d3deviceVLC->Release();
        m_d3deviceVLC = nullptr;
    }
    
    if(m_d3dctxVLC)
    {
        m_d3dctxVLC->Release();
Martin Finkel's avatar
Martin Finkel committed
421
        m_d3dctxVLC = nullptr;
422
423
424
    }
}

425
bool RenderAPI_D3D11::UpdateOutput( const libvlc_video_render_cfg_t *cfg, libvlc_video_output_cfg_t *out )
426
427
428
{
    DEBUG("Entering UpdateOutput_cb.\n");

429
    DXGI_FORMAT renderFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
430
    Update(cfg->width, cfg->height);
431

432
    out->dxgi_format = renderFormat;
433
434
435
    out->full_range     = true;
    out->colorspace     = libvlc_video_colorspace_BT709;
    out->primaries      = libvlc_video_primaries_BT709;
436
    out->transfer       = m_linear ? libvlc_video_transfer_func_LINEAR : libvlc_video_transfer_func_SRGB;
437

Martin Finkel's avatar
Martin Finkel committed
438
439
    DEBUG("Exiting UpdateOutput_cb \n");

440
    return true;
Martin Finkel's avatar
Martin Finkel committed
441
442
}

443
void RenderAPI_D3D11::Swap()
444
{
Steve Lhomme's avatar
Steve Lhomme committed
445
    EnterCriticalSection(&m_outputLock);
446
    m_updated = true;
447
    write_on_first = !write_on_first;
Steve Lhomme's avatar
Steve Lhomme committed
448
    LeaveCriticalSection(&m_outputLock);
449
}
Martin Finkel's avatar
Martin Finkel committed
450

451
bool RenderAPI_D3D11::StartRendering( bool enter )
452
453
{
    if ( enter )
454
        EnterCriticalSection(&m_outputLock);
455
456
    else
        LeaveCriticalSection(&m_outputLock);
457
458
459
460

    return true;
}

461
bool RenderAPI_D3D11::SelectPlane( size_t plane, void *output )
462
{
Martin Finkel's avatar
Martin Finkel committed
463
    if ( plane != 0 || m_d3dctxVLC == NULL ) // we only support one packed RGBA plane (DXGI_FORMAT_R8G8B8A8_UNORM)
464
        return false;
465
    size_t write_index = write_on_first ? 0 : 1;
466
    static const FLOAT blackRGBA[4] = {0.0f, 0.0f, 0.0f, 1.0f};
467
468
    m_d3dctxVLC->OMSetRenderTargets( 1, &read_write[write_index]->m_textureRenderTarget, NULL );
    m_d3dctxVLC->ClearRenderTargetView( read_write[write_index]->m_textureRenderTarget, blackRGBA);
469
470
471
    return true;
}

472
bool RenderAPI_D3D11::Setup( const libvlc_video_setup_device_cfg_t *cfg, libvlc_video_setup_device_info_t *out )
473
{
Martin Finkel's avatar
Martin Finkel committed
474
    DEBUG("Setup m_d3dctxVLC = %p this = %p \n", m_d3dctxVLC, this);
Martin Finkel's avatar
Martin Finkel committed
475
    out->d3d11.device_context = m_d3dctxVLC;
476
477
478
    return true;
}

479
void RenderAPI_D3D11::Resize(void (*report_size_change)(void *report_opaque, unsigned width, unsigned height),
480
481
                       void *report_opaque )
{
482
    DEBUG("Resize_cb called \n");
483
484
485
    EnterCriticalSection(&m_sizeLock);
    m_ReportSize = report_size_change;
    m_reportOpaque = report_opaque;
Martin Finkel's avatar
Martin Finkel committed
486

487
    if (m_ReportSize != nullptr)
488
    {
489
        DEBUG("Invoking m_ReportSize(m_reportOpaque, m_width, m_height) with width=%u and height=%u \n", m_width, m_height);
490

491
        /* report our initial size */
492
        m_ReportSize(m_reportOpaque, m_width, m_height);
493
    }
494
    LeaveCriticalSection(&m_sizeLock);
495
496

    DEBUG("Exiting Resize_cb");
497
}
Martin Finkel's avatar
Martin Finkel committed
498

499
500
void* RenderAPI_D3D11::getVideoFrame(bool* out_updated)
{
501
    void* result;
502
    EnterCriticalSection(&m_outputLock);
503
    if(m_d3dctxUnity == NULL)
504
    {
505
        DEBUG("m_d3dctxUnity is NULL...");
506
        result = nullptr;
507
    }
508
509
510
    else
    {
        *out_updated = m_updated;
Martin Finkel's avatar
Martin Finkel committed
511
        m_updated = false;
512
513
        size_t read_index = write_on_first ? 1 : 0;
        result = read_write[read_index]->m_textureShaderInput;
514
515
    }

516
    LeaveCriticalSection(&m_outputLock);
517
    return result;
518
519
}

520
521
522
523
void RenderAPI_D3D11::setColorSpace(int color_space)
{
    m_linear = color_space == 1;
}
524

Martin Finkel's avatar
WIP    
Martin Finkel committed
525
// #endif // #if SUPPORT_D3D11