My work environment is Ubuntu 22.04, using an NVIDIA 4090 D GPU with an AMD chipset.
I aim to directly transfer offscreen-rendered content to the DisplayPort interface via EGLStream for display.
Currently, I have successfully initialized DRM and selected the corresponding CRTC. I created a buffer in GPU memory through GBM, bound it to the CRTC, and then retrieved the output layer associated with the CRTC, which I successfully bound to the EGLStream consumer. After that, I created a PSurface and bound it as the producer.
The rendered content appears correct when checked with glReadPixels.
After calling eglSwapBuffers, the stream status is EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR, but the display connected to the DisplayPort still shows a black screen.
Could you help identify the possible cause of this issue?"
// Function to print EGLStream state
void printEglStreamState(EGLDisplay eglDpy, EGLStreamKHR eglStream, PFNEGLQUERYSTREAMKHRPROC eglQueryStreamKHR) {
EGLint streamState;
if (eglQueryStreamKHR(eglDpy, eglStream, EGL_STREAM_STATE_KHR, &streamState)) {
std::cout << "EGLStream State: ";
switch (streamState) {
case EGL_STREAM_STATE_CREATED_KHR:
std::cout << "EGL_STREAM_STATE_CREATED_KHR" << std::endl;
break;
case EGL_STREAM_STATE_CONNECTING_KHR:
std::cout << "EGL_STREAM_STATE_CONNECTING_KHR" << std::endl;
break;
case EGL_STREAM_STATE_EMPTY_KHR:
std::cout << "EGL_STREAM_STATE_EMPTY_KHR" << std::endl;
break;
case EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR:
std::cout << "EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR" << std::endl;
break;
case EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR:
std::cout << "EGL_STREAM_STATE_OLD_FRAME_AVAILABLE_KHR" << std::endl;
break;
case EGL_STREAM_STATE_DISCONNECTED_KHR:
std::cout << "EGL_STREAM_STATE_DISCONNECTED_KHR" << std::endl;
break;
default:
std::cout << "Unknown State (0x" << std::hex << streamState << ")" << std::endl;
}
} else {
EGLint error = eglGetError();
std::cerr << "Failed to query EGLStream state. EGL Error: 0x" << std::hex << error << std::endl;
}
}
// Function to check EGL errors
void checkEglError(const std::string& msg) {
EGLint error = eglGetError();
if (error != EGL_SUCCESS) {
std::cerr << "EGL error after " << msg << ": 0x" << std::hex << error << std::endl;
throw std::runtime_error("EGL error occurred");
}
}
void checkGlError(const std::string& msg) {
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
std::cerr << "OpenGL error after " << msg << ": 0x" << std::hex << error << std::endl;
throw std::runtime_error("OpenGL error occurred");
}
}
struct gbm_bo* createGbmFramebuffer(int drm_fd, gbm_device* gbm, uint32_t* fb_id, uint32_t* width, uint32_t* height, uint32_t* handle) {
// Create GBM buffer object
std::cout << "Creating GBM buffer object with width=" << *width << " and height=" << *height << std::endl;
// Use AR24 (GBM_FORMAT_ARGB8888)
uint64_t modifiers[] = { DRM_FORMAT_MOD_LINEAR };
struct gbm_bo* bo = gbm_bo_create_with_modifiers(gbm, *width, *height,
GBM_FORMAT_XRGB8888,
modifiers, 1);
if (!bo) {
std::cerr << "Failed to create GBM buffer object with modifiers: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
uint64_t modifier = gbm_bo_get_modifier(bo);
// Get buffer handle and stride
gbm_bo_handle bo_handle = gbm_bo_get_handle(bo);
if (!bo_handle.u32) {
std::cerr << "Failed to get GBM buffer handle" << std::endl;
gbm_bo_destroy(bo);
exit(EXIT_FAILURE);
}
*handle = bo_handle.u32;
uint32_t stride = gbm_bo_get_stride(bo);
std::cout << "GBM buffer object created. Handle=" << *handle << ", stride=" << stride << std::endl;
// Add framebuffer using drmModeAddFB2 for better format handling
uint32_t handles[4] = { *handle, 0, 0, 0 };
uint32_t pitches[4] = { stride, 0, 0, 0 };
uint32_t offsets[4] = { 0, 0, 0, 0 };
uint64_t modifiers_array[4] = { modifier, 0, 0, 0 };
int ret = drmModeAddFB2WithModifiers(drm_fd, *width, *height, GBM_FORMAT_XRGB8888,
handles, pitches, offsets, modifiers_array, fb_id, DRM_MODE_FB_MODIFIERS);
if (ret) {
std::cerr << "Failed to add framebuffer using drmModeAddFB2WithModifiers: " << strerror(errno) << std::endl;
gbm_bo_destroy(bo);
exit(EXIT_FAILURE);
}
std::cout << "Framebuffer successfully created: fb_id=" << *fb_id
<< ", width=" << *width << ", height=" << *height << std::endl;
return bo;
}
void setCrtcForDP(int drm_fd, drmModeRes* resources, drmModeConnector* connector, uint32_t fb_id, uint32_t width, uint32_t height, uint32_t& crtc_id) {
drmModeEncoder* encoder = nullptr;
if (connector->encoder_id) {
encoder = drmModeGetEncoder(drm_fd, connector->encoder_id);
}
if (!encoder) {
for (int i = 0; i < resources->count_encoders; ++i) {
encoder = drmModeGetEncoder(drm_fd, resources->encoders[i]);
if (encoder && (encoder->possible_crtcs & (1 << i))) {
break;
}
drmModeFreeEncoder(encoder);
encoder = nullptr;
}
}
if (!encoder) {
std::cerr << "Failed to find a suitable encoder, exiting..." << std::endl;
exit(EXIT_FAILURE);
}
drmModeCrtc* crtc = nullptr;
for (int i = 0; i < resources->count_crtcs; ++i) {
if (encoder->possible_crtcs & (1 << i)) {
crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]);
if (crtc) {
crtc_id = crtc->crtc_id;
std::cout << "crtc_id = " << crtc->crtc_id << std::endl;
break;
}
}
}
if (!crtc) {
std::cerr << "Failed to find a suitable CRTC, exiting..." << std::endl;
drmModeFreeEncoder(encoder);
exit(EXIT_FAILURE);
}
drmModeModeInfo mode = connector->modes[3]; // Use first mode (1920x1080 @ 60Hz)
std::cout << "Trying mode 3: " << mode.hdisplay << "x" << mode.vdisplay << "@" << mode.vrefresh << "Hz" << std::endl;
if (drmModeSetCrtc(drm_fd, crtc->crtc_id, fb_id, 0, 0, &connector->connector_id, 1, &mode) < 0) {
std::cerr << "Failed to set CRTC: " << strerror(errno) << std::endl;
drmModeFreeEncoder(encoder);
drmModeFreeCrtc(crtc); // 确保释放 CRTC
} else {
std::cout << "Successfully set CRTC with framebuffer." << std::endl;
}
}
const char* vertexShaderSource =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\n";
const char* fragmentShaderSource =
"#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n";
int main() {
try {
// int drm_fd = open("/dev/dri/card2", O_RDWR | O_CLOEXEC);
// if (drm_fd < 0) {
// std::cerr << "Failed to open DRM device" << std::endl;
// return -1;
// }
// 初始化 EGL 和选择 GPU 使用 EGLStream 扩展
static const EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_STREAM_BIT_KHR | EGL_PBUFFER_BIT, // 支持 Stream 和 Pbuffer
EGL_BLUE_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8,
EGL_ALPHA_SIZE, 8, // 添加 alpha 通道
EGL_DEPTH_SIZE, 24,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, // 指定颜色缓冲区类型
EGL_NONE
};
// 加载必要的 EGL 扩展函数
PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT = (PFNEGLQUERYDEVICESEXTPROC)eglGetProcAddress("eglQueryDevicesEXT");
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT");
PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT = (PFNEGLQUERYDEVICESTRINGEXTPROC)eglGetProcAddress("eglQueryDeviceStringEXT");
if (!eglQueryDevicesEXT || !eglGetPlatformDisplayEXT || !eglQueryDeviceStringEXT) {
std::cerr << "Failed to get EGL device extensions" << std::endl;
return -1;
}
// 查询所有可用设备
const int maxDevices = 10;
EGLDeviceEXT eglDevices[maxDevices];
EGLint numDevices;
if (!eglQueryDevicesEXT(maxDevices, eglDevices, &numDevices)) {
std::cerr << "Failed to query EGL devices." << std::endl;
return -1;
}
if (numDevices < 1) {
std::cerr << "No EGL devices found" << std::endl;
return -1;
}
std::cout << "Found " << numDevices << " devices" << std::endl;
const char *clientExtensionString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if (strstr(clientExtensionString, "EGL_EXT_device_base") == nullptr) {
std::cerr << "EGL_EXT_device_base extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_device_base extension is supported." << std::endl;
}
if (strstr(clientExtensionString, "EGL_EXT_device_enumeration") == nullptr) {
std::cerr << "EGL_EXT_device_enumeration extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_device_enumeration extension is supported." << std::endl;
}
if (strstr(clientExtensionString, "EGL_EXT_device_query") == nullptr) {
std::cerr << "EGL_EXT_device_query extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_device_query extension is supported." << std::endl;
}
// EGL_NO_DEVICE_EXT
EGLDeviceEXT eglDevice = EGL_NO_DEVICE_EXT;
for (int i = 0; i < numDevices; ++i)
{
const char *deviceExtensionString = eglQueryDeviceStringEXT(eglDevices[i], EGL_EXTENSIONS);
if (strstr(deviceExtensionString, "EGL_EXT_device_drm") == nullptr) {
std::cerr << "EGL_EXT_device_drm extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_device_drm extension is supported." << std::endl;
eglDevice = eglDevices[i];
break;
}
}
if (eglDevice == EGL_NO_DEVICE_EXT) {
std::cerr << "No EGL_EXT_device_drm-capable EGL device found." << std::endl;
return -1;
}
const char *drmDeviceFile;
drmDeviceFile = eglQueryDeviceStringEXT(eglDevice, EGL_DRM_DEVICE_FILE_EXT);
if (drmDeviceFile == NULL) {
std::cerr << "No DRM device file found for EGL device.\n";
return -1;
}
std::cout << "Supported EGL drmDeviceFile:" << std::endl;
std::cout << drmDeviceFile << std::endl;
int drm_fd = open(drmDeviceFile, O_RDWR | O_CLOEXEC, 0);
if (drm_fd < 0) {
std::cerr << "Failed to open DRM device" << std::endl;
return -1;
}else{
std::cout << "drm_fd = " << drm_fd << std::endl;
}
// 检查是否为 DRM Master
if (drmIsMaster(drm_fd)) {
std::cout << "DRM 文件描述符拥有 DRM Master 权限。" << std::endl;
} else {
std::cout << "DRM 文件描述符没有 DRM Master 权限,尝试获取..." << std::endl;
// 尝试获取 DRM Master 权限
int ret = drmSetMaster(drm_fd);
if (ret == 0) {
std::cout << "成功获取 DRM Master 权限。" << std::endl;
} else {
std::cerr << "无法获取 DRM Master 权限:" << strerror(errno) << std::endl;
// 根据需要处理错误,例如退出程序或重试
}
}
drmModeRes* resources = drmModeGetResources(drm_fd);
if (!resources) {
std::cerr << "Failed to get DRM resources" << std::endl;
return -1;
}
drmModeConnector* connector = nullptr;
for (int i = 0; i < resources->count_connectors; ++i) {
connector = drmModeGetConnector(drm_fd, resources->connectors[i]);
if (connector->connection == DRM_MODE_CONNECTED &&
connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
std::cout << "Connected to DP-1" << std::endl;
break;
}
drmModeFreeConnector(connector);
connector = nullptr;
}
if (!connector) {
std::cerr << "No connected DisplayPort found" << std::endl;
return -1;
}
const int WIDTH = 1920;
const int HEIGHT = 1080;
// Setup GBM device and framebuffer
gbm_device* gbm = gbm_create_device(drm_fd);
uint32_t fb_id_1 = 0, width = WIDTH, height = HEIGHT, handle_1 = 0;
uint32_t fb_id_2 = 0, handle_2 = 0;
struct gbm_bo* bo_1 = createGbmFramebuffer(drm_fd, gbm, &fb_id_1, &width, &height, &handle_1);
struct gbm_bo* bo_2 = createGbmFramebuffer(drm_fd, gbm, &fb_id_2, &width, &height, &handle_2);
std::cout << "fb_id_1 = " << fb_id_1 << std::endl;
std::cout << "fb_id_2 = " << fb_id_2 << std::endl;
uint32_t stride_1;
void *map_data_1;
void *mapped_buffer_1 = gbm_bo_map(bo_1, 0, 0, WIDTH, HEIGHT, GBM_BO_TRANSFER_WRITE, &stride_1, &map_data_1);
if (!mapped_buffer_1) {
std::cerr << "Failed to map GBM buffer 1" << std::endl;
return -1;
}
uint32_t stride_2;
void *map_data_2;
void *mapped_buffer_2 = gbm_bo_map(bo_2, 0, 0, WIDTH, HEIGHT, GBM_BO_TRANSFER_WRITE, &stride_2, &map_data_2);
if (!mapped_buffer_2) {
std::cerr << "Failed to map GBM buffer 2" << std::endl;
return -1;
}
/*
* eglGetPlatformDisplayEXT requires EGL client extension
* EGL_EXT_platform_base.
*/
if (strstr(clientExtensionString, "EGL_EXT_platform_base") == nullptr) {
std::cerr << "EGL_EXT_platform_base extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_platform_base extension is supported." << std::endl;
}
/*
* EGL_EXT_platform_device is required to pass
* EGL_PLATFORM_DEVICE_EXT to eglGetPlatformDisplayEXT().
*/
if (strstr(clientExtensionString, "EGL_EXT_platform_device") == nullptr) {
std::cerr << "EGL_EXT_platform_device extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_platform_device extension is supported." << std::endl;
}
EGLint dispAttribs[] = {
EGL_DRM_MASTER_FD_EXT,
drm_fd,
EGL_NONE
};
EGLDisplay eglDpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_DEVICE_EXT, eglDevice, dispAttribs);
if (eglDpy == EGL_NO_DISPLAY) {
std::cerr << "Failed to get EGL display from device" << std::endl;
return -1;
}
// 初始化 display
EGLint major, minor;
if (!eglInitialize(eglDpy, &major, &minor)) {
checkEglError("eglInitialize");
}
std::cout << "EGL initialized, version " << major << "." << minor << std::endl;
if (!eglBindAPI(EGL_OPENGL_API)) {
checkEglError("eglBindAPI");
}
std::cout << "EGL_OPENGL_API bound." << std::endl;
// 查询所有支持的 EGL 扩展
const char* extensions = eglQueryString(eglDpy, EGL_EXTENSIONS);
if (extensions == nullptr) {
std::cerr << "Failed to query EGL extensions" << std::endl;
return -1;
}
// 打印扩展列表
std::cout << "Supported EGL extensions:" << std::endl;
std::cout << extensions << std::endl;
// 检查是否支持 EGL_KHR_stream 扩展
if (strstr(extensions, "EGL_KHR_stream") == nullptr) {
std::cerr << "EGL_KHR_stream extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_KHR_stream extension is supported." << std::endl;
}
/*
* EGL_EXT_output_base and EGL_EXT_output_drm are needed to find
* the EGLOutputLayer for the DRM KMS plane.
*/
if (strstr(extensions, "EGL_EXT_output_base") == nullptr) {
std::cerr << "EGL_EXT_output_base extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_output_base extension is supported." << std::endl;
}
if (strstr(extensions, "EGL_EXT_output_drm") == nullptr) {
std::cerr << "EGL_EXT_output_drm extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_output_drm extension is supported." << std::endl;
}
/*
* EGL_KHR_stream, EGL_EXT_stream_consumer_egloutput, and
* EGL_KHR_stream_producer_eglsurface are needed to create an
* EGLStream connecting an EGLSurface and an EGLOutputLayer.
*/
if (strstr(extensions, "EGL_EXT_stream_consumer_egloutput") == nullptr) {
std::cerr << "EGL_EXT_stream_consumer_egloutput extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_EXT_stream_consumer_egloutput extension is supported." << std::endl;
}
if (strstr(extensions, "EGL_KHR_stream_producer_eglsurface") == nullptr) {
std::cerr << "EGL_KHR_stream_producer_eglsurface extension is not supported." << std::endl;
return -1;
} else {
std::cout << "EGL_KHR_stream_producer_eglsurface extension is supported." << std::endl;
}
// 选择第一个符合要求的 EGL 配置
EGLint numConfigs;
EGLConfig eglCfg;
if (!eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs) || numConfigs == 0) {
checkEglError("eglChooseConfig");
}
std::cout << "EGLConfig chosen successfully." << std::endl;
// 验证 EGLConfig 是否支持 streams
EGLint surfaceType;
if (!eglGetConfigAttrib(eglDpy, eglCfg, EGL_SURFACE_TYPE, &surfaceType)) {
checkEglError("eglGetConfigAttrib");
}
if (!(surfaceType & EGL_STREAM_BIT_KHR)) {
std::cerr << "EGLConfig does not support streams." << std::endl;
return -1;
} else {
std::cout << "EGLConfig supports streams." << std::endl;
}
// 加载 EGLStream 函数
loadEglStreamFunctions();
std::cout << "EGLStream functions loaded successfully." << std::endl;
// 创建 EGLStream
const EGLint streamAttribs[] = {
EGL_CONSUMER_LATENCY_USEC_KHR, 0, // 低延迟
EGL_STREAM_FIFO_LENGTH_KHR, 1, // 指定最多4个缓冲区
EGL_NONE
};
EGLStreamKHR eglStream = eglCreateStreamKHR(eglDpy, streamAttribs);
if (eglStream == EGL_NO_STREAM_KHR) {
checkEglError("eglCreateStreamKHR");
} else {
std::cout << "EGLStream created successfully: " << eglStream << std::endl;
}
// 查询所有可用的 Output Layers(Consumer)
EGLint numLayers = 0;
EGLAttrib layerAttribs[] = { EGL_NONE };
if (!eglGetOutputLayersEXT(eglDpy, layerAttribs, nullptr, 0, &numLayers)) {
std::cerr << "Failed to get number of output layers." << std::endl;
return -1;
}
if (numLayers == 0) {
throw std::runtime_error("No available output layers found for DisplayPort");
}
std::cout << "Found " << numLayers << " available output layers." << std::endl;
// 分配空间并查询 Output Layers
EGLOutputLayerEXT* eglOutputLayers = new EGLOutputLayerEXT[numLayers];
if (!eglGetOutputLayersEXT(eglDpy, layerAttribs, eglOutputLayers, numLayers, &numLayers)) {
std::cerr << "Failed to get output layers." << std::endl;
delete[] eglOutputLayers;
return -1;
}
// 打印并查询每个 Output Layer 的属性
for (int i = 0; i < numLayers; ++i) {
EGLOutputLayerEXT layer = eglOutputLayers[i];
std::cout << "Output Layer " << i << ": " << layer << std::endl;
EGLAttrib crtc_id;
if (eglQueryOutputLayerAttribEXT(eglDpy, layer, EGL_DRM_CRTC_EXT, &crtc_id)) {
std::cout << " CRTC ID: " << crtc_id << std::endl;
} else {
std::cerr << " Failed to query CRTC ID for Output Layer " << i << std::endl;
}
EGLAttrib format;
if (eglQueryOutputLayerAttribEXT(eglDpy, layer, EGL_RENDER_BUFFER, &format)) { // 使用标准 EGL_RENDER_BUFFER
std::cout << " Render Buffer Format: " << format << std::endl;
} else {
std::cerr << " Failed to query Render Buffer Format for Output Layer " << i << std::endl;
}
// 其他属性查询(根据需要)
}
// 选择第一个 Output Layer 作为 Consumer
EGLOutputLayerEXT eglOutputLayer = eglOutputLayers[0];
std::cout << "Using Output Layer " << 0 << " as Consumer." << std::endl;
// 绑定 EGLStream 到 Output Layer(Consumer)
if (!eglStreamConsumerOutputEXT(eglDpy, eglStream, eglOutputLayer)) {
checkEglError("eglStreamConsumerOutputEXT");
} else {
std::cout << "Successfully bound EGLStream to the output layer." << std::endl;
}
uint32_t crtc_id = 0;
setCrtcForDP(drm_fd, resources, connector, fb_id_1, width, height, crtc_id);
// 创建 Producer Surface
const EGLint surfaceAttribs[] = {
EGL_WIDTH, 1920,
EGL_HEIGHT, 1080,
EGL_NONE
};
EGLSurface eglSurf = eglCreateStreamProducerSurfaceKHR(eglDpy, eglCfg, eglStream, surfaceAttribs);
if (eglSurf == EGL_NO_SURFACE) {
EGLint error = eglGetError();
std::cerr << "Failed to create stream producer surface. EGL Error: 0x" << std::hex << error << std::endl;
throw std::runtime_error("eglCreateStreamProducerSurfaceKHR failed");
} else {
std::cout << "EGLStream producer surface created successfully: " << eglSurf << std::endl;
}
// 创建 OpenGL 上下文
EGLint contextAttribs[] = {
EGL_CONTEXT_MAJOR_VERSION, 3,
EGL_CONTEXT_MINOR_VERSION, 0,
EGL_NONE
};
EGLContext eglContext = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, contextAttribs);
if (eglContext == EGL_NO_CONTEXT) {
checkEglError("eglCreateContext");
}
// 绑定上下文到 Producer Surface
if (!eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglContext)) {
checkEglError("eglMakeCurrent for Producer Surface");
} else {
std::cout << "EGLContext bound to producer surface successfully." << std::endl;
}
// 加载 OpenGL 函数
loadOpenGLFunctions();
std::cout << "OpenGL functions loaded successfully." << std::endl;
// 编译着色器并渲染
GLuint vertex, fragment;
vertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex, 1, &vertexShaderSource, nullptr);
glCompileShader(vertex);
GLint success;
glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(vertex, 512, nullptr, infoLog);
std::cerr << "Vertex shader compilation failed: " << infoLog << std::endl;
return -1;
}
checkGlError("glCompileShader (vertex)");
fragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragment);
glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(fragment, 512, nullptr, infoLog);
std::cerr << "Fragment shader compilation failed: " << infoLog << std::endl;
return -1;
}
checkGlError("glCompileShader (fragment)");
GLuint program = glCreateProgram();
glAttachShader(program, vertex);
glAttachShader(program, fragment);
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(program, 512, nullptr, infoLog);
std::cerr << "Shader program linking failed: " << infoLog << std::endl;
return -1;
}
checkGlError("glLinkProgram");
GLfloat vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
GLuint vbo;
glGenBuffers(1, &vbo);
checkGlError("glGenBuffers");
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
checkGlError("glBufferData");
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
checkGlError("glClear");
glUseProgram(program);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
checkGlError("glVertexAttribPointer");
printEglStreamState(eglDpy, eglStream, eglQueryStreamKHR);
double time_tc = 0;
const int count = 1;
for(int i=0; i<count; i++){
// 设置背景颜色为绿色(测试渲染)
std::cout << "Started to render " << i << " frame." << std::endl;
// glClearColor(0.0f, 1.0f, 0.0f, 1.0f); // 绿色背景
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set viewport to match surface dimensions
glViewport(0, 0, WIDTH, HEIGHT);
// Clear frame buffer with non-black color to confirm rendering
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Render triangle
glUseProgram(program);
glDrawArrays(GL_TRIANGLES, 0, 3);
checkGlError("glDrawArrays");
glFinish();
checkGlError("glFinish");// Ensure rendering commands are executed
std::vector<GLubyte> pixels(WIDTH * HEIGHT * 4);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
checkGlError("glReadPixels");
// 保存第一个帧缓冲内容到PPM文件
std::ofstream ofs_1("output_1.ppm", std::ios::binary);
ofs_1 << "P6\n" << WIDTH << " " << HEIGHT << "\n255\n";
for (int y = HEIGHT - 1; y >= 0; --y) {
for (int x = 0; x < WIDTH; ++x) {
int index = (y * WIDTH + x) * 4;
ofs_1.put(pixels[index]); // R
ofs_1.put(pixels[index + 1]); // G
ofs_1.put(pixels[index + 2]); // B
}
}
ofs_1.close();
auto start = std::chrono::high_resolution_clock::now();
// 推送渲染帧到 EGLStream 消费者(DisplayPort)
if (!eglSwapBuffers(eglDpy, eglSurf)) {
EGLint error = eglGetError();
std::cerr << "eglSwapBuffers failed. EGL Error: 0x" << std::hex << error << std::endl;
} else {
std::cout << "Rendered frame pushed to EGLStream." << std::endl;
printEglStreamState(eglDpy, eglStream, eglQueryStreamKHR);
// while(true){
// // 消费者部分:尝试获取并处理帧
// std::cout << "Trying to acquire frame." << std::endl;
// printEglStreamState(eglDpy, eglStream, eglQueryStreamKHR);
// if (eglStreamConsumerAcquireKHR(eglDpy, eglStream)) {
// std::cout << "Consumer acquired a frame." << std::endl;
// // 这里可以添加消费者处理帧的逻辑,例如显示或编码
// // 目前仅模拟处理延迟
// std::this_thread::sleep_for(std::chrono::milliseconds(10));
// // 释放帧后
// if (!eglStreamConsumerReleaseKHR(eglDpy, eglStream)) {
// checkEglError("eglStreamConsumerReleaseKHR");
// } else {
// std::cout << "Consumer released the frame." << std::endl;
// }
// break;
// } else {
// EGLint error = eglGetError();
// if (error != EGL_BAD_STREAM_KHR && error != EGL_BAD_STATE_KHR) {
// std::cerr << "Failed to acquire frame. EGL Error: 0x" << std::hex << error << std::endl;
// }
// }
// std::this_thread::sleep_for(std::chrono::milliseconds(16)); // 大约16ms用于60fps
// }
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "eglSwapBuffers execution time from VIDEO mem to CRTC Video mem: " << elapsed.count() << " ms" << std::endl;
time_tc += elapsed.count();
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // 大约16ms
}
std::cout << "Average eglSwapBuffers execution time from VIDEO mem to CRTC Video mem: " << time_tc / double(count) << " ms" << std::endl;
// Detect key press to exit
while (true) {
if(checkForKeyPress()){
std::cout << "Key pressed, exiting..." << std::endl;
break;
}
// Maintain 60fps
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // 大约16ms用于60fps
}
// 检查渲染器信息
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* version = glGetString(GL_VERSION);
std::cout << "Renderer: " << renderer << std::endl;
std::cout << "OpenGL version supported: " << version << std::endl;
// 清理资源
eglDestroySurface(eglDpy, eglSurf);
eglDestroyStreamKHR(eglDpy, eglStream);
eglDestroyContext(eglDpy, eglContext);
eglTerminate(eglDpy);
// 清理分配的 Output Layers 数组
delete[] eglOutputLayers;
return 0;
} catch (const std::exception &ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return EXIT_FAILURE;
}
}
Thanks