SDL2 - SDL_PollEvent медленнее в Ubuntu / Mac OS X, чем в Windows - PullRequest
0 голосов
/ 01 октября 2019

Я написал эмулятор Space Invaders на C ++, используя SDL2 только для создания игрового окна и для воспроизведения звуков (sdl2_mixer). В Windows эмулятор работает со скоростью 60 FPS (я могу изменить это значение на любое, которое я хочу, и оно работает без каких-либо проблем), но если я собираю его на Ubuntu или Mac OS X, игра не воспроизводится (возможно, 10% от требуемого FPS).

Есть ли объяснение этому?

  • Имя рендерера в Ubuntu: opengl

  • Имя рендерера в Windows: direct3d

  • Флажки рендерера * 0x0a на Ubuntu, чем на Windows.

Скомпилировано с:

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

Вот некоторый код:

// MAIN loop

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();
        }
    }
}

Я попытался закомментировать часть, где создается кадр (поэтому обновление окна в окне не выполняется)и игра остается медленной (звук все еще воспроизводится), поэтому проблема не в функции создания кадра.

Я обнаружил, что проблема в функциях SDL_PumpEvents и SDL_PollEvent. Можно исключить первый в основном цикле, но, очевидно, не второй. Почему опросы проводятся слишком медленно? Количество событий одинаково как в Windows, так и в Ubuntu

Ответы [ 2 ]

0 голосов
/ 14 октября 2019

Как я уже писал ранее, проблема в том, что Функция 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();
        }
    }
}
0 голосов
/ 01 октября 2019

Вы создаете новую поверхность и новую текстуру каждый кадр. Да, это будет медленно, создание текстур - смехотворно медленная операция. Не делайте это так часто.

Если вам нужно написать пиксели напрямую, создайте текстуру правильного размера один раз (с SDL_TEXTUREACCESS_STREAMING), а затем используйте SDL_LockTexture и SDL_UnlockTexture для ее обновления (примечание: не используйте SDL_UpdateTexture для этого, это не так уж много - если вообще вообще - лучше, чем воссоздание текстуры с точки зрения производительности). Я предполагаю, что версия для Windows быстрая "случайно", потому что ваша реализация драйвера замечает, что вы делаете что-то странное, и тихо игнорирует то, что вы говорите, и делает что-то быстрее.

Конечно, не глядяв коде трудно сказать, что это проблема only , но это, скорее всего, будет (или, может быть, даже) узким местом.

...