#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
#ifdef HAVE_EGL
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <libdrm/drm_fourcc.h>
#include "drmtap_internal.h"
static EGLDisplay g_egl_display = EGL_NO_DISPLAY;
static int g_egl_display_initialized = 0;
typedef struct {
EGLDisplay display;
EGLContext context;
GLuint program;
GLuint fbo;
GLuint linear_texture;
uint32_t tex_width;
uint32_t tex_height;
int initialized;
pthread_t owner_thread;
} egl_state_t;
static PFNEGLQUERYDEVICESEXTPROC pfn_eglQueryDevicesEXT;
static PFNEGLQUERYDEVICESTRINGEXTPROC pfn_eglQueryDeviceStringEXT;
static PFNEGLGETPLATFORMDISPLAYEXTPROC pfn_eglGetPlatformDisplayEXT;
static PFNEGLCREATEIMAGEKHRPROC pfn_eglCreateImageKHR;
static PFNEGLDESTROYIMAGEKHRPROC pfn_eglDestroyImageKHR;
static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC pfn_glEGLImageTargetTexture2DOES;
static int load_egl_procs(void) {
pfn_eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)
eglGetProcAddress("eglQueryDevicesEXT");
pfn_eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)
eglGetProcAddress("eglQueryDeviceStringEXT");
pfn_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)
eglGetProcAddress("eglGetPlatformDisplayEXT");
pfn_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)
eglGetProcAddress("eglCreateImageKHR");
pfn_eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)
eglGetProcAddress("eglDestroyImageKHR");
pfn_glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
eglGetProcAddress("glEGLImageTargetTexture2DOES");
return (pfn_eglCreateImageKHR && pfn_eglDestroyImageKHR &&
pfn_glEGLImageTargetTexture2DOES) ? 0 : -1;
}
static const char *vertex_shader_src =
"#version 100\n"
"attribute vec2 in_position;\n"
"attribute vec2 in_texcoord;\n"
"varying vec2 v_texcoord;\n"
"void main() {\n"
" gl_Position = vec4(in_position, 0.0, 1.0);\n"
" v_texcoord = in_texcoord;\n"
"}\n";
static const char *fragment_shader_src =
"#version 100\n"
"#extension GL_OES_EGL_image_external : require\n"
"precision highp float;\n"
"uniform samplerExternalOES image;\n"
"uniform int u_hdr;\n"
"uniform float u_peak_n;\n"
"varying vec2 v_texcoord;\n"
"float pq_eotf(float e) {\n"
" float m1 = 0.1593017578125;\n"
" float m2 = 78.84375;\n"
" float c1 = 0.8359375;\n"
" float c2 = 18.8515625;\n"
" float c3 = 18.6875;\n"
" float ep = pow(max(e, 0.0), 1.0 / m2);\n"
" float num = max(ep - c1, 0.0);\n"
" float den = max(c2 - c3 * ep, 1e-6);\n"
" return 10000.0 * pow(num / den, 1.0 / m1);\n"
"}\n"
"float knee_curve(float x, float p) {\n"
" float knee = 0.9;\n"
" if (x <= knee) return x;\n"
" float pk = max(p, knee + 0.5);\n"
" if (x >= pk) return 1.0;\n"
" float t = (x - knee) / (pk - knee);\n"
" return knee + (1.0 - knee) * (t * (2.0 - t));\n"
"}\n"
"float srgb_oetf(float c) {\n"
" c = clamp(c, 0.0, 1.0);\n"
" if (c <= 0.0031308) return 12.92 * c;\n"
" return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n"
"}\n"
"void main() {\n"
" vec4 c = texture2D(image, v_texcoord);\n"
" if (u_hdr == 1) {\n"
" float R = pq_eotf(c.r);\n"
" float G = pq_eotf(c.g);\n"
" float B = pq_eotf(c.b);\n"
" float r = 1.660491 * R - 0.587641 * G - 0.072850 * B;\n"
" float g = -0.124550 * R + 1.132900 * G - 0.008349 * B;\n"
" float b = -0.018151 * R - 0.100579 * G + 1.118730 * B;\n"
" float w = 203.0;\n"
" r = srgb_oetf(knee_curve(max(r, 0.0) / w, u_peak_n));\n"
" g = srgb_oetf(knee_curve(max(g, 0.0) / w, u_peak_n));\n"
" b = srgb_oetf(knee_curve(max(b, 0.0) / w, u_peak_n));\n"
" gl_FragColor = vec4(b, g, r, c.a);\n"
" } else {\n"
" gl_FragColor = vec4(c.b, c.g, c.r, c.a);\n"
" }\n"
"}\n";
static GLuint compile_shader(GLenum type, const char *source) {
GLuint shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
glShaderSource(shader, 1, &source, NULL);
glCompileShader(shader);
GLint compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
glDeleteShader(shader);
return 0;
}
return shader;
}
static GLuint create_program(void) {
GLuint vs = compile_shader(GL_VERTEX_SHADER, vertex_shader_src);
GLuint fs = compile_shader(GL_FRAGMENT_SHADER, fragment_shader_src);
if (vs == 0 || fs == 0) {
if (vs) glDeleteShader(vs);
if (fs) glDeleteShader(fs);
return 0;
}
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glAttachShader(program, fs);
glBindAttribLocation(program, 0, "in_position");
glBindAttribLocation(program, 1, "in_texcoord");
glLinkProgram(program);
glDeleteShader(vs);
glDeleteShader(fs);
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if (!linked) {
glDeleteProgram(program);
return 0;
}
return program;
}
static EGLDisplay get_egl_display_for_card(const char *card_path) {
if (!pfn_eglQueryDevicesEXT || !pfn_eglQueryDeviceStringEXT ||
!pfn_eglGetPlatformDisplayEXT) {
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLint max_devices = 0;
pfn_eglQueryDevicesEXT(0, NULL, &max_devices);
if (max_devices <= 0) {
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLDeviceEXT *devices = calloc(max_devices, sizeof(EGLDeviceEXT));
if (!devices) {
return eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
EGLint num_devices = 0;
pfn_eglQueryDevicesEXT(max_devices, devices, &num_devices);
EGLDisplay display = EGL_NO_DISPLAY;
for (int i = 0; i < num_devices; i++) {
const char *drm_path = pfn_eglQueryDeviceStringEXT(
devices[i], EGL_DRM_DEVICE_FILE_EXT);
if (drm_path && strcmp(drm_path, card_path) == 0) {
display = pfn_eglGetPlatformDisplayEXT(
EGL_PLATFORM_DEVICE_EXT, devices[i], NULL);
break;
}
}
free(devices);
if (display == EGL_NO_DISPLAY) {
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
return display;
}
static int egl_init(drmtap_ctx *ctx, egl_state_t *state) {
if (load_egl_procs() < 0) {
drmtap_debug_log(ctx, "egl: required EGL procs not available");
return -ENOTSUP;
}
if (!g_egl_display_initialized) {
g_egl_display = get_egl_display_for_card(ctx->device_path);
if (g_egl_display == EGL_NO_DISPLAY) {
drmtap_debug_log(ctx, "egl: no EGL display available");
return -ENODEV;
}
EGLint major, minor;
if (!eglInitialize(g_egl_display, &major, &minor)) {
drmtap_debug_log(ctx, "egl: eglInitialize failed: 0x%x",
eglGetError());
return -EIO;
}
g_egl_display_initialized = 1;
drmtap_debug_log(NULL, "EGL: global display initialized: %p (%d.%d)",
(void*)g_egl_display, major, minor);
}
state->display = g_egl_display;
eglBindAPI(EGL_OPENGL_ES_API);
EGLint config_attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
EGLint reinit_maj, reinit_min;
if (!eglInitialize(state->display, &reinit_maj, &reinit_min)) {
drmtap_debug_log(NULL, "EGL: eglInitialize for thread FAILED: 0x%x", eglGetError());
return -EIO;
}
drmtap_debug_log(NULL, "EGL: eglInitialize OK (%d.%d)", reinit_maj, reinit_min);
drmtap_debug_log(NULL, "EGL: eglChooseConfig...");
EGLConfig config;
EGLint num_configs = 0;
if (!eglChooseConfig(state->display, config_attribs, &config, 1,
&num_configs) || num_configs == 0) {
drmtap_debug_log(NULL, "EGL: eglChooseConfig FAILED num=%d err=0x%x", num_configs, eglGetError());
drmtap_debug_log(ctx, "egl: eglChooseConfig failed or no configs");
return -EIO;
}
EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
drmtap_debug_log(NULL, "EGL: eglCreateContext(config=%p)...", (void*)config);
state->context = eglCreateContext(state->display, config,
EGL_NO_CONTEXT, context_attribs);
if (state->context == EGL_NO_CONTEXT) {
drmtap_debug_log(NULL, "EGL: eglCreateContext FAILED err=0x%x", eglGetError());
drmtap_debug_log(ctx, "egl: eglCreateContext failed: 0x%x",
eglGetError());
return -EIO;
}
eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
state->context);
state->program = create_program();
if (state->program == 0) {
drmtap_debug_log(NULL, "EGL: shader compilation FAILED");
drmtap_debug_log(ctx, "egl: shader compilation failed");
eglDestroyContext(state->display, state->context);
return -EIO;
}
glGenFramebuffers(1, &state->fbo);
state->linear_texture = 0;
state->tex_width = 0;
state->tex_height = 0;
state->initialized = 1;
state->owner_thread = pthread_self();
drmtap_debug_log(NULL, "EGL: init OK: display=%p ctx=%p program=%u fbo=%u (tid=%ld)",
(void*)state->display, (void*)state->context, state->program, state->fbo, (long)(long)0);
drmtap_debug_log(ctx, "egl: GPU-universal backend initialized");
return 0;
}
static void __attribute__((unused)) egl_cleanup(egl_state_t *state) {
if (!state->initialized) {
return;
}
eglMakeCurrent(state->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
state->context);
if (state->linear_texture) {
glDeleteTextures(1, &state->linear_texture);
}
if (state->fbo) {
glDeleteFramebuffers(1, &state->fbo);
}
if (state->program) {
glDeleteProgram(state->program);
}
eglDestroyContext(state->display, state->context);
eglTerminate(state->display);
state->initialized = 0;
}
static void ensure_linear_texture(egl_state_t *state, uint32_t w, uint32_t h) {
if (state->tex_width == w && state->tex_height == h) {
return;
}
if (state->linear_texture) {
glDeleteTextures(1, &state->linear_texture);
}
glGenTextures(1, &state->linear_texture);
glBindTexture(GL_TEXTURE_2D, state->linear_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
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);
state->tex_width = w;
state->tex_height = h;
}
int drmtap_gpu_egl_available(drmtap_ctx *ctx) {
static pthread_mutex_t lk = PTHREAD_MUTEX_INITIALIZER;
static int cached = -1;
pthread_mutex_lock(&lk);
if (cached < 0) {
if (load_egl_procs() < 0) {
cached = 0;
} else {
EGLDisplay display = get_egl_display_for_card(ctx->device_path);
if (display == EGL_NO_DISPLAY) {
cached = 0;
} else {
EGLint major, minor;
cached = eglInitialize(display, &major, &minor) ? 1 : 0;
}
}
}
pthread_mutex_unlock(&lk);
return cached;
}
static int egl_convert_impl(drmtap_ctx *ctx,
int dma_buf_fd,
uint32_t width, uint32_t height,
uint32_t stride, uint32_t fourcc,
uint64_t modifier,
void **out_data, size_t *out_size);
int drmtap_gpu_egl_convert(drmtap_ctx *ctx,
int dma_buf_fd,
uint32_t width, uint32_t height,
uint32_t stride, uint32_t fourcc,
uint64_t modifier,
void **out_data, size_t *out_size) {
static pthread_mutex_t g_convert_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&g_convert_lock);
int _ret = egl_convert_impl(ctx, dma_buf_fd, width, height, stride, fourcc,
modifier, out_data, out_size);
pthread_mutex_unlock(&g_convert_lock);
return _ret;
}
static int egl_convert_impl(drmtap_ctx *ctx,
int dma_buf_fd,
uint32_t width, uint32_t height,
uint32_t stride, uint32_t fourcc,
uint64_t modifier,
void **out_data, size_t *out_size) {
if (!out_data || !out_size) {
return -EINVAL;
}
static _Thread_local egl_state_t state = {0};
int ret;
drmtap_debug_log(NULL, "EGL: state.initialized=%d", state.initialized);
if (state.initialized && state.owner_thread != pthread_self()) {
drmtap_debug_log(NULL, "EGL: thread changed! old=%lu new=%lu, re-creating context",
(unsigned long)state.owner_thread, (unsigned long)pthread_self());
eglMakeCurrent(state.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
if (state.program) glDeleteProgram(state.program);
if (state.fbo) glDeleteFramebuffers(1, &state.fbo);
if (state.linear_texture) glDeleteTextures(1, &state.linear_texture);
eglDestroyContext(state.display, state.context);
state.initialized = 0;
state.program = 0;
state.fbo = 0;
state.linear_texture = 0;
state.tex_width = 0;
state.tex_height = 0;
}
if (!state.initialized) {
ret = egl_init(ctx, &state);
drmtap_debug_log(NULL, "EGL: egl_init returned %d", ret);
if (ret < 0) {
return ret;
}
}
eglBindAPI(EGL_OPENGL_ES_API);
{
EGLint _maj, _min;
eglInitialize(state.display, &_maj, &_min);
}
if (!eglMakeCurrent(state.display, EGL_NO_SURFACE, EGL_NO_SURFACE,
state.context)) {
drmtap_debug_log(NULL, "EGL: eglMakeCurrent in convert failed: 0x%x", eglGetError());
}
EGLint image_attribs[64];
int ai = 0;
image_attribs[ai++] = EGL_WIDTH;
image_attribs[ai++] = (EGLint)width;
image_attribs[ai++] = EGL_HEIGHT;
image_attribs[ai++] = (EGLint)height;
image_attribs[ai++] = EGL_LINUX_DRM_FOURCC_EXT;
image_attribs[ai++] = (EGLint)fourcc;
image_attribs[ai++] = EGL_DMA_BUF_PLANE0_FD_EXT;
image_attribs[ai++] = dma_buf_fd;
image_attribs[ai++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
image_attribs[ai++] = (EGLint)ctx->fb2_offsets[0];
image_attribs[ai++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
image_attribs[ai++] = (EGLint)stride;
if (modifier != DRM_FORMAT_MOD_INVALID) {
image_attribs[ai++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
image_attribs[ai++] = (EGLint)(modifier & 0xFFFFFFFF);
image_attribs[ai++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
image_attribs[ai++] = (EGLint)(modifier >> 32);
}
if (ctx->fb2_num_planes >= 2 && modifier != DRM_FORMAT_MOD_INVALID) {
drmtap_debug_log(ctx, "egl: adding CCS aux plane1 "
"(offset=%u pitch=%u)",
ctx->fb2_offsets[1], ctx->fb2_pitches[1]);
image_attribs[ai++] = EGL_DMA_BUF_PLANE1_FD_EXT;
image_attribs[ai++] = dma_buf_fd;
image_attribs[ai++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
image_attribs[ai++] = (EGLint)ctx->fb2_offsets[1];
image_attribs[ai++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
image_attribs[ai++] = (EGLint)ctx->fb2_pitches[1];
image_attribs[ai++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
image_attribs[ai++] = (EGLint)(modifier & 0xFFFFFFFF);
image_attribs[ai++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
image_attribs[ai++] = (EGLint)(modifier >> 32);
}
if (ctx->fb2_num_planes >= 3 && modifier != DRM_FORMAT_MOD_INVALID) {
drmtap_debug_log(ctx, "egl: adding CC plane2 "
"(offset=%u pitch=%u)",
ctx->fb2_offsets[2], ctx->fb2_pitches[2]);
image_attribs[ai++] = EGL_DMA_BUF_PLANE2_FD_EXT;
image_attribs[ai++] = dma_buf_fd;
image_attribs[ai++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
image_attribs[ai++] = (EGLint)ctx->fb2_offsets[2];
image_attribs[ai++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
image_attribs[ai++] = (EGLint)ctx->fb2_pitches[2];
image_attribs[ai++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
image_attribs[ai++] = (EGLint)(modifier & 0xFFFFFFFF);
image_attribs[ai++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
image_attribs[ai++] = (EGLint)(modifier >> 32);
}
image_attribs[ai++] = EGL_NONE;
drmtap_debug_log(NULL, "EGL: tid=%lu creating EGLImage: fd=%d %ux%u stride=%u fourcc=0x%x mod=0x%lx ai=%d",
(unsigned long)pthread_self(), dma_buf_fd, width, height, stride, fourcc, (unsigned long)modifier, ai);
EGLImageKHR image = pfn_eglCreateImageKHR(
state.display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, image_attribs);
if (image == EGL_NO_IMAGE_KHR) {
EGLint first_err = eglGetError();
drmtap_debug_log(NULL, "EGL: eglCreateImage FAILED: err=0x%x fourcc=0x%x mod=0x%lx",
first_err, fourcc, (unsigned long)modifier);
drmtap_debug_log(ctx, "egl: eglCreateImage failed: 0x%x "
"(fourcc=%.4s modifier=0x%lx)",
first_err, (const char *)&fourcc,
(unsigned long)modifier);
uint32_t xrgb8888 = DRM_FORMAT_XRGB8888;
EGLint retry_attribs[32];
int ri = 0;
retry_attribs[ri++] = EGL_WIDTH;
retry_attribs[ri++] = (EGLint)width;
retry_attribs[ri++] = EGL_HEIGHT;
retry_attribs[ri++] = (EGLint)height;
retry_attribs[ri++] = EGL_LINUX_DRM_FOURCC_EXT;
retry_attribs[ri++] = (EGLint)xrgb8888;
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_FD_EXT;
retry_attribs[ri++] = dma_buf_fd;
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
retry_attribs[ri++] = 0;
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
retry_attribs[ri++] = (EGLint)stride;
if (modifier != DRM_FORMAT_MOD_INVALID) {
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
retry_attribs[ri++] = (EGLint)(modifier & 0xFFFFFFFF);
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
retry_attribs[ri++] = (EGLint)(modifier >> 32);
}
retry_attribs[ri++] = EGL_NONE;
drmtap_debug_log(ctx, "egl: retrying with XRGB8888 + modifier");
image = pfn_eglCreateImageKHR(
state.display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, retry_attribs);
if (image == EGL_NO_IMAGE_KHR) {
ri = 0;
retry_attribs[ri++] = EGL_WIDTH;
retry_attribs[ri++] = (EGLint)width;
retry_attribs[ri++] = EGL_HEIGHT;
retry_attribs[ri++] = (EGLint)height;
retry_attribs[ri++] = EGL_LINUX_DRM_FOURCC_EXT;
retry_attribs[ri++] = (EGLint)xrgb8888;
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_FD_EXT;
retry_attribs[ri++] = dma_buf_fd;
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
retry_attribs[ri++] = 0;
retry_attribs[ri++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
retry_attribs[ri++] = (EGLint)stride;
retry_attribs[ri++] = EGL_NONE;
drmtap_debug_log(ctx, "egl: retrying XRGB8888 no-modifier");
image = pfn_eglCreateImageKHR(
state.display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, retry_attribs);
}
if (image == EGL_NO_IMAGE_KHR) {
drmtap_debug_log(NULL, "EGL: retry XRGB8888+nomod also FAILED: err=0x%x", eglGetError());
drmtap_debug_log(ctx, "egl: all retries failed: 0x%x",
eglGetError());
return -EIO;
}
drmtap_debug_log(NULL, "EGL: retry SUCCEEDED");
drmtap_debug_log(ctx, "egl: retry succeeded");
}
GLuint ext_texture;
glGenTextures(1, &ext_texture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, ext_texture);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
pfn_glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
GLenum gl_err = glGetError();
drmtap_debug_log(NULL, "EGL: glEGLImageTargetTexture2DOES: gl_err=0x%x", gl_err);
if (gl_err != GL_NO_ERROR) {
drmtap_debug_log(ctx, "egl: glEGLImageTargetTexture2DOES failed: 0x%x",
gl_err);
glDeleteTextures(1, &ext_texture);
pfn_eglDestroyImageKHR(state.display, image);
return -EIO;
}
ensure_linear_texture(&state, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, state.fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, state.linear_texture, 0);
glViewport(0, 0, width, height);
glUseProgram(state.program);
glUniform1i(glGetUniformLocation(state.program, "image"), 0);
int hdr_on = (ctx && ctx->cur_hdr_eotf == DRMTAP_EOTF_PQ) ? 1 : 0;
glUniform1i(glGetUniformLocation(state.program, "u_hdr"), hdr_on);
{
uint32_t mn = (ctx && ctx->cur_hdr_max_nits > 0)
? ctx->cur_hdr_max_nits : 1000u;
glUniform1f(glGetUniformLocation(state.program, "u_peak_n"),
(float)((double)mn / 203.0));
}
if (hdr_on) {
drmtap_debug_log(ctx, "EGL: HDR tone-map in shader (peak=%u nits)",
ctx->cur_hdr_max_nits);
}
static const float vertices[] = {
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
};
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
vertices);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
vertices + 2);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glFinish();
size_t rgba_size = (size_t)width * height * 4;
void *pixels = malloc(rgba_size);
if (!pixels) {
ret = -ENOMEM;
goto cleanup;
}
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
drmtap_debug_log(NULL, "EGL: glReadPixels done, gl_err=0x%x", glGetError());
*out_data = pixels;
*out_size = rgba_size;
ret = 0;
drmtap_debug_log(ctx, "egl: converted %ux%u tiled→linear (%zu bytes)",
width, height, rgba_size);
cleanup:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
glDeleteTextures(1, &ext_texture);
pfn_eglDestroyImageKHR(state.display, image);
return ret;
}
#else
#include "drmtap_internal.h"
int drmtap_gpu_egl_available(drmtap_ctx *ctx) {
(void)ctx;
return 0;
}
int drmtap_gpu_egl_convert(drmtap_ctx *ctx,
int dma_buf_fd,
uint32_t width, uint32_t height,
uint32_t stride, uint32_t fourcc,
uint64_t modifier,
void **out_data, size_t *out_size) {
(void)ctx; (void)dma_buf_fd; (void)width; (void)height;
(void)stride; (void)fourcc; (void)modifier;
(void)out_data; (void)out_size;
drmtap_set_error(ctx, "EGL backend not available (built without EGL)");
return -ENOTSUP;
}
#endif