PDA

View Full Version : Optimize drawing 10.000 2D-lines with VBO?



WhatJac3
22-09-2010, 08:36 PM
System: Windows Vista, AMD 64 X2 Dual Core 2.7 Ghz, ATI Radeon 2600 PRO HD
Compiler/IDE: Delphi 2009 for Win32
API: OpenGL

Hello,

I am trying to optimize my code since it seems to be rather slow. I am drawing 10.000 lines and I heard that the usage of a VBO would be drastically faster. But I have some problems with it. So maybe someone could help me since I can't find good examples/tutorials. Maybe someone can help me with my little example. I try to draw 2 lines:
(0,0) - (100,100) and (0,100) - (100,0).

This is what I have so far:


var:
arrLines: array[4] of array [2] of GLFloat;
FVBO: GLUInt;
begin
glGenBuffers(1, @FVBO);
glBindBufferARB(GL_ARRAY_BUFFER, FVBO);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, ); // What should be here?

//Initialize array:
arrlines[0][0] := 0;
arrlines[0][1] := 0;
arrlines[1][0] := 100;
arrlines[1][1] := 100;
arrlines[2][0] := 0;
arrlines[2][1] := 100;
arrlines[3][0] := 100;
arrlines[3][1] := 0;
//Draw the lines

glDrawElements(GL_LINES, 2*4 , GL_FLOAT, arrLines); //Is this correct?
glDisableClientState(GL_VERTEX_ARRAY);


Please help and thanks in advance

code_glitch
22-09-2010, 09:28 PM
glVertexPointer(2, GL_FLOAT, 0, ???); // What should be here????


To answer that, and I admit I am no good at OpenGl, I would say 0. See [URL="http://www.opengl.org/sdk/docs/man/xhtml/glVertexPointer.xml"]

from their description I would assume where the first variable in the array is? Please correct me, I think I am probably wrong here, but you never know. It never hurts to try/help? As the expression kind of goes... I'm tired. Maybe I should just go to sleep already.

edit:
one other thing, what do you specifically mean by VBO?

chronozphere
22-09-2010, 10:00 PM
OpenGL rendering with VBO's is quite confusing. Seems like you're mixing things up.
You're missing a glBufferDataARB(). That function is used to fill the buffer with data.
In your code, you send the vertex data to the draw call, which is wrong. The fourth parameter of glDrawElements is a pointer to the array of indices.

This tutorial shows how to use VBO's to render indexed geometry. For lines, you don't really want indexed geometry so you have to dig a little further yourself. Check out this tutorial:


http://www.songho.ca/opengl/gl_vbo.html (http://www.songho.ca/opengl/gl_vbo.html)

Also have a look at their vertex array's tutorial. That may give you some ideas about the differences between VBO's and vertex arrays, because people confuse those two often.

Good luck. :)

WhatJac3
23-09-2010, 12:04 AM
I expected to do something wrong. So VBO is the thing I am looking for for speed improvement?
Thanks for the link but it is in C++ code again and that is confusing for me (pointer things and such). Isn't there a good pascal example? (I am looking for that for over a week already, so therefore I posted it here, since all those C++ example aint doing it for me).

And while we are at it? What would be better? VBO or PBO?
I want to do a lot with transparency.

WhatJac3
23-09-2010, 12:34 AM
So it should be more like this? But I still have problems on 3 lines (4 parameters in total)


glGenBuffersARB(1, @FVBO);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, FVBO);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, , ,GL_DYNAMIC_DRAW_ARB); //2nd and 3th parameter
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, ); // 4th parameter?

arrlines[0][0] := 0;
arrlines[0][1] := 0;
arrlines[1][0] := 100;
arrlines[1][1] := 100;
arrlines[2][0] := 0;
arrlines[2][1] := 100;
arrlines[3][0] := 100;
arrlines[3][1] := 0;

glDrawElements(GL_LINES, , GL_FLOAT, @arrLines[0][0]); //Second parameter?
glDisableClientState(GL_VERTEX_ARRAY);

// bind with 0, so, switch back to normal pointer operation
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
glDeleteBuffersARB(1, &vboId);

chronozphere
23-09-2010, 06:49 AM
Thanks for the link but it is in C++ code again and that is confusing for me (pointer things and such). Isn't there a good pascal example? (I am looking for that for over a week already, so therefore I posted it here, since all those C++ example aint doing it for me).

Just get used to it. You won't get far if you only want to read pascal-tutorials because there aren't many. Moreover, there is almost no difference between pascal and C++ in that example, because it's just a few function calls. Just look at the global flow of the program and look up the documentation for each function and read it carefully. Also, browse these forums because I think there are more people who had problems with VBO's.

PBO's are totally different and I have not used them much. My guess would be that it's not the fastest way to draw lines. :)

The last parameter of glVertexPointer() is the offset into the given vertex array. If you want to use your entire vertex array, just set this to zero. That was all covered in the link I gave you. The only way to know how to use functions is read the documentation. I'm not going to do that for you. ;)

Good luck!

WILL
23-09-2010, 01:03 PM
Hi WhatJac3, I just modified your posts slightly to put your code into a code block. You should really try to use them as they help when you are trying to show your programming code nicely on the forums.

Also, you have not followed the Help Me! forum rules for posting in this special forum. Please read item 3 from the FAQ (http://www.pascalgamedevelopment.com/faq.php?faq=rules#faq_rules_forums) for a clear definition of what I mean. However, since this forum here may be removed and each individual thread will be moved to a more appropriate on-topic forum instead, I've chosen to forgo the traditional warning. You may read about the decisions behind this at the following thread (http://www.pascalgamedevelopment.com/showthread.php?6018-Help-Me%21-Forum-In-or-Out).

Thanks for your attention and I hope that you get the help you require for your project. :)

WhatJac3
23-09-2010, 04:24 PM
Hello Will,

Thanks for your reply. Well I didn't really see why posting my system specs would result in a better understanding of the problem. The option to format it is code I was looking for in the editor but couldn't find it (not in the quick, neither in the advanced mode). So therefore I could but the correct tags around it since it might differ per forum. So maybe it would be nice to make an Icon in the editor to add the code tags.

Thanks for the help so far, but it still gives me problems
@chronozphere, I didn't asked to read the documentation for me. But I am trying for over a week already and cant seem to get it working. I have seen all kind of examples but almost all are C++. I have copied an example from this OpenGL tutorial (http://wiki.lazarus.freepascal.org/OpenGL_Tutorial) which compiles but just doesn't display anything and I have no clue why not. So I was just looking for someone who could point me in the right direction.



VertexBuffer: array [0..5] of TVertex2f = (
(X : 200; Y : 200;),
(X : 0; Y : 200;),
(X : 0; Y : 0;),
(X : 200; Y : 200;),
(X : 0; Y : 0;),
(X : 200; Y : 0;)
);
ColorBuffer: array [0..5] of TColor3f = (
(R : 1; G : 0; B : 1),
(R : 0; G : 0; B : 1),
(R : 0; G : 1; B : 0),
(R : 1; G : 0; B : 1),
(R : 0; G : 1; B : 0),
(R : 1; G : 1; B : 0)
);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);

glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);

chronozphere
23-09-2010, 07:55 PM
Ok, I'll give you some tips then. :)

First of all, can you show me your complete sourcecode? The last post only contains the render code, not the creation of the buffer etc..

The most confusing part of VBO's is that it uses the functions that were meant for vertex array's. I see that you are passing a pointer to the array to glVertexPointer() and glColorPointer(). This is correct for vertex array's but NOT for VBO's. The functions are used differently. You must pass the data in a glBufferDataARB() call and use 0 as the last parameter for both glVertexPointer and glColorPointer().

Just google some more and ask more specific questions. You could also check the following tutorial:

http://playcontrol.net/ewing/jibberjabber/opengl_vertex_buffer_object.html

I know that VBO's are confusing. You just HAVE to go thourgh one of the tutorials, write down the functions that are used and figure out what happens exactly.

I may write a VBO tutorial one day, for pascal. Right now, I dont have time for that.

Good luck!

WhatJac3
23-09-2010, 09:46 PM
Thanks for giving me tips. I have been playing with it all day again and this is what I have at the moment (but still nothing is drawn)
(is from: http://www.ozone3d.net/tutorials/opengl_vbo_p2.php, my translation from C++ to Delphi). I checked it with your tutorial and did not see any strange things. My viewwindow is set up from (0,0) - (1024,768)



var
vc: Integer;
ColorSize, PositionSize : Integer;
const
BufferSize = 2;
POSITION_OBJECT = 0;
COLOR_OBJECT = 1;
var
BufferName: array [0..BufferSize-1] of GLuint;
begin

glGenBuffersARB := wglGetProcAddress('glGenBuffersARB');
glBindBufferARB := wglGetProcAddress('glBindBufferARB');
glBufferDataARB := wglGetProcAddress('glBufferDataARB');
glDeleteBuffersARB := wglGetProcAddress('glDeleteBuffersARB');

PositionSize := length(VertexBuffer) * SizeOf(TVertex2f);
ColorSize := length(ColorBuffer) * SizeOf(TColor3f);

vc := Length(VertexBuffer);

// allocate a new buffer
glGenBuffersARB(1, @BufferName[COLOR_OBJECT]);
// bind the buffer object to use
glBindBufferARB(GL_ARRAY_BUFFER_ARB, BufferName[COLOR_OBJECT]);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ColorSize, @ColorBuffer[0], GL_STREAM_DRAW);
glColorPointer(3, GL_UNSIGNED_BYTE, 0, 0);

glGenBuffersARB(1, @BufferName[POSITION_OBJECT]);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, BufferName[POSITION_OBJECT]);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, PositionSize, @VertexBuffer[0], GL_STREAM_DRAW);
glVertexPointer(2, GL_FLOAT, 0, 0);

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

glDrawArrays(GL_TRIANGLES, 0, vc);

glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);


with data:


type
TVertex2f = record
X, Y: TPositionType;
end;

TColor3f = record
R, G, B: GLubyte;
end;

const
VertexBuffer: array [0..5] of TVertex2f = (
(X : 0; Y : 0;),
(X : 200; Y : 0;),
(X : 200; Y : 200;),
(X : 200; Y : 200;),
(X : 0; Y : 200;),
(X : 0; Y : 0;)
);
ColorBuffer: array [0..5] of TColor3f = (
(R : 255; G : 0; B : 0),
(R : 255; G : 255; B : 0),
(R : 0; G : 255; B : 0),
(R : 0; G : 255; B : 0),
(R : 0; G : 0; B : 255),
(R : 255; G : 0; B : 0)
);

WILL
23-09-2010, 10:35 PM
Thanks for your reply. Well I didn't really see why posting my system specs would result in a better understanding of the problem. The option to format it is code I was looking for in the editor but couldn't find it (not in the quick, neither in the advanced mode). So therefore I could but the correct tags around it since it might differ per forum. So maybe it would be nice to make an Icon in the editor to add the code tags.

Hey no problem, I'm here to help. ;) Well the Help Me forum may be nuked soon anyhow. If that happens, don't worry, I'll move your thread over to the Programming -> Graphics forum so it is not lost. (Along with all the other threads too.) Next time you are using the advanced editor, look for an icon that looks like a number sign ala "#". That's the code BB tag button you're looking for. It's not in the quick reply editor unfortunately, but clicking on 'Go Advanced' will keep any quoted text you have and enable all the other nice WYSIWYG buttons.

WhatJac3
23-09-2010, 11:13 PM
What WYSIWUG buttons? Can you please show them in my screenshot? This is what I get when I use the advanced editor:

ps: I am using FireFox, but my IE8 gives me exactly the same view.

chronozphere
24-09-2010, 06:38 AM
I noticed the following line:



glBufferDataARB(GL_ARRAY_BUFFER_ARB, ColorSize, @ColorBuffer[0], GL_STREAM_DRAW);


Have a look at the last GL_STREAM_DRAW parameter. I couldn't find official documentation of this function, so I just quote the tutorial:



"stream" means the data will be changed every frame (specified once and used once).


I suggest you change it to GL_STATIC_DRAW, because I think GL_STREAM_DRAW will throw away your data after it is rendered once.

The rest of your code looks ok. ;)

Hope this helps.

Edit: About the WYSIWYG problem, you have to enable those buttons in your profile. Forum -> Forum Actions -> General settings, and then scroll down until you see the "Message Editor Interface" optoin. :)

WhatJac3
24-09-2010, 08:21 PM
Well I finally solved it. The GL_STREAM_DRAW was not the solution. My problem was that I was using multiple libraries. The default 'OpenGL' from Delphi combined with the dglOpenGL (and sometimes even GL library and copied GL functions from NeHe). The combination of this all made it impossible to use VBO's. So I removed all other code except for the dglOpenGL. Thanks for the help guys.

chronozphere
25-09-2010, 07:48 AM
Good to know you solved it. :)

I'd suggest that you only use dglOpengl. It's the best set of headers out there IMHO. ;)

phibermon
09-10-2010, 01:58 PM
Here's some VBO code from my P3D Model lib :



procedure TP3DModel.UploadVBOS;
var
VerticiesSize : uint32;
NormalsSize : uint32;
UVsSize : uint32;
VertexBonesSize : uint32;
VertexRelToJointSize : uint32;
CurrentOffset : uint32;
TotalSize : uint32;
begin
if FLoaded and not FVBOUploaded then
begin
//Generate VBOs
glGenBuffers(1, @VBO);

VerticiesSize := length(mesh.Vertex) * sizeof(tvertex);
NormalsSize := length(mesh.Normal) * sizeof(tvertex);
UVsSize := length(mesh.UV) * sizeof(TVector2f);
VertexBonesSize := length(mesh.VertexBones) * sizeof(tvector);
VertexRelToJointSize := length(mesh.VertexRelToJoint) * sizeof(tvector);

VBOVerticesOffset := 0;
VBONormalsOffset := VerticiesSize;
VBOUVsOffset := VBONormalsOffset + NormalsSize;
VBOVertexBonesOffset := VBOUVsOffset + UVsSize;
VBOVertexRelToJointOffset := VBOVertexBonesOffset + VertexBonesSize;

TotalSize := VerticiesSize + NormalsSize + UVsSize + VertexBonesSize + VertexRelToJointSize;

//bind and upload data
glBindBuffer(GL_ARRAY_BUFFER, VBO);

glBufferData(GL_ARRAY_BUFFER, TotalSize , nil , GL_STATIC_DRAW); //allocate memory for buffer

//load in seperate buffers at correct offsets
glBufferSubData(GL_ARRAY_BUFFER, VBOVerticesOffset, VerticiesSize , @mesh.Vertex[0]);
glBufferSubData(GL_ARRAY_BUFFER, VBONormalsOffset, NormalsSize , @mesh.normal[0]);
glBufferSubData(GL_ARRAY_BUFFER, VBOUVsOffset, UVsSize , @mesh.UV[0]);
glBufferSubData(GL_ARRAY_BUFFER, VBOVertexBonesOffset, VertexBonesSize , @mesh.VertexBones[0]);
glBufferSubData(GL_ARRAY_BUFFER, VBOVertexRelToJointOffset, VertexRelToJointSize , @mesh.VertexRelToJoint[0]);
glBindBuffer(GL_ARRAY_BUFFER, 0);

//create index vbo
glGenBuffers(1, @VBOIndicies);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOIndicies);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, length(Mesh.Indicies)*sizeof(GLuint), @mesh.Indicies[0], GL_STATIC_DRAW);

glbindbuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
end;
end;

procedure TP3DModel.DeleteVBOS;
begin
if FVBOUploaded then
begin
glDeleteBuffers(1, @VBO);
glDeleteBuffers(1, @VBOIndicies);
FVBOUploaded := false;
VBO := 0;
VBOIndicies := 0;
end;
end;



and here's how it renders :



procedure TP3DModel.RenderDynamic;
var
i1, i2, k, m: integer;
currentMaterial: integer;
currentShaderId: integer;
temptime: single;


attribname : string;
v : TVector;
begin
//enable shader
FArmatureShader.Enable;

if FDynamicRenderMode = DRMVBO then
begin
//Bind VBO Before setting pointers
glBindBuffer(GL_ARRAY_BUFFER, VBO);

//enable Attrib Arrays VBO
glEnableVertexAttribArray(FModelManagerLink^.Attri bNormals);
glEnableVertexAttribArray(FModelManagerLink^.Attri bVertices);
glEnableVertexAttribArray(FModelManagerLink^.Attri bVertexRelToJoint);
glEnableVertexAttribArray(FModelManagerLink^.Attri bUVs);
glEnableVertexAttribArray(FModelManagerLink^.Attri bVertexBones);

//set attrib pointers VBO
glVertexAttribPointer(FModelManagerLink^.AttribVer tices,3,GL_FLOAT,false,0,nil);
glVertexAttribPointer(FModelManagerLink^.AttribNor mals,3,GL_FLOAT,false,0,pglvoid(VBONormalsOffset)) ;
glVertexAttribPointer(FModelManagerLink^.AttribUVs ,2,GL_FLOAT,false,0,pglvoid(VBOUVsOffset));
glVertexAttribPointer(FModelManagerLink^.AttribVer texBones,4,GL_FLOAT,false,0,pglvoid(VBOVertexBones Offset));
glVertexAttribPointer(FModelManagerLink^.AttribVer texRelToJoint,4,GL_FLOAT,false,0,pglvoid(VBOVertex RelToJointOffset));

//bind index buffer for rendering
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBOIndicies);
end;

if FDynamicRenderMode = DRMAttribArray then
begin
//enable the attrib arrays used in shader
glEnableVertexAttribArray(FModelManagerLink^.Attri bNormals);
glEnableVertexAttribArray(FModelManagerLink^.Attri bVertices);
glEnableVertexAttribArray(FModelManagerLink^.Attri bVertexRelToJoint);
glEnableVertexAttribArray(FModelManagerLink^.Attri bUVs);
glEnableVertexAttribArray(FModelManagerLink^.Attri bVertexBones);

//point the arrays at the data in system memory
glVertexAttribPointer(FModelManagerLink^.AttribVer tices,3,GL_FLOAT,false,0,@Mesh.Vertex[0][0]);
glVertexAttribPointer(FModelManagerLink^.AttribNor mals,3,GL_FLOAT,false,0,@Mesh.Normal[0][0]);
glVertexAttribPointer(FModelManagerLink^.AttribUVs ,2,GL_FLOAT,false,0,@Mesh.UV[0][0]);
glVertexAttribPointer(FModelManagerLink^.AttribVer texBones,4,GL_FLOAT,false,0,@Mesh.VertexBones[0][0]);
glVertexAttribPointer(FModelManagerLink^.AttribVer texRelToJoint,4,GL_FLOAT,false,0,@Mesh.VertexRelTo Joint[0][0]);

end;

//i3 := round((SDL_GetTicks/10)) mod 100;


inc(i7);
if i7 >= length(Self.Armature.Actions[0].AnimFrames) then i7 := 0;

//bind uniforms for mesh (can only bind after shader is bound)

if (FModelManagerLink^.SkinningMode = SMQuat) or (FModelManagerLink^.SkinningMode = SMDualQuat) then
begin
glUniform4fv(FModelManagerLink^.UPos,31,@armature. Actions[0].AnimFrames[i7].BonePos[0]);
glUniform4fv(FModelManagerLink^.URot,31,@armature. Actions[0].AnimFrames[i7].BoneRot[0]);
end else
if (FModelManagerLink^.SkinningMode = SMMatrix) then
begin
gluniformmatrix4fv(FModelManagerLink^.UMat,31,fals e,@armature.Actions[0].AnimFrames[i7].BoneMatrix[0][0][0]);
end;

glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, TextureID );


if DynamicRenderMode = DRMVBO then
glDrawElements( GL_TRIANGLES,Mesh.IndexCount, GL_UNSIGNED_INT, nil) else
if DynamicRenderMode = DRMAttribArray then
glDrawElements( GL_TRIANGLES,Mesh.IndexCount, GL_UNSIGNED_INT, @Mesh.Indicies[0]);

FArmatureShader.disable;

if FDynamicRenderMode = DRMVBO then
begin
gldisableVertexAttribArray(FModelManagerLink^.Attr ibNormals);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibVertices);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibVertexRelToJoint);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibUVs);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibVertexBones);

glbindbuffer(GL_ARRAY_BUFFER,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
end;


if FDynamicRenderMode = DRMAttribArray then
begin
gldisableVertexAttribArray(FModelManagerLink^.Attr ibNormals);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibVertices);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibVertexRelToJoint);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibUVs);
gldisableVertexAttribArray(FModelManagerLink^.Attr ibVertexBones);
end;

end;



sorry if it's a bit confusing, it's designed for bone-animation on the GPU (called hardware skinning) but it does show how to populate a VBO, how to render from it and a reasonably good way of falling back on cards without VBO.

I'll try and help best I can if you get stuck, let me know.