Quick answer: OpenGL core profile requires a bound Vertex Array Object. With no VAO bound, vertex attribute calls and draws are no-ops — black screen.
A renderer ported from a tutorial to a core-profile context shows nothing. The compatibility profile had a default VAO; core profile doesn’t.
Core Profile Has No Default VAO
In the compatibility profile, VAO 0 is usable, so old code “just works”. In core profile, VAO 0 is invalid — glVertexAttribPointer, glEnableVertexAttribArray, and glDrawArrays all silently do nothing without a real VAO bound.
Create and Bind One
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
// now attribute setup is recorded into this VAO
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, 0);
glEnableVertexAttribArray(0);
Bind Before Drawing
The VAO stores the attribute layout. Bind it again before each draw (or keep it bound). Forgetting to re-bind after switching VAOs is the second-most-common black-screen cause.
One VAO Per Mesh
Give each mesh its own VAO that captures its buffer + attribute setup. Drawing a mesh is then just bind-VAO, draw — clean and fast.
Verifying
Geometry appears. glGetError is clean after setup. RenderDoc shows the VAO bound with the expected attributes for each draw call.
“Core profile has no default VAO. Generate, bind, and keep one bound — or you draw nothing.”
Always request a core profile context for new code — and then remember the VAO. The two go together.