0

我正在尝试为一个跨平台、内存安全的 API 做出贡献,用于在 Rust 中创建和使用称为glutin的 OpenGL 上下文。正在努力以允许用户为预先存在的窗口创建 GL 上下文的方式重新设计 API。

提出的一个问题是,如果用户尝试为具有预先存在的 DirectX 上下文的窗口创建 GL 上下文,这可能不是内存安全的。

的文档wglCreateContext表明它将NULL在失败时返回,但是它没有详细说明可能导致这种情况的条件。

如果给定(设备上下文)的 DirectX 上下文已经存在,是否会wglCreateContext安全失败(通过返回)?还是这种情况下的行为未定义?NULLHDC

我无法访问支持 OpenGL 的 Windows 机器,也无法自己直接测试。

4

1 回答 1

0

我在这里看到的真正问题是,它wglCreateContext可能因任何原因而失败,你必须能够处理它。

话虽如此,您提出问题的方式让人觉得 OpenGL 上下文、设备上下文和窗口之间的关系存在根本性的误解。简单地说三个字:没有!

好的,这需要澄清。这是怎么回事?如果这些不相关,为什么会有一个HDC参数?wglCreateContext

这一切都归结为像素格式(或帧缓冲区配置)。您可以在屏幕上看到的窗口是内存块的直接 1:1 表示。这块内存具有一定的像素格式。然而,只要只使用抽象绘图方法(就像 GDI 一样),所使用的精确像素格式并不重要,并且图形系统可以在它认为合适的时候默默地切换像素格式。在很久以前,当图形内存稀缺时,这可能意味着巨大的节省。

然而,OpenGL 假定使用特定的、不变的像素格式来操作帧缓冲区。因此,为了支持添加了一个新的 API,该 API 允许确定给定窗口的内部像素格式。然而,由于图形系统中唯一真正关心帧缓冲区格式的部分是绘制内容的部分,即 GDI,帧缓冲区配置通过该部分进行。HWNDs 与传递消息、将输入事件与应用程序相关联以及所有爵士乐相关。但它HDC与所有图形相关。这就是为什么您通过HDC.

创建 OpenGL 上下文时,必须为打算使用它的图形设备配置该上下文。这再次通过HDC句柄寻址的 GDI 和数据结构。但是,一旦创建了 OpenGL 上下文,它就可以与任何 HDC引用帧缓冲区的内容一起使用,该帧缓冲区的像素格式配置与HDC最初创建的 OpenGL 上下文兼容。它可以是HDC同一个窗口的不同,也可以是HDC完全不同的窗口。并且自从 OpenGL-3.3 核心以来,OpenGL 上下文可以完全不HDC更新,完全自包含,在自我管理的帧缓冲区上运行。最后但并非最不重要的一点是,该绑定可以随时更改。

每当对此没有清晰理解的人实现一些 OpenGL 绑定或抽象包装器时,他们往往会弄错这部分并创建不必要的紧身夹克,然后其他人,比如我,不得不拼命解决,因为抽象的工作方式是错误的。Qt 人犯了这个错误,GTK+ 人也犯了这个错误,现在看来你也犯了。您的 Github 项目页面上有此代码段:

let events_loop = glutin::EventsLoop::new();
let window = glutin::WindowBuilder::new()
    .with_title("Hello, world!".to_string())
    .with_dimensions(1024, 768)
    .with_vsync()
    .build(&events_loop)
    .unwrap();

unsafe {
    window.make_current()
}.unwrap();

unsafe {
    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);

    gl::ClearColor(0.0, 1.0, 0.0, 1.0);
}

啊啊啊啊。为什么这些方法make_currentget_proc_address窗口相关联?为什么?!这是谁想出来的?不要做这种狗屁事,它会让不得不使用这种东西的人的生活变得悲惨和痛苦。

你想知道这会导致什么吗?可怕的,凌乱的不安全代码,它会做令人作呕和肮脏的事情来强行直截了当地禁用一些现有的保护措施,以便它可以开始工作。就像我不得不做的这件可怕的事情一样,为了让 Qt4 对 OpenGL 如何工作的错误假设不再有影响。

#ifndef _WIN32
#if OCTPROCESSOR_CREATE_GLWIDGET_IN_THREAD
    QGLFormat glformat(
        QGL::DirectRendering |
        QGL::DoubleBuffer |
        QGL::Rgba |
        QGL::AlphaChannel |
        QGL::DepthBuffer,
        0 );
    glformat.setProfile(QGLFormat::CompatibilityProfile);

    gl_hidden_widget = new QGLWidget(glformat);
    if( !gl_hidden_widget ) {
        qDebug() << "creating woker context failed";
        goto fail_init_glcontext;
    }
    gl_hidden_widget->moveToThread( QThread::currentThread() );
#endif

    if( !gl_hidden_widget->isValid() ) {
        qDebug() << "worker context invalid";
        goto fail_glcontext_valid;
    }

    gl_hidden_widget->makeCurrent();
#else
    if( wglu_create_pbuffer_with_glrc(
        3,3,WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
        &m_hpbuffer,
        &m_hdc,
        &m_hglrc,
        &m_share_hglrc)
    ){
        qDebug() << "failed to create worker PBuffer and OpenGL context";
        goto fail_init_glcontext;
    }
    qDebug()
        << "m_hdc" << m_hdc
        << "m_hglrc" << m_hglrc;
    if( !wglMakeCurrent(m_hdc, m_hglrc) ){
        qDebug() << "failed making OpenGL context current on PBuffer HDC";
        goto fail_glcontext_valid;
    }
#endif
于 2017-06-30T19:24:06.813 回答