Я пытаюсь сделать игру 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.