我正在尝试制作UWP游戏,但遇到一个问题,我的游戏在发布模式下比在调试模式下慢得多。
我的游戏将绘制3D视图(Dungeon主样式),并具有绘制3D视图的UI部分。由于3D视图可以减慢每秒少量的帧(FPS),因此我决定让我的游戏始终以60 FPS的速度运行UI部分。
这是主游戏循环的样子,用一些伪代码:
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;
}
在调试模式下,游戏的UI以60 FPS的速度工作,而3D视图在我的PC上约为1 FPS。但是即使到那里,我也不确定为什么我必须在一个游戏时间的45%时停止我的纹理绘制,并调用此命令才能获得60 FPS,如果我等待的时间更长,我只能获得30 FPS。 (此价格在RitonTimer的“ enoughTimeForOneMoreTexture()”中设置。
在“释放”模式下,它会急剧下降,UI部分为10 FPS,3D部分为1 FPS。我试图找出为什么在过去两天都没有找到它。
我还有一个小问题:我如何告诉Visual Studio我的游戏实际上是游戏而不是应用程序?还是当我将游戏发送到他们的商店时,微软会执行“切换”吗?
在这里,我已将游戏放在OneDrive上,以便每个人都可以下载源文件并尝试对其进行编译,并查看是否获得与我相同的结果:
OneDrive链接:https://1drv.ms/f/s!Aj7wxGmZTdftgZAZT5YAbLDxbtMNVg
以x64调试或x64发布模式进行编译。
更新:
我想我找到了解释为什么我的游戏在发布模式下速度较慢的原因。 CPU可能不会等待绘制指令完成,而只是将其添加到一个列表中,该列表将按照自己的步调在单独的任务中转发给GPU(或者GPU可以自己缓存)。那将解释一切。
我的计划是先绘制UI,然后从3D视图中绘制尽可能多的纹理,直到经过1/60秒帧时间的95%,然后将其呈现给swapchain。 UI始终为60 FPS,而3D视图将尽可能快于系统允许的速度(如果可以全部以95%的帧时间绘制,则为60 FPS)。 这行不通,因为它可能在一帧时间内缓存了我的3D视图所拥有的所有指令(我正在测试150000 BIG纹理绘制指令用于3D视图)在一帧时间内,因此UI的速度和3D视图一样慢结束或接近。
这也是为什么即使在调试模式下,当我等待95%的帧时间时我也没有获得60FPS,为什么我不得不等待45%的帧时间才能获得我想要的UI 60 FPS。
我在发布模式下以较低的值对其进行了测试,以验证该理论,实际上,当我仅在15%的帧时间停止绘图时,我的UI也获得了60 FPS。
我坚信它仅在DirectX12中如此工作。
答案 0 :(得分:2)
“我如何告诉Visual Studio我的游戏实际上是游戏而不是应用程序”-没什么区别,游戏是应用程序。
我现在在调试模式下以300-400 FPS的速度运行您的代码。
首先,我注释掉了您的代码,该代码检查您是否有时间渲染另一个纹理。不要那样做玩家看到的所有内容都应在单个帧内渲染。如果您的帧耗时超过16毫秒(以60fps为目标),则需要进行昂贵的操作或重复进行呼叫,这可能会增加一些意外费用。当代码每帧或每次调整大小仅需要执行一次时,查找可能重复执行某些操作的代码。等
所以问题是您正在渲染非常大的纹理,并且其中很多。您要避免过度绘制(在已经渲染像素的地方渲染像素)。您可能会有一些透支,有时比学究更可取。但是您一次又一次地绘制1000x2000纹理。因此,您绝对要杀死像素着色器。它只是无法渲染那么多像素。我没有理会尝试根据剩余帧时间来控制纹理渲染的代码。对于您要尝试执行的操作,这没有帮助。
在您的render方法内部注释掉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并非功能强大,如果使用正确,它可以起到很多作用,但是如果您对它进行疯狂的操作,则可以将其磨碎,就像使用CPU一样。因此,尝试渲染“看起来很正常”的东西,您可以想象在游戏中。您将及时了解什么才是明智的。
在释放模式下代码运行较慢的最可能解释是时序和渲染限制器代码已损坏。由于3d视图以1fps的速度运行,因此无法正常工作,因此谁能知道它的行为。经过我的更改,该程序似乎在发行模式下按预期运行得更快。您的时钟代码现在在发布模式下显示600-1600fps。