Skip to main content
GameDev.net gamedev.net
🔒 Locked

rendering engine questions

Started by jorgander May 23, 2003 at 1:18 AM 9 replies 1k views
Original Post
jorgander
jorgander
sorry if this topic has already been discussed... i''m considering which optimizations to add to my rendering engine, and i''m curious about a few things. I''m currently using an octree, so as the user looks around and moves, the software vertex buffer is filled with only the visible triangles, blah blah. i''m wondering if creating a hardware accelerated vertex buffer will noticably speed things up. the reason i''m thinking it might not be a major speed improvement is that the triangles have to be updated every frame anyway, unless the user stops moving and looking around. so moving them back and forth to the video hardware might not be faster than just sending them from client memory every frame. obviously if the user stops moving and the visible triangles are the same from one frame to another, it is an improvement, but aside from that... another improvement i am thinking about is calls like glDrawElements (or a similar function in another API), where u have a triangle buffer and an index buffer, and only draw those triangles that are indexed by the index buffer. the problem i can see with this method is with *large* levels. if u have to store a monumental amount of triangles (and only a small amount will be indexed at any one time), will there be enough memory on the video hardware to support it? i know this strongly depends on the video card capability and other things, but to the extent that it is a general question...
Yann L
Yann L
quote:

i''m wondering if creating a hardware accelerated vertex buffer will noticably speed things up. the reason i''m thinking it might not be a major speed improvement is that the triangles have to be updated every frame anyway


That''s called per-frame repopulation, and is Extremely Evil™. Avoid it at all costs, it will eat up tremendeous amounts of performance. What you should do, is store a local mesh pool (a vertex and index array) per octree node. And at run-time, if a node is visible (ie. in frustum, not occluded, etc), then simply render that node (by a call to glDrawElements, in the case of OpenGL).

this is one of the most important rules in high-performance realtime graphics: the CPU should never ever touch individual faces for rendering purposes. Only groups of faces, batches, clusters, arrays, whatever you want to call them.

To get even more performance, you can copy those arrays to video RAM, rendering will be more efficient from there. you are right, there won''t be enough VRAM for large scenes. That''s why you need a cache: only store the nodes that are currently visible, or have the potential to be visible. In a large world, most of the nodes will be hidden (occluded, or far away).
Zarmax
Zarmax
Yann, I''ve read a number of your posts on either spatial sorting (ABT) and rendering. I''ve noticed also that some of your scenes contain lots of tris. I''ve been scratching my head trying to figure out how you are rendering these efficiently. The sorting and occlusion make sense, but how do you actually render those sorted and occluded tris?

The limitations I see are that if you are using VAs in vid card mem and then for each frame spatially sorting your model and creating a list of indices that index the VAs and using glDrawElements, do you hit limitations in the size of the VAs in vid mem? It seems with very large models having all the vertices in video card memory would burn up most of the available memory. Also most of the time when using indices, using WORD sized indices seems to be the most efficient, but limits you to 65k per VA.

On the other hand the approach I was thinking of was to create a vertex array in sysmem for each shader, then go through my spatial data (octree now with 1-N*3 vertices/texcoords per node) and fill a vertex array for each shader, effectively sorting by shader and visibility. This would be done by traversing the leaf nodes and memcpying the vertices contained in the visible leafs to the appropriate shader vertex array. Once done, then iterate through each of these vertex arrays setting the correct shader/renderstate and calling glDrawArrays. This should allow very large models to be rendered and eliminate the need for indices. But, by not having my vertex arrays in video memory will this approach really slow down the rendering?

It all boils down to how do you optimally deal with your vertex data when you have very large models, render state changes and are spatially sorting the data?
jorgander
jorgander
i don''t re-populate *all* the triangles every frame, just those that are visible. how else do u make sure the "visible buffer" contains only visible triangles? i could go through last frame''s visible octree nodes and remove this frame''s invisible ones, but then i''d have to go through all of last frames invisible octree nodes and add those that are visible.

i just assume that all nodes are invisible at the start of a frame (if the user has changed position or orientation), and add those that are visible. i''m assuming there is a faster way of doing this, but is there a *much* faster way? oh yeah, i never use immediate mode anymore. all my calls are batches of triangles.


something else i was wondering, how do u support multiple textures for a level? obviously, u should sort your geometry by material, but what is an effecient way of doing this?

currently, i store the material index along with other information in a triangle structure, and when an octree node is found to be visible, i add each triangle it contains to it''s appropriate vertex buffer based on it''s material index. when octree culling is finished, i draw each group of triangles with a common material (of course, i check if there are visible triangles for a group before loading it''s material).
Ingenu
Ingenu
quote:
Original post by Zarmax
lso most of the time when using indices, using WORD sized indices seems to be the most efficient, but limits you to 65k per VA.
...
This would be done by traversing the leaf nodes and memcpying the vertices contained in the visible leafs to the appropriate shader vertex array. Once done, then iterate through each of these vertex arrays setting the correct shader/renderstate and calling glDrawArrays. This should allow very large models to be rendered and eliminate the need for indices. But, by not having my vertex arrays in video memory will this approach really slow down the rendering?

It all boils down to how do you optimally deal with your vertex data when you have very large models, render state changes and are spatially sorting the data?



I''m not YannL (shame on me ) but I think I can still help you a little.

First 65k per array isn''t that little. (going to have more than that in an octree Node or object ?)

Second with your idea you could have a hell LOT of memcopy, and if using VRAM you would prolly bump into the AGP bandwidth limit, if in system RAM it might not be that much better.

Third using Indices is a good thing. (I know a few 3D hardware engineers that would kill you if they had the right, because you didn''t use indices)

Put Static arrays often used inside VRAM, use VBO, you can more or less control whether it''s in Video, AGP, or System RAM.

I''ve what I call a SceneGraph (although what it really is, is debattable), which does ''automatically'' sort data per state (''shader'' personnaly I call them RenderPath).
Basically a model know and selects (LOD mechanism on RenderPath) it''s RenderPath, and when it''s visible, it either adds itself to the SceneGraph if not already in, or if in, update it''s status (as active, the SG is used as a cache too).
When it adds itself to the SceneGraph it in fact, ask it''s RenderPath to insert them both into the SceneGraph.
In the end, you have two lists : one for non transparent RenderPath, the other for Transparent RenderPath, each having a list of RenderPath, each having a list of Meshs (yep those are Meshs, cause a model could use more than one RenderPath, so I choose to make it divided into 1 Mesh per RenderPath).

I use VBO on MeshModels (a Mesh of Model, a ''Mesh'' being a MeshObject, an instance of that MeshModel, that uses its VB except if Morphing).


-* So many things to do, so little time to spend. *-
-* So many things to do, so little time to spend. *-
Ingenu
Ingenu
quote:
Original post by jorgander
i just assume that all nodes are invisible at the start of a frame (if the user has changed position or orientation), and add those that are visible. i''m assuming there is a faster way of doing this, but is there a *much* faster way? oh yeah, i never use immediate mode anymore. all my calls are batches of triangles.


something else i was wondering, how do u support multiple textures for a level? obviously, u should sort your geometry by material, but what is an effecient way of doing this?



For the last part, see my previous post where I explain how I simply sort stuff using a so called ''SceneGraph''.

Now about your invisibility stuff, I use something called time coherency (or is it space?) which assumes that the camera don''t teleport or move very fast, so what was true for last frame still is.

Then, I cache the discarding plane into the object, and I check that the plane still rejects the object, is not, then I check the object against every frustrum plane to know its new status.
Visible objects are checked against the frustrum (potentialy as much as 6 plane checks to know that they are visible but if you learn that it''s not you won )

Treat your spatial structure Nodes as if they were objects, if possible.

If there''s any faster way to discard geometry, I''ll be happy to learn about it.

-* So many things to do, so little time to spend. *-
-* So many things to do, so little time to spend. *-
Ingenu
Ingenu
quote:
Original post by Zarmax
fill a vertex array for each shader, effectively sorting by shader and visibility.


Forgot to mention that wouldn''t work as you wish, because there''s ''a lot'' of stuff that depends on your Mesh/Object/Model, and you wouldn''t know about to it.
That could work for simple ''shaders'', but for characters it wouldn''t, because of the lack of data concerning say bones, and other states you might need.

IMO a unified pipeline is the key to get something fast and decent which isn''t a nightmare to maintain and expand.


-* So many things to do, so little time to spend. *-
-* So many things to do, so little time to spend. *-
Zarmax
Zarmax
Thinking about the problem again and after reading some posts...

65k + tris could happen when you are talking about the "world" or "level" object. I was originally thinking that you needed one big vertex array and each leaf node contains indices into the one big array. But if each leaf has its own VA, then I suspect 65k will be MORE than enough.

So the optimization problem seems to be like the old Features, Resources, Schedule problem where you get to pick two...and take the hit on the third.

So what I was thinking to sort out the static poly soup is to put it into a spatial tree (oct/bin/etc) and at each node have a vertex array (which can be cached and locked in VRAM based on frame coherency) and an index array all sorted by shader and then by vertex (for cache hit performance). Transparently shaded objects would go at the end of the list and be zsorted right before rendering. This could all be done at build time for the tree. I would then take the hit and have potentially multiple same shader swaps when rendering the tree, but would save any CPU memory copies to reorder into render buckets. From what I have read you can traverse an octree by depth, so transparent objects would still work out if you went back to front in the tree traversal.

I was also thinking you could also do a quick front to back traversal of the tree setting visibility as you went by doing some occlusion testing. This would just set "visible/invisible" flags. Then traverse the tree back to front and render the visible nodes.

That sound better?
Ingenu
Ingenu
Sounds much better to me, now you need to code the beast... which might not be that easy ^^

-* So many things to do, so little time to spend. *-
-* So many things to do, so little time to spend. *-
Monochrome
Monochrome
quote:
To get even more performance, you can copy those arrays to video RAM, rendering will be more efficient from there.


How exactly do you copy arrays as well as textures to video RAM ? Which GL extension do you use?

I always wanted to know this bit, but people keep telling me that the drivers handle these things, and no guarantee your array is going to be in Video RAM.

Please enlighten me, Thanks.




[edited by - Monochrome on May 24, 2003 1:50:49 AM]
Ingenu
Ingenu
Using VBO you can tell what usage you''ll make of it, so the driver will place it where appropriate, although you cannot be sure, it is commonly agreed that it''ll do what you expect.

I believe that write once render many times is VRAM, write/render often is AGP, write often render few times is RAM.

See the extensions specs to know more about the different settings.


-* So many things to do, so little time to spend. *-
-* So many things to do, so little time to spend. *-

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.