glBufferSubData has peaks with small buffer sizes - c++

I am making a simple 2D game engine with OpenGL, and my SpriteRenderer class makes batches of sprites which use the same shader or texture. Once I sort them first by texture, then by shader, and then by depth, the uploadData() method of my SpriteRenderer class uploads the CPU generated vertex data to the GPU. For this I am using the orphaning method, which is said to be faster than a simple glBufferData call.
My problem is, when I have small numbers of sprites (<15), I have frames with huge stalls, but with numbers >20 up to 10 000 it runs smoothly. I wrapped opengl function into c++ classes. If I replace the VertexBufferObject::uploadOrphaned() method by the VertexBufferObject::upload() method, the stall goes away. Here is my vbo class:
//vbo.h
class VertexBufferObject
{
public:
VertexBufferObject();
VertexBufferObject(size_t allocSize);
~VertexBufferObject();
inline ui32 id(){ return (ui32)m_id; }
template <class T>
void addData(const T& data)
{
const byte* convertedData = reinterpret_cast<const byte*>(&data);
size_t size = sizeof(T);
for (ui32 i = 0; i < size; i++)m_data.add(convertedData[i]);
}
void initialize();
void resetData();
void upload(GLenum drawType);
void uploadOrphaned(GLenum drawType);
inline void bind() { ::glBindBuffer(GL_ARRAY_BUFFER, m_id); }
inline void unbind(){ ::glBindBuffer(GL_ARRAY_BUFFER, 0); }
private:
GLuint m_id;
//this is my custom dynamic array class, this is a hobby project
//and I enjoy reinventing the wheel :)
Array<byte> m_data;
and below is the cpp file:
VertexBufferObject::VertexBufferObject() :m_id(0)
{
}
VertexBufferObject::VertexBufferObject(size_t allocSize) : m_id(0), m_data(allocSize)
{
}
VertexBufferObject::~VertexBufferObject()
{
if (m_id != 0) ::glDeleteBuffers(1, &m_id);
}
void VertexBufferObject::initialize()
{
if (m_id == 0) ::glGenBuffers(1, &m_id);
}
void VertexBufferObject::upload(GLenum drawType)
{
glBufferData(GL_ARRAY_BUFFER, m_data.size(), &m_data[0], drawType);
}
void VertexBufferObject::uploadOrphaned(GLenum drawType)
{
glBufferData(GL_ARRAY_BUFFER, m_data.size() * 2, NULL, drawType);
glBufferSubData(GL_ARRAY_BUFFER, 0, m_data.size(), &m_data[0]);
}
void VertexBufferObject::resetData()
{
m_data.strip();
}
I am learning opengl, so my VertexBufferObject class contains what I already know. I know there are other methods to upload data, but I want to understand the reason of the current problem with this simple method. I looked around forums including here, and nobody had a similar issue that I found of. Maybe my class has some errors?

Related

Is it possible to draw 3d segments with CGAL?

I want to draw 3d segments, and the camera can rotate, so that I can observe the segments from various perspectives. I wonder if there is a way to draw them with CGAL? I know that CGAL is not specific for visualization, so the question itself may be some kind of silly. But it will be really helpful for me if it has this function, because I have some experience with CGAL. I have tried to learn OpenGL, but it's not possible for me to master it in a short time. And I don't want to spend much time to learn OpenGL, because I will not use it again in future work.
If CGAL doesn't have this function, could you please recommend some lightweight open source libraries which can draw 3d segments? I don't need a very feature-rich, but huge library. One easy to use and lightweight is best for me. Thanks a lot!
CGAL::Basic_viewer_qt allows to draw points, segments and faces in 2D/3D.
You can define your own viewer inheriting from this class.
As suggested by Marc, have a look at the different draw_XXX.h files to see how this is achieved for several viewers in CGAL.
Following Marc Glisse's and gdamiand's advice, I imitated the class SimpleTriangulation3ViewerQt in file draw_triangulation3.h and wrote a class named SimpleSegments3ViewerQt. It does work. Thanks for your advice! Here is the code.
#pragma once
#include<CGAL/Qt/Basic_viewer_qt.h>
//a struct that describes 3d segment, Point is the data structure of vertex of segment.
template<typename Point>
struct mySegment3d {
Point begin;
Point end;
mySegment3d() {}
mySegment3d(Point b,Point e):begin(b),end(e){}
};
#ifdef CGAL_USE_BASIC_VIEWER
#include<CGAL/Qt/init_ogl_context.h>
//viewer for mySegment3d
//Segs3 is an array of mySegment3d which can be traveled by iterator, such as std::veector<mySegment3d>.
template<class Segs3,class point>
class SimpleSegments3ViewerQt :public CGAL::Basic_viewer_qt
{
typedef Basic_viewer_qt Base;
typedef mySegment3d<point> mySegment3d;
public:
//construct the viewer
SimpleSegments3ViewerQt(QWidget* parent,
const Segs3& seg3,
const char* title = "Basic Segs3 Viewer") :
//First draw: vertices, edges, faces, multi-color, no inverse normal
Base(parent, title, true, true, false, false, true),
s3(seg3)
{
compute_elements();
}
protected:
const Segs3& s3;
protected:
void compute_edge(const mySegment3d& seg) {
add_segment(seg.begin, seg.end, CGAL::IO::blue());
}
void compute_vertex(const mySegment3d& seg) {
add_point(seg.begin, CGAL::IO::red());
add_point(seg.end,CGAL::IO::red());
}
void compute_elements() {
clear();
for (auto itor = s3.begin(); itor != s3.end(); ++itor) {
compute_vertex(*itor);
compute_edge(*itor);
}
}
virtual void keyPressEvent(QKeyEvent* e) {
Base::keyPressEvent(e);
}
};
//draw function
template<typename Segs3,typename Point>
void draw(const Segs3& s3, const char* title = "Segs3 Basic Viewer") {
#if defined(CGAL_TEST_SUITE)
bool cgal_test_suite = true;
#else
bool cgal_test_suite = qEnvironmentVariableIsSet("CGAL_TEST_SUITE");
#endif
if (!cgal_test_suite) {
CGAL::Qt::init_ogl_context(4, 3);
int argc = 1;
const char* argv[2] = { "segs3_viewer","\0" };
QApplication app(argc, const_cast<char**>(argv));
SimpleSegments3ViewerQt<Segs3,Point> mainwindow(app.activeWindow(),
s3, title);
mainwindow.show();
app.exec();
}
}
#endif
Here is an example of usage.
#include<vector>
#include<CGAL/Simple_cartesian.h>
#include"draw_segments_3.h"
typedef CGAL::Simple_cartesian<double> kernel;
typedef kernel::Point_3 Point_3;
//draw_segments3 test
int main() {
std::vector<mySegment3d<Point_3>> segs;
Point_3 p0(0, 0, 0), p1(1, 2, 3), p2(5, 3, 1), p3(3, 1, 10);
mySegment3d<Point_3> s0(p0, p1), s1(p0, p2), s2(p0, p3);
segs.emplace_back(s0);
segs.emplace_back(s1);
segs.emplace_back(s2);
draw<std::vector<mySegment3d<Point_3>>,Point_3>(segs);
}

Vertex Buffer Abstraction in OpenGl [duplicate]

This question already has answers here:
OpenGL object in C++ RAII class no longer works
(2 answers)
Closed 1 year ago.
I have been trying to write some classes to abstract OpenGl. So far I have written a VertexArray class that uses template functions which are working fine so far. However I have encountered problems with my VertexBuffer class that I have not been capable of fixing.
C++ and OpenGl are still fairly new to me so the solution might be pretty simple.
Whenever I try to replace the Vertex Buffer creation code with the constructor of the VertexBuffer class things go wrong and nothing is displayed on the screen even though the code of the constructor and the one directly written in the Mesh class are (I believe) the same.
Here is the code:
VertexBuffer.h:
#ifndef VERTEX_BUFFER_H
#define VERTEX_BUFFER_H
class VertexBuffer {
private:
public:
unsigned int ID;
VertexBuffer(const void* data, unsigned int size);
VertexBuffer();
~VertexBuffer();
void bind();
void unbind();
};
#endif
VertexBuffer.cpp:
#include "VertexBuffer.h"
#define GLEW_STATIC
#include <GL/glew.h>
#include <iostream>
VertexBuffer::VertexBuffer(const void* data, unsigned int size) {
glGenBuffers(1, &ID);
glBindBuffer(GL_ARRAY_BUFFER, ID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
std::cout << "ID = " << ID << std::endl;
std::cout << "data = " << data << std::endl;
std::cout << "size = " << size << std::endl << std::endl;
}
VertexBuffer::VertexBuffer() {}
VertexBuffer::~VertexBuffer() {
glDeleteBuffers(1, &ID);
}
void VertexBuffer::bind() {
glBindBuffer(GL_ARRAY_BUFFER, ID);
}
void VertexBuffer::unbind() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
The Mesh constructor where I have been trying to implement it via the class:
Mesh::Mesh(std::vector<Vertex> vertices) {
model = glm::mat4(1.0f);
indicesSize = vertices.size();
// generate vertex array object
va = VertexArray();
va.bind();
//============================================================
//This code works:
/*
glGenBuffers(1, &vb.ID);
glBindBuffer(GL_ARRAY_BUFFER, vb.ID);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices[0], GL_STATIC_DRAW);
*/
//------------------------------------------------------------
//This code does not work:
vb = VertexBuffer(&vertices[0], sizeof(Vertex) * vertices.size());
//============================================================
// points
va.push<float>(3, 9, false);
// colors
va.push<float>(3, 9, false);
// normals
va.push<float>(3, 9, false);
};
I would be happy if anyone can help me. Thanks in advance!
You have to store vb in some kind of smart pointer or add move semantics to VertexBuffer.
vb = VertexBuffer(&vertices[0], sizeof(Vertex) * vertices.size());
^^^ [1]
in [1] temporary VertexBuffer is created, then by operator =, the member ID is copied in shallow way into vb and finally at the end of expression temporary VertexBuffer is destroyed with deleting generated ID (by destructor of temporary instance), which is still stored in vb, but it is not valid anymore.
Solution with move semantics could be like:
class VertexBuffer {
std::unique_ptr<unsigned int> ID;
public:
VertexBuffer& operator=(VertexBuffer&& other) {
ID = std::move(other.ID);
return *this;
}
~VertexBuffer() {
if (ID)
glDeleteBuffers(1, ID.get() );
}
};

Bone weights to GPU in vectors

I've been working on skeletal animation using OPenGL and sending bone weight influences to the GPU in a struct containing a pair of arrays, however changing these arrays to vectors doesn't seem to work (as in the model ceases to render, as if the bone information was missing or wrong in some manner).
Asides from the declaration of the vectors within the struct, the code path is identical. I've debugged through and ensured the size/values of the elements are okay. The only thing I can think of is that C++ is freeing the contents of the vectors before it can be uploaded to the GPU, but I've seen no evidence to support that claim.
It's worth noting that the resizes used are to make the structure compatible with the existing array functionality and will be removed to allow for dynamic scaling after this issue is resolved.
Here's the structure as it stands, using vectors:
struct VertexBoneData
{
std::vector<glm::uint> IDs;
std::vector<float> weights;
VertexBoneData()
{
Reset();
IDs.resize(NUM_BONES_PER_VEREX);
weights.resize(NUM_BONES_PER_VEREX);
};
void Reset()
{
IDs.clear();
weights.clear();
}
void AddBoneData(glm::uint p_boneID, float p_weight);
};
Edit: Additional information.
Here's the loop to get the bone information in to the struct:
void Skeleton::LoadBones(glm::uint baseVertex, const aiMesh* mesh, std::vector<VertexBoneData>& bones)
{
for (glm::uint i = 0; i < p_mesh->mNumBones; i++) {
glm::uint boneIndex = 0;
std::string boneName(mesh->mBones[i]->mName.data);
if (_boneMap.find(boneName) == _boneMap.end()) {
//Allocate an index for a new bone
boneIndex = _numBones;
_numBones++;
BoneInfo bi;
_boneInfo.push_back(bi);
SetMat4x4(_boneInfo[boneIndex].boneOffset, mesh->mBones[i]->mOffsetMatrix);
_boneMap[boneName] = boneIndex;
}
else {
boneIndex = _boneMap[boneName];
}
for (glm::uint j = 0; j < mesh->mBones[i]->mNumWeights; j++) {
glm::uint vertId = baseVertex + mesh->mBones[i]->mWeights[j].mVertexId;
float weight = mesh->mBones[i]->mWeights[j].mWeight;
bones[vertId].AddBoneData(boneIndex, weight);
}
}
}
The code to add the ID/weights to the vertex:
void VertexBoneData::AddBoneData(glm::uint p_boneID, float p_weight)
{
for (glm::uint i = 0; i < ARRAY_SIZE_IN_ELEMENTS(IDs); i++) {
if (weights[i] == 0.0) {
IDs[i] = p_boneID;
weights[i] = p_weight;
return;
}
}
assert(0); //Should never get here - more bones than we have space for
}
Loop to load the Skeleton and insert to the VBO object from the Mesh class:
if (_animator.HasAnimations())
{
bones.resize(totalIndices);
for (unsigned int i = 0; i < _scene->mNumMeshes; ++i){
_animator.LoadSkeleton(_subMeshes[i].baseVertex, _scene->mMeshes[i], bones);
}
_vertexBuffer[2].AddData(&bones);
}
Insert to the VBO object:
void VertexBufferObject::AddData(void* ptr_data)
{
data = *static_cast<std::vector<BYTE>*>(ptr_data);
}
Finally the code to add the information to the GPU from the Mesh class:
_vertexBuffer[2].BindVBO();
_vertexBuffer[2].UploadDataToGPU(GL_STATIC_DRAW);
glEnableVertexAttribArray(BONE_ID_LOCATION);
glVertexAttribIPointer(BONE_ID_LOCATION, 4, GL_INT, sizeof(VertexBoneData), (const GLvoid*)0);
glEnableVertexAttribArray(BONE_WEIGHT_LOCATION);
glVertexAttribPointer(BONE_WEIGHT_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(VertexBoneData), (const GLvoid*)(sizeof(VertexBoneData) / 2));
This can't possibly work. A std::vector instance is an object that internally contains a pointer to dynamically allocated data.
So with this definition:
struct VertexBoneData
{
std::vector<glm::uint> IDs;
std::vector<float> weights;
The values for the IDs and weights aren't stored directly in the structure. The are in dynamically allocated heap memory. The structure only contains the vector objects, which contain pointers and some bookkeeping attributes.
You can't store these structs directly in an OpenGL buffer, and have the GPU access the data. OpenGL won't know what do with vector objects.

Importing and Displaying .fbx files in OpenGl

I have been trying to import and display an fbx file using the FBX SDK.Untill. I managed to load in the file, but I got stuck at the part where I have to display it.
The questions:
What exactly are those indices?
How should I display the vertices?
Here is the class that I made:
3dModelBasicStructs.h
struct vertex
{
float x,y,z;
};
struct texturecoords
{
float a,b;
};
struct poligon
{
int a,b,c;
};
Model.h
#ifndef MODEL_H
#define MODEL_H
#define FBXSDK_NEW_API
#define MAX_VERTICES 80000
#define MAX_POLIGONS 80000
#include <fbxsdk.h>
#include "3dModelBasicStructs.h"
#include <iostream>
#include <GL/glut.h>
using namespace std;
class Model
{
public:
Model(char*);
~Model();
void ShowDetails();
char* GetModelName();
void SetModelName( char* );
void GetFbxInfo( FbxNode* );
void RenderModel();
void InitializeVertexBuffer( vertex* );
private:
char Name[25];
vertex vertices[MAX_VERTICES];
poligon poligons[MAX_POLIGONS];
int *indices;
int numIndices;
int numVertices;
};
#endif
Model.cpp
#include "Model.h"
Model::Model(char *filename)
{
cout<<"\nA model has been built!";
numVertices=0;
numIndices=0;
FbxManager *manager = FbxManager::Create();
FbxIOSettings *ioSettings = FbxIOSettings::Create(manager, IOSROOT);
manager->SetIOSettings(ioSettings);
FbxImporter *importer=FbxImporter::Create(manager,"");
importer->Initialize(filename,-1,manager->GetIOSettings());
FbxScene *scene = FbxScene::Create(manager,"tempName");
importer->Import(scene);
importer->Destroy();
FbxNode* rootNode = scene->GetRootNode();
this->SetModelName(filename);
if(rootNode) { this->GetFbxInfo(rootNode); }
}
Model::~Model()
{
cout<<"\nA model has been destroied!";
}
void Model::ShowDetails()
{
cout<<"\nName:"<<Name;
cout<<"\nVertices Number:"<<numVertices;
cout<<"\nIndices which i never get:"<<indices;
}
char* Model::GetModelName()
{
return Name;
}
void Model::SetModelName(char *x)
{
strcpy(Name,x);
}
void Model::GetFbxInfo( FbxNode* Node )
{
int numKids = Node->GetChildCount();
FbxNode *childNode = 0;
for ( int i=0 ; i<numKids ; i++)
{
childNode = Node->GetChild(i);
FbxMesh *mesh = childNode->GetMesh();
if ( mesh != NULL)
{
//================= Get Vertices ====================================
int numVerts = mesh->GetControlPointsCount();
for ( int j=0; j<numVerts; j++)
{
FbxVector4 vert = mesh->GetControlPointAt(j);
vertices[numVertices].x=(float)vert.mData[0];
vertices[numVertices].y=(float)vert.mData[1];
vertices[numVertices++].z=(float)vert.mData[2];
cout<<"\n"<<vertices[numVertices-1].x<<" "<<vertices[numVertices- 1].y<<" "<<vertices[numVertices-1].z;
this->InitializeVertexBuffer(vertices);
}
//================= Get Indices ====================================
int *indices = mesh->GetPolygonVertices();
numIndices+=mesh->GetPolygonVertexCount();
}
this->GetFbxInfo(childNode);
}
}
void Model::RenderModel()
{
glDrawElements(GL_TRIANGLES,36,GL_INT,indices);
}
void Model::InitializeVertexBuffer(vertex *vertices)
{
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3,GL_FLOAT,0,vertices);
//glDrawArrays(GL_TRIANGLES,0,36);
}
Sadly , When i try to use drawelements i get this error:
Unhandled exception at 0x77e215de in A new begging.exe: 0xC0000005: Access violation reading location 0xcdcdcdcd.
2) How should I display the vertices?
Questions like these indicate, that you should work through some OpenGL tutorials. Those are the basics and you need to know them.
This is a good start regarding your problem, but you'll need to work through the whole tutorial
http://opengl.datenwolf.net/gltut/html/Basics/Tut01%20Following%20the%20Data.html
1) What exactly are those indices ?
You have a list of vertices. The index of a vertex is the position at which it is in that list. You can draw vertex arrays by its indices using glDrawElements
Update due to comment
Say you have a cube with shared vertices (uncommon in OpenGL, but I'm too lazy for writing down 24 vertices).
I have them in my program in an array, that forms a list of their positions. You load them from a file, I'm writing them a C array:
GLfloat vertices[3][] = {
{-1,-1, 1},
{ 1,-1, 1},
{ 1, 1, 1},
{-1, 1, 1},
{-1,-1,-1},
{ 1,-1,-1},
{ 1, 1,-1},
{-1, 1,-1},
};
This gives the vertices indices (position in the array), in the picture it looks like
To draw a cube we have to tell OpenGL in which vertices, in which order make a face. So let's have a look at the faces:
We're going to build that cube out of triangles. 3 consecutive indices make up a triangle. For the cube this is
GLuint face_indices[3][] = {
{0,1,2},{2,3,0},
{1,5,6},{6,2,1},
{5,4,7},{7,6,5},
{4,0,3},{3,7,4},
{3,2,6},{6,7,2},
{4,5,0},{1,0,5}
};
You can draw this then by pointing OpenGL to the vertex array
glVertexPointer(3, GL_FLOAT, 0, &vertices[0][0]);
and issuing a batches call on the array with vertices. There are 6*2 = 12 triangles, each triangle consisting of 3 vertices, which makes a list of 36 indices.
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, &face_indices[0][0]);

Accessing calloc'd data through a shared_ptr

I'm trying to access the data that I previously allocated with the calloc method through a shared_ptr. For some reason I can't access it (keeps on crashing with EXC_BAD_ACCESS) on glTexImage2D (last line of my code snippets).
My util method to load the data:
shared_ptr<ImageData> IOSFileSystem::loadImageFile(string path) const
{
// Result
shared_ptr<ImageData> result = shared_ptr<ImageData>();
...
// Check if file exists
if([[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:NO])
{
...
GLubyte *spriteData = (GLubyte*) calloc(width * height * 4, sizeof(GLubyte));
...
// Put result in shared ptr
shared_ptr<GLubyte> spriteDataPtr = shared_ptr<GLubyte>(spriteData);
result = shared_ptr<ImageData>(new ImageData(path, width, height, spriteDataPtr));
}
else
{
cout << "IOSFileSystem::loadImageFile -> File does not exist at path.\nPath: " + path;
exit(1);
}
return result;
}
Header for ImageData:
class ImageData
{
public:
ImageData(string path, int width, int height, shared_ptr<GLubyte> data);
~ImageData();
string getPath() const;
int getWidth() const;
int getHeight() const;
shared_ptr<GLubyte> getData() const;
private:
string path;
int width;
int height;
shared_ptr<GLubyte> data;
};
File that calls the util class:
void TextureMaterial::load()
{
shared_ptr<IFileSystem> fileSystem = ServiceLocator::getFileSystem();
shared_ptr<ImageData> imageData = fileSystem->loadImageFile(path);
this->bind(imageData);
}
void TextureMaterial::bind(shared_ptr<ImageData> data)
{
// Pointer to pixel data
shared_ptr<GLubyte> pixelData = data->getData();
...
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data->getWidth(), data->getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixelData);
}
Just for the record: if I throw out all shared_ptr's I'm able to access the data. Signature for glTexImage2D:
void glTexImage2D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *data);
Additional question: normally you have to free(spriteData) but since I gave the data to a shared_ptr, will the data be free'd when the shared_ptr is removed?
shared_ptr cannot magically guess how to release the memory. By default it tries to delete it, and since you didn't use new, that ends up in disaster.
You need to tell it how to do it:
shared_ptr<GLubyte>(spriteData, &std::free);
I think this is your problem:
..., &pixelData);
You are taking an address of a local variable (of type shared_ptr<GLubyte>), which is silently cast to void*, instead of getting the pointer from it. Replace it with:
..., pixelData.get());