forked from wang-bin/QtAV
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSurfaceInteropD3D9EGL.cpp
204 lines (185 loc) · 8.34 KB
/
SurfaceInteropD3D9EGL.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/******************************************************************************
QtAV: Multimedia framework based on Qt and FFmpeg
Copyright (C) 2012-2016 Wang Bin <[email protected]>
* This file is part of QtAV (from 2015)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
******************************************************************************/
#include "SurfaceInteropD3D9.h"
#define DX_LOG_COMPONENT "D3D9EGL Interop"
#include "utils/DirectXHelper.h"
#include "opengl/OpenGLHelper.h"
#ifdef QT_OPENGL_ES_2_ANGLE_STATIC
#define CAPI_LINK_EGL
#else
#define EGL_CAPI_NS
#endif //QT_OPENGL_ES_2_ANGLE_STATIC
#include "capi/egl_api.h"
#include <EGL/eglext.h> //include after egl_capi.h to match types
namespace QtAV {
namespace d3d9 { //d3d9
class EGL {
public:
EGL() : dpy(EGL_NO_DISPLAY), surface(EGL_NO_SURFACE) {}
EGLDisplay dpy;
EGLSurface surface;
};
class EGLInteropResource Q_DECL_FINAL: public InteropResource
{
public:
EGLInteropResource(IDirect3DDevice9 * d3device);
~EGLInteropResource();
bool map(IDirect3DSurface9 *surface, GLuint tex, int w, int h, int) Q_DECL_OVERRIDE;
private:
void releaseEGL();
bool ensureSurface(int w, int h);
EGL* egl;
IDirect3DQuery9 *dx_query;
};
InteropResource* CreateInteropEGL(IDirect3DDevice9 *dev)
{
return new EGLInteropResource(dev);
}
EGLInteropResource::EGLInteropResource(IDirect3DDevice9 * d3device)
: InteropResource(d3device)
, egl(new EGL())
, dx_query(NULL)
{
DX_ENSURE_OK(d3device->CreateQuery(D3DQUERYTYPE_EVENT, &dx_query));
dx_query->Issue(D3DISSUE_END);
}
EGLInteropResource::~EGLInteropResource()
{
releaseEGL();
if (egl) {
delete egl;
egl = NULL;
}
SafeRelease(&dx_query);
}
void EGLInteropResource::releaseEGL() {
if (egl->surface != EGL_NO_SURFACE) {
eglReleaseTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER);
eglDestroySurface(egl->dpy, egl->surface);
egl->surface = EGL_NO_SURFACE;
}
}
bool EGLInteropResource::ensureSurface(int w, int h) {
if (egl->surface && width == w && height == h)
return true;
releaseEGL(); //
egl->dpy = eglGetCurrentDisplay();
qDebug("EGL version: %s, client api: %s", eglQueryString(egl->dpy, EGL_VERSION), eglQueryString(egl->dpy, EGL_CLIENT_APIS));
EGLint cfg_attribs[] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8, //
EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE, //remove?
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_NONE
};
EGLint nb_cfgs;
EGLConfig egl_cfg;
if (!eglChooseConfig(egl->dpy, cfg_attribs, &egl_cfg, 1, &nb_cfgs)) {
qWarning("Failed to create EGL configuration");
return false;
}
// check extensions
QList<QByteArray> extensions = QByteArray(eglQueryString(egl->dpy, EGL_EXTENSIONS)).split(' ');
// ANGLE_d3d_share_handle_client_buffer will be used if possible
// TODO: strstr is enough
const bool kEGL_ANGLE_d3d_share_handle_client_buffer = extensions.contains("EGL_ANGLE_d3d_share_handle_client_buffer");
const bool kEGL_ANGLE_query_surface_pointer = extensions.contains("EGL_ANGLE_query_surface_pointer");
if (!kEGL_ANGLE_d3d_share_handle_client_buffer && !kEGL_ANGLE_query_surface_pointer) {
qWarning("EGL extension 'kEGL_ANGLE_query_surface_pointer' or 'ANGLE_d3d_share_handle_client_buffer' is required!");
return false;
}
GLint has_alpha = 1; //QOpenGLContext::currentContext()->format().hasAlpha()
eglGetConfigAttrib(egl->dpy, egl_cfg, EGL_BIND_TO_TEXTURE_RGBA, &has_alpha); //EGL_ALPHA_SIZE
qDebug("choose egl display:%p config: %p/%d, has alpha: %d", egl->dpy, egl_cfg, nb_cfgs, has_alpha);
EGLint attribs[] = {
EGL_WIDTH, w,
EGL_HEIGHT, h,
EGL_TEXTURE_FORMAT, has_alpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB,
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_NONE
};
HANDLE share_handle = NULL;
if (!kEGL_ANGLE_d3d_share_handle_client_buffer && kEGL_ANGLE_query_surface_pointer) {
EGL_ENSURE((egl->surface = eglCreatePbufferSurface(egl->dpy, egl_cfg, attribs)) != EGL_NO_SURFACE, false);
qDebug("pbuffer surface: %p", egl->surface);
PFNEGLQUERYSURFACEPOINTERANGLEPROC eglQuerySurfacePointerANGLE = reinterpret_cast<PFNEGLQUERYSURFACEPOINTERANGLEPROC>(eglGetProcAddress("eglQuerySurfacePointerANGLE"));
if (!eglQuerySurfacePointerANGLE) {
qWarning("EGL_ANGLE_query_surface_pointer is not supported");
return false;
}
EGL_ENSURE(eglQuerySurfacePointerANGLE(egl->dpy, egl->surface, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, &share_handle), false);
}
releaseDX();
// _A8 for a yuv plane
/*
* d3d resource share requires windows >= vista: https://msdn.microsoft.com/en-us/library/windows/desktop/bb219800(v=vs.85).aspx
* from extension files:
* d3d9: level must be 1, dimensions must match EGL surface's
* d3d9ex or d3d10:
*/
DX_ENSURE_OK(d3ddev->CreateTexture(w, h, 1,
D3DUSAGE_RENDERTARGET,
has_alpha ? D3DFMT_A8R8G8B8 : D3DFMT_X8R8G8B8,
D3DPOOL_DEFAULT,
&dx_texture,
&share_handle) , false);
DX_ENSURE_OK(dx_texture->GetSurfaceLevel(0, &dx_surface), false);
if (kEGL_ANGLE_d3d_share_handle_client_buffer) {
// requires extension EGL_ANGLE_d3d_share_handle_client_buffer
// egl surface size must match d3d texture's
// d3d9ex or d3d10 is required
EGL_ENSURE((egl->surface = eglCreatePbufferFromClientBuffer(egl->dpy, EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE, share_handle, egl_cfg, attribs)), false);
qDebug("pbuffer surface from client buffer: %p", egl->surface);
}
width = w;
height = h;
return true;
}
bool EGLInteropResource::map(IDirect3DSurface9* surface, GLuint tex, int w, int h, int)
{
if (!ensureSurface(w, h)) {
releaseEGL();
releaseDX();
return false;
}
const RECT src = { 0, 0, (~0-1)&w, (~0-1)&h};
DX_ENSURE(d3ddev->StretchRect(surface, &src, dx_surface, NULL, D3DTEXF_NONE), false);
if (dx_query) {
// Flush the draw command now. Ideally, this should be done immediately before the draw call that uses the texture. Flush it once here though.
dx_query->Issue(D3DISSUE_END); //StretchRect does not supports odd values
// ensure data is copied to egl surface. Solution and comment is from chromium
// The DXVA decoder has its own device which it uses for decoding. ANGLE has its own device which we don't have access to.
// The above code attempts to copy the decoded picture into a surface which is owned by ANGLE.
// As there are multiple devices involved in this, the StretchRect call above is not synchronous.
// We attempt to flush the batched operations to ensure that the picture is copied to the surface owned by ANGLE.
// We need to do this in a loop and call flush multiple times.
// We have seen the GetData call for flushing the command buffer fail to return success occassionally on multi core machines, leading to an infinite loop.
// Workaround is to have an upper limit of 10 on the number of iterations to wait for the Flush to finish.
int k = 0;
while ((dx_query->GetData(NULL, 0, D3DGETDATA_FLUSH) == FALSE) && ++k < 10) {
Sleep(1);
}
}
DYGL(glBindTexture(GL_TEXTURE_2D, tex));
eglBindTexImage(egl->dpy, egl->surface, EGL_BACK_BUFFER);
DYGL(glBindTexture(GL_TEXTURE_2D, 0));
return true;
}
} //namespace d3d9
} //namespace QtAV