You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux下GLX/OpenGL中调整已有PBuffer表面尺寸的方法

调整GLX PBuffer尺寸以适配X11窗口大小变更

GLX PBuffer无法直接动态调整尺寸,必须销毁旧的PBuffer实例并重新创建对应新尺寸的资源。下面是完整的实现方案,包含窗口事件处理逻辑和PBuffer的重建流程。

核心实现步骤

  1. 扩展GLOffscreenBuffer类,添加尺寸调整方法,负责销毁旧资源并重新初始化
  2. 在X11事件循环中捕获ConfigureNotify事件(窗口大小变更事件),触发PBuffer尺寸更新
  3. 操作时严格管理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 05:14:58