c++ - 为什么我的 UWP 游戏在发布时比在 Debug模式下慢?

标签 c++ uwp directx-11 query-performance

我正在尝试做一个 UWP 游戏,我遇到了一个问题,我的游戏在 Release模式下比在 Debug模式下慢得多。

我的游戏将绘制一个 3D View (地牢大师风格),并且将有一个在 3D View 上绘制的 UI 部分。因为 3D View 可以减慢到少量每秒帧数 (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;
    }

在 Debug模式下,游戏的 UI 以 60 FPS 运行,而 3D View 在我的 PC 上大约为 1 FPS。但即使在那里,我也不确定为什么我必须在一场比赛时间的 45% 时停止我的纹理绘制并调用当前,以获得 60 FPS,如果我等待更长时间我只能得到 30 FPS。 (此值在 RitonTimer 的“enoughTimeForOneMoreTexture()”中设置。

在 Release模式下,它急剧下降,UI 部分大约为 10 FPS,3D 部分为 1 FPS。在过去的两天里,我试图找出原因,但没有找到。

我还有一个小问题:如何告诉 visual studio 我的游戏实际上是游戏而不是应用程序?或者当我将我的游戏发送到他们的商店时,微软会进行“切换”吗?

这里我已经把我的游戏放在我的OneDrive上了,大家可以下载源文件并尝试编译它,看看你是否得到和我一样的结果:

OneDrive 链接:https://1drv.ms/f/s!Aj7wxGmZTdftgZAZT5YAbLDxbtMNVg

在 x64 调试或 x64 Release模式下编译。

更新:

我想我找到了为什么我的游戏在 Release模式下变慢的原因。 CPU 可能不会等待绘图指令完成,而只是将其添加到一个列表中,该列表将在单独的任务中以自己的速度转发给 GPU(或者 GPU 自己缓存)。那可以解释一切。

我的计划是先绘制 UI,然后从 3D View 中绘制尽可能多的纹理,直到 1/60 秒帧时间的 95% 过去,然后将其呈现给交换链。 UI 始终为 60 FPS,3D View 将与系统允许的速度一样快(如果可以在 95% 的帧时间内全部绘制,则也为 60FPS)。 这没有用,因为它可能在一个帧时间内缓存了我的 3D View 的所有指令(我正在测试 3D View 的 150000 BIG 纹理绘制指令),所以当然 UI 和 3D View 一样慢结束,或接近。

这也是为什么即使在 Debug模式下,当我等待 95% 的帧时间时我也没有获得 60FPS,我不得不等待 45% 的帧时间才能获得我想要的 UI 60FPS。

我在 Release模式下以较低的值对其进行了测试以验证该理论,事实上,当我仅在帧时间的 15% 处停止绘图时,我的 UI 也获得了 60 FPS。

我认为它只在 DirectX12 中是这样工作的。

最佳答案

“我如何告诉 visual studio 我的游戏实际上是游戏而不是应用程序”- 没有区别,游戏就是应用程序。

我让您的代码在 Debug模式下以 300-400 FPS 的速度运行。

首先,我注释掉了检查您是否有时间渲染另一个纹理的代码。不要那样做。玩家看到的一切都应该在一个帧内呈现。如果您的帧花费的时间超过 16 毫秒(目标为 60fps),请寻找昂贵的操作或重复进行的调用,这可能会增加一些意外的成本。当每帧或每次调整大小时只需要执行一次时,寻找可能重复执行某项操作的代码。等等

所以问题是您渲染了非常大的纹理,而且数量很多。您希望避免 overdraw (在已经渲染过像素的地方渲染像素)。你可以有一点 overdraw ,这有时比迂腐更可取。但是您一遍又一遍地绘制 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 并非全都强大,如果您正确使用它,它可以做很多事情,但如果您只是对它进行疯狂的操作,您可以让它停止运转,就像 CPU 一样.因此,尝试渲染“看起来正常”的事物,您可以想象在游戏中。您会及时了解什么是明智的,什么不是。

对于在 Release模式下运行速度较慢的代码,最可能的解释是您的计时和渲染限制器代码已损坏。它无法正常工作,因为 3d View 以 1fps 的速度运行,所以谁知道它的行为是什么。通过我所做的更改,该程序似乎按预期在 Release模式下运行得更快。您的时钟代码现在在 Release模式下为我显示 600-1600fps。

关于c++ - 为什么我的 UWP 游戏在发布时比在 Debug模式下慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56638488/

相关文章:

c++ - 将流绑定(bind)到自身

xamarin - Dotfuscator 在 Xamarin UWP 项目上失败

c++ - 在 Direct3D11 中模拟 Direct3D9 固定功能管道

c++ - DirectX11 HLSL 着色器未运行

c++ - 内部类可以访问私有(private)变量吗?

c++ - 使用 MSVC++ 9 进行 C 编程的建议

javascript - 如何重置 UWP WebView 的内容缩放因子

c# - UWP App 中未链接到 UI 的计时器

directx-11 - DirectX 11 像素着色器 什么是 SV_POSITION?

c++ - 使用 Debian Box 中的 Eclipse CDT 构建 TFS