gpt4 book ai didi

c++ - 为什么OpenGL立即模式比内核快?

转载 作者:行者123 更新时间:2023-12-03 07:14:50 25 4
gpt4 key购买 nike

我正在使用以下库在OpenGL中渲染文本:fontstash。我还有另一个header file,它添加了对OpenGL 3.0+的支持。问题是,为什么核心配置文件的渲染实现比即时模式要慢得多?
这是即时模式的渲染代码:

static void glfons__renderDraw(void* userPtr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
{
GLFONScontext* gl = (GLFONScontext*)userPtr;
if (gl->tex == 0) return;
glBindTexture(GL_TEXTURE_2D, gl->tex);
glEnable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glVertexPointer(2, GL_FLOAT, sizeof(float)*2, verts);
glTexCoordPointer(2, GL_FLOAT, sizeof(float)*2, tcoords);
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(unsigned int), colors);

glDrawArrays(GL_TRIANGLES, 0, nverts);

glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
}
这是核心配置文件渲染代码:
static void gl3fons__renderDraw(void* userPtr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts)
{
GLFONScontext* gl = (GLFONScontext*)userPtr;
if (gl->tex == 0) return;

if (gl->shader == 0) return;
if (gl->vao == 0) return;
if (gl->vbo == 0) return;

// init shader
glUseProgram(gl->shader);

// init texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gl->tex);
glUniform1i(gl->texture_uniform, 0);

// init our projection matrix
glUniformMatrix4fv(gl->projMat_uniform, 1, false, gl->projMat);

// bind our vao
glBindVertexArray(gl->vao);

// setup our buffer
glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
glBufferData(GL_ARRAY_BUFFER, (2 * sizeof(float) * 2 * nverts) + (sizeof(int) * nverts), NULL, GL_DYNAMIC_DRAW);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(float) * 2 * nverts, verts);
glBufferSubData(GL_ARRAY_BUFFER, sizeof(float) * 2 * nverts, sizeof(float) * 2 * nverts, tcoords);
glBufferSubData(GL_ARRAY_BUFFER, 2 * sizeof(float) * 2 * nverts, sizeof(int) * nverts, colors);

// setup our attributes
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, (void *) (sizeof(float) * 2 * nverts));
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(int), (void *) (2 * sizeof(float) * 2 * nverts));

glDrawArrays(GL_TRIANGLES, 0, nverts);

glBindBuffer(GL_ARRAY_BUFFER, 0);

glBindVertexArray(0);
glUseProgram(0);
}
我对每种实现进行了一个小测试,结果表明即时模式的 显着比核心模式的快。
两项测试都用 AAA...填充屏幕,我记录了每一帧完成此操作所花费的时间。这是循环:
// Create GL stash for 512x512 texture, our coordinate system has zero at top-left.
struct FONScontext* fs = glfonsCreate(512, 512, FONS_ZERO_TOPLEFT);

// Add font to stash.
int fontNormal = fonsAddFont(fs, "sans", "fontstash/example/DroidSerif-Regular.ttf");

// Render some text
float dx = 10, dy = 10;
unsigned int white = glfonsRGBA(255,255,255,255);

std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now();

fonsSetFont(fs, fontNormal);
fonsSetSize(fs, 20.0f);
fonsSetColor(fs, white);

for(int i = 0; i < 90; i++){
for( int j = 0; j < 190; j++){
dx += 10;
fonsDrawText(fs, dx, dy, "A", NULL);
}
dy += 10;
dx = 10;
}

std::chrono::high_resolution_clock::time_point t2 = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> time_span = t2 - t1;

std::cout<<"Time to render: "<<time_span.count()<<"ms"<<std::endl;
结果显示两者之间的差异超过400ms:
Core profile (left) vs Immediate mode (right)
为了提高性能应该更改什么?

最佳答案

我不确切知道此程序中的gl是什么,但是很明显,每次要渲染一段文本时,都需要执行以下操作:

  • 为缓冲区分配存储空间,重新分配自上次调用存储空间以来创建的任何存储空间。
  • 将三个单独的数据上传到该缓冲区。

  • 这些不是将顶点数据流传输到GPU的好方法。有 specific techniques for doing this well,但这不是其中之一。特别是,您不断重新分配相同的缓冲区这一事实将导致性能下降。
    解决此问题的最有效方法是使用一个具有固定存储量的缓冲区。它只会分配一次,而不会再次分配。理想情况下,无论您从中获取顶点数据的任何API都将以交错格式提供数据,因此您只需执行一次上传,而不需要执行三个上传。但显然,Fonstash显然不那么慷慨。
    无论如何,主要思想是避免重新分配和同步。后者意味着永远不要尝试覆盖最近写入的数据。因此,缓冲区必须足够大,以容纳两倍于您希望渲染的字体顶点数量。本质上,您对顶点数据进行了双缓冲:在读取一组数据时写入一组数据。
    因此,在帧的开头,您要弄清楚要呈现的字节偏移量是多少。这将是缓冲区的开始,也可能是缓冲区的中途。然后,对于每个文本块,将顶点数据写入此偏移量,并相应地增加偏移量。
    为避免必须更改VAO状态,应手动交织顶点数据。而不是上传三个阵列,您应该交错顶点,以便有效地制作一个巨大的顶点阵列。因此,您无需在此函数中间调用 glVertexAttribPointer;您只需使用 glDraw*的参数来绘制所需数组的一部分。
    这也意味着您只需要一个 glBufferSubData调用。但是,如果您可以访问持久性映射的缓冲区,则甚至不需要它,因为您可以在使用内存的其他部分时直接直接写入内存。尽管如果您使用持久性映射,则在切换缓冲区时需要使用 fence sync object,以确保不写入仍由GPU读取的顶点数据。

    关于c++ - 为什么OpenGL立即模式比内核快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65006063/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com