如何制作便携式和便携式编译器无关的glBufferData?

时间:2016-07-18 06:10:45

标签: c++ opengl

This question询问是否可以依赖编译器来处理<button data-ng-disabled="!form.valid && !uploaded">Submit</button> s值顺序和填充。

根据该问题的答案,

  

OpenGL非常清楚地定义了struct接口块的字节布局。

     

C ++ 11定义了一个名为"standard layout types"的概念。

     

C ++告诉你关于布局的标准布局类型的唯一事情是忽略空基类(只要它仍然是标准布局),并且第一个NSDM将在类的最开始。也就是说,前面永远不会有填充物。

     

标准所说的另一件事是,相同访问类的NSDM将按顺序分配,后者的偏移量比之前的偏移量大。

     

但就C ++标准而言,就是这样。 [class.mem] / 13声明实现可以出于各种原因在成员之间添加填充。

那可能但并不总是存在填充可能真的搞砸了,最糟糕的部分 - 它取决于编译器。

为了避免错误和恶梦,使用与编译器无关的方法是不是更好?

例如:

std140

与天真相反:

class BufferData
{
private:
    GLfloat data[12];

public:
    GLfloat* getCameraPosition()
    {
        return (GLfloat*) &data[0];
    }
    GLfloat* getLightPosition()
    {
        return (GLfloat*) &data[4];
    }
    GLfloat* getLightDiffuse()
    {
        return (GLfloat*) &data[8];
    }
    GLfloat* getData()
    {
        return data;
    }
};

或者天真的做法是否足够好?

(假设类/结构不仅仅是那个,而且可能会改变)

1 个答案:

答案 0 :(得分:1)

“编译器不可知”?没有这样的动物。你试图写一个证明了它。考虑您的结构成员定义:

GLfloat data[12];

这需要存在GLfloat类型。但问题是,C ++没有定义那种类型。 OpenGL可以。

OpenGL非常清楚地定义了这种类型:它是IEEE-754 floating-point type, using the BINARY32 format

问题是,C ++不要求float符合这一点。实际上,C ++并不要求其类型的任何符合这一点。如果编译器希望float使用IEEE-754之外的其他东西,那就没问题了。

现在,您可以说OpenGL头可以将GLfloat定义为类型,大小为32位,将从编译器的float类型转换为IEEE-754。当然,这可能发生......除非没有办法获得32位值。

有些系统有9位字节。或18位字节。这些系统有C ++编译器。此类系统无法声明只有32位大小的类型。

但能够传递32位值(更不用说16位和8位)是OpenGL的硬性要求。没有它,您将无法在缓冲区对象中传递任何数据。然而,C ++并不需要它。

说到顶点数据,半现代OpenGL中最基本的功能之一是glVertexAttribPointer。并且依赖将一个字节偏移量转换为void*,然后它将被转回。

C ++不保证这有效。在C ++标准中没有任何地方要求如果你将一个整数转换为指针,然后将该指针强制转换为整数,你将得到相同的整数(它确实说ptr-&gt; int-&gt; ptr工作,但这并不意味着反过来。)

然而OpenGL需要它。除非您使用单独的属性缓冲区(和I strongly suggest you do if it's available),否则您的代码和您调用的OpenGL代码将依赖于此未定义的行为。

OpenGL将GLint定义为带符号的二进制补码,32位整数。但是C ++不要求任何整数类型是两个补码。

但是OpenGL可以。

OpenGL flat out无法在类型异常的系统上运行。它不能在具有9位字节的系统上运行。它不能在使用有符号整数数学补码的系统上运行。我可以继续这样做,但我认为我的观点很明确。

通过选择使用OpenGL(Vulkan,如果你想知道),你已经依赖于实现定义的行为。那么,当您已经依赖于大量其他实现定义的行为时,为什么还要让您的生活更难以避免这种特定的实现定义行为呢?

这匹马离开了谷仓;现在关门没有帮助。