Jump to content
  • Advertisement
Sign in to follow this  
Rarosu

OpenGL Only the last instance is rendered

This topic is 1033 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello GameDev!

 

I am porting an OpenGL 4.4 application to OpenGL 3.1 to get it to run on my Intel HD Graphics 3000 card (on my laptop). So far, things have been going well, but I ran into a problem when rendering more than one instance using the same vertex buffer (just changing the uniform buffer between the draw calls). When I do this, only the last instance is rendered. The non-ported code did not exhibit this problem and I just cannot see what I might have done to cause it to fail.

 

I have managed to isolate the bug in a minimal sample: http://pastebin.com/87DQhdrS

#include <SDL2/SDL.h>
#include <GL/gl3w.h>
#include <iostream>
#include <string>

const int OPENGL_MAJOR = 3;
const int OPENGL_MINOR = 1;
const int UNIFORM_BINDING = 1;

const char* VERTEX_SOURCE =
	"#version 140                                    \n"
	"                                                \n"
	"in vec2 in_position;                            \n"
	"                                                \n"
	"layout(std140) uniform UniformBuffer            \n"
	"{                                               \n"
	"    vec4 offset;                                \n"
	"};                                              \n"
	"                                                \n"
	"void main()                                     \n"
	"{                                               \n"
	"    gl_Position = vec4(in_position, 0.0f, 1.0f);\n"
	"    gl_Position.x += offset.x;                  \n"
	"}                                               \n";

const char* FRAGMENT_SOURCE =
	"#version 140                                    \n"
	"                                                \n"
	"out vec4 out_color;                             \n"
	"                                                \n"
	"void main()                                     \n"
	"{                                               \n"
	"    out_color = vec4(1.0f, 1.0f, 1.0f, 1.0f);   \n"
	"}                                               \n";

struct UniformBuffer
{
	float offset[4];
};

SDL_Window* window = nullptr;
SDL_GLContext glcontext = nullptr;
unsigned int viewport_width = 800;
unsigned int viewport_height = 600;
bool running = true;
GLuint vertexShader = 0;
GLuint fragmentShader = 0;
GLuint program = 0;
GLuint position_vbo = 0;
GLuint vao = 0;
UniformBuffer uniform_buffer_data;
GLuint uniform_buffer = 0;

void initialize();
void loadResources();
GLuint compileShader(const char* source, GLuint type);
void linkProgram(GLuint program);
void run();
void handleEvents();
void render();

int main(int argc, char* argv[])
{
	initialize();
	loadResources();
	run();

	return 0;
}

void initialize()
{
	if (SDL_Init(0) != 0)
	{
		throw std::runtime_error("Failed to initialize SDL");
	}

	window = SDL_CreateWindow("gl3", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, viewport_width, viewport_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	if (window == nullptr)
	{
		throw std::runtime_error("Failed to create SDL window");
	}

	SDL_GLcontextFlag flags = SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG;
#ifndef NDEBUG
	flags = (SDL_GLcontextFlag)(flags | SDL_GL_CONTEXT_DEBUG_FLAG);
#endif
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, OPENGL_MAJOR);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, OPENGL_MINOR);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, flags);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);

	glcontext = SDL_GL_CreateContext(window);
	if (glcontext == nullptr)
	{
		throw std::runtime_error("Failed to create OpenGL context");
	}

	if (gl3wInit() != 0)
	{
		throw std::runtime_error(std::string("Failed to initialize gl3w"));
	}

	if (gl3wIsSupported(OPENGL_MAJOR, OPENGL_MINOR) != 1)
	{
		throw std::runtime_error("OpenGL version not supported");
	}

	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
	glViewport(0, 0, viewport_width, viewport_height);

	SDL_GL_SetSwapInterval(1);
}

void loadResources()
{
	// VBOs.
	float positions[] = { -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, 0.5f };

	glGenVertexArrays(1, &vao);
	glBindVertexArray(vao);

	glGenBuffers(1, &position_vbo);
	glBindBuffer(GL_ARRAY_BUFFER, position_vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 8, positions, GL_STATIC_DRAW);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
	glEnableVertexAttribArray(0);

	// Shaders.
	vertexShader = compileShader(VERTEX_SOURCE, GL_VERTEX_SHADER);
	fragmentShader = compileShader(FRAGMENT_SOURCE, GL_FRAGMENT_SHADER);

	program = glCreateProgram();
	glAttachShader(program, vertexShader);
	glAttachShader(program, fragmentShader);
	linkProgram(program);

	glUniformBlockBinding(program, glGetUniformBlockIndex(program, "UniformBuffer"), UNIFORM_BINDING);

	// UBOs.
	uniform_buffer_data.offset[0] = 0.0f;
	uniform_buffer_data.offset[1] = 0.0f;
	uniform_buffer_data.offset[2] = 0.0f;
	uniform_buffer_data.offset[3] = 0.0f;

	glGenBuffers(1, &uniform_buffer);
	glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
	glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformBuffer) * sizeof(float), &uniform_buffer_data, GL_DYNAMIC_DRAW);
}

GLuint compileShader(const char* source, GLuint type)
{
	GLuint shader = glCreateShader(type);
	glShaderSource(shader, 1, &source, nullptr);
	glCompileShader(shader);

	GLint status;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (status != GL_TRUE)
	{
		GLint logSize;
		glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logSize);

		std::string log;
		if (logSize > 0)
		{
			int written;
			log.resize(logSize);
			glGetShaderInfoLog(shader, logSize, &written, &log[0]);
		}

		throw std::runtime_error("Failed to compile shader: " + log);
	}

	return shader;
}

void linkProgram(GLuint program)
{
	glLinkProgram(program);

	GLint status;
	glGetProgramiv(program, GL_LINK_STATUS, &status);
	if (status != GL_TRUE)
	{
		GLint logSize;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logSize);

		std::string log;
		if (logSize > 0)
		{
			int written;
			log.resize(logSize);
			glGetProgramInfoLog(program, logSize, &written, &log[0]);
		}

		throw std::runtime_error("Failed to link program: " + log);
	}
}

void run()
{
	while (running)
	{
		handleEvents();
		render();
	}
}

void handleEvents()
{
	SDL_Event event;
	while (SDL_PollEvent(&event))
	{
		switch (event.type)
		{
			case SDL_QUIT:
			{
				running = false;
			} break;

			case SDL_WINDOWEVENT:
			{
				switch (event.window.event)
				{
					case SDL_WINDOWEVENT_RESIZED:
					{
						viewport_width = event.window.data1;
						viewport_height = event.window.data2;

						glViewport(0, 0, viewport_width, viewport_height);
					} break;
				}
			} break;
		}
	}
}

void render()
{
	glClear(GL_COLOR_BUFFER_BIT);

	glUseProgram(program);
	glBindVertexArray(vao);

	uniform_buffer_data.offset[0] = 1.3f;
	glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer) * sizeof(float), &uniform_buffer_data);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	uniform_buffer_data.offset[0] = -0.4f;
	glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
	glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer) * sizeof(float), &uniform_buffer_data);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	SDL_GL_SwapWindow(window);
}

I am using SDL2 and GL3W and I am working in Visual Studio 2013 on Windows 10. The biggest change when I ported was that I no longer could specify the uniform binding point in the shader, but had to use glUniformBlockBinding instead. Anyone familiar with this version of OpenGL who could tell me if I am doing something stupid?

 

Cheers!

- Rarosu

Edited by Rarosu

Share this post


Link to post
Share on other sites
Advertisement

1.  Please post the code in this forum so future readers will have it.  (Who knows what will happen to pastebin in the future)

2.  Most of the time, a post that says "Here's my broken code.  Please fix it." does not get very good results.

 

Please tell us (and future readers) what you have tried and what you think the problem is, or where you are stuck.

 

Have you used any OpenGL debugging tools to inspect the frame and data?  That can help with silly errors you can't see.  Maybe https://github.com/apitrace/apitrace?

Edited by Glass_Knife

Share this post


Link to post
Share on other sites

The std130 layout will pad out the float to a vec4. The buffer won't be the right size for the uniform struct in the shader and the shader will read zero.

Share this post


Link to post
Share on other sites

Thanks for the reply, GlassKnife, I'll edit the original post with the code.

 

I have reduced the issue to where I know there is something going on with the uniform bindings. I have tried making a separate program, VBO and VAO for the second instance and the same problem occurs. When I make a separate uniform buffer object however and fill it with the same data, it works. That is, the difference between:

glUseProgram(program);
glBindVertexArray(vao);

// Instance is rendered.
uniform_buffer_data.offset = 1.3f;
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer), &uniform_buffer_data);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// This instance is also rendered.
ubo_data2.offset = -0.4f;
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, ubo2);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer), &ubo_data2);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

And this:

glUseProgram(program);
glBindVertexArray(vao);

// This instance is no longer visible.
uniform_buffer_data.offset = 1.3f;
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer), &uniform_buffer_data);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

// And only this one is rendered.
uniform_buffer_data.offset = -0.4f;
glBindBufferBase(GL_UNIFORM_BUFFER, UNIFORM_BINDING, uniform_buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(UniformBuffer), &uniform_buffer_data);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

As for checking the state, I am not sure what to look for in this scenario. I tried apitrace at your suggestion (which seems awesome) and the state looks fine to me; as I would expect it.

Share this post


Link to post
Share on other sites

@beans222 I don't think that is the problem in this case, but to be safe, I have tried changing the uniform data to an array of 4 floats and will include it in the edit.

Share this post


Link to post
Share on other sites

Running this project on an Nvidia card instead yielded the expected results... Two boxes. Either Nvidia is lenient with whatever error I might be doing, or this Intel driver has some bug. I will look into updating my drivers and see if this fixes the problem.

Share this post


Link to post
Share on other sites

Man, I hate it when the driver seems to be at fault.  I had something like that once.  A beta Linux driver wasn't compiling the shader correctly.  It was so hard to find.

 

If you do find an answer, be sure to post it here in case this comes up for someone else.

Edited by Glass_Knife

Share this post


Link to post
Share on other sites

@Glass_Knife Hah. It it a frightening thought to think how many hidden driver errors there are out there.

 

Updating the Intel driver to 9.17.10.4229 did not solve the problem unfortunately. The only solution I can see currently is revert to using glUniform* or create separate uniform buffer objects for each instance. Which sucks. If I find out anything else I'll update this thread.

 

- Rarosu

Share this post


Link to post
Share on other sites

Calling glFinish() after the first draw call seems to fix this problem. Obviously this is not ideal as it will block the execution until the draw call has finished, but it will at least fix the symptoms.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!