SDL2 - SDL_PollEvent 在 Ubuntu/Mac OS X 上比 Windows 慢
SDL2 - SDL_PollEvent slower on Ubuntu/Mac OS X than Windows
我使用 SDL2 用 C++ 编写了一个 Space Invaders 模拟器,仅用于创建游戏 window 和播放声音 (sdl2_mixer)。在 Windows 上,模拟器以 60 FPS 的速度运行(我可以将此值更改为我想要的任何值并且它可以正常工作)但是如果我在 Ubuntu 或 Mac [=60= 上构建它] X 游戏无法玩(可能是想要的 FPS 的 10%)。
这有解释吗?
Ubuntu 上的渲染器名称:opengl
Windows 上的渲染器名称:direct3d
渲染器标志在 Ubuntu 和 Windows 上都是 0x0a。
编译:
g++ -std=c++11 main.cpp spaceinvadersmachine.cpp intel8080.cpp -lSDL2 -lSDL2_mixer -I/usr/include/SDL2 -I/usr/include/SDL2_mixer -D_REENTRANT -o spaceinvaders.app
这里是一些代码:
// 主循环
while (!quitEmulator)
{
// Handle events on queue
while (SDL_PollEvent(&events) == 1)
{
switch (events.type)
{
case SDL_QUIT:
{
// Game's window has been closed
quitEmulator = true;
break;
}
case SDL_KEYDOWN:
{
// Set magnification to 1x
if (events.key.keysym.sym == SDLK_1)
{
machine.magnificationFactor = 1.0;
machine.magnificationRequest = true;
}
// Save game request
if (events.key.keysym.sym == SDLK_2)
{
machine.saveGameRequest = true;
}
// Load game request
if (events.key.keysym.sym == SDLK_3)
{
machine.loadGameRequest = true;
}
// Set magnification to 4x
if (events.key.keysym.sym == SDLK_4)
{
machine.magnificationFactor = 2.0;
machine.magnificationRequest = true;
}
// Set bases dipswitch request
if (events.key.keysym.sym == SDLK_7)
{
machine.dipBasesRequest = true;
}
// Set bonus base dipswitch request
if (events.key.keysym.sym == SDLK_8)
{
machine.dipBonusBaseRequest = true;
}
// Set magnification to 9x
if (events.key.keysym.sym == SDLK_9)
{
machine.magnificationFactor = 3.0;
machine.magnificationRequest = true;
}
// Set coin informations dipswitch request
if (events.key.keysym.sym == SDLK_i)
{
machine.dipCoinInfoRequest = true;
}
// Game paused
if (events.key.keysym.sym == SDLK_p)
{
gamePaused = !gamePaused;
if (gamePaused)
cout << "INFO: Game paused!\n";
else
cout << "INFO: Game resumed!\n";
}
// Reset request
if (events.key.keysym.sym == SDLK_r)
{
machine.resetRequest = true;
}
// Change color mode
if (events.key.keysym.sym == SDLK_c)
{
machine.coloredFrame = !machine.coloredFrame;
if (machine.coloredFrame)
cout << "INFO: Color mode set to RGB\n";
else
cout << "INFO: Color mode set to B/W\n";
}
break;
}
}
}
if (!gamePaused)
{
// Check keyboard inputs
SDL_PumpEvents();
machine.checkKeyboardInput();
// Set bases dipswitch if requested
if (machine.dipBasesRequest)
{
machine.setBasesDipswitch();
}
// Set bonus base dipswitch if requested
if (machine.dipBonusBaseRequest)
{
machine.setBonusBaseDipswitch();
}
// Set coin informations dipswitch if requested
if (machine.dipCoinInfoRequest)
{
machine.setCoinInfoDipswitch();
}
// Change magnification factor if requested
if (machine.magnificationRequest)
{
machine.setMagnification();
}
// Check for interrupt
if (CPUInterruptDeltaCycles >= cyclePerInterrupt)
{
CPUInterruptDeltaCycles = 0;
machine.interruptRequest = true;
if (machine.lastInterruptNumber == 1) // RST 1
{
machine.interruptNumber = 2; // RST 2
}
else // RST 2
{
machine.interruptNumber = 1; // RST 1
}
machine.lastInterruptNumber = machine.interruptNumber;
}
else
{
machine.interruptRequest = false;
}
// Execute next instruction
CPU.executeROM(machine.interruptRequest, machine.interruptNumber);
// Increments CPU's cycle counters
CPUDeltaCycles += CPU.CPUCycles;
CPUInterruptDeltaCycles += CPU.CPUCycles;
// Check if OPCode is known
if (CPU.unimplementedOPCode)
quitEmulator = true;
// Check for I/O
machine.checkIO();
// Check if a frame must be drawn
// Save and Load if requested
if (CPUDeltaCycles >= cyclePerFrame)
{
CPUDeltaCycles = 0;
machine.createFrame();
machine.showFrame();
drewFrames += 1;
while ((SDL_GetTicks() - lastFPSSynchronization) < frameTimeInterval)
{
// Waiting
;
}
lastFPSSynchronization = SDL_GetTicks();
if (machine.saveGameRequest)
{
machine.saveGameRequest = false;
machine.saveGame(CPUInterruptDeltaCycles);
}
if (machine.loadGameRequest)
{
machine.loadGameRequest = false;
machine.loadGame(CPUInterruptDeltaCycles);
// Remove pending keyboard events
SDL_FlushEvent(SDL_KEYDOWN);
SDL_FlushEvent(SDL_KEYUP);
SDL_PumpEvents();
}
}
// Reset if requested
if (machine.resetRequest)
{
machine.resetRequest = false;
machine.resetMachine();
}
}
}
我试图注释掉创建框架的部分(因此 window 上没有框架更新)并且游戏仍然很慢(音频仍在播放),所以问题不在框架内创建函数。
我发现问题出在 SDL_PumpEvents
和 SDL_PollEvent
函数上。可以在主循环中消除第一个,但显然不能消除第二个。为什么轮询事件太慢? Windows 和 Ubuntu
的事件数相同
您每帧都在创建一个新的表面和一个新的纹理。是的,它会很慢,创建纹理是一个非常慢的操作。不要经常这样做。
如果需要直接写像素,创建一次大小合适的贴图(用SDL_TEXTUREACCESS_STREAMING
)然后用SDL_LockTexture
和SDL_UnlockTexture
更新(注意:不要不要为此使用 SDL_UpdateTexture
,它实际上并不比重新创建纹理性能更好(如果有的话))。我猜 windows 版本很快 "by accident" 因为你的驱动程序实现注意到你在做一些奇怪的事情并且悄悄地忽略你告诉它做的事情并更快地做一些事情。
当然,如果不查看代码,很难说这是 唯一的 问题,但这很可能是一个(甚至可能是)瓶颈。
正如我之前所写,问题是 SDL_PollEvent 函数在 Ubuntu 上比在 Windows.
上慢
这让我注意到了主循环,我发现了一个问题:新事件轮询是在主循环的每个循环上完成的,而循环而不是在每一帧上(每帧 60 次)秒)所以这会严重减慢执行速度。这在 Windows 上不是问题,因为有足够的时间每 16 毫秒绘制一帧,但在 Ubuntu 上是一个问题,其中 SDL_PollEvent 较慢并且帧绘制不在时间.
所以 我移动了轮询处理程序以便每秒仅检查 60 次用户输入。不要在没有强大控制的情况下将轮询函数放在主 while 循环中!
这里是新代码:
while (!quitEmulator)
{
if (!gamePaused)
{
// Check keyboard inputs
machine.checkKeyboardInput();
// Set bases dipswitch if requested
if (machine.dipBasesRequest)
{
machine.setBasesDipswitch();
}
// Set bonus base dipswitch if requested
if (machine.dipBonusBaseRequest)
{
machine.setBonusBaseDipswitch();
}
// Set coin informations dipswitch if requested
if (machine.dipCoinInfoRequest)
{
machine.setCoinInfoDipswitch();
}
// Change magnification factor if requested
if (machine.magnificationRequest)
{
machine.setMagnification();
}
// Check for interrupt
if (CPUInterruptDeltaCycles >= cyclePerInterrupt)
{
CPUInterruptDeltaCycles = 0;
machine.interruptRequest = true;
if (machine.lastInterruptNumber == 1) // RST 1
{
machine.interruptNumber = 2; // RST 2
}
else // RST 2
{
machine.interruptNumber = 1; // RST 1
}
machine.lastInterruptNumber = machine.interruptNumber;
}
else
{
machine.interruptRequest = false;
}
// Execute next instruction
CPU.executeROM(machine.interruptRequest, machine.interruptNumber);
// Increments CPU's cycle counters
CPUDeltaCycles += CPU.CPUCycles;
CPUInterruptDeltaCycles += CPU.CPUCycles;
// Check if OPCode is known
if (CPU.unimplementedOPCode)
quitEmulator = true;
// Check for I/O
machine.checkIO();
// Check if a frame must be drawn
// Save and Load if requested
if (CPUDeltaCycles >= cyclePerFrame)
{
CPUDeltaCycles = 0;
// Handle events on queue
while (SDL_PollEvent(&events) == 1)
{
switch (events.type)
{
case SDL_QUIT:
{
// Game's window has been closed
quitEmulator = true;
break;
}
case SDL_KEYDOWN:
{
// Set magnification to 1x
if (events.key.keysym.sym == SDLK_1)
{
machine.magnificationFactor = 1.0;
machine.magnificationRequest = true;
}
// Save game request
if (events.key.keysym.sym == SDLK_2)
{
machine.saveGameRequest = true;
}
// Load game request
if (events.key.keysym.sym == SDLK_3)
{
machine.loadGameRequest = true;
}
// Set magnification to 4x
if (events.key.keysym.sym == SDLK_4)
{
machine.magnificationFactor = 2.0;
machine.magnificationRequest = true;
}
// Set bases dipswitch request
if (events.key.keysym.sym == SDLK_7)
{
machine.dipBasesRequest = true;
}
// Set bonus base dipswitch request
if (events.key.keysym.sym == SDLK_8)
{
machine.dipBonusBaseRequest = true;
}
// Set magnification to 9x
if (events.key.keysym.sym == SDLK_9)
{
machine.magnificationFactor = 3.0;
machine.magnificationRequest = true;
}
// Set coin informations dipswitch request
if (events.key.keysym.sym == SDLK_i)
{
machine.dipCoinInfoRequest = true;
}
// Game paused
if (events.key.keysym.sym == SDLK_p)
{
gamePaused = !gamePaused;
if (gamePaused)
cout << "INFO: Game paused!\n";
else
cout << "INFO: Game resumed!\n";
}
// Reset request
if (events.key.keysym.sym == SDLK_r)
{
machine.resetRequest = true;
}
// Change color mode
if (events.key.keysym.sym == SDLK_c)
{
machine.coloredFrame = !machine.coloredFrame;
if (machine.coloredFrame)
cout << "INFO: Color mode set to RGB\n";
else
cout << "INFO: Color mode set to B/W\n";
}
break;
}
}
}
machine.createFrame();
machine.showFrame();
drewFrames += 1;
while ((SDL_GetTicks() - lastFPSSynchronization) < frameTimeInterval)
{
// Waiting
;
}
lastFPSSynchronization = SDL_GetTicks();
if (machine.saveGameRequest)
{
machine.saveGameRequest = false;
machine.saveGame(CPUInterruptDeltaCycles);
}
if (machine.loadGameRequest)
{
machine.loadGameRequest = false;
machine.loadGame(CPUInterruptDeltaCycles);
// Remove pending keyboard events
SDL_FlushEvent(SDL_KEYDOWN);
SDL_FlushEvent(SDL_KEYUP);
SDL_PumpEvents();
}
}
// Reset if requested
if (machine.resetRequest)
{
machine.resetRequest = false;
machine.resetMachine();
}
}
}
我使用 SDL2 用 C++ 编写了一个 Space Invaders 模拟器,仅用于创建游戏 window 和播放声音 (sdl2_mixer)。在 Windows 上,模拟器以 60 FPS 的速度运行(我可以将此值更改为我想要的任何值并且它可以正常工作)但是如果我在 Ubuntu 或 Mac [=60= 上构建它] X 游戏无法玩(可能是想要的 FPS 的 10%)。
这有解释吗?
Ubuntu 上的渲染器名称:opengl
Windows 上的渲染器名称:direct3d
渲染器标志在 Ubuntu 和 Windows 上都是 0x0a。
编译:
g++ -std=c++11 main.cpp spaceinvadersmachine.cpp intel8080.cpp -lSDL2 -lSDL2_mixer -I/usr/include/SDL2 -I/usr/include/SDL2_mixer -D_REENTRANT -o spaceinvaders.app
这里是一些代码:
// 主循环
while (!quitEmulator)
{
// Handle events on queue
while (SDL_PollEvent(&events) == 1)
{
switch (events.type)
{
case SDL_QUIT:
{
// Game's window has been closed
quitEmulator = true;
break;
}
case SDL_KEYDOWN:
{
// Set magnification to 1x
if (events.key.keysym.sym == SDLK_1)
{
machine.magnificationFactor = 1.0;
machine.magnificationRequest = true;
}
// Save game request
if (events.key.keysym.sym == SDLK_2)
{
machine.saveGameRequest = true;
}
// Load game request
if (events.key.keysym.sym == SDLK_3)
{
machine.loadGameRequest = true;
}
// Set magnification to 4x
if (events.key.keysym.sym == SDLK_4)
{
machine.magnificationFactor = 2.0;
machine.magnificationRequest = true;
}
// Set bases dipswitch request
if (events.key.keysym.sym == SDLK_7)
{
machine.dipBasesRequest = true;
}
// Set bonus base dipswitch request
if (events.key.keysym.sym == SDLK_8)
{
machine.dipBonusBaseRequest = true;
}
// Set magnification to 9x
if (events.key.keysym.sym == SDLK_9)
{
machine.magnificationFactor = 3.0;
machine.magnificationRequest = true;
}
// Set coin informations dipswitch request
if (events.key.keysym.sym == SDLK_i)
{
machine.dipCoinInfoRequest = true;
}
// Game paused
if (events.key.keysym.sym == SDLK_p)
{
gamePaused = !gamePaused;
if (gamePaused)
cout << "INFO: Game paused!\n";
else
cout << "INFO: Game resumed!\n";
}
// Reset request
if (events.key.keysym.sym == SDLK_r)
{
machine.resetRequest = true;
}
// Change color mode
if (events.key.keysym.sym == SDLK_c)
{
machine.coloredFrame = !machine.coloredFrame;
if (machine.coloredFrame)
cout << "INFO: Color mode set to RGB\n";
else
cout << "INFO: Color mode set to B/W\n";
}
break;
}
}
}
if (!gamePaused)
{
// Check keyboard inputs
SDL_PumpEvents();
machine.checkKeyboardInput();
// Set bases dipswitch if requested
if (machine.dipBasesRequest)
{
machine.setBasesDipswitch();
}
// Set bonus base dipswitch if requested
if (machine.dipBonusBaseRequest)
{
machine.setBonusBaseDipswitch();
}
// Set coin informations dipswitch if requested
if (machine.dipCoinInfoRequest)
{
machine.setCoinInfoDipswitch();
}
// Change magnification factor if requested
if (machine.magnificationRequest)
{
machine.setMagnification();
}
// Check for interrupt
if (CPUInterruptDeltaCycles >= cyclePerInterrupt)
{
CPUInterruptDeltaCycles = 0;
machine.interruptRequest = true;
if (machine.lastInterruptNumber == 1) // RST 1
{
machine.interruptNumber = 2; // RST 2
}
else // RST 2
{
machine.interruptNumber = 1; // RST 1
}
machine.lastInterruptNumber = machine.interruptNumber;
}
else
{
machine.interruptRequest = false;
}
// Execute next instruction
CPU.executeROM(machine.interruptRequest, machine.interruptNumber);
// Increments CPU's cycle counters
CPUDeltaCycles += CPU.CPUCycles;
CPUInterruptDeltaCycles += CPU.CPUCycles;
// Check if OPCode is known
if (CPU.unimplementedOPCode)
quitEmulator = true;
// Check for I/O
machine.checkIO();
// Check if a frame must be drawn
// Save and Load if requested
if (CPUDeltaCycles >= cyclePerFrame)
{
CPUDeltaCycles = 0;
machine.createFrame();
machine.showFrame();
drewFrames += 1;
while ((SDL_GetTicks() - lastFPSSynchronization) < frameTimeInterval)
{
// Waiting
;
}
lastFPSSynchronization = SDL_GetTicks();
if (machine.saveGameRequest)
{
machine.saveGameRequest = false;
machine.saveGame(CPUInterruptDeltaCycles);
}
if (machine.loadGameRequest)
{
machine.loadGameRequest = false;
machine.loadGame(CPUInterruptDeltaCycles);
// Remove pending keyboard events
SDL_FlushEvent(SDL_KEYDOWN);
SDL_FlushEvent(SDL_KEYUP);
SDL_PumpEvents();
}
}
// Reset if requested
if (machine.resetRequest)
{
machine.resetRequest = false;
machine.resetMachine();
}
}
}
我试图注释掉创建框架的部分(因此 window 上没有框架更新)并且游戏仍然很慢(音频仍在播放),所以问题不在框架内创建函数。
我发现问题出在 SDL_PumpEvents
和 SDL_PollEvent
函数上。可以在主循环中消除第一个,但显然不能消除第二个。为什么轮询事件太慢? Windows 和 Ubuntu
您每帧都在创建一个新的表面和一个新的纹理。是的,它会很慢,创建纹理是一个非常慢的操作。不要经常这样做。
如果需要直接写像素,创建一次大小合适的贴图(用SDL_TEXTUREACCESS_STREAMING
)然后用SDL_LockTexture
和SDL_UnlockTexture
更新(注意:不要不要为此使用 SDL_UpdateTexture
,它实际上并不比重新创建纹理性能更好(如果有的话))。我猜 windows 版本很快 "by accident" 因为你的驱动程序实现注意到你在做一些奇怪的事情并且悄悄地忽略你告诉它做的事情并更快地做一些事情。
当然,如果不查看代码,很难说这是 唯一的 问题,但这很可能是一个(甚至可能是)瓶颈。
正如我之前所写,问题是 SDL_PollEvent 函数在 Ubuntu 上比在 Windows.
上慢这让我注意到了主循环,我发现了一个问题:新事件轮询是在主循环的每个循环上完成的,而循环而不是在每一帧上(每帧 60 次)秒)所以这会严重减慢执行速度。这在 Windows 上不是问题,因为有足够的时间每 16 毫秒绘制一帧,但在 Ubuntu 上是一个问题,其中 SDL_PollEvent 较慢并且帧绘制不在时间.
所以 我移动了轮询处理程序以便每秒仅检查 60 次用户输入。不要在没有强大控制的情况下将轮询函数放在主 while 循环中!
这里是新代码:
while (!quitEmulator)
{
if (!gamePaused)
{
// Check keyboard inputs
machine.checkKeyboardInput();
// Set bases dipswitch if requested
if (machine.dipBasesRequest)
{
machine.setBasesDipswitch();
}
// Set bonus base dipswitch if requested
if (machine.dipBonusBaseRequest)
{
machine.setBonusBaseDipswitch();
}
// Set coin informations dipswitch if requested
if (machine.dipCoinInfoRequest)
{
machine.setCoinInfoDipswitch();
}
// Change magnification factor if requested
if (machine.magnificationRequest)
{
machine.setMagnification();
}
// Check for interrupt
if (CPUInterruptDeltaCycles >= cyclePerInterrupt)
{
CPUInterruptDeltaCycles = 0;
machine.interruptRequest = true;
if (machine.lastInterruptNumber == 1) // RST 1
{
machine.interruptNumber = 2; // RST 2
}
else // RST 2
{
machine.interruptNumber = 1; // RST 1
}
machine.lastInterruptNumber = machine.interruptNumber;
}
else
{
machine.interruptRequest = false;
}
// Execute next instruction
CPU.executeROM(machine.interruptRequest, machine.interruptNumber);
// Increments CPU's cycle counters
CPUDeltaCycles += CPU.CPUCycles;
CPUInterruptDeltaCycles += CPU.CPUCycles;
// Check if OPCode is known
if (CPU.unimplementedOPCode)
quitEmulator = true;
// Check for I/O
machine.checkIO();
// Check if a frame must be drawn
// Save and Load if requested
if (CPUDeltaCycles >= cyclePerFrame)
{
CPUDeltaCycles = 0;
// Handle events on queue
while (SDL_PollEvent(&events) == 1)
{
switch (events.type)
{
case SDL_QUIT:
{
// Game's window has been closed
quitEmulator = true;
break;
}
case SDL_KEYDOWN:
{
// Set magnification to 1x
if (events.key.keysym.sym == SDLK_1)
{
machine.magnificationFactor = 1.0;
machine.magnificationRequest = true;
}
// Save game request
if (events.key.keysym.sym == SDLK_2)
{
machine.saveGameRequest = true;
}
// Load game request
if (events.key.keysym.sym == SDLK_3)
{
machine.loadGameRequest = true;
}
// Set magnification to 4x
if (events.key.keysym.sym == SDLK_4)
{
machine.magnificationFactor = 2.0;
machine.magnificationRequest = true;
}
// Set bases dipswitch request
if (events.key.keysym.sym == SDLK_7)
{
machine.dipBasesRequest = true;
}
// Set bonus base dipswitch request
if (events.key.keysym.sym == SDLK_8)
{
machine.dipBonusBaseRequest = true;
}
// Set magnification to 9x
if (events.key.keysym.sym == SDLK_9)
{
machine.magnificationFactor = 3.0;
machine.magnificationRequest = true;
}
// Set coin informations dipswitch request
if (events.key.keysym.sym == SDLK_i)
{
machine.dipCoinInfoRequest = true;
}
// Game paused
if (events.key.keysym.sym == SDLK_p)
{
gamePaused = !gamePaused;
if (gamePaused)
cout << "INFO: Game paused!\n";
else
cout << "INFO: Game resumed!\n";
}
// Reset request
if (events.key.keysym.sym == SDLK_r)
{
machine.resetRequest = true;
}
// Change color mode
if (events.key.keysym.sym == SDLK_c)
{
machine.coloredFrame = !machine.coloredFrame;
if (machine.coloredFrame)
cout << "INFO: Color mode set to RGB\n";
else
cout << "INFO: Color mode set to B/W\n";
}
break;
}
}
}
machine.createFrame();
machine.showFrame();
drewFrames += 1;
while ((SDL_GetTicks() - lastFPSSynchronization) < frameTimeInterval)
{
// Waiting
;
}
lastFPSSynchronization = SDL_GetTicks();
if (machine.saveGameRequest)
{
machine.saveGameRequest = false;
machine.saveGame(CPUInterruptDeltaCycles);
}
if (machine.loadGameRequest)
{
machine.loadGameRequest = false;
machine.loadGame(CPUInterruptDeltaCycles);
// Remove pending keyboard events
SDL_FlushEvent(SDL_KEYDOWN);
SDL_FlushEvent(SDL_KEYUP);
SDL_PumpEvents();
}
}
// Reset if requested
if (machine.resetRequest)
{
machine.resetRequest = false;
machine.resetMachine();
}
}
}