1
\$\begingroup\$

I am trying to implement instanced rendering for objects that repeat, so I came up with idea I could simply group objects that loaded same model files, create array with their Model matrices and so on, and finally pass this array as uniform array to shaders, which will be indexed by gl_instanceID to get correct model matrix for given objects.

C++ implementation looks pretty much like this: std::vector RelativePositions, Scales; std::vector Models;

int ctr = 0;
bool first = true;
int isz = 0; obj->getIndices(isz);

// pDrawData->refObjects holds all Object3D instances which share same model geometry

for (std::list<IObject3D*>::iterator pObj = pDrawData->refObjects.begin(); pObj != pDrawData->refObjects.end(); pObj++) {
    IObject3D *obj = *pObj;
    int render = prepareForRender(obj, flags, isShadow, offset);
    if (!render) continue;
    if (first) {    //this is gonna be same for all objects of this class
        setUniform(Type, (int)obj->getType());
        setUniform(Flags, (int)obj->getFlags());
        first = false;
    }
    Scales.push_back(obj->getScale());
    RelativePositions.push_back(obj->getPos());
    Models.push_back(obj->getModel());
    ctr++;
}
if (ctr) {
    int Rem = 0;
    const int Step = MAX_OBJ_PER_INST;  // = 64
    for (size_t i = 0, j = Scales.size(); i < j; i+=Step) {
        Rem = Scales.size() - i;
        if (Rem > Step) Rem = Step;
        //following functions set uniform array of specified length
        setUniform(Scale, Scales.data()+i, Rem);
        setUniform(RelativePosition, RelativePositions.data()+i, Rem);
        setUniform(Model, Models.data()+i, Rem);
        glDrawElementsInstanced(GL_TRIANGLES, (GLsizei)isz, GL_UNSIGNED_INT, 0, Rem);
    }
}

Rendering object is done via Vertex Shader > Tess Control Shader > Tess Eval Shader > Geom Shader > FragmentShader. Instance ID is being passed using flat out/in int InstanceID; from shader to shader while being once set in Vertex shader as gl_InstanceID.

Tessellation Evaluation Shader logics look like this:

    #version 430
    layout(triangles) in;

    in vec3 tcPosition[];
    in vec3 tcTexCoord[];
    in vec3 tcNormal[];
    flat in int tcInstanceID[];

    out vec3 tePosition;
    out vec3 teDistance;
    out vec3 teTexCoord;
    out vec3 teNormal;
    flat out int teInstanceID;

    #define MAX_OBJ_PER_INST 64

    uniform mat4 Model[MAX_OBJ_PER_INST];
    uniform vec3 Scale[MAX_OBJ_PER_INST];
    uniform vec3 RelativePosition[MAX_OBJ_PER_INST];

    // ... other uniforms

    void main(void)
    {
        vec3 p0 = gl_TessCoord.x * tcPosition[0];
        vec3 p1 = gl_TessCoord.y * tcPosition[1];
        vec3 p2 = gl_TessCoord.z * tcPosition[2];

        // ... other stuff ...

        teDistance = gl_TessCoord;
        tePosition = (p0 + p1 + p2);
        teInstanceID = tcInstanceID[0];

        switch(Type){
            // ... other types ...
            default:
                tePosition = (vec4(tePosition,1)* Model[teInstanceID]).xyz * Scale[teInstanceID] + RelativePosition[teInstanceID];
                break;
        }
    }

This works fine when I run it on any nVidia GPU when on laptop, but when I try to run it on AMD GPU or Intel Integrated Graphics, it suddenly doesn't work and I only get blank screen.

But if I remove uniform arrays and pass only single variable for uniform, so all instances would suddenly have same model matrix, position and scale, it works even on other GPUs, so problem is definitely with arrays...

Is there any solution to this?

\$\endgroup\$
  • 1
    \$\begingroup\$ "only works on NVidia" is generally an.indicator that you're doing something wrong. NVidia's GL driver is more permissive and can accept incorrect behaviour which other vendors do not. Have you tried checking for errors? \$\endgroup\$ – Maximus Minimus Apr 24 '18 at 20:25
  • \$\begingroup\$ Yes, shader compiles correctly on other platforms too and I did not use any nV specific code anywhere, not even ext or some special functions \$\endgroup\$ – jakubinf Apr 24 '18 at 20:29
  • 2
    \$\begingroup\$ And what about errors at runtime, checking with glGetError? \$\endgroup\$ – Maximus Minimus Apr 25 '18 at 5:53
  • 2
    \$\begingroup\$ Only because there is no error doesn't mean that your usage is completely to spec. I thought about memory alignment, which may be less restrictive on Nvidias side compared to others, but I haven't checked that. You should read the OpenGL spec for the functionality you are using, OpenGL implementations guarantee to support this, but are open to also support more. \$\endgroup\$ – cozmic Apr 25 '18 at 10:39
  • \$\begingroup\$ You were right, I was not checking glGetError, so I set debug callback and now see that I had uniform vec3 RelativePositions[MAX_OBJ_PER_INST]; in one shader and uniform vec3 RelativePositions; in other shader. nVidia didn't have problem with this and treated second one simply as one-item array, but other GPUs have had problem with it. \$\endgroup\$ – jakubinf Apr 25 '18 at 19:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.