【问题标题】:Using glMultiDrawElements in 64bit OS在 64 位操作系统中使用 glMultiDrawElements
【发布时间】:2012-01-03 22:04:50
【问题描述】:

我最近从 32 位环境迁移到 64 位环境,除了一个问题之外,它进展顺利:glMultiDrawElements 使用了一些在 64 位操作系统下如果不进行一些调整就无法工作的数组。

glMultiDrawElements( GL_LINE_LOOP, fCount_, GL_UNSIGNED_INT,
                     reinterpret_cast< const GLvoid** >( iOffset_ ),
                     mesh().faces().size() );

我对顶点和顶点索引都使用 VBO。 fCount_iOffset_GLsizei 的数组。由于缓冲区绑定到GL_ELEMENT_ARRAY_BUFFER,因此iOffset_ 的元素用作从VBO 开始的字节偏移量。这在 32 位操作系统下完美运行。

如果我将 glMultiDrawElements 更改为 glDrawElements 并将其放入循环中,它在两个平台上都可以正常工作:

int offset = 0;
for ( Sy_meshData::Faces::ConstIterator i  = mesh().faces().constBegin();
                                        i != mesh().faces().constEnd(); ++i ) {
    glDrawElements( GL_LINE_LOOP, i->vertexIndices.size(), GL_UNSIGNED_INT,
                    reinterpret_cast< const GLvoid* >( sizeof( GLsizei ) * offset ) );
    offset += i->vertexIndices.size();
 }

我认为我看到的是 OpenGL 读取 iOffset_ 的 64 位块导致大量数字,但 glMultiDrawElements 不支持任何超过 32 位 (GL_UNSIGNED_INT) 的类型,所以我不知道如何更正它。

有其他人遇到过这种情况并解决了吗?还是我完全错误地处理了这个问题,只是在 32 位操作系统上很幸运?

更新

将我现有的代码换成:

typedef void ( *testPtr )( GLenum mode, const GLsizei* count, GLenum type,
                           const GLuint* indices, GLsizei primcount );
testPtr ptr = (testPtr)glMultiDrawElements;
ptr( GL_LINE_LOOP, fCount_, GL_UNSIGNED_INT, iOffset_, mesh().faces().size() );

结果完全相同。

【问题讨论】:

  • fCount_iOffset_ 是什么类型?

标签: c++ opengl 64-bit vbo


【解决方案1】:

原因很简单,glMultiDrawElements 不需要整数偏移数组(您的平台上为 32 位),而是指针数组(您的平台上为 64 位),被解释为缓冲区偏移。

但是您只是将整数数组(或指针)转换为指针数组(或指针),这是行不通的,因为该函数现在只是将您的 n 个连续 32 位值重新解释为 n 个连续 64 位值.当然,它适用于glDrawElements,因为您只是将单个整数转换为单个指针,这实际上将您的 32 位值转换为 64 位值。

你需要做的不是转换你的指针/数组,而是这个偏移数组中的每个单独的值:

std::vector<void*> pointers(mesh().faces().size());
for(size_t i=0; i<pointers.size(); ++i)
    pointers[i] = static_cast<void*>(iOffset_[i]);
glMultiDrawElements( GL_LINE_LOOP, fCount_, GL_UNSIGNED_INT, 
                     &pointers.front(), mesh().faces().size() );

或者更好的是,从一开始就将偏移量存储为指针而不是整数。

【讨论】:

  • 啊,和我想的相反。实际上,我只需维护一个 quintptr 数组(Qt 提供的整数类型,即指针类型的宽度),然后保持我的 reinterpret_cast - 更少循环,更少强制转换,更少的数组,并且仍然与平台无关。
  • @cbamber85 您还可以使用标准 C 和 C++ 类型 size_t(恕我直言,它也保证是指针大小)与框架无关。
  • @cbamber85:如果有标准的 C 类型,为什么要使用 Qt 定义的类型:uintptr_t
  • @ChristianRau:不能保证 size_t 是指针大小的。您要查找的类型 uintptr_t
  • @datenwolf:它们是等价的,为什么不使用它呢? quintptr 少了一个字符。
【解决方案2】:

你遇到了我在https://stackoverflow.com/a/8284829/524368中彻底剖析的问题

我建议您在答案的最后按照我的建议进行操作,不要尝试将您的数字转换为编译器认为是指针的东西,而是将函数签名转换为带数字的东西。

请注意,在 glMultiDrawElements 的情况下,第一个间接不会进入 VBO,而是进入客户端内存。所以要转换的签名是例如。

void myglMultiDrawElementsOffset(GLenum mode,
    const GLsizei * count,
    GLenum type,
    const uintptr_t * indices,
    GLsizei  primcount);

【讨论】:

  • @cbamber85:您必须在调用glewInit()之后执行对签名调整函数指针的赋值注意,您也可以转换函数:((TFPTR_VertexOffset)&amp;glVertexPointer)(…)
  • 谢谢,我应该注意到了。请参阅我的更新,如果它适用于您的项目,它一定是我的语法,但我无法让函数指针的行为与我现有的错误代码有任何不同。
  • 这行不通,因为函数(无论哪个签名)仍然需要一个 64 位值(指针)的数组,他给它一个 32 位值(整数)的数组。
  • @BenVoigt:我再次问你:你读过我的其他答案吗?您是否知道将不是将指向 ([u]intptr_t) 的指针转换回指针的结果的转换数字会调用未定义的行为?如果您将任意数字转换为指针,编译器也可能会说“这不是指针值,我不会转换它”。例如,您可能在一个实现中,其中所有指针值(空指针除外)都 > 0x1000,因此编译器很容易确定数字
  • @BenVoigt:为了明确一点:C99 标准在第 6.3.2.3.5 节中说:“整数可以转换为任何指针类型。除非前面指定,结果是实现-已定义,可能未正确对齐,可能未指向引用类型的实体,并且可能是陷阱表示。”。此外,第 6.3.2.3.8 节说:“指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再转换回来;结果应与原始指针相等。" - 所以你有它。
猜你喜欢
  • 2013-08-29
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
  • 2011-03-02
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
  • 2015-02-11
相关资源
最近更新 更多