Linux下GLX/OpenGL中调整已有PBuffer表面尺寸的方法
调整GLX PBuffer尺寸以适配X11窗口大小变更
GLX PBuffer无法直接动态调整尺寸,必须销毁旧的PBuffer实例并重新创建对应新尺寸的资源。下面是完整的实现方案,包含窗口事件处理逻辑和PBuffer的重建流程。
核心实现步骤
- 扩展
GLOffscreenBuffer类,添加尺寸调整方法,负责销毁旧资源并重新初始化 - 在X11事件循环中捕获
ConfigureNotify事件(窗口大小变更事件),触发PBuffer尺寸更新 - 操作时严格管理OpenGL上下文的绑定状态,避免资源泄漏
完整代码示例
1. 扩展GLOffscreenBuffer类
#include <X11/Xlib.h> #include <GL/glx.h> #include <iostream> class GLOffscreenBuffer { public: GLOffscreenBuffer(unsigned width, unsigned height) : m_width(width) , m_height(height) , m_display(XOpenDisplay(0)) , m_pbuffer(0) , m_context(0) , m_fbConfig(nullptr) { initialize(); } ~GLOffscreenBuffer() { cleanup(); if (m_display) { XCloseDisplay(m_display); } } // 调整PBuffer尺寸的核心方法 bool resize(unsigned newWidth, unsigned newHeight) { if (newWidth == m_width && newHeight == m_height) { return true; // 尺寸未变化,无需操作 } // 先解绑当前上下文(如果当前线程绑定了该上下文) if (glXGetCurrentContext() == m_context) { glXMakeCurrent(m_display, None, nullptr); } // 清理旧资源 cleanup(); // 更新尺寸并重新初始化 m_width = newWidth; m_height = newHeight; return initialize(); } // 绑定上下文到当前线程 bool bind() { return glXMakeCurrent(m_display, m_pbuffer, m_context); } // 解绑上下文 void unbind() { glXMakeCurrent(m_display, None, nullptr); } unsigned width() const { return m_width; } unsigned height() const { return m_height; } private: bool initialize() { if (!m_display) { std::cerr << "Error: XOpenDisplay() failed\n"; return false; } static const int configAttributes[] = { GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, GL_FALSE, 0 }; int configCount; m_fbConfig = glXChooseFBConfig(m_display, 0, configAttributes, &configCount); if (!configCount) { std::cerr << "Error: glXChooseFBConfig() failed\n"; return false; } static const int pbufferAttributes[] = { GLX_PBUFFER_WIDTH, static_cast<int>(m_width), GLX_PBUFFER_HEIGHT, static_cast<int>(m_height), 0 }; m_pbuffer = glXCreatePbuffer(m_display, m_fbConfig[0], pbufferAttributes); if (!m_pbuffer) { std::cerr << "Error: glXCreatePbuffer() failed\n"; cleanup(); return false; } m_context = glXCreateNewContext(m_display, m_fbConfig[0], GLX_RGBA_TYPE, nullptr, GL_TRUE); if (!m_context) { std::cerr << "Error: glXCreateNewContext() failed\n"; cleanup(); return false; } return true; } void cleanup() { if (m_context) { glXDestroyContext(m_display, m_context); m_context = nullptr; } if (m_pbuffer) { glXDestroyPbuffer(m_display, m_pbuffer); m_pbuffer = nullptr; } if (m_fbConfig) { XFree(m_fbConfig); m_fbConfig = nullptr; } } unsigned m_width, m_height; Display* m_display; GLXPbuffer m_pbuffer; GLXContext m_context; GLXFBConfig* m_fbConfig; };
2. X11事件循环处理窗口调整事件
int main() { Display* display = XOpenDisplay(nullptr); if (!display) { std::cerr << "Failed to open X display\n"; return 1; } int screen = DefaultScreen(display); Window root = RootWindow(display, screen); // 创建测试窗口 Window window = XCreateSimpleWindow(display, root, 100, 100, 800, 600, 1, BlackPixel(display, screen), WhitePixel(display, screen)); XSelectInput(display, window, StructureNotifyMask | ExposureMask); XMapWindow(display, window); // 初始化PBuffer GLOffscreenBuffer pbuffer(800, 600); XEvent event; while (true) { XNextEvent(display, &event); switch (event.type) { case ConfigureNotify: { // 捕获窗口大小变更事件 unsigned newWidth = event.xconfigure.width; unsigned newHeight = event.xconfigure.height; std::cout << "Window resized to: " << newWidth << "x" << newHeight << "\n"; // 调整PBuffer尺寸 if (!pbuffer.resize(newWidth, newHeight)) { std::cerr << "Failed to resize PBuffer\n"; } // 绑定上下文并更新视口(示例) if (pbuffer.bind()) { glViewport(0, 0, pbuffer.width(), pbuffer.height()); // 此处可添加重新渲染逻辑 pbuffer.unbind(); } break; } case Expose: { // 窗口暴露事件,触发渲染 if (pbuffer.bind()) { glClearColor(0.2f, 0.3f, 0.4f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); // 此处添加你的渲染逻辑 pbuffer.unbind(); } break; } case DestroyNotify: { // 窗口关闭事件,退出程序 XCloseDisplay(display); return 0; } } } return 0; }
关键注意事项
- PBuffer尺寸限制:GLX规范未提供修改现有PBuffer尺寸的API,必须销毁重建
- 上下文管理:调整前必须解绑当前上下文,避免资源占用导致的销毁失败
- FBConfig复用:如果不需要修改像素格式,可以复用之前选择的FBConfig,减少重复配置开销
- 错误处理:每个GLX调用都要检查返回值,确保资源被正确清理
内容的提问来源于stack exchange,提问作者Chen Chen
相关产品推荐
相关产品推荐

