859 lines
27 KiB
C
859 lines
27 KiB
C
/**
|
|
* 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;
|
|
}
|