从 SFML 提供的 X11 句柄创建 Irrlicht 设备。运行时 X11/OpenGL 错误

Creating Irrlicht Device from X11 handle provided by SFML. Runtime X11/OpenGL error

我想使用 Irrlicht 渲染到由 SFML

创建的 window

sf::WindowHandle sf::Window::getSystemHandle() const returns当然是sf::WindowHandle,这是一个typedef。在 Windows 上它是 HWND__*,在 linux 上它是 unsigned long(可能是 X11 windows ID),在任何其他平台上它是 void*。在 Irrlicht 文档中,他们说我应该在 Windows 上传递 HWND,但其他系统没有别的。

Irrlicht 允许使用来自外部 window 的专门方法创建它的主要对象 irr::IrrlichtDevice。它需要 struct irr::SIrrlichtCreationParameters。结构变量之一是 void* WindowId

所以我想做的是使用 SFML 创建一个 window(然后使用所有 SFML 函数来轮询事件等)并使 Irrlicht 在这个 window 中呈现。

核心代码:

sf::VideoMode desktop = sf::VideoMode::getDesktopMode();
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
settings.depthBits = 16;
settings.stencilBits = 0;

//my own Logger class
logger.AddLog("Requesting following settings:\n");
logger.AddLog(settings);

window = std::make_shared<sf::RenderWindow>(
    sf::VideoMode(WINDOW_SIZE_X, WINDOW_SIZE_Y, desktop.bitsPerPixel),
    sf::String("Irrlicht + SFML test"),
    sf::Style::Default,
    settings);

logger.AddLog("Created SFML Window with following settings:\n");
logger.AddLog(window->getSettings());

irr::SIrrlichtCreationParameters params;
params.AntiAlias = 8;
params.Bits = 16;
params.DeviceType = irr::EIDT_BEST;
params.DriverType = irr::video::EDT_OPENGL;
params.Doublebuffer = true;
params.EventReceiver = nullptr;
params.Fullscreen = false;
params.HandleSRGB = false;
params.IgnoreInput = true;
params.Stencilbuffer = false;
params.UsePerformanceTimer = false;
params.Vsync = false;
params.WithAlphaChannel = false;
params.ZBufferBits = 24;
params.LoggingLevel = irr::ELL_DEBUG;
params.WindowId = GetWindowHandle(window->getSystemHandle()); // Described below

device.reset(irr::createDeviceEx(params)); // device is std::unique_ptr
if(device == nullptr)
    std::cout << "Can't create device!\n";

我的功能描述 SFML 的 getSystemHandle returns unsigned long on Linux,它是 X11 widnow ID,如果我理解正确的话。 我的函数 GetWindowHandle 采用 sf::WindowHandle 和 returns void* 对于 Linux 它确实: return reinterpret_cast< void* >(handle); 这似乎是正确的方法,我尝试过的任何其他方法都会产生 X11 错误 BadWindowId

程序在创建设备后停止执行,未到达 if(device == nullptr) 块。我的程序被终止并且 returns 1.

我在控制台中遇到的错误:("Irrlicht Engine" 之前的所有输出都由我的记录器对象完成)

[2017.01.28 13:15:51]
Starting Application
Requesting following settings:
OpenGL context        : 1.1
flags                 : 0
antialiasing level    : 8
bits of depth   buffer: 16
bits of stencil buffer: 0
Created SFML Window with following settings:
OpenGL context        : 4.5
flags                 : 0
antialiasing level    : 8
bits of depth   buffer: 24
bits of stencil buffer: 0
Irrlicht Engine version 1.8.4
Linux 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64
X Error of failed request:  BadAlloc (insufficient resources for operation)
  Major opcode of failed request:  154 (GLX)
  Minor opcode of failed request:  31 (X_GLXCreateWindow)
  Serial number of failed request:  26
  Current serial number in output stream:  27

什么会导致这样的错误?

编辑 尝试使用 sf::Window。也不行。我犯了同样的错误。根据 SFML 文档,此 class 还创建了它自己的上下文。 我可能不得不放弃组合这 2 个库并仅将 SFML 用于音频并且仅使用 Irrlicht 进行渲染。

简单:SFML 已经为 window 配置并在其上创建了 OpenGL 上下文。然后你将 window 传递给 Irrlicht,Irrlicht 尝试(再次)配置 window 用于它的目的但失败了,因为它已经被配置了。从技术上讲,创建额外的 OpenGL 上下文是完全可能的,但是创建一个 GLX window 作为另一个 GLX window 的子级(GLX windows 是常规 X11 windows 的特化)会导致问题.

您的解决方案是修补 SFML,使其不执行 "fancy" OpenGL window 配置和上下文创建,这也意味着它 returns 普通 X11 window 没有通常在其中为 OpenGL 创建的 GLX window(长话短说为什么这样做,主要与直接渲染问题有关)。

也就是说,将 Irrlicht 嵌入到宿主程序提供的 window 中是为了支持地图编辑器或类似的预览应用程序。