I’m implementing a vegetation rendering system using GPU-based frustum culling with transform feedback. I write the instance IDs into a texture during the culling pre-pass, and then I read the IDs back in the rendering pass.
On Nvidia hardware this works fine. On AMD hardware I am receiving an INVALID_OPERATION error on glDrawElements() during the rendering pass. I’m not seeing anything in the documentation that really explains this:
https://www.opengl.org/sdk/docs/man/html/glDrawElements.xhtml
I also notice that if the pre-pass shader emits no vertices, the error does not occur. An error only occurs when data has been written into the transform feedback buffer.
This is my pre-pass function. Is there something else I need to call to make the transform feedback texture ready for reading?
void VegetationLayer::PrePass(Camera* camera) {
if (variationmatrices == NULL) BuildVariationMatrices();
model->surfaces[0]->material->SetTexture(variationmap, 5);
if (query[camera] == 0)
{
glGenQueries(1, &query[camera]);
glGenTransformFeedbacks(1, &transformfeedback[camera]);
glGenBuffers(1, &feedbackbuffer[camera]);
glBindBuffer(GL_TEXTURE_BUFFER, feedbackbuffer[camera]);
glBufferData(GL_TEXTURE_BUFFER, maxinstancesdrawn * 4, NULL, GL_DYNAMIC_COPY);
glGenTextures(1, &buffertexture);
glBindTexture(GL_TEXTURE_BUFFER, buffertexture);
glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, feedbackbuffer[camera]);
}
Mat4 mat;
Vec4 color = Vec4(1.0);
Vec3 campos = camera->GetPosition(true);
int count = cellresolution * cellresolution * cellcount * cellcount;
instancesurface->Enable();
shader_frustumcull->Enable();
for (int n = 0; n < 6; n++)
{
shader_frustumcull->SetVec4("frustumplane" + String(n), Vec4(camera->frustum->face[n]->plane.x, camera->frustum->face[n]->plane.y, camera->frustum->face[n]->plane.z, camera->frustum->face[n]->plane.d));
}
shader_frustumcull->SetVec3("aabbmin", model->localaabb.min);
shader_frustumcull->SetVec3("aabbmax", model->localaabb.max);
shader_frustumcull->SetFloat("TerrainSize", terrain->resolution * terrain->scale.x);
shader_frustumcull->SetFloat("TerrainHeight", terrain->scale.y);
shader_frustumcull->SetFloat("TerrainResolution", terrain->resolution);
shader_frustumcull->SetFloat("CellResolution", cellresolution * cellcount);
shader_frustumcull->SetFloat("Density", density);
shader_frustumcull->SetFloat("BillboardDistance", billboarddistance);
shader_frustumcull->SetVec2("InstanceOffset", Vec2(0));
shader_frustumcull->SetInt("VBOSize", instancesurface->CountVertices());
shader_frustumcull->SetInt("NumInstances", count);
terrain->heightmap->SetFilter(Texture::Smooth);
terrain->heightmap->Bind(6);
terrain->normalmap->Bind(7);
glEnable(GL_RASTERIZER_DISCARD);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, feedbackbuffer[camera]);
glBeginTransformFeedback(GL_POINTS);
glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query[camera]);
int passsize = instancesurface->CountVertices();
int passes = ceil((float)count / (float)passsize);
instancesurface->Draw(passes);
glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
glEndTransformFeedback();
instancesurface->Disable();
glDisable(GL_RASTERIZER_DISCARD);
}