Merge pull request #3320 from jsorg71/nvenc

add support for nvenc and accel_assist
This commit is contained in:
jsorg71
2025-02-21 22:39:57 -08:00
committed by GitHub
22 changed files with 11019 additions and 1 deletions
+1
View File
@@ -63,3 +63,4 @@ xrdp/xrdp.ini
xrdp_configure_options.h xrdp_configure_options.h
xrdpapi/xrdp-xrdpapi-simple xrdpapi/xrdp-xrdpapi-simple
.vscode/* .vscode/*
xrdp_accel_assist/xrdp-accel-assist
+8 -1
View File
@@ -44,6 +44,12 @@ else
ULALACADIR = ULALACADIR =
endif endif
if XRDP_ACCEL
ACCELDIR = xrdp_accel_assist
else
ACCELDIR =
endif
# This should not be dictionary order but build order # This should not be dictionary order but build order
SUBDIRS = \ SUBDIRS = \
third_party \ third_party \
@@ -70,7 +76,8 @@ SUBDIRS = \
$(XRDPVRDIR) \ $(XRDPVRDIR) \
$(ULALACADIR) \ $(ULALACADIR) \
tests \ tests \
tools tools \
$(ACCELDIR)
distclean-local: distclean-local:
-rm -f xrdp_configure_options.h -rm -f xrdp_configure_options.h
+4
View File
@@ -30,6 +30,10 @@ AM_CPPFLAGS = \
-DXRDP_PID_PATH=\"${localstatedir}/run\" \ -DXRDP_PID_PATH=\"${localstatedir}/run\" \
-DXRDP_LOG_PATH=\"${localstatedir}/log\" -DXRDP_LOG_PATH=\"${localstatedir}/log\"
if XRDP_NVENC
AM_CPPFLAGS += -DXRDP_NVENC
endif
# -no-suppress is an automake-specific flag which is needed # -no-suppress is an automake-specific flag which is needed
# to prevent us missing compiler errors in some circumstances # to prevent us missing compiler errors in some circumstances
# (see https://github.com/neutrinolabs/xrdp/pull/1843 ) # (see https://github.com/neutrinolabs/xrdp/pull/1843 )
+63
View File
@@ -154,6 +154,36 @@ g_init(const char *app_name)
WSAStartup(2, &wsadata); WSAStartup(2, &wsadata);
#endif #endif
#if defined(XRDP_NVENC)
if (g_strcmp(app_name, "xrdp-sesman") == 0)
{
/* call cuInit() to initalize the nvidia drivers */
/* TODO create an issue on nvidia forums to figure out why we need to
* do this */
if (g_fork() == 0)
{
typedef int (*cu_init_proc)(int flags);
cu_init_proc cu_init;
long lib;
char cuda_lib_name[] = "libcuda.so";
char cuda_func_name[] = "cuInit";
lib = g_load_library(cuda_lib_name);
if (lib != 0)
{
cu_init = (cu_init_proc)
g_get_proc_address(lib, cuda_func_name);
if (cu_init != NULL)
{
cu_init(0);
}
}
log_end();
g_deinit();
g_exit(0);
}
}
#endif
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -1530,6 +1560,39 @@ g_sck_send_fd_set(int sck, const void *ptr, unsigned int len,
return rv; return rv;
} }
/******************************************************************************/
int
g_alloc_shm_map_fd(void **addr, int *fd, size_t size)
{
int lfd = -1;
void *laddr;
char name[128];
static unsigned int autoinc;
snprintf(name, 128, "/%8.8X%8.8X", getpid(), autoinc++);
lfd = shm_open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (lfd == -1)
{
return 1;
}
shm_unlink(name);
if (ftruncate(lfd, size) == -1)
{
close(lfd);
return 2;
}
/* map fd to address space */
laddr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, lfd, 0);
if (laddr == MAP_FAILED)
{
close(lfd);
return 3;
}
*addr = laddr;
*fd = lfd;
return 0;
}
/*****************************************************************************/ /*****************************************************************************/
/* returns boolean */ /* returns boolean */
int int
+1
View File
@@ -124,6 +124,7 @@ int g_sck_recv_fd_set(int sck, void *ptr, unsigned int len,
*/ */
int g_sck_send_fd_set(int sck, const void *ptr, unsigned int len, int g_sck_send_fd_set(int sck, const void *ptr, unsigned int len,
int fds[], unsigned int fdcount); int fds[], unsigned int fdcount);
int g_alloc_shm_map_fd(void **addr, int *fd, size_t size);
int g_sck_last_error_would_block(int sck); int g_sck_last_error_would_block(int sck);
int g_sck_socket_ok(int sck); int g_sck_socket_ok(int sck);
/** /**
+2
View File
@@ -57,6 +57,8 @@ enum xrdp_source
XRDP_SOURCE_SESMAN, XRDP_SOURCE_SESMAN,
XRDP_SOURCE_CHANSRV, XRDP_SOURCE_CHANSRV,
XRDP_SOURCE_MOD, XRDP_SOURCE_MOD,
XORGXRDP_SOURCE_XORG,
XORGXRDP_SOURCE_XRDP,
XRDP_SOURCE_MAX_COUNT XRDP_SOURCE_MAX_COUNT
}; };
+31
View File
@@ -179,6 +179,15 @@ AC_ARG_ENABLE(openh264, AS_HELP_STRING([--enable-openh264],
[Use Cisco OpenH264 library (default: no)]), [Use Cisco OpenH264 library (default: no)]),
[], [enable_openh264=no]) [], [enable_openh264=no])
AM_CONDITIONAL(XRDP_OPENH264, [test x$enable_openh264 = xyes]) AM_CONDITIONAL(XRDP_OPENH264, [test x$enable_openh264 = xyes])
AC_ARG_ENABLE(nvenc, AS_HELP_STRING([--enable-nvenc],
[Use nvenc library (default: no), env vars XRDP_NVENC_CFLAGS and
XRDP_NVENC_LIBS should be set if used]),
[], [enable_nvenc=no])
AM_CONDITIONAL(XRDP_NVENC, [test x$enable_nvenc = xyes])
AC_ARG_ENABLE(accel, AS_HELP_STRING([--enable-accel],
[Build xrdp_accel_assist (default: no, auto set if --enable-nvenc)]),
[], [enable_accel=no])
# AM_CONDITIONAL(XRDP_ACCEL, [test x$enable_accel = xyes]) later in this file
AC_ARG_ENABLE(painter, AS_HELP_STRING([--disable-painter], AC_ARG_ENABLE(painter, AS_HELP_STRING([--disable-painter],
[Do not use included painter library (default: no)]), [Do not use included painter library (default: no)]),
[], [enable_painter=yes]) [], [enable_painter=yes])
@@ -263,6 +272,9 @@ AC_CHECK_HEADER([security/_pam_types.h],
AC_CHECK_HEADER([security/pam_constants.h], AC_CHECK_HEADER([security/pam_constants.h],
[AC_DEFINE([HAVE_PAM_CONSTANTS_H], 1, [Using OpenPAM], [])]) [AC_DEFINE([HAVE_PAM_CONSTANTS_H], 1, [Using OpenPAM], [])])
# shm_open may not be in the C library
AC_SEARCH_LIBS([shm_open], [rt])
# Find imlib2 # Find imlib2
case "$with_imlib2" in case "$with_imlib2" in
'' | no) AC_MSG_NOTICE([imlib2 will not be supported]) '' | no) AC_MSG_NOTICE([imlib2 will not be supported])
@@ -499,6 +511,21 @@ AS_IF( [test "x$enable_x264" = "xyes"] , [PKG_CHECK_MODULES(XRDP_X264, x264 >= 0
AS_IF( [test "x$enable_openh264" = "xyes"] , [PKG_CHECK_MODULES(XRDP_OPENH264, openh264 >= 2.0.0)] ) AS_IF( [test "x$enable_openh264" = "xyes"] , [PKG_CHECK_MODULES(XRDP_OPENH264, openh264 >= 2.0.0)] )
if test "x$enable_nvenc" = "xyes"
then
enable_accel=$enable_nvenc
if test ! -z "$XRDP_NVENC_CFLAGS"
then
AC_SUBST(XRDP_NVENC_CFLAGS, ["$XRDP_NVENC_CFLAGS"])
fi
if test ! -z "$XRDP_NVENC_LIBS"
then
AC_SUBST(XRDP_NVENC_LIBS, ["$XRDP_NVENC_LIBS"])
fi
fi
AM_CONDITIONAL(XRDP_ACCEL, [test x$enable_accel = xyes])
# checking for TurboJPEG # checking for TurboJPEG
if test "x$enable_tjpeg" = "xyes" if test "x$enable_tjpeg" = "xyes"
then then
@@ -665,6 +692,8 @@ AC_CONFIG_FILES([
xup/Makefile xup/Makefile
third_party/Makefile third_party/Makefile
third_party/tomlc99/Makefile third_party/tomlc99/Makefile
xrdp_accel_assist/Makefile
]) ])
AC_REQUIRE_AUX_FILE([tap-driver.sh]) AC_REQUIRE_AUX_FILE([tap-driver.sh])
@@ -681,6 +710,8 @@ echo " turbo jpeg $enable_tjpeg"
echo " rfxcodec $enable_rfxcodec" echo " rfxcodec $enable_rfxcodec"
echo " x264 $enable_x264" echo " x264 $enable_x264"
echo " openh264 $enable_openh264" echo " openh264 $enable_openh264"
echo " nvenc $enable_nvenc"
echo " accel $enable_accel"
echo " painter $enable_painter" echo " painter $enable_painter"
echo " pixman $enable_pixman" echo " pixman $enable_pixman"
echo " fuse $enable_fuse" echo " fuse $enable_fuse"
+4
View File
@@ -147,6 +147,8 @@ param=Xorg
; Leave the rest parameters as-is unless you understand what will happen. ; Leave the rest parameters as-is unless you understand what will happen.
param=-config param=-config
param=xrdp/xorg.conf param=xrdp/xorg.conf
;param=xrdp/xorg_nvidia.conf
;param=xrdp/xorg_nvidia_grid.conf
param=-noreset param=-noreset
param=-nolisten param=-nolisten
param=tcp param=tcp
@@ -219,3 +221,5 @@ EnableSyslog=true
[SessionVariables] [SessionVariables]
PULSE_SCRIPT=@sesmansysconfdir@/pulse/default.pa PULSE_SCRIPT=@sesmansysconfdir@/pulse/default.pa
;XRDP_USE_ACCEL_ASSIST=1
;XRDP_NVIDIA_GRID=1
+34
View File
@@ -0,0 +1,34 @@
AM_CPPFLAGS = \
-I$(top_srcdir)/common
EXTRA_DIST = xrdp_accel_assist_shaders.c
XRDP_EXTRA_LIBS =
XRDP_EXTRA_SOURCES =
if XRDP_NVENC
AM_CPPFLAGS += -DXRDP_NVENC
AM_CPPFLAGS += $(XRDP_NVENC_CFLAGS)
XRDP_EXTRA_LIBS += $(XRDP_NVENC_LIBS)
XRDP_EXTRA_SOURCES += xrdp_accel_assist_nvenc.c xrdp_accel_assist_nvenc.h encoder_headers/nvEncodeAPI_11_1.h
endif
pkglibexec_PROGRAMS = \
xrdp-accel-assist
xrdp_accel_assist_SOURCES = \
xrdp_accel_assist.c \
xrdp_accel_assist.h \
xrdp_accel_assist_x11.c \
xrdp_accel_assist_x11.h \
xrdp_accel_assist_egl.c \
xrdp_accel_assist_egl.h \
xrdp_accel_assist_glx.c \
xrdp_accel_assist_glx.h \
$(XRDP_EXTRA_SOURCES)
xrdp_accel_assist_LDADD = \
$(top_builddir)/common/libcommon.la \
$(XRDP_EXTRA_LIBS) \
-lX11 -lepoxy
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+47
View File
@@ -0,0 +1,47 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2020-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _XRDP_ACCEL_ASSIST_H
#define _XRDP_ACCEL_ASSIST_H
#define XH_YUV420 1
#define XH_YUV422 2
#define XH_YUV444 3
#define XH_BT601 0
#define XH_BT709FR 1
#define XH_BTRFX 2
struct xh_rect
{
short x;
short y;
short w;
short h;
};
#define XH_ENC_FLAGS_FORCEIDR (1 << 0)
enum encoder_result
{
INCREMENTAL_FRAME_ENCODED, /* P frame */
KEY_FRAME_ENCODED, /* IDR frame */
ENCODER_ERROR
};
#endif
+159
View File
@@ -0,0 +1,159 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <epoxy/gl.h>
#include <epoxy/egl.h>
#include "arch.h"
#include "os_calls.h"
#include "string_calls.h"
#include "xrdp_accel_assist.h"
#include "xrdp_accel_assist_x11.h"
#include "xrdp_accel_assist_egl.h"
#include "log.h"
EGLDisplay g_egl_display;
EGLContext g_egl_context;
static EGLSurface g_egl_surface;
static EGLConfig g_ecfg;
static EGLint g_num_config;
/* X11 */
extern Display *g_display; /* in xrdp_accel_assist_x11.c */
extern Window g_root_window; /* in xrdp_accel_assist_x11.c */
static EGLint g_choose_config_attr[] =
{
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_NONE
};
static EGLint g_create_context_attr[] =
{
EGL_CONTEXT_MAJOR_VERSION, 3,
EGL_CONTEXT_MINOR_VERSION, 3,
EGL_NONE
};
static const EGLint g_create_surface_attr[] =
{
EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_NONE
};
/*****************************************************************************/
static EGLBoolean
xrdp_accel_assist_check_ext(const char *ext_name)
{
if (!epoxy_has_egl_extension(g_egl_display, ext_name))
{
LOG(LOG_LEVEL_INFO, "%s not present", ext_name);
return EGL_FALSE;
}
LOG(LOG_LEVEL_INFO, "%s present", ext_name);
return EGL_TRUE;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_egl_init(void)
{
int egl_ver;
int ok;
ok = eglBindAPI(EGL_OPENGL_API);
LOG(LOG_LEVEL_INFO, "eglBindAPI ok %d", ok);
g_egl_display = eglGetDisplay((EGLNativeDisplayType) g_display);
LOG(LOG_LEVEL_INFO, "g_egl_display %p", g_egl_display);
eglInitialize(g_egl_display, NULL, NULL);
egl_ver = epoxy_egl_version(g_egl_display);
LOG(LOG_LEVEL_INFO, "egl_ver %d", egl_ver);
if (egl_ver < 11) /* EGL version 1.1 */
{
LOG(LOG_LEVEL_INFO, "egl_ver too old %d", egl_ver);
eglTerminate(g_egl_display);
return 1;
}
if ((!xrdp_accel_assist_check_ext("EGL_NOK_texture_from_pixmap")) ||
(!xrdp_accel_assist_check_ext("EGL_MESA_image_dma_buf_export")) ||
(!xrdp_accel_assist_check_ext("EGL_KHR_image_base")))
{
LOG(LOG_LEVEL_INFO, "missing ext");
eglTerminate(g_egl_display);
return 1;
}
eglChooseConfig(g_egl_display, g_choose_config_attr, &g_ecfg,
1, &g_num_config);
LOG(LOG_LEVEL_INFO, "g_ecfg %p g_num_config %d", g_ecfg, g_num_config);
g_egl_surface = eglCreateWindowSurface(g_egl_display, g_ecfg,
g_root_window, NULL);
LOG(LOG_LEVEL_INFO, "g_egl_surface %p", g_egl_surface);
g_egl_context = eglCreateContext(g_egl_display, g_ecfg,
EGL_NO_CONTEXT, g_create_context_attr);
LOG(LOG_LEVEL_INFO, "g_egl_context %p", g_egl_context);
ok = eglMakeCurrent(g_egl_display, g_egl_surface, g_egl_surface,
g_egl_context);
LOG(LOG_LEVEL_INFO, "eglMakeCurrent ok %d", ok);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_egl_create_image(Pixmap pixmap, inf_image_t *inf_image)
{
*inf_image = (inf_image_t)eglCreatePixmapSurface(g_egl_display,
g_ecfg, pixmap, g_create_surface_attr);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_egl_destroy_image(inf_image_t inf_image)
{
eglDestroySurface(g_egl_display, (EGLSurface)inf_image);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_egl_bind_tex_image(inf_image_t inf_image)
{
eglBindTexImage(g_egl_display, (EGLSurface)inf_image, EGL_BACK_BUFFER);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_egl_release_tex_image(inf_image_t inf_image)
{
eglReleaseTexImage(g_egl_display, (EGLSurface)inf_image, EGL_BACK_BUFFER);
return 0;
}
+33
View File
@@ -0,0 +1,33 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _XRDP_ACCEL_ASSIST_EGL_H
#define _XRDP_ACCEL_ASSIST_EGL_H
int
xrdp_accel_assist_inf_egl_init(void);
int
xrdp_accel_assist_inf_egl_create_image(Pixmap pixmap, inf_image_t *inf_image);
int
xrdp_accel_assist_inf_egl_destroy_image(inf_image_t inf_image);
int
xrdp_accel_assist_inf_egl_bind_tex_image(inf_image_t inf_image);
int
xrdp_accel_assist_inf_egl_release_tex_image(inf_image_t inf_image);
#endif
+149
View File
@@ -0,0 +1,149 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <epoxy/gl.h>
#include <epoxy/glx.h>
#include "arch.h"
#include "os_calls.h"
#include "string_calls.h"
#include "xrdp_accel_assist.h"
#include "xrdp_accel_assist_x11.h"
#include "xrdp_accel_assist_glx.h"
#include "log.h"
static int g_n_fbconfigs = 0;
static int g_n_pixconfigs = 0;
static GLXFBConfig *g_fbconfigs = NULL;
static GLXFBConfig *g_pixconfigs = NULL;
static GLXContext g_gl_context = 0;
/* X11 */
extern Display *g_display; /* in xrdp_accel_assist_x11.c */
extern Window g_root_window; /* in xrdp_accel_assist_x11.c */
extern int g_screen_num; /* in xrdp_accel_assist_x11.c */
static const int g_fbconfig_attrs[] =
{
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
GLX_RENDER_TYPE, GLX_RGBA_BIT,
GLX_DOUBLEBUFFER, True,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
None
};
static const int g_pixconfig_attrs[] =
{
GLX_BIND_TO_TEXTURE_RGBA_EXT, True,
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
GLX_DOUBLEBUFFER, False,
GLX_Y_INVERTED_EXT, True,
None
};
static const int g_pixmap_attribs[] =
{
GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT,
None
};
/*****************************************************************************/
int
xrdp_accel_assist_inf_glx_init(void)
{
const char *ext_str;
int glx_ver;
int ok;
glx_ver = epoxy_glx_version(g_display, g_screen_num);
LOG(LOG_LEVEL_INFO, "glx_ver %d", glx_ver);
if (glx_ver < 11) /* GLX version 1.1 */
{
LOG(LOG_LEVEL_INFO, "glx_ver too old %d", glx_ver);
return 1;
}
if (!epoxy_has_glx_extension(g_display, g_screen_num,
"GLX_EXT_texture_from_pixmap"))
{
ext_str = glXQueryExtensionsString(g_display, g_screen_num);
LOG(LOG_LEVEL_INFO, "GLX_EXT_texture_from_pixmap not present [%s]",
ext_str);
return 1;
}
LOG(LOG_LEVEL_INFO, "GLX_EXT_texture_from_pixmap present");
g_fbconfigs = glXChooseFBConfig(g_display, g_screen_num,
g_fbconfig_attrs, &g_n_fbconfigs);
LOG(LOG_LEVEL_INFO, "g_fbconfigs %p", g_fbconfigs);
g_gl_context = glXCreateNewContext(g_display, g_fbconfigs[0],
GLX_RGBA_TYPE, NULL, 1);
LOG(LOG_LEVEL_INFO, "g_gl_context %p", g_gl_context);
ok = glXMakeCurrent(g_display, g_root_window, g_gl_context);
LOG(LOG_LEVEL_INFO, "ok %d", ok);
g_pixconfigs = glXChooseFBConfig(g_display, g_screen_num,
g_pixconfig_attrs, &g_n_pixconfigs);
LOG(LOG_LEVEL_INFO, "g_pixconfigs %p g_n_pixconfigs %d",
g_pixconfigs, g_n_pixconfigs);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_glx_create_image(Pixmap pixmap, inf_image_t *inf_image)
{
*inf_image = (inf_image_t)glXCreatePixmap(g_display, g_pixconfigs[0],
pixmap, g_pixmap_attribs);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_glx_destroy_image(inf_image_t inf_image)
{
glXDestroyPixmap(g_display, (GLXPixmap)inf_image);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_glx_bind_tex_image(inf_image_t inf_image)
{
glXBindTexImageEXT(g_display, (GLXPixmap)inf_image, GLX_FRONT_EXT, NULL);
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_inf_glx_release_tex_image(inf_image_t inf_image)
{
glXReleaseTexImageEXT(g_display, (GLXPixmap)inf_image, GLX_FRONT_EXT);
return 0;
}
+33
View File
@@ -0,0 +1,33 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _XRDP_ACCEL_ASSIST_GLX_H
#define _XRDP_ACCEL_ASSIST_GLX_H
int
xrdp_accel_assist_inf_glx_init(void);
int
xrdp_accel_assist_inf_glx_create_image(Pixmap pixmap, inf_image_t *inf_image);
int
xrdp_accel_assist_inf_glx_destroy_image(inf_image_t inf_image);
int
xrdp_accel_assist_inf_glx_bind_tex_image(inf_image_t inf_image);
int
xrdp_accel_assist_inf_glx_release_tex_image(inf_image_t inf_image);
#endif
+383
View File
@@ -0,0 +1,383 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "string_calls.h"
#include <epoxy/gl.h>
#include "encoder_headers/nvEncodeAPI_11_1.h"
#include "arch.h"
#include "os_calls.h"
#include "xrdp_accel_assist.h"
#include "xrdp_accel_assist_x11.h"
#include "xrdp_accel_assist_nvenc.h"
#include "log.h"
#define XH_NVENV_DEFAULT_QP 28
typedef NVENCSTATUS
(NVENCAPI *NvEncodeAPICreateInstanceProc)
(NV_ENCODE_API_FUNCTION_LIST *functionList);
static char g_lib_name[] = "libnvidia-encode.so";
static char g_lib_name1[] = "libnvidia-encode.so.1";
static char g_func_name[] = "NvEncodeAPICreateInstance";
static NvEncodeAPICreateInstanceProc g_NvEncodeAPICreateInstance = NULL;
static NV_ENCODE_API_FUNCTION_LIST g_enc_funcs;
static long g_lib = 0;
struct enc_info
{
int width;
int height;
int frameCount;
int pad0;
void *enc;
NV_ENC_OUTPUT_PTR bitstreamBuffer;
NV_ENC_INPUT_PTR mappedResource;
NV_ENC_BUFFER_FORMAT mappedBufferFmt;
NV_ENC_REGISTERED_PTR registeredResource;
};
/*****************************************************************************/
int
xrdp_accel_assist_nvenc_init(void)
{
NVENCSTATUS nv_error;
g_lib = g_load_library(g_lib_name);
if (g_lib == 0)
{
g_lib = g_load_library(g_lib_name1);
if (g_lib == 0)
{
LOG(LOG_LEVEL_ERROR, "load library for %s/%s failed", g_lib_name, g_lib_name1);
return 1;
}
}
g_NvEncodeAPICreateInstance = g_get_proc_address(g_lib, g_func_name);
if (g_NvEncodeAPICreateInstance == NULL)
{
LOG(LOG_LEVEL_ERROR, "get proc address for %s failed", g_func_name);
return 1;
}
g_memset(&g_enc_funcs, 0, sizeof(g_enc_funcs));
g_enc_funcs.version = NV_ENCODE_API_FUNCTION_LIST_VER;
nv_error = g_NvEncodeAPICreateInstance(&g_enc_funcs);
LOG(LOG_LEVEL_INFO, "NvEncodeAPICreateInstance rv %d", nv_error);
if (nv_error != NV_ENC_SUCCESS)
{
return 1;
}
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_nvenc_create_encoder(int width, int height, int tex,
int tex_format, struct enc_info **ei)
{
NV_ENC_CREATE_BITSTREAM_BUFFER bitstreamParams;
NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS params;
NV_ENC_INITIALIZE_PARAMS createEncodeParams;
NV_ENC_MAP_INPUT_RESOURCE mapInputResource;
NV_ENC_INPUT_RESOURCE_OPENGL_TEX res;
NV_ENC_REGISTER_RESOURCE reg_res;
NV_ENC_CONFIG encCfg;
NVENCSTATUS nv_error;
struct enc_info *lei;
char *rateControlMode_str;
char *averageBitRate_str;
char *qp_str;
int qp_int;
int averageBitRate_int;
int rc_set;
lei = g_new0(struct enc_info, 1);
if (lei == NULL)
{
return 1;
}
g_memset(&params, 0, sizeof(params));
params.version = NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER;
params.deviceType = NV_ENC_DEVICE_TYPE_OPENGL;
params.apiVersion = NVENCAPI_VERSION;
nv_error = g_enc_funcs.nvEncOpenEncodeSessionEx(&params, &(lei->enc));
LOG(LOG_LEVEL_INFO, "nvEncOpenEncodeSessionEx rv %d enc %p", nv_error, lei->enc);
if (nv_error != NV_ENC_SUCCESS)
{
g_free(lei);
return 1;
}
g_memset(&encCfg, 0, sizeof(encCfg));
encCfg.version = NV_ENC_CONFIG_VER;
encCfg.profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
encCfg.gopLength = NVENC_INFINITE_GOPLENGTH;
encCfg.frameIntervalP = 1; /* 1 + B_Frame_Count */
encCfg.frameFieldMode = NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME;
encCfg.mvPrecision = NV_ENC_MV_PRECISION_QUARTER_PEL;
/* these env vars can be added / changed in sesman.ini SessionVariables
example
XRDP_NVENC_RATE_CONTROL_MODE=NV_ENC_PARAMS_RC_CONSTQP
XRDP_NVENC_QP=30
or
XRDP_NVENC_RATE_CONTROL_MODE=NV_ENC_PARAMS_RC_VBR
XRDP_NVENC_AVERAGE_BITRATE=2000000 */
rateControlMode_str = g_getenv("XRDP_NVENC_RATE_CONTROL_MODE");
averageBitRate_str = g_getenv("XRDP_NVENC_AVERAGE_BITRATE");
qp_str = g_getenv("XRDP_NVENC_QP");
rc_set = 0;
if (rateControlMode_str != NULL)
{
if (g_strcmp(rateControlMode_str, "NV_ENC_PARAMS_RC_CONSTQP") == 0)
{
if (qp_str != NULL)
{
qp_int = g_atoi(qp_str);
if ((qp_int >= 0) && (qp_int <= 51))
{
LOG(LOG_LEVEL_INFO,
"using NV_ENC_PARAMS_RC_CONSTQP qp %d",
qp_int);
encCfg.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
encCfg.rcParams.constQP.qpInterP = qp_int;
encCfg.rcParams.constQP.qpInterB = qp_int;
encCfg.rcParams.constQP.qpIntra = qp_int;
rc_set = 1;
}
}
}
else if (g_strcmp(rateControlMode_str, "NV_ENC_PARAMS_RC_VBR") == 0)
{
if (averageBitRate_str != NULL)
{
averageBitRate_int = g_atoi(averageBitRate_str);
if ((averageBitRate_int >= 5000) &&
(averageBitRate_int <= 1000000000))
{
LOG(LOG_LEVEL_INFO,
"using NV_ENC_PARAMS_RC_VBR averageBitRate %d",
averageBitRate_int);
encCfg.rcParams.rateControlMode = NV_ENC_PARAMS_RC_VBR;
encCfg.rcParams.averageBitRate = averageBitRate_int;
rc_set = 1;
}
}
}
}
if (!rc_set)
{
LOG(LOG_LEVEL_INFO,
"using default NV_ENC_PARAMS_RC_CONSTQP qp %d",
XH_NVENV_DEFAULT_QP);
encCfg.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP;
encCfg.rcParams.constQP.qpInterP = XH_NVENV_DEFAULT_QP;
encCfg.rcParams.constQP.qpInterB = XH_NVENV_DEFAULT_QP;
encCfg.rcParams.constQP.qpIntra = XH_NVENV_DEFAULT_QP;
rc_set = 1;
}
encCfg.encodeCodecConfig.h264Config.chromaFormatIDC = 1;
encCfg.encodeCodecConfig.h264Config.idrPeriod = NVENC_INFINITE_GOPLENGTH;
encCfg.encodeCodecConfig.h264Config.repeatSPSPPS = 1;
encCfg.encodeCodecConfig.h264Config.disableSPSPPS = 0;
g_memset(&createEncodeParams, 0, sizeof(createEncodeParams));
createEncodeParams.version = NV_ENC_INITIALIZE_PARAMS_VER;
createEncodeParams.encodeGUID = NV_ENC_CODEC_H264_GUID;
createEncodeParams.encodeWidth = width;
createEncodeParams.encodeHeight = height;
createEncodeParams.darWidth = width;
createEncodeParams.darHeight = height;
createEncodeParams.frameRateNum = 30;
createEncodeParams.frameRateDen = 1;
createEncodeParams.enablePTD = 1;
createEncodeParams.encodeConfig = &encCfg;
nv_error = g_enc_funcs.nvEncInitializeEncoder(lei->enc,
&createEncodeParams);
LOG(LOG_LEVEL_INFO, "nvEncInitializeEncoder rv %d", nv_error);
if (nv_error != NV_ENC_SUCCESS)
{
g_free(lei);
return 1;
}
g_memset(&res, 0, sizeof(res));
res.texture = tex;
res.target = GL_TEXTURE_2D;
g_memset(&reg_res, 0, sizeof(reg_res));
reg_res.version = NV_ENC_REGISTER_RESOURCE_VER;
reg_res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX;
reg_res.width = width;
reg_res.height = height;
if (tex_format == XH_YUV420)
{
reg_res.pitch = width;
reg_res.bufferFormat = NV_ENC_BUFFER_FORMAT_NV12;
}
else
{
reg_res.pitch = width * 4;
reg_res.bufferFormat = NV_ENC_BUFFER_FORMAT_AYUV;
}
reg_res.resourceToRegister = &res;
reg_res.bufferUsage = NV_ENC_INPUT_IMAGE;
nv_error = g_enc_funcs.nvEncRegisterResource(lei->enc, &reg_res);
LOG(LOG_LEVEL_INFO, "nvEncRegisterResource rv %d", nv_error);
if (nv_error != NV_ENC_SUCCESS)
{
g_free(lei);
return 1;
}
g_memset(&mapInputResource, 0, sizeof(mapInputResource));
mapInputResource.version = NV_ENC_LOCK_INPUT_BUFFER_VER;
mapInputResource.registeredResource = reg_res.registeredResource;
nv_error = g_enc_funcs.nvEncMapInputResource(lei->enc, &mapInputResource);
LOG(LOG_LEVEL_INFO, "nvEncMapInputResource rv %d", nv_error);
if (nv_error != NV_ENC_SUCCESS)
{
g_free(lei);
return 1;
}
g_memset(&bitstreamParams, 0, sizeof(bitstreamParams));
bitstreamParams.version = NV_ENC_CREATE_BITSTREAM_BUFFER_VER;
nv_error = g_enc_funcs.nvEncCreateBitstreamBuffer(lei->enc,
&bitstreamParams);
LOG(LOG_LEVEL_INFO, "nvEncCreateBitstreamBuffer rv %d", nv_error);
if (nv_error != NV_ENC_SUCCESS)
{
g_free(lei);
return 1;
}
lei->bitstreamBuffer = bitstreamParams.bitstreamBuffer;
lei->mappedResource = mapInputResource.mappedResource;
lei->mappedBufferFmt = mapInputResource.mappedBufferFmt;
lei->registeredResource = reg_res.registeredResource;
lei->width = width;
lei->height = height;
*ei = lei;
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_nvenc_delete_encoder(struct enc_info *ei)
{
g_enc_funcs.nvEncUnmapInputResource(ei->enc, ei->mappedResource);
g_enc_funcs.nvEncUnregisterResource(ei->enc, ei->registeredResource);
g_enc_funcs.nvEncDestroyBitstreamBuffer(ei->enc, ei->bitstreamBuffer);
g_enc_funcs.nvEncDestroyEncoder(ei->enc);
g_free(ei);
return 0;
}
/*****************************************************************************/
enum encoder_result
xrdp_accel_assist_nvenc_encode(struct enc_info *ei, int tex,
void *cdata, int *cdata_bytes,
int flags)
{
NV_ENC_PIC_PARAMS picParams;
NV_ENC_LOCK_BITSTREAM lockBitstream;
NVENCSTATUS nv_error;
enum encoder_result rv;
/* sync before encoding */
glFinish();
g_memset(&picParams, 0, sizeof(picParams));
picParams.version = NV_ENC_PIC_PARAMS_VER;
picParams.inputBuffer = ei->mappedResource;
picParams.bufferFmt = ei->mappedBufferFmt;
picParams.inputWidth = ei->width;
picParams.inputHeight = ei->height;
picParams.outputBitstream = ei->bitstreamBuffer;
picParams.inputTimeStamp = ei->frameCount;
picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME;
picParams.encodePicFlags = NV_ENC_PIC_FLAG_OUTPUT_SPSPPS;
if ((flags & XH_ENC_FLAGS_FORCEIDR) || (ei->frameCount < 1))
{
picParams.encodePicFlags |= NV_ENC_PIC_FLAG_FORCEIDR;
LOG(LOG_LEVEL_INFO, "Forcing NVENC H264 IDR SPSPPS for frame id: %d",
ei->frameCount);
}
nv_error = g_enc_funcs.nvEncEncodePicture(ei->enc, &picParams);
rv = ENCODER_ERROR;
if (nv_error == NV_ENC_SUCCESS)
{
g_memset(&lockBitstream, 0, sizeof(lockBitstream));
lockBitstream.version = NV_ENC_LOCK_BITSTREAM_VER;
lockBitstream.outputBitstream = ei->bitstreamBuffer;
lockBitstream.doNotWait = 0;
nv_error = g_enc_funcs.nvEncLockBitstream(ei->enc, &lockBitstream);
if (nv_error == NV_ENC_SUCCESS)
{
if (*cdata_bytes >= ((int) (lockBitstream.bitstreamSizeInBytes)))
{
g_memcpy(cdata, lockBitstream.bitstreamBufferPtr,
lockBitstream.bitstreamSizeInBytes);
*cdata_bytes = lockBitstream.bitstreamSizeInBytes;
rv = INCREMENTAL_FRAME_ENCODED;
}
else
{
LOG(LOG_LEVEL_ERROR, "error not enough room %d %d",
*cdata_bytes,
(int) (lockBitstream.bitstreamSizeInBytes));
}
g_enc_funcs.nvEncUnlockBitstream(ei->enc,
lockBitstream.outputBitstream);
}
else
{
LOG(LOG_LEVEL_ERROR, "error nvEncLockBitstream %d",
nv_error);
}
ei->frameCount++;
}
else
{
LOG(LOG_LEVEL_ERROR, "error nvEncEncodePicture %d", nv_error);
}
if (rv == INCREMENTAL_FRAME_ENCODED
&& (picParams.encodePicFlags & NV_ENC_PIC_FLAG_FORCEIDR))
{
return KEY_FRAME_ENCODED;
}
return rv;
}
@@ -0,0 +1,34 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _XRDP_ACCEL_ASSIST_NVENC_H
#define _XRDP_ACCEL_ASSIST_NVENC_H
int
xrdp_accel_assist_nvenc_init(void);
int
xrdp_accel_assist_nvenc_create_encoder(int width, int height, int tex,
int tex_format, struct enc_info **ei);
int
xrdp_accel_assist_nvenc_delete_encoder(struct enc_info *ei);
enum encoder_result
xrdp_accel_assist_nvenc_encode(struct enc_info *ei, int tex,
void *cdata, int *cdata_bytes,
int flags);
#endif
@@ -0,0 +1,347 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2022-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* GLSL shaders
* this file is not compiled directly, it is included in
* xrdp_accel_assist_x11.c */
static const GLchar g_vs[] = "\
attribute vec4 position;\n\
void main(void)\n\
{\n\
gl_Position = vec4(position.xy, 0.0, 1.0);\n\
}\n";
static const GLchar g_fs_copy[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
void main(void)\n\
{\n\
gl_FragColor = texture2D(tex, gl_FragCoord.xy / tex_size);\n\
}\n";
static const GLchar g_fs_rgb_to_yuv420[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
uniform vec4 ymath;\n\
uniform vec4 umath;\n\
uniform vec4 vmath;\n\
void main(void)\n\
{\n\
vec4 pix;\n\
float x;\n\
float y;\n\
x = gl_FragCoord.x;\n\
y = gl_FragCoord.y;\n\
if (y < tex_size.y)\n\
{\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(ymath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
y = floor(y - tex_size.y) * 2.0 + 0.5;\n\
if (mod(x, 2.0) < 1.0)\n\
{\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix += texture2D(tex, vec2(x + 1.0, y) / tex_size);\n\
pix += texture2D(tex, vec2(x, y + 1.0) / tex_size);\n\
pix += texture2D(tex, vec2(x + 1.0, y + 1.0) / tex_size);\n\
pix /= 4.0;\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(umath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix += texture2D(tex, vec2(x - 1.0, y) / tex_size);\n\
pix += texture2D(tex, vec2(x, y + 1.0) / tex_size);\n\
pix += texture2D(tex, vec2(x - 1.0, y + 1.0) / tex_size);\n\
pix /= 4.0;\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(vmath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
}\n\
}\n";
static const GLchar g_fs_rgb_to_yuv422[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
uniform vec4 ymath;\n\
uniform vec4 umath;\n\
uniform vec4 vmath;\n\
void main(void)\n\
{\n\
vec4 pix;\n\
vec4 pix1;\n\
vec4 pixs;\n\
float x;\n\
float y;\n\
x = gl_FragCoord.x;\n\
x = floor(x) * 2.0 + 0.5;\n\
y = gl_FragCoord.y;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix1 = texture2D(tex, vec2(x + 1.0, y) / tex_size);\n\
pixs = (pix + pix1) / 2.0;\n\
pix.a = 1.0;\n\
pix1.a = 1.0;\n\
pixs.a = 1.0;\n\
pix.r = dot(ymath, pix);\n\
pix.g = dot(umath, pixs);\n\
pix.b = dot(ymath, pix1);\n\
pix.a = dot(vmath, pixs);\n\
gl_FragColor = clamp(pix, 0.0, 1.0);\n\
}\n";
static const GLchar g_fs_rgb_to_yuv444[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
uniform vec4 ymath;\n\
uniform vec4 umath;\n\
uniform vec4 vmath;\n\
void main(void)\n\
{\n\
vec4 pix;\n\
pix = texture2D(tex, gl_FragCoord.xy / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(dot(vmath, pix), dot(umath, pix), dot(ymath, pix), 1.0);\n\
gl_FragColor = clamp(pix, 0.0, 1.0);\n\
}\n";
/*
RGB
00 10 20 30 40 50 60 70 80 90 A0 B0 C0 D0 E0 F0
01 11 21 31 41 51 61 71 81 91 A1 B1 C1 D1 E1 F1
02 12 22 32 42 52 62 72 82 92 A2 B2 C2 D2 E2 F2
03 13 23 33 43 53 63 73 83 93 A3 B3 C3 D3 E3 F3
04 14 24 34 44 54 64 74 84 94 A4 B4 C4 D4 E4 F4
05 15 25 35 45 55 65 75 85 95 A5 B5 C5 D5 E5 F5
06 16 26 36 46 56 66 76 86 96 A6 B6 C6 D6 E6 F6
07 17 27 37 47 57 67 77 87 97 A7 B7 C7 D7 E7 F7
08 18 28 38 48 58 68 78 88 98 A8 B8 C8 D8 E8 F8
09 19 29 39 49 59 69 79 89 99 A9 B9 C9 D9 E9 F9
0A 1A 2A 3A 4A 5A 6A 7A 8A 9A AA BA CA DA EA FA
0B 1B 2B 3B 4B 5B 6B 7B 8B 9B AB BB CB DB EB FB
0C 1C 2C 3C 4C 5C 6C 7C 8C 9C AC BC CC DC EC FC
0D 1D 2D 3D 4D 5D 6D 7D 8D 9D AD BD CD DD ED FD
0E 1E 2E 3E 4E 5E 6E 7E 8E 9E AE BE CE DE EE FE
0F 1F 2F 3F 4F 5F 6F 7F 8F 9F AF BF CF DF EF FF
MAIN VIEW - NV12
/---------------------Y-----------------------\
00 10 20 30 40 50 60 70 80 90 A0 B0 C0 D0 E0 F0
01 11 21 31 41 51 61 71 81 91 A1 B1 C1 D1 E1 F1
...
0F 1F 2F 3F 4F 5F 6F 7F 8F 9F AF BF CF DF EF FF
/U /V /U /V /U /V /U /V /U /V /U /V /U /V /U /V
00 00 20 20 40 40 60 60 80 80 A0 A0 C0 C0 E0 E0
02 02 22 22 42 42 62 62 82 82 A2 A2 C2 C2 E2 E2
...
0E 0E 2E 2E 4E 4E 6E 6E 8E 8E AE AE CE CE EE EE
*/
static const GLchar g_fs_rgb_to_yuv420_mv[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
uniform vec4 ymath;\n\
uniform vec4 umath;\n\
uniform vec4 vmath;\n\
void main(void)\n\
{\n\
vec4 pix;\n\
float x;\n\
float y;\n\
x = gl_FragCoord.x;\n\
y = gl_FragCoord.y;\n\
if (y < tex_size.y)\n\
{\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(ymath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
y = floor(y - tex_size.y) * 2.0 + 0.5;\n\
if (mod(x, 2.0) < 1.0)\n\
{\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(umath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
pix = texture2D(tex, vec2(x - 1.0, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(vmath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
}\n\
}\n";
/*
AUXILIARY VIEW - NV12
/---------------------U-----------------------\
01 11 21 31 41 51 61 71 81 91 A1 B1 C1 D1 E1 F1
03 13 23 33 43 53 63 73 83 93 A3 B3 C3 D3 E3 F4
...
0F 1F 2F 3F 4F 5F 6F 7F 8F 9F AF BF CF DF EF FF
/---------------------V-----------------------\
01 11 21 31 41 51 61 71 81 91 A1 B1 C1 D1 E1 F1
03 13 23 33 43 53 63 73 83 93 A3 B3 C3 D3 E3 F4
...
0F 1F 2F 3F 4F 5F 6F 7F 8F 9F AF BF CF DF EF FF
... (8 LINES U, 8 LINES V, REPEAT)
/U /V /U /V /U /V /U /V /U /V /U /V /U /V /U /V
10 10 30 30 50 50 70 70 90 90 B0 B0 D0 D0 F0 F0
12 12 32 32 52 52 72 72 92 92 B2 B2 D2 D2 F2 F2
...
1E 1E 3E 3E 5E 5E 7E 7E 9E 9E BE BE DE DE FE FE
*/
static const GLchar g_fs_rgb_to_yuv420_av[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
uniform vec4 umath;\n\
uniform vec4 vmath;\n\
void main(void)\n\
{\n\
vec4 pix;\n\
float x;\n\
float y;\n\
float y1;\n\
x = gl_FragCoord.x;\n\
y = gl_FragCoord.y;\n\
if (y < tex_size.y)\n\
{\n\
y1 = mod(y, 16.0);\n\
if (y1 < 8.0)\n\
{\n\
y = floor(y / 16.0) * 8.0 + y1;\n\
y = floor(y) * 2.0 + 1.5;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(umath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
y = floor(y / 16.0) * 8.0 + (y1 - 8.0);\n\
y = floor(y) * 2.0 + 1.5;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(vmath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
}\n\
else\n\
{\n\
y = floor(y - tex_size.y) * 2.0 + 0.5;\n\
if (mod(x, 2.0) < 1.0)\n\
{\n\
pix = texture2D(tex, vec2(x + 1.0, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(umath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(vmath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
}\n\
}\n";
/*
AUXILIARY VIEW V2 - NV12
/----------U----------\ /----------V----------\
10 30 50 70 90 B0 D0 F0 10 30 50 70 90 B0 D0 F0
11 31 51 71 91 B1 D1 F1 11 31 51 71 91 B1 D1 F1
...
1F 3F 5F 7F 9F BF DF FF 1F 3F 5F 7F 9F BF DF FF
/----------U----------\ /----------V----------\
01 21 41 61 81 A1 C1 E1 01 21 41 61 81 A1 C1 E1
03 23 43 63 83 A3 C3 E3 03 23 43 63 83 A3 C3 E3
...
0F 2F 4F 6F 8F AF CF EF 0F 2F 4F 6F 8F AF CF EF
*/
static const GLchar g_fs_rgb_to_yuv420_av_v2[] = "\
uniform sampler2D tex;\n\
uniform vec2 tex_size;\n\
uniform vec4 umath;\n\
uniform vec4 vmath;\n\
void main(void)\n\
{\n\
vec4 pix;\n\
float x;\n\
float y;\n\
float x1;\n\
x = gl_FragCoord.x;\n\
y = gl_FragCoord.y;\n\
x1 = tex_size.x / 2.0;\n\
if (y < tex_size.y)\n\
{\n\
if (x < x1)\n\
{\n\
x = floor(x) * 2.0 + 1.5;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(umath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
x = floor(x - x1) * 2.0 + 1.5;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(vmath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
}\n\
else\n\
{\n\
y = floor(y - tex_size.y) * 2.0 + 1.5;\n\
if (x < x1)\n\
{\n\
x = floor(x) * 2.0 + 0.5;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(umath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
else\n\
{\n\
x = floor(x - x1) * 2.0 + 0.5;\n\
pix = texture2D(tex, vec2(x, y) / tex_size);\n\
pix.a = 1.0;\n\
pix = vec4(clamp(dot(vmath, pix), 0.0, 1.0), 0.0, 0.0, 1.0);\n\
gl_FragColor = pix;\n\
}\n\
}\n\
}\n";
+858
View File
@@ -0,0 +1,858 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2020-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Currently nvenc requires GLX because NVidia's EGL does not have
* EGL_NOK_texture_from_pixmap extension but NVidia's GLX does have
* GLX_EXT_texture_from_pixmap. We require one if those,
* also, va required EGL because it used dma bufs.
* I do not think any vendor's GLX support dma bufs */
/* Things like render on one GPU and encode with another is possible
* but not supported now. */
/* One suggestion about dma bufs and GLX, one can use the DRI3
* extension to get dma buffs for pixmaps */
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <X11/Xlib.h>
#include <epoxy/gl.h>
#include "arch.h"
#include "os_calls.h"
#include "string_calls.h"
#include "xrdp_accel_assist.h"
#include "xrdp_accel_assist_x11.h"
#include "xrdp_accel_assist_glx.h"
#include "xrdp_accel_assist_egl.h"
#include "log.h"
/* set to 1 to dump bmp files into /tmp */
#define XR_DUMP_FRAMEBUFFER 0
/* set to 1 to dump bmp files into /tmp */
#define XR_DUMP_PIXMAP 0
#if defined(XRDP_NVENC)
#include "xrdp_accel_assist_nvenc.h"
#endif
/* X11 */
Display *g_display = NULL;
static int g_x_socket = 0;
int g_screen_num = 0;
static Screen *g_screen = NULL;
Window g_root_window = None;
static Visual *g_vis = NULL;
static GC g_gc;
/* encoders: nvenc or va */
struct enc_funcs
{
int (*init)(void);
int (*create_enc)(int width, int height, int tex, int tex_format,
struct enc_info **ei);
int (*destroy_enc)(struct enc_info *ei);
enum encoder_result (*encode)(struct enc_info *ei, int tex,
void *cdata, int *cdata_bytes,
int flags);
};
static struct enc_funcs g_enc_funcs[] =
{
{
NULL, NULL, NULL, NULL
},
{
#if defined(XRDP_NVENC)
xrdp_accel_assist_nvenc_init,
xrdp_accel_assist_nvenc_create_encoder,
xrdp_accel_assist_nvenc_delete_encoder,
xrdp_accel_assist_nvenc_encode
#else
NULL, NULL, NULL, NULL
#endif
}
};
/* GL interface: EGL or GLX */
struct inf_funcs
{
int (*init)(void);
int (*create_image)(Pixmap pixmap, inf_image_t *inf_image);
int (*destroy_image)(inf_image_t inf_image);
int (*bind_tex_image)(inf_image_t inf_image);
int (*release_tex_image)(inf_image_t inf_image);
};
static struct inf_funcs g_inf_funcs[] =
{
{
xrdp_accel_assist_inf_egl_init,
xrdp_accel_assist_inf_egl_create_image,
xrdp_accel_assist_inf_egl_destroy_image,
xrdp_accel_assist_inf_egl_bind_tex_image,
xrdp_accel_assist_inf_egl_release_tex_image
},
{
xrdp_accel_assist_inf_glx_init,
xrdp_accel_assist_inf_glx_create_image,
xrdp_accel_assist_inf_glx_destroy_image,
xrdp_accel_assist_inf_glx_bind_tex_image,
xrdp_accel_assist_inf_glx_release_tex_image
}
};
/* 0 = EGL, 1 = GLX */
/* 0 = va, 1 = nvenc */
#define INF_EGL 0
#define INF_GLX 1
#define ENC_VA 0
#define ENC_NVENC 1
static int g_inf = INF_EGL;
static int g_enc = ENC_VA;
struct mon_info
{
int width;
int height;
Pixmap pixmap;
inf_image_t inf_image;
GLuint bmp_texture;
GLuint enc_texture;
int tex_format;
GLfloat *(*get_vertices)(GLuint *vertices_bytes,
GLuint *vertices_pointes,
int num_crects, struct xh_rect *crects,
int left, int top, int width, int height);
struct xh_rect viewport;
struct enc_info *ei;
};
#define MAX_MON 16
static struct mon_info g_mons[MAX_MON];
static GLuint g_quad_vao = 0;
static GLuint g_fb = 0;
#define XH_SHADERCOPY 0
#define XH_SHADERRGB2YUV420 1
#define XH_SHADERRGB2YUV422 2
#define XH_SHADERRGB2YUV444 3
#define XH_SHADERRGB2YUV420MV 4
#define XH_SHADERRGB2YUV420AV 5
#define XH_SHADERRGB2YUV420AVV2 6
#define XH_NUM_SHADERS 7
struct shader_info
{
GLuint vertex_shader;
GLuint fragment_shader;
GLuint program;
GLint tex_loc;
GLint tex_size_loc;
GLint ymath_loc;
GLint umath_loc;
GLint vmath_loc;
int current_matrix;
};
static struct shader_info g_si[XH_NUM_SHADERS];
/* *INDENT-OFF* */
static const GLfloat g_vertices[] =
{
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f
};
/* *INDENT-ON* */
struct rgb2yuv_matrix
{
GLfloat ymath[4];
GLfloat umath[4];
GLfloat vmath[4];
};
static struct rgb2yuv_matrix g_rgb2yux_matrix[3] =
{
{
/* yuv bt601 lagecy */
{ 66.0 / 256.0, 129.0 / 256.0, 25.0 / 256.0, 16.0 / 256.0 },
{ -38.0 / 256.0, -74.0 / 256.0, 112.0 / 256.0, 128.0 / 256.0 },
{ 112.0 / 256.0, -94.0 / 256.0, -18.0 / 256.0, 128.0 / 256.0 }
},
{
/* yuv bt709 full range, used in gfx h264 */
{ 54.0 / 256.0, 183.0 / 256.0, 18.0 / 256.0, 0.0 / 256.0 },
{ -29.0 / 256.0, -99.0 / 256.0, 128.0 / 256.0, 128.0 / 256.0 },
{ 128.0 / 256.0, -116.0 / 256.0, -12.0 / 256.0, 128.0 / 256.0 }
},
{
/* yuv remotefx and gfx progressive remotefx */
{ 0.299000, 0.587000, 0.114000, 0.0 },
{ -0.168935, -0.331665, 0.500590, 0.5 },
{ 0.499830, -0.418531, -0.081282, 0.5 }
}
};
#include "xrdp_accel_assist_shaders.c"
/*****************************************************************************/
int
xrdp_accel_assist_x11_init(void)
{
const GLchar *vsource[XH_NUM_SHADERS];
const GLchar *fsource[XH_NUM_SHADERS];
GLint linked;
GLint compiled;
GLint vlength;
GLint flength;
GLuint quad_vbo;
int index;
int gl_ver;
int major_opcode, first_event, first_error;
/* x11 */
g_display = XOpenDisplay(0);
if (g_display == NULL)
{
return 1;
}
g_x_socket = XConnectionNumber(g_display);
g_screen_num = DefaultScreen(g_display);
g_screen = ScreenOfDisplay(g_display, g_screen_num);
g_root_window = RootWindowOfScreen(g_screen);
g_vis = XDefaultVisual(g_display, g_screen_num);
g_gc = DefaultGC(g_display, 0);
if (XQueryExtension(g_display, "NV-CONTROL", &major_opcode, &first_event,
&first_error))
{
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: "
"detected NVIDIA XServer");
g_inf = INF_GLX;
g_enc = ENC_NVENC;
if (g_inf_funcs[g_inf].init() != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_init: "
"GLX init failed");
return 1;
}
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: using GLX");
}
else
{
g_inf = INF_EGL;
g_enc = ENC_VA;
if (g_inf_funcs[g_inf].init() != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_init: "
"EGL init failed");
return 1;
}
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: using EGL");
}
gl_ver = epoxy_gl_version();
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: gl_ver %d", gl_ver);
if (gl_ver < 30)
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_init: "
"gl_ver too old %d", gl_ver);
return 1;
}
LOG(LOG_LEVEL_INFO, "vendor: %s",
(const char *) glGetString(GL_VENDOR));
LOG(LOG_LEVEL_INFO, "version: %s",
(const char *) glGetString(GL_VERSION));
/* create vertex array */
glGenVertexArrays(1, &g_quad_vao);
glBindVertexArray(g_quad_vao);
glGenBuffers(1, &quad_vbo);
glBindBuffer(GL_ARRAY_BUFFER, quad_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertices), g_vertices,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, NULL);
glGenFramebuffers(1, &g_fb);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDeleteBuffers(1, &quad_vbo);
/* create copy shader */
vsource[XH_SHADERCOPY] = g_vs;
fsource[XH_SHADERCOPY] = g_fs_copy;
/* create rgb2yuv shader */
vsource[XH_SHADERRGB2YUV420] = g_vs;
fsource[XH_SHADERRGB2YUV420] = g_fs_rgb_to_yuv420;
/* create rgb2yuv shader */
vsource[XH_SHADERRGB2YUV422] = g_vs;
fsource[XH_SHADERRGB2YUV422] = g_fs_rgb_to_yuv422;
/* create rgb2yuv shader */
vsource[XH_SHADERRGB2YUV444] = g_vs;
fsource[XH_SHADERRGB2YUV444] = g_fs_rgb_to_yuv444;
vsource[XH_SHADERRGB2YUV420MV] = g_vs;
fsource[XH_SHADERRGB2YUV420MV] = g_fs_rgb_to_yuv420_mv;
vsource[XH_SHADERRGB2YUV420AV] = g_vs;
fsource[XH_SHADERRGB2YUV420AV] = g_fs_rgb_to_yuv420_av;
vsource[XH_SHADERRGB2YUV420AVV2] = g_vs;
fsource[XH_SHADERRGB2YUV420AVV2] = g_fs_rgb_to_yuv420_av_v2;
for (index = 0; index < XH_NUM_SHADERS; index++)
{
g_si[index].vertex_shader = glCreateShader(GL_VERTEX_SHADER);
g_si[index].fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
vlength = g_strlen(vsource[index]);
flength = g_strlen(fsource[index]);
glShaderSource(g_si[index].vertex_shader, 1,
&(vsource[index]), &vlength);
glShaderSource(g_si[index].fragment_shader, 1,
&(fsource[index]), &flength);
glCompileShader(g_si[index].vertex_shader);
glGetShaderiv(g_si[index].vertex_shader, GL_COMPILE_STATUS,
&compiled);
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: "
"vertex_shader compiled %d", compiled);
glCompileShader(g_si[index].fragment_shader);
glGetShaderiv(g_si[index].fragment_shader, GL_COMPILE_STATUS,
&compiled);
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: "
"fragment_shader compiled %d", compiled);
g_si[index].program = glCreateProgram();
glAttachShader(g_si[index].program, g_si[index].vertex_shader);
glAttachShader(g_si[index].program, g_si[index].fragment_shader);
glLinkProgram(g_si[index].program);
glGetProgramiv(g_si[index].program, GL_LINK_STATUS, &linked);
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: linked %d", linked);
g_si[index].tex_loc =
glGetUniformLocation(g_si[index].program, "tex");
g_si[index].tex_size_loc =
glGetUniformLocation(g_si[index].program, "tex_size");
g_si[index].ymath_loc =
glGetUniformLocation(g_si[index].program, "ymath");
g_si[index].umath_loc =
glGetUniformLocation(g_si[index].program, "umath");
g_si[index].vmath_loc =
glGetUniformLocation(g_si[index].program, "vmath");
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_init: tex_loc %d "
"tex_size_loc %d ymath_loc %d umath_loc %d vmath_loc %d",
g_si[index].tex_loc, g_si[index].tex_size_loc,
g_si[index].ymath_loc, g_si[index].umath_loc,
g_si[index].vmath_loc);
/* set default matrix */
glUseProgram(g_si[index].program);
if (g_si[index].ymath_loc >= 0)
{
glUniform4fv(g_si[index].ymath_loc, 1, g_rgb2yux_matrix[1].ymath);
}
if (g_si[index].umath_loc >= 0)
{
glUniform4fv(g_si[index].umath_loc, 1, g_rgb2yux_matrix[1].umath);
}
if (g_si[index].vmath_loc >= 0)
{
glUniform4fv(g_si[index].vmath_loc, 1, g_rgb2yux_matrix[1].vmath);
}
glUseProgram(0);
}
g_memset(g_mons, 0, sizeof(g_mons));
if (g_enc_funcs[g_enc].init() != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_init: "
"encoder init failed");
return 1;
}
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_x11_get_wait_objs(intptr_t *objs, int *obj_count)
{
objs[*obj_count] = g_x_socket;
(*obj_count)++;
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_x11_check_wait_objs(void)
{
XEvent xevent;
while (XPending(g_display) > 0)
{
LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_check_wait_objs: "
"loop");
XNextEvent(g_display, &xevent);
}
return 0;
}
/*****************************************************************************/
int
xrdp_accel_assist_x11_delete_all_pixmaps(void)
{
int index;
struct mon_info *mi;
for (index = 0; index < MAX_MON; index++)
{
mi = g_mons + index;
if (mi->pixmap != 0)
{
g_enc_funcs[g_enc].destroy_enc(mi->ei);
glDeleteTextures(1, &(mi->bmp_texture));
glDeleteTextures(1, &(mi->enc_texture));
g_inf_funcs[g_inf].destroy_image(mi->inf_image);
XFreePixmap(g_display, mi->pixmap);
mi->pixmap = 0;
}
}
return 0;
}
/*****************************************************************************/
static GLfloat *
get_vertices_all(GLuint *vertices_bytes, GLuint *vertices_pointes,
int num_crects, struct xh_rect *crects,
int left, int top, int width, int height)
{
GLfloat *vertices;
(void)num_crects;
(void)crects;
(void)width;
(void)height;
vertices = g_new(GLfloat, 12);
if (vertices == NULL)
{
return NULL;
}
vertices[0] = -1;
vertices[1] = 1;
vertices[2] = -1;
vertices[3] = -1;
vertices[4] = 1;
vertices[5] = 1;
vertices[6] = -1;
vertices[7] = -1;
vertices[8] = 1;
vertices[9] = 1;
vertices[10] = 1;
vertices[11] = -1;
*vertices_bytes = sizeof(GLfloat) * 12;
*vertices_pointes = 6;
return vertices;
}
/*****************************************************************************/
static GLfloat *
get_vertices420(GLuint *vertices_bytes, GLuint *vertices_pointes,
int num_crects, struct xh_rect *crects,
int left, int top, int width, int height)
{
GLfloat *vertices;
GLfloat *vert;
GLfloat x1;
GLfloat x2;
GLfloat y1;
GLfloat y2;
int index;
GLfloat fwidth;
GLfloat fheight;
const GLfloat fac13 = 1.0 / 3.0;
const GLfloat fac23 = 2.0 / 3.0;
const GLfloat fac43 = 4.0 / 3.0;
struct xh_rect *crect;
if (num_crects < 1)
{
return get_vertices_all(vertices_bytes, vertices_pointes,
num_crects, crects, left, top, width, height);
}
vertices = g_new(GLfloat, num_crects * 24);
if (vertices == NULL)
{
return NULL;
}
fwidth = width / 2.0;
fheight = height / 2.0;
for (index = 0; index < num_crects; index++)
{
crect = crects + index;
LOG_DEVEL(LOG_LEVEL_INFO, "get_vertices420: "
"rect index %d x %d y %d w %d h %d",
index, crect->x, crect->y, crect->w, crect->h);
x1 = (crect->x - left) / fwidth;
y1 = (crect->y - top) / fheight;
x2 = ((crect->x - left) + crect->w) / fwidth;
y2 = ((crect->y - top) + crect->h) / fheight;
vert = vertices + index * 24;
/* y box */
vert[0] = x1 - 1.0;
vert[1] = y1 * fac23 - 1.0;
vert[2] = x1 - 1.0;
vert[3] = y2 * fac23 - 1.0;
vert[4] = x2 - 1.0;
vert[5] = y1 * fac23 - 1.0;
vert[6] = x1 - 1.0;
vert[7] = y2 * fac23 - 1.0;
vert[8] = x2 - 1.0;
vert[9] = y1 * fac23 - 1.0;
vert[10] = x2 - 1.0;
vert[11] = y2 * fac23 - 1.0;
/* uv box */
vert[12] = x1 - 1.0;
vert[13] = (y1 * fac13 + fac43) - 1.0;
vert[14] = x1 - 1.0;
vert[15] = (y2 * fac13 + fac43) - 1.0;
vert[16] = x2 - 1.0;
vert[17] = (y1 * fac13 + fac43) - 1.0;
vert[18] = x1 - 1.0;
vert[19] = (y2 * fac13 + fac43) - 1.0;
vert[20] = x2 - 1.0;
vert[21] = (y1 * fac13 + fac43) - 1.0;
vert[22] = x2 - 1.0;
vert[23] = (y2 * fac13 + fac43) - 1.0;
}
*vertices_bytes = sizeof(GLfloat) * num_crects * 24;
*vertices_pointes = num_crects * 12;
return vertices;
}
/*****************************************************************************/
static GLfloat *
get_vertices444(GLuint *vertices_bytes, GLuint *vertices_pointes,
int num_crects, struct xh_rect *crects,
int left, int top, int width, int height)
{
GLfloat *vertices;
GLfloat *vert;
GLfloat x1;
GLfloat x2;
GLfloat y1;
GLfloat y2;
int index;
GLfloat fwidth;
GLfloat fheight;
struct xh_rect *crect;
if (num_crects < 1)
{
return get_vertices_all(vertices_bytes, vertices_pointes,
num_crects, crects, left, top, width, height);
}
vertices = g_new(GLfloat, num_crects * 12);
if (vertices == NULL)
{
return NULL;
}
fwidth = width / 2.0;
fheight = height / 2.0;
for (index = 0; index < num_crects; index++)
{
crect = crects + index;
x1 = (crect->x - left) / fwidth;
y1 = (crect->y - top) / fheight;
x2 = ((crect->x - left) + crect->w) / fwidth;
y2 = ((crect->y - top) + crect->h) / fheight;
vert = vertices + index * 12;
vert[0] = x1 - 1.0;
vert[1] = y1 - 1.0;
vert[2] = x1 - 1.0;
vert[3] = y2 - 1.0;
vert[4] = x2 - 1.0;
vert[5] = y1 - 1.0;
vert[6] = x1 - 1.0;
vert[7] = y2 - 1.0;
vert[8] = x2 - 1.0;
vert[9] = y1 - 1.0;
vert[10] = x2 - 1.0;
vert[11] = y2 - 1.0;
}
*vertices_bytes = sizeof(GLfloat) * num_crects * 12;
*vertices_pointes = num_crects * 6;
return vertices;
}
/*****************************************************************************/
int
xrdp_accel_assist_x11_create_pixmap(int width, int height, int magic,
int con_id, int mon_id)
{
struct mon_info *mi;
Pixmap pixmap;
XImage *ximage;
int img[64];
inf_image_t inf_image;
GLuint bmp_texture;
GLuint enc_texture;
mi = g_mons + mon_id % MAX_MON;
if (mi->pixmap != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_create_pixmap: "
"error already setup");
return 1;
}
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_create_pixmap: "
"width %d height %d, magic 0x%8.8x, con_id %d mod_id %d",
width, height, magic, con_id, mon_id);
pixmap = XCreatePixmap(g_display, g_root_window, width, height, 24);
LOG(LOG_LEVEL_INFO, "pixmap %d", (int) pixmap);
if (g_inf_funcs[g_inf].create_image(pixmap, &inf_image) != 0)
{
return 1;
}
LOG(LOG_LEVEL_INFO, "inf_image %p", (void *) inf_image);
g_memset(img, 0, sizeof(img));
img[0] = magic;
img[1] = con_id;
img[2] = mon_id;
ximage = XCreateImage(g_display, g_vis, 24, ZPixmap, 0, (char *) img,
4, 4, 32, 0);
XPutImage(g_display, pixmap, g_gc, ximage, 0, 0, 0, 0, 4, 4);
XFree(ximage);
glEnable(GL_TEXTURE_2D);
/* texture that gets encoded */
glGenTextures(1, &enc_texture);
glBindTexture(GL_TEXTURE_2D, enc_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
if (g_enc == ENC_NVENC)
{
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_create_pixmap: "
"using XH_YUV420");
mi->tex_format = XH_YUV420;
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height * 3 / 2, 0,
GL_RED, GL_UNSIGNED_BYTE, NULL);
mi->get_vertices = get_vertices420;
mi->viewport.x = 0;
mi->viewport.y = 0;
mi->viewport.w = width;
mi->viewport.h = height * 3 / 2;
}
else if (g_enc == ENC_VA)
{
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_create_pixmap: "
"using XH_YUV422");
mi->tex_format = XH_YUV422;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width / 2, height, 0,
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL);
mi->get_vertices = get_vertices444; /* same as 444 */
mi->viewport.x = 0;
mi->viewport.y = 0;
mi->viewport.w = width / 2;
mi->viewport.h = height;
}
else
{
LOG(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_create_pixmap: "
"using XH_YUV444");
mi->tex_format = XH_YUV444;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL);
mi->get_vertices = get_vertices444;
mi->viewport.x = 0;
mi->viewport.y = 0;
mi->viewport.w = width;
mi->viewport.h = height;
}
/* texture that binds with pixmap */
glGenTextures(1, &bmp_texture);
glBindTexture(GL_TEXTURE_2D, bmp_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
if (g_enc_funcs[g_enc].create_enc(width, height,
enc_texture, mi->tex_format,
&(mi->ei)) != 0)
{
return 1;
}
mi->pixmap = pixmap;
mi->inf_image = inf_image;
mi->enc_texture = enc_texture;
mi->width = width;
mi->height = height;
mi->bmp_texture = bmp_texture;
return 0;
}
#if XR_DUMP_FRAMEBUFFER
static int g_framebuffer_file_index = 0;
/*****************************************************************************/
static int
save_fb_to_file(int width, int height)
{
char *pixels;
char filename[256];
pixels = (char *) g_malloc(width * height * 4, 0);
if (pixels != NULL)
{
glReadPixels(0, 0, width / 2, height, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
snprintf(filename, 255, "/tmp/gl_surface%8.8x.bmp",
g_framebuffer_file_index++);
g_save_to_bmp(filename, pixels, width * 2, width / 2, height, 24, 32);
g_free(pixels);
}
return 0;
}
#endif
/*****************************************************************************/
static void
xrdp_accel_assist_x11_run_shader(int left, int top, int width, int height,
struct mon_info *mi,
struct shader_info *si,
int num_crects, struct xh_rect *crects)
{
GLuint vao;
GLuint vbo;
GLfloat *vertices;
GLuint vertices_bytes;
GLuint vertices_pointes;
/* rgb to yuv */
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, mi->bmp_texture);
g_inf_funcs[g_inf].bind_tex_image(mi->inf_image);
glBindFramebuffer(GL_FRAMEBUFFER, g_fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, mi->enc_texture, 0);
glUseProgram(si->program);
/* setup vertices from crects */
vertices = mi->get_vertices(&vertices_bytes, &vertices_pointes,
num_crects, crects,
left, top, width, height);
if (vertices == NULL)
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_run_shader: "
"error get_vertices failed num_crects %d",
num_crects);
return;
}
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices_bytes, vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, NULL);
/* uniforms */
glUniform2f(si->tex_size_loc, mi->width, mi->height);
/* viewport and draw */
glViewport(mi->viewport.x, mi->viewport.y, mi->viewport.w, mi->viewport.h);
glDrawArrays(GL_TRIANGLES, 0, vertices_pointes);
/* cleanup */
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
g_free(vertices);
#if XR_DUMP_FRAMEBUFFER
save_fb_to_file(width, height);
#endif
glBindFramebuffer(GL_FRAMEBUFFER, 0);
g_inf_funcs[g_inf].release_tex_image(mi->inf_image);
glBindTexture(GL_TEXTURE_2D, 0);
glUseProgram(0);
}
#if XR_DUMP_PIXMAP
static int g_pixmap_file_index = 0;
/*****************************************************************************/
static int
save_pixmap_to_file(Pixmap pix, int width, int height)
{
XImage *image;
char filename[256];
image = XGetImage(g_display, pix, 0, 0, width, height, AllPlanes, ZPixmap);
if (image != NULL)
{
snprintf(filename, 255, "/tmp/pixmap%8.8x.bmp", g_pixmap_file_index++);
g_save_to_bmp(filename, image->data, width * 4, width, height, 24, 32);
XFree(image);
}
return 0;
}
#endif
/*****************************************************************************/
enum encoder_result
xrdp_accel_assist_x11_encode_pixmap(int left, int top, int width, int height,
int mon_id, int num_crects,
struct xh_rect *crects,
void *cdata, int *cdata_bytes,
int flags)
{
struct mon_info *mi;
struct shader_info *si;
enum encoder_result rv;
mi = g_mons + mon_id % MAX_MON;
LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_accel_assist_x11_encode_pixmap: "
"left %d top %d width %d height %d mon_id %d",
left, top, width, height, mon_id);
if ((width != mi->width) || (height != mi->height))
{
LOG(LOG_LEVEL_ERROR, "xrdp_accel_assist_x11_encode_pixmap: "
"error width %d should be %d "
"height %d should be %d",
width, mi->width, height, mi->height);
return ENCODER_ERROR;
}
#if XR_DUMP_PIXMAP
save_pixmap_to_file(mi->pixmap, width, height);
#endif
si = g_si + mi->tex_format % XH_NUM_SHADERS;
xrdp_accel_assist_x11_run_shader(left, top, width, height, mi, si,
num_crects, crects);
/* flush before encoding, let encoders call glFinish() as needed */
XFlush(g_display);
/* encode */
rv = g_enc_funcs[g_enc].encode(mi->ei, mi->enc_texture,
cdata, cdata_bytes, flags);
return rv;
}
+48
View File
@@ -0,0 +1,48 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2020-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _XRDP_ACCEL_ASSIST_X11_H
#define _XRDP_ACCEL_ASSIST_X11_H
/* generic type that can hold either a GLXPixmap(XID, unsigned int or long)
* or EGLSurface(void*) */
typedef intptr_t inf_image_t;
/* forward declaration used in xrdp_accel_assist_nvenc and
xrdp_accel_assist_yami */
struct enc_info;
int
xrdp_accel_assist_x11_init(void);
int
xrdp_accel_assist_x11_get_wait_objs(intptr_t *objs, int *obj_count);
int
xrdp_accel_assist_x11_check_wait_objs(void);
int
xrdp_accel_assist_x11_delete_all_pixmaps(void);
int
xrdp_accel_assist_x11_create_pixmap(int width, int height, int magic,
int con_id, int mon_id);
enum encoder_result
xrdp_accel_assist_x11_encode_pixmap(int left, int top, int width, int height,
int mon_id, int num_crects,
struct xh_rect *crects,
void *cdata, int *cdata_bytes,
int flags);
#endif