WIP Wayland rendering code for VAAPI

This commit is contained in:
Cameron Gutman
2018-08-11 19:43:36 -07:00
parent 8e2ad133ea
commit bb18519e44
4 changed files with 159 additions and 57 deletions

View File

@@ -6,9 +6,7 @@
#include <SDL_syswm.h>
VAAPIRenderer::VAAPIRenderer()
: m_HwContext(nullptr),
m_X11VaLibHandle(nullptr),
m_vaPutSurface(nullptr)
: m_HwContext(nullptr)
{
}
@@ -16,14 +14,30 @@ VAAPIRenderer::VAAPIRenderer()
VAAPIRenderer::~VAAPIRenderer()
{
if (m_HwContext != nullptr) {
av_buffer_unref(&m_HwContext);
}
AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data;
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)deviceContext->hwctx;
if (m_X11VaLibHandle != nullptr) {
dlclose(m_X11VaLibHandle);
// Hold onto this VADisplay since we'll need it to uninitialize VAAPI
VADisplay display = vaDeviceContext->display;
av_buffer_unref(&m_HwContext);
if (display) {
vaTerminate(display);
}
}
}
void VAAPIRenderer::vaapiLogError(void*, const char *message)
{
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "VAAPI: %s", message);
}
void VAAPIRenderer::vaapiLogInfo(void*, const char *message)
{
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "VAAPI: %s", message);
}
bool
VAAPIRenderer::initialize(SDL_Window* window, int, int width, int height)
{
@@ -38,31 +52,53 @@ VAAPIRenderer::initialize(SDL_Window* window, int, int width, int height)
SDL_VERSION(&info.version);
if (!SDL_GetWindowWMInfo(window, &info)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"SDL_GetWindowWMInfo() failed: %s",
SDL_GetError());
return false;
}
SDL_assert(info.subsystem == SDL_SYSWM_X11);
m_HwContext = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI);
if (!m_HwContext) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to allocate VAAPI context");
return false;
}
AVHWDeviceContext* deviceContext = (AVHWDeviceContext*)m_HwContext->data;
AVVAAPIDeviceContext* vaDeviceContext = (AVVAAPIDeviceContext*)deviceContext->hwctx;
m_WindowSystem = info.subsystem;
if (info.subsystem == SDL_SYSWM_X11) {
#ifdef HAVE_LIBVA_X11
m_XWindow = info.info.x11.window;
m_X11VaLibHandle = dlopen("libva-x11.so", RTLD_LAZY);
if (!m_X11VaLibHandle) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"dlopen(libva.so) failed: %s",
dlerror());
vaDeviceContext->display = vaGetDisplay(info.info.x11.display);
if (!vaDeviceContext->display) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to open X11 display for VAAPI");
return false;
}
m_vaPutSurface = (vaPutSurface_t)dlsym(m_X11VaLibHandle, "vaPutSurface");
#else
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Moonlight not compiled with VAAPI X11 support!");
return false;
#endif
}
else if (info.subsystem == SDL_SYSWM_WAYLAND) {
#ifdef HAVE_LIBVA_WAYLAND
m_WaylandSurface = info.info.wl.surface;
m_WaylandDisplay = info.info.wl.display;
vaDeviceContext->display = vaGetDisplayWl(info.info.wl.display);
if (!vaDeviceContext->display) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Unable to open Wayland display for VAAPI");
return false;
}
#else
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"VAAPI backend does not currently support Wayland");
"Moonlight not compiled with VAAPI Wayland support!");
return false;
#endif
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
@@ -71,18 +107,34 @@ VAAPIRenderer::initialize(SDL_Window* window, int, int width, int height)
return false;
}
err = av_hwdevice_ctx_create(&m_HwContext,
AV_HWDEVICE_TYPE_VAAPI,
nullptr, nullptr, 0);
vaSetErrorCallback(vaDeviceContext->display, &VAAPIRenderer::vaapiLogError, nullptr);
vaSetInfoCallback(vaDeviceContext->display, &VAAPIRenderer::vaapiLogInfo, nullptr);
int major, minor;
VAStatus status;
status = vaInitialize(vaDeviceContext->display, &major, &minor);
if (status != VA_STATUS_SUCCESS) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to initialize VAAPI: %d",
status);
return false;
}
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Initialized VAAPI %d.%d",
major, minor);
// This will populate the driver_quirks
err = av_hwdevice_ctx_init(m_HwContext);
if (err < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"Failed to create VAAPI context: %d",
"Failed to initialize VAAPI context: %d",
err);
return false;
}
// This quirk is set for the VDPAU wrapper which doesn't work with our VAAPI renderer
if (((AVVAAPIDeviceContext*)((AVHWDeviceContext*)(m_HwContext->data))->hwctx)->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES) {
if (vaDeviceContext->driver_quirks & AV_VAAPI_DRIVER_QUIRK_SURFACE_ATTRIBUTES) {
// Fail and let our VDPAU renderer pick this up
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,
"Avoiding VDPAU wrapper for VAAPI decoding");
@@ -98,7 +150,8 @@ VAAPIRenderer::prepareDecoderContext(AVCodecContext* context)
context->hw_device_ctx = av_buffer_ref(m_HwContext);
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION,
"Using VAAPI accelerated renderer");
"Using VAAPI accelerated renderer on %s",
m_WindowSystem == SDL_SYSWM_X11 ? "X11" : "Wayland");
return true;
}
@@ -120,14 +173,45 @@ VAAPIRenderer::renderFrame(AVFrame* frame)
StreamUtils::scaleSourceToDestinationSurface(&src, &dst);
m_vaPutSurface(vaDeviceContext->display,
surface,
m_XWindow,
0, 0,
m_VideoWidth, m_VideoHeight,
dst.x, dst.y,
dst.w, dst.h,
NULL, 0, 0);
if (m_WindowSystem == SDL_SYSWM_X11) {
#ifdef HAVE_LIBVA_X11
vaPutSurface(vaDeviceContext->display,
surface,
m_XWindow,
0, 0,
m_VideoWidth, m_VideoHeight,
dst.x, dst.y,
dst.w, dst.h,
NULL, 0, 0);
#endif
}
else if (m_WindowSystem == SDL_SYSWM_WAYLAND) {
#ifdef HAVE_LIBVA_WAYLAND
struct wl_buffer* buffer;
VAStatus status;
status = vaGetSurfaceBufferWl(vaDeviceContext->display,
surface,
VA_FRAME_PICTURE,
&buffer);
if (status == VA_STATUS_SUCCESS) {
wl_surface_attach(m_WaylandSurface, buffer, 0, 0);
wl_surface_damage(m_WaylandSurface, dst.x, dst.y, dst.w, dst.h);
wl_display_flush(m_WaylandDisplay);
wl_surface_commit(m_WaylandSurface);
}
else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION,
"vaGetSurfaceBufferWl failed(): %d",
status);
}
#endif
}
else {
// We don't accept anything else in initialize().
SDL_assert(false);
}
av_frame_free(&frame);
}