Почему моя игра UWP медленнее в выпуске, чем в режиме отладки? - PullRequest
0 голосов
/ 17 июня 2019

Я пытаюсь сделать игру UWP, и я столкнулся с проблемой, когда моя игра намного медленнее в режиме релиза, чем в режиме отладки.

Моя игра будет отображать 3D-вид (мастер-стиль Dungeon) и иметь часть пользовательского интерфейса, которая рисует поверх 3D-вида. Поскольку трехмерное изображение может замедляться до небольшого количества кадров в секунду (FPS), я решил, чтобы моя игра всегда выполняла часть пользовательского интерфейса со скоростью 60 FPS.

Вот как выглядит основной игровой цикл в некотором псевдокоде:

Gameloop start
  Update game datas
  copy actual finished 3D view from buffer to screen
  draw UI part
  3D view loop start
    If no more time to draw more textures on the 3D view exit 3D view loop
    Draw one texture to 3D view buffer
  3D view loop end --> 3D view loop start
Gameloop end --> Gameloop start

Вот актуальные функции обновления и рендеринга:

    void Dungeons_of_NargothMain::Update() 
    {
        m_ritonTimer.startTimer(static_cast<int>(E_RITON_TIMER::UI));

        m_ritonTimer.frameCountPlusOne((int)E_RITON_TIMER::UI_FRAME_COUNT);
        m_ritonTimer.manageFramesPerSecond((int)E_RITON_TIMER::UI_FRAME_COUNT);

        m_ritonTimer.manageFramesPerSecond((int)E_RITON_TIMER::LABY_FRAME_COUNT);

        if (m_sceneRenderer->m_numberTotalOfTexturesToDraw == 0 ||
            m_sceneRenderer->m_numberTotalOfTexturesToDraw <= m_sceneRenderer->m_numberOfTexturesDrawn)
        {
            m_sceneRenderer->m_numberTotalOfTexturesToDraw = 150000;
            m_sceneRenderer->m_numberOfTexturesDrawn = 0;
        }

    }

    // RENDER
    bool Dungeons_of_NargothMain::Render() 
    {
        //********************************//
        // Render UI part here            //
        //********************************//


        //**********************************//
        // Render 3D view to 960X540 screen //
        //**********************************//
        m_sceneRenderer->setRenderTargetTo960X540Screen(); // 3D view buffer screen

        bool screen960GotFullDrawn = false;
        bool stillenoughTimeLeft = true;

        while (stillenoughTimeLeft && (!screen960GotFullDrawn))
        {
            stillenoughTimeLeft = m_ritonTimer.enoughTimeForOneMoreTexture((int)E_RITON_TIMER::UI);
            screen960GotFullDrawn = m_sceneRenderer->renderNextTextureTo960X540Screen();
        }

        if (screen960GotFullDrawn)
            m_ritonTimer.frameCountPlusOne((int)E_RITON_TIMER::LABY_FRAME_COUNT);

        return true;
    }

Я удалил то, что не нужно.

Вот часть таймера (RitonTimer):


    #pragma once

    #include "pch.h"
    #include <wrl.h>
    #include "RitonTimer.h"

    Dungeons_of_Nargoth::RitonTimer::RitonTimer()
    {
        initTimer();
        if (!QueryPerformanceCounter(&m_qpcGameStartTime))
        {
            throw ref new Platform::FailureException();
        }
    }

    void Dungeons_of_Nargoth::RitonTimer::startTimer(int timerIndex)
    {
        if (!QueryPerformanceCounter(&m_qpcNowTime))
        {
            throw ref new Platform::FailureException();
        }
        m_qpcStartTime[timerIndex] = m_qpcNowTime.QuadPart;
        m_framesPerSecond[timerIndex] = 0;
        m_frameCount[timerIndex] = 0;
    }

    void Dungeons_of_Nargoth::RitonTimer::resetTimer(int timerIndex)
    {
        if (!QueryPerformanceCounter(&m_qpcNowTime))
        {
            throw ref new Platform::FailureException();
        }
        m_qpcStartTime[timerIndex] = m_qpcNowTime.QuadPart;
        m_framesPerSecond[timerIndex] = m_frameCount[timerIndex];
        m_frameCount[timerIndex] = 0;
    }

    void Dungeons_of_Nargoth::RitonTimer::frameCountPlusOne(int timerIndex)
    {
        m_frameCount[timerIndex]++;
    }

    void Dungeons_of_Nargoth::RitonTimer::manageFramesPerSecond(int timerIndex)
    {
        if (!QueryPerformanceCounter(&m_qpcNowTime))
        {
            throw ref new Platform::FailureException();
        }
        m_qpcDeltaTime = m_qpcNowTime.QuadPart - m_qpcStartTime[timerIndex];

        if (m_qpcDeltaTime >= m_qpcFrequency.QuadPart)
        {
            m_framesPerSecond[timerIndex] = m_frameCount[timerIndex];
            m_frameCount[timerIndex] = 0;
            m_qpcStartTime[timerIndex] += m_qpcFrequency.QuadPart;
            if ((m_qpcStartTime[timerIndex] + m_qpcFrequency.QuadPart) < m_qpcNowTime.QuadPart)
                m_qpcStartTime[timerIndex] = m_qpcNowTime.QuadPart - m_qpcFrequency.QuadPart;
        }
    }

    void Dungeons_of_Nargoth::RitonTimer::initTimer()
    {
        if (!QueryPerformanceFrequency(&m_qpcFrequency))
        {
            throw ref new Platform::FailureException();
        }

        m_qpcOneFrameTime = m_qpcFrequency.QuadPart / 60;
        m_qpc5PercentOfOneFrameTime = m_qpcOneFrameTime / 20;
        m_qpc10PercentOfOneFrameTime = m_qpcOneFrameTime / 10;
        m_qpc95PercentOfOneFrameTime = m_qpcOneFrameTime - m_qpc5PercentOfOneFrameTime;
        m_qpc90PercentOfOneFrameTime = m_qpcOneFrameTime - m_qpc10PercentOfOneFrameTime;
        m_qpc80PercentOfOneFrameTime = m_qpcOneFrameTime - m_qpc10PercentOfOneFrameTime - m_qpc10PercentOfOneFrameTime;
        m_qpc70PercentOfOneFrameTime = m_qpcOneFrameTime - m_qpc10PercentOfOneFrameTime - m_qpc10PercentOfOneFrameTime - m_qpc10PercentOfOneFrameTime;
        m_qpc60PercentOfOneFrameTime = m_qpc70PercentOfOneFrameTime - m_qpc10PercentOfOneFrameTime;
        m_qpc50PercentOfOneFrameTime = m_qpc60PercentOfOneFrameTime - m_qpc10PercentOfOneFrameTime;
        m_qpc45PercentOfOneFrameTime = m_qpc50PercentOfOneFrameTime - m_qpc5PercentOfOneFrameTime;
    }


    bool Dungeons_of_Nargoth::RitonTimer::enoughTimeForOneMoreTexture(int timerIndex)
    {
        while (!QueryPerformanceCounter(&m_qpcNowTime));

        m_qpcDeltaTime = m_qpcNowTime.QuadPart - m_qpcStartTime[timerIndex];

        if (m_qpcDeltaTime < m_qpc45PercentOfOneFrameTime)
            return true;
        else
            return false;
    }

В режиме отладки пользовательский интерфейс игры работает на скорости 60 кадров в секунду, а 3D-вид на моем компьютере составляет около 1 кадра в секунду. Но даже там я не уверен, почему я должен остановить рисование текстур на 45% одного игрового времени и вызвать подарок, чтобы получить 60 FPS, если я подожду дольше, я получу только 30 FPS. (эта доблесть установлена ​​в "достаточное время дляOneMoreTexture ()" в RitonTimer.

В режиме Release он резко падает, имея около 10 FPS для части пользовательского интерфейса, 1 FPS для 3D-части. Я пытался выяснить, почему за последние 2 дня не нашел его.

Также у меня есть еще один маленький вопрос: как мне сказать Visual Studio, что моя игра на самом деле игра, а не приложение? Или Microsoft делает «переключение», когда я отправляю свою игру в их магазин?

Здесь я поместил свою игру на свой OneDrive, чтобы каждый мог загрузить исходные файлы и попытаться скомпилировать их, и посмотреть, получите ли вы те же результаты, что и у меня:

Ссылка OneDrive: https://1drv.ms/f/s!Aj7wxGmZTdftgZAZT5YAbLDxbtMNVg

компиляция в режиме отладки x64 или x64 Release.

ОБНОВЛЕНИЕ:

Мне кажется, я нашел объяснение, почему моя игра медленнее в режиме релиза. Процессор, вероятно, не ожидает выполнения инструкции рисования, а просто добавляет ее в список, который будет перенаправлен на графический процессор в своем собственном темпе в отдельной задаче (или, возможно, графический процессор выполняет это кеширование самостоятельно). Это объяснило бы все это.

Мой план состоял в том, чтобы сначала нарисовать пользовательский интерфейс, а затем нарисовать как можно больше текстур из трехмерного изображения, пока не пройдет 95% времени кадра 1/60 секунды, а затем представить его в цепочку обмена. Пользовательский интерфейс всегда будет на скорости 60 кадров в секунду, а трехмерное изображение будет настолько быстрым, насколько позволяет система (также на скорости 60 кадров в секунду, если все это может быть нарисовано за 95% времени кадра). Это не сработало, потому что оно, вероятно, кешировало все инструкции, которые имел мой трехмерный вид (я тестировал с 150000 БОЛЬШИМИ инструкциями рисования текстуры для трехмерного вида) за один кадр, и поэтому, конечно, пользовательский интерфейс был таким же медленным, как и трехмерный вид при конец или близко к.

Именно поэтому даже в режиме отладки я не получал 60FPS, когда я ждал 95% времени кадра, мне приходилось ждать 45% времени кадра, чтобы получить свои 60 FPS, которые я хотел получить для пользовательского интерфейса.

Я проверил его с более низким значением в режиме релиза, чтобы проверить эту теорию, и действительно, я также получаю 60 FPS для интерфейса пользователя, когда я останавливаю рисование только на 15% времени кадра.

Я думал, что это работает только в DirectX12.

1 Ответ

2 голосов
/ 18 июня 2019

«Как мне сказать Visual Studio, что моя игра на самом деле игра, а не приложение» - нет никакой разницы, игра - это приложение.

Ваш код работает на скорости 300-400 кадров в секунду в режиме отладки.

Сначала я прокомментировал ваш код, который проверяет, есть ли у вас время для рендеринга другой текстуры. Не делай этого. Все, что видит игрок, должно отображаться в одном кадре. Если ваш кадр занимает более 16 мс (с целью 60 кадров в секунду), ищите дорогостоящие операции или повторные вызовы, что может привести к неожиданным затратам. Ищите код, который может делать что-то несколько раз, когда это нужно сделать только один раз за кадр или за изменение размера. и т.д.

Итак, проблема в том, что вы рендерили очень большие текстуры и множество из них. Вы хотите избежать перерисовки (рендеринг пикселя там, где вы уже рендерили пиксель). Вы можете немного переиграть, и это иногда предпочтительнее, чем педантизм. Но вы рисовали 1000x2000 текстур снова и снова. Итак, вы абсолютно убивали пиксельный шейдер. Он просто не может рендерить столько пикселей. Я не стал смотреть на код, который пытается управлять рендерингом текстур на основе оставшегося времени кадра. Для того, что вы пытаетесь сделать, это не поможет.

Внутри вашего метода рендера закомментируйте секции while и if / else и используйте его для рисования массива ваших текстур.

// set sprite dimensions
int w = 64, h = 64;
for (int y = 0; y < 16; y++)
{
    for (int x = 0; x < 16; x++)
    {

        m_sceneRenderer->renderNextTextureTo960X540Screen(x*64, y*64, w, h);
    }
}

и в RenderNextTextureToScreen (int x, int y, int w, int h) ..

    m_squareBuffer.sizeX = w; // 1000;
    m_squareBuffer.sizeY = h; // 2000;
    m_squareBuffer.posX = x; // (float)(rand() % 1920);
    m_squareBuffer.posY = y; // (float)(rand() % 1080);

Посмотрите, как этот код рендерит текстуры гораздо меньшего размера, текстуры размером 64x64 и не перерисовываются.

И просто знайте, что GPU не все мощные, он может многое сделать, если вы правильно его используете, но если вы просто бросаете в него сумасшедшие операции, вы можете остановить его, как с процессором , Поэтому постарайтесь визуализировать вещи, которые «выглядят нормально», которые вы можете себе представить, находясь в игре. Со временем вы узнаете, что разумно, а что нет.

Наиболее вероятным объяснением того, что код работает медленнее в режиме выпуска, является то, что ваш код ограничения времени и рендеринга был нарушен. Он не работал должным образом, потому что 3d-представление работало со скоростью 1 кадр / с, так что кто знает, каково это поведение? С изменениями, которые я сделал, программа работает быстрее в режиме релиза, как и ожидалось. Ваш код часов показывает 600-1600 кадров в секунду в режиме релиза для меня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...