first commit
This commit is contained in:
210
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9EGL.cpp
Normal file
210
project/fm_viewer/fav/hw_decoder/SurfaceInteropD3D9EGL.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
/******************************************************************************
|
||||
QtAV: Multimedia framework based on Qt and FFmpeg
|
||||
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
|
||||
|
||||
* 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
|
||||
******************************************************************************/
|
||||
#ifdef RM_USE_HW_DECODER
|
||||
#define REMOVE_SURFACE_D3D_EGL 1
|
||||
|
||||
#if !(REMOVE_SURFACE_D3D_EGL)
|
||||
|
||||
#include "SurfaceInteropD3D9.h"
|
||||
#define DX_LOG_COMPONENT "D3D9EGL Interop"
|
||||
#include "../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 FAV {
|
||||
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 FAV
|
||||
|
||||
#endif // REMOVE_SURFACE_D3D_EGL
|
||||
#endif // #ifdef RM_USE_HW_DECODER
|
||||
|
||||
Reference in New Issue
Block a user