Quantcast
Channel: 英特尔开发人员专区文章
Viewing all articles
Browse latest Browse all 583

OpenGL* 性能提示: 使用本机格式,实现最佳渲染性能

$
0
0

下载 [PDF 703KB]

从 Github 下载代码示例

简介

游戏开发人员经常使用 OpenGL 来处理图形密集型游戏的渲染工作。 OpenGL 是一种用于高效渲染二维和三维矢量图形的应用程序接口。 大部分平台上都有 OpenGL 。

本文演示了使用合适的纹理格式如何改进 OpenGL 性能,特别是使用本机纹理格式将为游戏开发人员提供最出色的 OpenGL 性能。 本文随附一个 C++ 示例应用,它显示了使用各种纹理格式对渲染性能的影响。 请注意,尽管本文涉及与图形游戏开发人员相关的概念,但这些概念适用于使用 OpenGL 4.3 及更高版本的所有应用。 示例代码用 C ++ 编写,专为 Windows* 8.1 和 10 设备而设计。

要求

构建和运行示例应用需要具备以下条件:

  • 采用第六代智能英特尔® 酷睿™ 处理器(代号为 Skylake)的电脑
  • OpenGL 4.3 或更高版本
  • Microsoft Visual Studio 2013 或更高版本

Skylake 处理器图形

第六代智能英特尔酷睿处理器也称为 Skylake,提供卓越的二维和三维图形性能,最高可达 1152 GFLOPS。 其多核架构可提高性能并增加每个时钟周期的指令数。

与前几代相比,第六代智能英特尔酷睿处理器具有许多新的优势,可显著提升整体计算性能和视觉性能。 示例增强功能包括一个 GPU,加上 CPU 的增强型计算能力,可提供比之前的英特尔® 处理器显卡高 40% 的图形性能。 第六代智能英特尔酷睿处理器经过重新设计,可提供更高保真度的视频输出,更高分辨率的视频播放,以及对更低功耗系统的更加无缝的响应能力。 Skylake 支持 4K 视频播放和扩展的超频,是游戏开发人员的理想之选。 

GPU 内存访问包含原子最小值、最大值以及共享本地内存或全局内存中 32 位浮点值的比较和交换。 这一新架构还为到同一地址的紧接原子提供了性能改进。 平铺资源包括对部分驻留(稀疏)的大型纹理和缓冲区的支持。 读取未映射磁贴会返回零,并且对其的写入操作将被丢弃。 此外还有用于固定 LOD 和获取操作状态的新着色器指令。 现在还支持较大的纹理和缓冲区。 (例如,您最大可使用 128k x 128k x 8B 的细化 2D 纹理。)

由图形 API 提供支持时,无边界资源可将一个着色器可使用的动态资源数量从大约 256 个增加至 2,000,000 个。 这一改变可降低与更新绑定表相关的开销,为程序员提供更高的灵活性。

执行单元也改进了本地 16 位浮点支持。 使用半精度时,这种增强的浮点支持可带来功耗和性能方面的双重优势。

显示功能进一步提供了多平面重叠选项,拥有缩放、转换、颜色校正和显示时组合多个曲面的硬件支持。 曲面可来自使用不同更新频率和分辨率的单独交换链(例如,在放大、较低分辨率的帧渲染基础上组合的全分辨率 GUI 元素),从而提供显著增强。

其架构支持最多三个切片的 GPU(提供 72 个 EU)。 这一架构还提供了更高的功率门控和时钟域灵活性,这是值得利用的。

第 2 课: 使用本机纹理格式,实现最佳渲染性能

任何使用 OpenGL 的人都熟悉纹理。 然而,并非所有纹理都一样,某些纹理格式的渲染性能高于其他格式。 使用硬件本地格式意味着纹理可以“按原样”使用,从而避免不必要的转换。

本课显示了不同格式的影响——在窗口中渲染图像时,本示例在多种不同的纹理格式之间循环切换。 对于每种格式,当前性能以每帧毫秒数和每秒帧数显示。 按空格键可转到列表中的下一个纹理,这样您可以了解哪些格式在硬件上最适用。

示例使用以下格式。 下面的列表基于这一网址的 OpenGL“所需格式”列表: https://www.opengl.org/wiki/Image_Format

  • GL_RGBA8
  • GL_RGBA16
  • GL_RGBA16F
  • GL_RGBA32F
  • GL_RGBA8I
  • GL_RGBA16I
  • GL_RGBA32I
  • GL_RGBA8UI
  • GL_RGBA16UI
  • GL_RGBA32UI
  • GL_RGB10_A2
  • GL_RGB10_A2UI
  • GL_R11F_G11F_B10F
  • GL_SRGB8_ALPHA8
  • GL_RGB8
  • GL_RGB16
  • GL_RGBA8_SNORM
  • GL_RGBA16_SNORM
  • GL_RGB8_SNORM
  • GL_RGB16_SNORM
  • GL_RGB16F
  • GL_RGB32F
  • GL_RGB8I
  • GL_RGB16I
  • GL_RGB32I
  • GL_RGB8UI
  • GL_RGB16UI
  • GL_RGB32UI
  • GL_SRGB8
  • GL_SGB9_ES

构建和运行应用

按照下面的步骤编译和运行示例应用。

  1. 下载包含示例应用源代码的 ZIP 文件,将其解压到工作目录下。
  2. 在 Microsoft Visual Studio 2013 中打开lesson2a_textureformat/lesson2a.sln文件。
  3. 选择 <Build>/<Build Solution>,构建应用。
  4. 构建成功后,您可以在 Visual Studio 中运行示例。

应用运行时,主窗口将打开,您将看到一个图像。 Microsoft Visual Studio 2013 控制台窗口将显示渲染图像所用的纹理类型及其性能测量结果。 按空格键,更改为下一个纹理格式。 按 ESC 即可退出应用。

代码亮点

本示例的代码很简单,但有几个要强调的项。

首先,根据使用的纹理,我们将需要三个不同的片段着色器。 一个着色器将从一个标准化纹理获取其输出颜色,另一个专为非标准化的有符号整数纹理而设计,最后一个为非标准化的无符号整数纹理而设计。

// Fragment shader gets output color from a normalized texture
static std::string fragmentShader ="#version 430 core\n""\n""uniform sampler2D texUnit;\n""\n""smooth in vec2 texcoord;\n""\n""layout(location = 0) out vec4 fragColor;\n""\n""void main()\n""{\n""    fragColor = texture(texUnit, texcoord);\n""}\n"
;

// Fragment shader gets output color from a non-normalized signed integer texture
static std::string ifragmentShader =
    "#version 430 core\n""\n""uniform isampler2D texUnit;\n""\n""smooth in vec2 texcoord;\n""\n""layout(location = 0) out vec4 fragColor;\n""\n""void main()\n""{\n""    fragColor = vec4(texture(texUnit, texcoord))/255.0;\n""}\n"
;

// Fragment shader gets output color from a non-normalized unsigned integer texture
static std::string ufragmentShader =
    "#version 430 core\n""\n""uniform usampler2D texUnit;\n""\n""smooth in vec2 texcoord;\n""\n""layout(location = 0) out vec4 fragColor;\n""\n""void main()\n""{\n""    fragColor = vec4(texture(texUnit, texcoord))/255.0;\n""}\n"
;

这些着色器进行了编译和准备。

    // compile and link the shaders into a program, make it active
    vShader  = compileShader(vertexShader, GL_VERTEX_SHADER);
    fShader  = compileShader(fragmentShader, GL_FRAGMENT_SHADER);
    ifShader = compileShader(ifragmentShader, GL_FRAGMENT_SHADER);
    ufShader = compileShader(ufragmentShader, GL_FRAGMENT_SHADER);
    program  = createProgram({ vShader, fShader });
    iprogram = createProgram({ vShader, ifShader });
    uprogram = createProgram({ vShader, ufShader });

纹理集合存储在一个数组中,这个数组包含纹理和要使用的程序。

// Array of structures, one item for each option we're testing
#define F(x,y,z) x, y, #x, 0, z
static struct {
    GLint fmt, type;
    const char* str;
    GLuint obj, &pgm;
} textures[] = {
    F(GL_RGBA8,          GL_RGBA,         program),
    F(GL_RGBA16,         GL_RGBA,         program),
    F(GL_RGBA8_SNORM,    GL_RGBA,         program),
    F(GL_RGBA16_SNORM,   GL_RGBA,         program),
    F(GL_RGBA16F,        GL_RGBA,         program),
    F(GL_RGBA32F,        GL_RGBA,         program),
    F(GL_RGBA8I,         GL_RGBA_INTEGER, iprogram),
    F(GL_RGBA16I,        GL_RGBA_INTEGER, iprogram),
    F(GL_RGBA32I,        GL_RGBA_INTEGER, iprogram),
    F(GL_RGBA8UI,        GL_RGBA_INTEGER, uprogram),
    F(GL_RGBA16UI,       GL_RGBA_INTEGER, uprogram),
    F(GL_RGBA32UI,       GL_RGBA_INTEGER, uprogram),
    F(GL_RGB10_A2,       GL_RGBA,         program),
    F(GL_RGB10_A2UI,     GL_RGBA_INTEGER, uprogram),
    F(GL_R11F_G11F_B10F, GL_RGBA,         program),
    F(GL_SRGB8_ALPHA8,   GL_RGBA,         program),
    F(GL_RGB8,           GL_RGBA,         program),
    F(GL_RGB16,          GL_RGBA,         program),
    F(GL_RGB8_SNORM,     GL_RGBA,         program),
    F(GL_RGB16_SNORM,    GL_RGBA,         program),
    F(GL_RGB16F,         GL_RGBA,         program),
    F(GL_RGB32F,         GL_RGBA,         program),
    F(GL_RGB8I,          GL_RGBA_INTEGER, iprogram),
    F(GL_RGB16I,         GL_RGBA_INTEGER, iprogram),
    F(GL_RGB32I,         GL_RGBA_INTEGER, iprogram),
    F(GL_RGB8UI,         GL_RGBA_INTEGER, uprogram),
    F(GL_RGB16UI,        GL_RGBA_INTEGER, uprogram),
    F(GL_RGB32UI,        GL_RGBA_INTEGER, uprogram),
    F(GL_SRGB8,          GL_RGBA,         program),
};

需要绘制图像时,根据纹理数组使用正确的纹理和格式。

// GLUT display function.   Draw one frame's worth of imagery.
void display()
{
    // attribute-less rendering
    glUseProgram(textures[selector].pgm);                                         GLCHK;
    glClear(GL_COLOR_BUFFER_BIT);                                                 GLCHK;
    glBindTexture(GL_TEXTURE_2D, textures[selector].obj);                         GLCHK;
    if (animating) {
        glUniform1f(offset, animation);                                           GLCHK;
    }
    else if (selector) {
        glUniform1f(offset, 0.f);                                                 GLCHK;
    }
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);                                        GLCHK;
    glutSwapBuffers();
}

每次绘制视频帧时,控制台就会更新性能输出,且应用会检查空格键或 ESC 是否已按下。 按下空格键会导致应用切换至数组中的下一个纹理,按下 ESC 则退出应用。 加载新纹理时,性能测量会重置,图像会呈现动画效果,表明设置已改变。 如果未按任何键,则渲染下一帧。

// GLUT idle function.  Called once per video frame.
// Calculate and print timing reports and handle console input.
void idle()
{
    // Calculate performance
    static unsigned __int64 skip;  if (++skip < 512) return;
    static unsigned __int64 start; if (!start &&
         !QueryPerformanceCounter((PLARGE_INTEGER)&start))               __debugbreak();
    unsigned __int64 now;  if (!QueryPerformanceCounter((PLARGE_INTEGER)&now))
                                                                         __debugbreak();
    unsigned __int64 us = elapsedUS(now, start), sec = us / 1000000;
    static unsigned __int64 animationStart;
    static unsigned __int64 cnt; ++cnt;

    // We're either animating
    if (animating)
    {
        float sec = elapsedUS(now, animationStart) / 1000000.f; if (sec < 1.f) {
            animation = (sec < 0.5f ? sec : 1.f - sec) / 0.5f;
        } else {
            animating = false;
            selector = (selector + 1) % _countof(textures); skip = 0;
            print();
        }
    }

    // Or measuring
    else if (sec >= 2)
    {
        printf("frames rendered = %I64u, uS = %I64u, fps = %f,
               milliseconds-per-frame = %f\n", cnt, us, cnt * 1000000. / us,
               us / (cnt * 1000.));
        if (advance) {
            animating = true; animationStart = now;  advance = false;
        } else {
            cnt = start = 0;
        }
    }

    // Get input from the console too.
    HANDLE h = GetStdHandle(STD_INPUT_HANDLE); INPUT_RECORD r[128]; DWORD n;
    if (PeekConsoleInput(h, r, 128, &n) && n)
        if (ReadConsoleInput(h, r, n, &n))
            for (DWORD i = 0; i < n; ++i)
                if (r[i].EventType == KEY_EVENT && r[i].Event.KeyEvent.bKeyDown)
                    keyboard(r[i].Event.KeyEvent.uChar.AsciiChar, 0, 0);

    // Ask for another frame
    glutPostRedisplay();
}

结论

此示例表明,使用 GPU 的本地纹理格式可提高性能。 这一简单应用将帮助您为 Skylake 硬件确定适当的纹理。

将这一技术与先进的第六代智能英特尔酷睿处理器相结合,图形游戏开发人员可确保游戏运行符合设计初衷。

下载代码示例

下面为 Github 上的代码示例链接

https://github.com/IntelSoftware/OpenGLBestPracticesfor6thGenIntelProcessor

参考资料

第六代智能英特尔® 酷睿™ 处理器概述(代号为 Skylake)

第六代智能英特尔® 酷睿™ 处理器的图形 API 开发人员指南

关于作者

Praveen Kundurthy 任职于英特尔® 软件和服务事业部。 他拥有计算机工程硕士学位。 他主要专注于移动技术、Microsoft Windows* 和游戏开发领域。

注释

1 March, Meghana R.,“第六代智能英特尔® 酷睿™ 处理器(代号为 Skylake)概述”。 2016 年 3 月 23 日。 https://software.intel.com/zh-cn/articles/an-overview-of-the-6th-generation-intel-core-processor-code-named-skylake


Viewing all articles
Browse latest Browse all 583

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>