[C ++] [SFML 2.2]奇怪的性能问题 - 移动鼠标会降低CPU使用率

时间:2015-03-17 00:46:25

标签: c++ performance sfml


在过去的两周左右的时间里,我一直在制作2D地图编辑器,我遇到了一个非常奇怪的问题。当我试图优化我的代码时,例如,通过为真正应该开始使用的函数创建函数,我做了一些使我的CPU使用率达到顶峰的东西。我已经尝试评论出我认为很可能是罪魁祸首的代码的不同部分(滚动,渲染,其他计算),但无济于事。我认为这个问题可能只是试图从不同的函数中调用它们,但为了使它尽可能模块化,我真的很喜欢这个功能。

自从我将这个文件与之进行比较后,我所做的另一个重大改变是将所有变量移植到外部CPP文件中,因为将变量设置为全局变量使得从不同的变量中使用它变得更加容易功能。这可能是一个非常简单的问题,但我无法理解为什么会发生这种情况。

使用原始代码,我可以获得2-3%的CPU使用率。使用新代码,我得到大约20-35%,但是,如果我移动鼠标,它将下降到大约6-8%。

tl; dr:在尝试使用函数优化我的代码之后,我以某种方式杀死了我的性能,发现了一个奇怪的怪癖,移动鼠标大大降低了CPU使用率。

//Most variables are declared in an external file
void editorLoop()
{
/***************************/
//See what user wants to do
/***************************/
std::cout << "Would you like to [O]pen a file, make [N]ew one, or [E]xit?\n";
std::cin >> openOrNewFile;
/************************/
//Create a new file
/***********************/
if (openOrNewFile == 'n' || openOrNewFile == 'N')
{
    createMap();
}
/***********************/
//Quit the program
/**********************/
else if (openOrNewFile == 'e' || openOrNewFile == 'E')
{
    return;
}
/************************/
//Open an existing file
/***********************/
else if (openOrNewFile == 'o' || openOrNewFile == 'O')
{
    openMap();
}
/**********************/
//Invalid input
/*********************/
else
{
    std::cout << "Please enter a valid input!\n";
    main();
}


/***********************/
//Create the Window
/***********************/
sf::RenderWindow gameWindow(sf::VideoMode(screenSizeX, screenSizeY, mapTileSize), "Game");
//Artificially cap FPS to keep memory and CPU usage low
gameWindow.setFramerateLimit(60);
//view1's size is defined externally, same size as the gameWindow, though
view1.setCenter(screenSizeX / 2, screenSizeY / 2 + mapTileSize);

//Load GUI Elements
//I've found that trying to load these inside the setGUIElements function leads to the 
//texture memory to be deleted, so I'm left with white spaces
sf::Texture guiElements;
if (!guiElements.loadFromFile("Resources/guiElements.png"))
{
    std::cout << "Error loading guiElements.png\n";
}

sf::Font arial;
if (!arial.loadFromFile("Resources/arial.ttf"))
{
    std::cout << "Error loading arial.tff\n";
}
setGUIElements();
activeTileSprite.setTexture(tileTexture);
upArrowX.setTexture(guiElements);
downArrowX.setTexture(guiElements);
upArrowY.setTexture(guiElements);
downArrowY.setTexture(guiElements);
saveButton.setTexture(guiElements);
editBoxX.setFont(arial);
editBoxY.setFont(arial);

//Selector Rectangle
sf::RectangleShape Selected(sf::Vector2f(mapTileSize, mapTileSize));
Selected.setFillColor(sf::Color(0,0,0,0));
Selected.setOutlineThickness(2);
Selected.setOutlineColor(sf::Color(255,0,0));
Selected.setPosition(-1000,-1000);

//Set up toolbox string values
tempXTileValue = std::to_string((_ULonglong)xTileValue);
tempYTileValue = std::to_string((_ULonglong)yTileValue);

while (gameWindow.isOpen())
{   
    /************************************/
    //Mouse Input Defined Here
    /***********************************/
    //Get mouse position relative to the window
    sf::Vector2f mousePositionGlobal = gameWindow.mapPixelToCoords(sf::Mouse::getPosition(gameWindow));
    sf::Vector2i mousePositionLocal = sf::Mouse::getPosition(gameWindow);

    sf::Event event;
    while (gameWindow.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
        {
            gameWindow.close();
        }
        if (event.type == sf::Event::MouseButtonReleased)
        {
            if (event.key.code == sf::Mouse::Left)
            {
                std::cout << "Global Mouse Position:\n" << "X: " << mousePositionGlobal.x << " Y: " << mousePositionGlobal.y << "\n\n";
                std::cout << "Local Mouse Position:\n" << "X: " << mousePositionLocal.x << " Y: " << mousePositionLocal.y << "\n\n";

                int mouseX = mousePositionGlobal.x;
                int mouseY = mousePositionGlobal.y;

                //Move the selector rectangle to where the mouse clicked
                if ((mouseX >= 0) && (mouseY >= 0))
                {
                    if (mousePositionLocal.x > 900 && mousePositionLocal.x < 1100 && mousePositionLocal.y > 30 && mousePositionLocal.y < 286)
                    {

                    }
                    else
                    {
                        Selected.setPosition((mouseX/32)*32, (mouseY/32)*32);

                        /*****************************************/
                        //These lines define what tile to be placed
                        //NOTE: map[][]'s first [] is the Y axis, and the second [] is the X axis!!
                        /*****************************************/
                        map[mouseY/32*32/32][mouseX/32*32/32].x = yTileValue;
                        map[mouseY/32*32/32][mouseX/32*32/32].y = xTileValue;
                    }
                }

                /********************/
                //Toolbox stuff
                /********************/
                //This can probably be written better ...
                //Up Arrow X
                if ((mousePositionLocal.x > screenSizeX * 0.95) && (mousePositionLocal.x < screenSizeX * 0.95 + 32) && (mousePositionLocal.y > screenSizeY * 0.096) && (mousePositionLocal.y < screenSizeY * 0.096 + 32))
                {
                    if (xTileValue < 10)
                    {
                        xTileValue ++;
                        std::cout << xTileValue << std::endl;
                        tempXTileValue = std::to_string((_ULonglong)xTileValue);
                    }
                    else
                    {
                        xTileValue = 0;
                        std::cout << xTileValue << std::endl;
                        tempXTileValue = std::to_string((_ULonglong)xTileValue);
                    }
                }

                //Down Arrow X
                if ((mousePositionLocal.x > screenSizeX * 0.95) && (mousePositionLocal.x < screenSizeX * 0.95 + 32) && mousePositionLocal.y > 98 && mousePositionLocal.y < 130)
                {                   
                    if (xTileValue > 0)
                    {
                        xTileValue --;
                        std::cout << xTileValue << std::endl;
                        tempXTileValue = std::to_string((_ULonglong)xTileValue);
                    }
                    else
                    {
                        xTileValue = 10;
                        std::cout << xTileValue << std::endl;
                        tempXTileValue = std::to_string((_ULonglong)xTileValue);
                    }
                }

                //Up Arrow Y
                if ((mousePositionLocal.x > screenSizeX * 0.95) && (mousePositionLocal.x < screenSizeX * 0.95 + 32) && mousePositionLocal.y > 148 && mousePositionLocal.y < 180)
                {
                    if (yTileValue < 10)
                    {
                        yTileValue ++;
                        std::cout << yTileValue << std::endl;
                        tempYTileValue = std::to_string((_ULonglong)yTileValue);
                    }
                    else
                    {
                        yTileValue = 0;
                        std::cout << yTileValue << std::endl;
                        tempYTileValue = std::to_string((_ULonglong)yTileValue);
                    }
                }

                //Down Arrow Y
                if ((mousePositionLocal.x > screenSizeX * 0.95) && (mousePositionLocal.x < screenSizeX * 0.95 + 32) && mousePositionLocal.y > 188 && mousePositionLocal.y < 220)
                {
                    if (yTileValue > 0)
                    {
                        yTileValue --;
                        std::cout << yTileValue << std::endl;
                        tempYTileValue = std::to_string((_ULonglong)yTileValue);
                    }
                    else
                    {
                        yTileValue = 10;
                        std::cout << yTileValue << std::endl;
                        tempYTileValue = std::to_string((_ULonglong)yTileValue);
                    }
                }

                //Save Button
                if (mousePositionLocal.x > screenSizeX * 0.886 && mousePositionLocal.x < screenSizeX * 0.886 + 32 && mousePositionLocal.y > screenSizeY * 0.414 - 32 && mousePositionLocal.y < screenSizeY * 0.414)
                {
                    saveMap();
                }
            }

            if (event.key.code == sf::Mouse::Right)
            {
                Selected.setPosition(-1000, - 1000);
            }
        }
        if (event.type == sf::Event::KeyReleased)
        {
            if (event.key.code == sf::Keyboard::X)
            {
                showToolBox = !showToolBox;
            }
        }

        //View Managing
        viewCenter = view1.getCenter();
        viewSize = view1.getSize();

        /*************************/
        //Scrolling
        /************************/
        if ((mousePositionLocal.x > screenSizeX * 0.83) && (mousePositionLocal.x < screenSizeX) && (mousePositionLocal.y > mapTileSize) && (mousePositionLocal.y < screenSizeY * 0.43) && (showToolBox == true))
        {
            enableScroll = false;
        }
        else
        {
            enableScroll = true;
        }
        if ((mousePositionLocal.y < screenSizeY * 0.1) && (enableScroll == true))
        {
            //If the view would leave the map by scrolling; don't scroll
            if (viewCenter.y < mapTileSize * 10.7)
            {

            }
            else
            {
                scroll(0,-scrollSpeed);
            }
        }
        if ((mousePositionLocal.y > screenSizeY * 0.9) && (enableScroll == true))
        {
            if (viewCenter.y > ((mapSizeY * mapTileSize)) - (mapTileSize * 9))
            {

            }
            else
            {
                scroll(0,scrollSpeed);
            }
        }
        if (mousePositionLocal.x < screenSizeX * 0.1 && enableScroll == true)
        {
            if (viewCenter.x < (mapTileSize * 17))
            {

            }
            else
            {
                scroll(-scrollSpeed,0);
            }
        }
        if ((mousePositionLocal.x > screenSizeX * 0.9) && enableScroll == true)
        {
            if (viewCenter.x > (mapSizeX * mapTileSize) - (mapTileSize * 17))
            {

            }
            else
            {
                scroll(scrollSpeed,0);
            }
        }

        //Clear buffer
        gameWindow.clear();

        //The view has to be set in the draw function
        //for a reason that is not yet clear to me.
        gameWindow.setView(view1);

        //Add stuff to new buffer
        //Y loop
        for (int i = 0; i < map.size(); i++)
        {
            //X loop
            for (int j = 0; j < map[i].size(); j++)
            {
                if ((map[i][j].x != -1) && (map[i][j].y != -1))
                {
                    tiles.setPosition(j * mapTileSize, i * mapTileSize);
                    tiles.setTextureRect(sf::IntRect(map[i][j].x * mapTileSize, map[i][j].y * mapTileSize, mapTileSize, mapTileSize));
                    gameWindow.draw(tiles);
                }
            }
        }

        //Draw the Selector rectangle
        gameWindow.draw(Selected);

        if (showToolBox == true)
        {
            activeTileSprite.setTextureRect(sf::IntRect(mapTileSize*yTileValue, mapTileSize*xTileValue, mapTileSize, mapTileSize));

            //Draw toolbox
            gameWindow.draw(toolbarBox);
            gameWindow.draw(activeTileSprite);

            gameWindow.draw(upArrowX);
            gameWindow.draw(downArrowX);

            gameWindow.draw(upArrowY);
            gameWindow.draw(downArrowY);

            //Update the strings for X and Y here
            //Otherwise, it doesn't work for some reason
            editBoxX.setString("Y: " + tempXTileValue);
            editBoxY.setString("X: " + tempYTileValue);

            gameWindow.draw(editBoxX);
            gameWindow.draw(editBoxY);

            gameWindow.draw(saveButton);
        }
        //Render buffer
        gameWindow.display();
    }
}
openfile.close();
return;
}


如果我需要添加(或删除)任何信息,请告诉我。

我已经检查过,找不到任何类似的问题,所以希望我没有忽略任何事情,并正确解释了我的问题。
再次感谢,
helpMeLearnC ++

附:我应该提一下,我不是一个完全的初学者,但我也不是超级先进。

P.S.S.在尝试了Selbie建议之后,我注意到,相反自相矛盾的是,除非鼠标移动,否则我的游戏不会更新...现在我想知道为什么这会导致比没有移动时更好的性能...(或代码我使用不正常)。

P.S.S.S.在更多地弄乱了代码之后,我发现了我的想法:当鼠标移动时,gameWindow.pollEvent()优先于while(gameWindow.isOpen())循环,它执行绘图并且可能是什么杀了我的表演。现在问题变成了解决这个问题的最佳方法。

P.S.S.S.S.对于那些好奇的人,并假设我检查FPS的方法是正确的,我每秒渲染大约27,000帧(我的测试非常小,所以虽然我觉得它看起来有点高,但我不会完全抹黑它)。




通过将mousePositionGlobal和mousePositionLocal移动到pollEvent循环中,我能够将性能从大约20-35%降低到大约8-15%,并且当其余时间(约3%)下降时老鼠在动。所以,取得了进展,但我仍然不确定导致这种差异的原因......由于FPS已达到约35,000,我将假设我的计算代码是错误的。还有其他建议吗?

2 个答案:

答案 0 :(得分:1)

我愿意打赌你的CPU是四核的。您看到的CPU百分比来自Windows任务管理器(或使用Windows性能计数器的其他应用程序)。我怀疑这是因为你有一个单线程游戏循环,不停地运行,帧之间没有睡眠。因此,你的&#34; 20-35%&#34; CPU实际上是你的程序在你的芯片上占用一个完整的核心。这是任何想要以最高帧速率渲染的游戏循环的典型特征。

我怀疑在你的显卡上,&#34;移动鼠标&#34;与其他窗口游戏也产生类似的性能特征。移动鼠标时,Windows图形的优先级高于游戏窗口,并停止渲染游戏循环。因此,当鼠标移动时,您的代码仍在运行,但帧速率要低得多。由于图形系统必须处理游戏窗口之外的绘图请求,因此.draw()或.display()调用中的任何一个都被阻止。因此,您的游戏循环会阻塞并使用较少的CPU。

我的建议是在游戏窗口添加帧率计数器。显示在最后一秒累积的帧数的东西(需要一些额外的整数变量,时间函数和一些除法来计算帧速率)。我猜你在鼠标移动时会看到帧率计数器减速。

此外,在互联网上搜索&#34;代码分析工具&#34;。某些版本的Visual Studio已经内置。它们应该在代码占用大部分时间的地方。

答案 1 :(得分:0)

您想要锁定CPU的简单游戏循环可能类似于:

time last_render = now();
while( app_is_running ) {
  Event e;
  // eat all events.  Ideally, more important than rendering,
  // and should take only a little bit of time:
  while ( get_event(&e) ) {
    process_event(e); // <- note, does not render, should be **cheap**
  }
  // frame limit code:
  while (now()-last_render < frame_limit) {
    Event e;
    if (wait_for_event(&e, (frame_limit-delta)/2))
      process_event(e);
  }
  last_render = now(); // stamp before rendering
  render(); // also includes other game state updates.
}

现在,sfml似乎缺少wait_for_event(Event*, timeout)功能。这意味着你无法在仍然响应事件时进入睡眠状态#34;因此,睡眠会使您的事件处理无响应,或者您必须使用它,或者您需要多个线程。

我们可以创建一个线程安全队列,创建一个作业为waitForEvent的线程,并让它填充我们可以等待的队列,并在队列中休眠和超时(基本上可以解决看来是一个sfml缺乏症。)

或者我们可以轮询和控制CPU。离开我的手机!

或者我们可以忍受1帧用户输入响应延迟(我发现这是不可接受的)。

我不是游戏开发者,所以请大家采取这种建议。