glDrawElements gives 1282 error code

The project I am working on is not working due to “GL_INVALID_OPERATION” error when calling glDrawElements. I am using OpenGL version 4.1

My code:

main.cpp

#include "Window.h"
#include "Renderer.h"

int main() {
	Androneder::Window win("Hello World!");

	float vertices[] = {
		-0.5f, -0.5f, 0.0f, // 0
		0.5f, -0.5f, 0.0f, // 1
		0.5f, 0.5f, 0.0f  // 2
	};

	unsigned int indices[] = {
		0, 1, 2
	};

	Androneder::VertexBuffer vbo;
	Androneder::IndexBuffer ibo;
	ibo.CreateBuffer(indices);

	vbo.CreateBuffer(vertices);

	vbo.AddIBuff(ibo);

	vbo.GetVAO().Bind();
	ibo.Bind();

	std::string vertexShader =
		"#version 330 core\n"
		"\n"
		"layout(location = 0) in vec4 position;\n"
		"\n"
		"void main()\n"
		"{\n"
		"	gl_Position = position;\n"
		"}\n";

	std::string fragmentShader =
		"#version 330 core\n"
		"\n"
		"layout(location = 0) out vec4 color;\n"
		"\n"
		"void main()\n"
		"{\n"
		"	color = vec4(0.0, 0.0, 1.0, 1.0);\n"
		"}\n";

	Androneder::ShaderProgram shaderProgram;
	shaderProgram.CreateShader(vertexShader, fragmentShader);
	shaderProgram.Use();

	while (!win.ShouldClose()) {
		glEnable(GL_SCISSOR_TEST);
		glViewport(0, 0, 1280, 920);
		glScissor(0, 0, 1280, 920);
		glDisable(GL_SCISSOR_TEST);

		Androneder::Renderer::ClearSolid(1, 0, 0, 1);

		//Androneder::Renderer::Draw(ibo, vbo.GetVAO());
		std::cout << glGetError() << std::endl;
		glDrawElements(GL_TRIANGLES, ibo.length, GL_UNSIGNED_INT, indices);

		win.Poll();
	}

	ibo.Unbind();
	vbo.GetVAO().Unbind();
	vbo.Destroy();

	return 0;
}

IndexBuffer.cpp

#include "IndexBuffer.h"

namespace Androneder {
	void IndexBuffer::CreateBuffer(unsigned int indices[])
	{
		glGenBuffers(1, &buffer);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_DYNAMIC_DRAW);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		length = (sizeof(indices) / sizeof(indices[0])) + 1;
	}

	void IndexBuffer::ChangeIndices(unsigned int indices[])
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
		glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(indices), indices);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	}

	void IndexBuffer::Destroy()
	{
		glDeleteBuffers(1, &buffer);
	}

	void IndexBuffer::Bind()
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
	}

	void IndexBuffer::Unbind()
	{
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	}
}

IndexBuffer.h

#pragma once

#include "glad/glad.h"

#include <iostream>

namespace Androneder {
	class IndexBuffer
	{
	private:
		GLuint buffer;
	public:
		int length;

		void CreateBuffer(unsigned int indices[]);
		void ChangeIndices(unsigned int indices[]);

		const GLuint* GetBuffer() { return &buffer; }

		void Destroy();

		void Bind();
		void Unbind();
	};
}

VertexBuffer.h

#pragma once

#include "glad/glad.h"

#include "IndexBuffer.h"
#include "VertexArrayObject.h"

#include <iostream>
#include <vector>

namespace Androneder{
	class VertexBuffer
	{
	private:
		std::vector<IndexBuffer> ibuffers;
		VertexArrayObject vao;
		GLuint buffer;
		std::vector<float> currentVertices;
	public:
		void CreateBuffer(float vertices[]);
		void ChangeVertices(float vertices[]);

		void AddIBuff(IndexBuffer buff);
		void SetIBuffs();

		void Bind();
		void Unbind();

		void Destroy();

		VertexArrayObject GetVAO() { return vao; }
	};
}

VertexBuffer.cpp

#include "VertexBuffer.h"

namespace Androneder {
	void VertexBuffer::CreateBuffer(float vertices[]) {
		vao.CreateVAO();

		glGenBuffers(1, &buffer);
		glBindBuffer(GL_ARRAY_BUFFER, buffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);

		int bsize = 0;
		glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bsize);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		if (bsize == 0)
		{
			std::cout << "Vertex buffer is empty\n";
			return;
		}

		std::copy(currentVertices.begin(), currentVertices.end(), vertices);
	}

	void VertexBuffer::ChangeVertices(float vertices[]) {
		glBindBuffer(GL_ARRAY_BUFFER, buffer);
		glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);

		int bsize = 0;
		glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bsize);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

		if (bsize == 0)
		{
			std::cout << "Vertex buffer is empty\n";
			return;
		}

		std::copy(currentVertices.begin(), currentVertices.end(), vertices);
	}

	void VertexBuffer::AddIBuff(IndexBuffer buff)
	{
		ibuffers.push_back(buff);
		SetIBuffs();
	}

	void VertexBuffer::SetIBuffs()
	{
		vao.Bind();
		Bind();
		for (int i = 0; i < ibuffers.size(); i++) {
			glEnableVertexAttribArray(i);
			glVertexAttribPointer(i, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), 0);
		}
		Unbind();
		vao.Unbind();
	}

	void VertexBuffer::Bind() {
		glBindBuffer(GL_ARRAY_BUFFER, buffer);
	}

	void VertexBuffer::Unbind() {
		glBindBuffer(GL_ARRAY_BUFFER, 0);
	}

	void VertexBuffer::Destroy() {
		for (IndexBuffer ibo : ibuffers)
		{
			glDeleteBuffers(1, ibo.GetBuffer());
		}
		glDeleteBuffers(1, &buffer);
	}
}

VertexArrayObject.h

#pragma once

#include "glad/glad.h"

#include <iostream>

namespace Androneder {
	class VertexArrayObject
	{
	private:
		GLuint vao;
	public:
		virtual ~VertexArrayObject();

		void CreateVAO();

		void Bind();
		void Unbind();
	};
}

VertexArrayObject.cpp

#include "VertexArrayObject.h"

namespace Androneder {
	VertexArrayObject::~VertexArrayObject() {
		glDeleteVertexArrays(1, &vao);
	}

	void VertexArrayObject::CreateVAO() {
		glGenVertexArrays(1, &vao);
	}

	void VertexArrayObject::Bind() {
		glBindVertexArray(vao);
	}

	void VertexArrayObject::Unbind() {
		glBindVertexArray(0);
	}
}

ShaderProgram.h

#pragma once

#include "glad/glad.h"

#include <iostream>

namespace Androneder{
	class ShaderProgram
	{
	private:
		unsigned int program;
	public:
		void CreateShader(const std::string vertexShader, const std::string fragmentShader);
		unsigned int CompileShader(const int type, const std::string source);

		void Use();
	};
}

ShaderProgram.cpp

#pragma once

#include "glad/glad.h"

#include <iostream>

namespace Androneder{
	class ShaderProgram
	{
	private:
		unsigned int program;
	public:
		void CreateShader(const std::string vertexShader, const std::string fragmentShader);
		unsigned int CompileShader(const int type, const std::string source);

		void Use();
	};
}

You have a lifetime issue with the VertexArrayObject returned by value from VertexBuffer::GetVAO - so here:

a temporary VertexArrayObject is created, the underlying OpenGL VAO is bound to the context and then the temporary goes out of scope destroying the just bound VAO.

1 Like

Thanks, there are no errors now but still nothing shows up.

Hmm, well given that I don’t know what you changed this has basically become an impossible ask: you are asking people on this board to debug a program that you don’t show the code :wink: That you are using your own abstractions does not make this task easier either - in general please see Forum Posting Guidelines for some recommendations on posting.

I would suggest using this as an opportunity to practice debugging OpenGL applications, e.g. run your program under RenderDoc so that you can inspect the OpenGL state at the draw call and verify it matches what you expect (are all vertex attributes set up correctly and enabled, shaders compiled, linked and bound, etc.). Try to reduce the complexity of what you are doing until the problem no longer appears (in this particular case there may not be a whole lot that you can remove, this is more general debugging advice).

Thanks, I checked it out but everything seems to be fine and all the inputs are correct. I only changed .GetVAO() to .vao

It doesn’t show the triangle in texture viewer though. I haven’t used OpenGL for a few months so sorry if it is a dumb question.

I’m afraid I can’t spot the problem either. Have you tried setting up a debug message callback (see here), sometimes the implementation will report useful information about incorrect (or suspect looking) state.

I assume the clear works and gives you a solid red background?

Do you change any OpenGL state in your window/context creation code? Hmm, where do you swap buffers, assuming you have a double buffered context?

I got this:

GL CALLBACK: type = 0x8251, severity = 0x826b, message = Buffer detailed info: Buffer object 1 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.
GL CALLBACK: type = 0x8251, severity = 0x826b, message = Buffer detailed info: Buffer object 2 (bound to GL_ARRAY_BUFFER_ARB, usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations.

and I had to switch my version to 4.3 and I switched it from GL_DYNAMIC_DRAW to GL_STATIC_DRAW and it still says the same thing. Nothing is showing up as usual.

Those messages are normal and just provide feedback from the implementation about where it allocated memory for the buffer objects.

Thanks but why does it still show that even after I changed it?

Why would it not? It is an informational message from the implementation providing insight into what it is doing; specifically which memory it is allocating a buffer in. In OpenGL where you only have very indirect control over memory allocation and can only provide hints to the implementation (as opposed to more explicit APIs like Vulkan or D3D12) this can be valuable to find performance issues caused by the implementation making a sub-optimal decision.
If those are the only messages you are getting it unfortunately doesn’t help with your problem. You may get additional (though not necessarily more relevant) information if you create a debug context - by passing a flag/attrib to the context creation function; the exact details are platform dependent.

Oh ok, thanks but why does nothing show? It seems like it should but it wont.

As I’ve mentioned before I cannot spot the problem either, sorry. You’ve not answered the questions I asked:

glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);

This isn’t doing what you think it’s doing. Hint: what’s the value of sizeof (vertices)?

So… I just realised for the array buffer it shouldn’t say 0. Thanks for that but I changed up the code and it still doesn’t work.

Render doc:
image

Edit: I forgot to say opengl gives an error. “GL CALLBACK: ** GL ERROR ** type = 0x824c, severity = 0x9146, message = GL_INVALID_VALUE error generated. Invalid size.”

Also, I don’t change the opengl state, I just keep it at 4.3 and I swap buffers at glfwSwapBuffers after all the rendering in win.Poll().

This is not the code you posted up above.

In the code you posted up above, you’re correctly using GL_ARRAY_BUFFER or GL_ELEMENT_ARRAY_BUFFER as the first param to glBufferData.

Here you’re not.

If you do this kind of thing, you’re making it impossible for anyone to help you resolve this problem.

Sorry if it is confusing but I didn’t change the first parameter. That is just how render doc shows it. Again, sorry for the confusion

You still haven’t answered the question about sizeof(vertices). It will be the size of the pointer, not the size of the array to which it points. CreateBuffer and ChangeVertices need the array size passed as a parameter (or use std::vector instead).

It takes in the size as bytes not the size of the array. I don’t know why I need the length of the array. I changed the code if you looked at the image from render doc. In render doc, it shows that the size in bytes is 36. Why would I need to change it to std::vector? I have looked at a few tutorials and all of them show that they use size and not length. Maybe I misunderstood what you are trying to say.

The same will apply in your IndexBuffer::CreateBuffer and IndexBuffer::ChangeIndices - in fact, I’d recommend that you review all of your usages of sizeof as it’s clear you’ve been making the wrong assumption about how it works and what it does.