Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

A question about efficency

edited July 2014 in Questions Posts: 43

Here's the deal; I want to create a minecraft-like environment as far as chunks and blocks go. I have got some code in place that is delivering terrible performance when I get above 10 by 10 by 10 in one chunk. I am doing some face culling in form of removing faces in between blocks and backface culling cutting vertices to about 3600 in this case, which is giving me about 20fps.

One block consists of 6 Polygon faces which in turn is made up of one mesh and 6 vertices, the whole mesh is removed when I do the culling in between blocks.

With backface culling off I get a more constant fps but lower than with it on, clocking in at 17-18fps.
Note: The backface culling is not removing polygons, only deciding if they should be drawn or not.

I do have a simple lightsystem in place which is only calculated once and will probably be remade when I learn more about shaders. It does not deliver any performance hit at runtime.

When it comes to optimizing this I do have a few thoughts in mind but I would like some input about these and which one that might be the best option in this case.

  • Generating a chunk as one complete mesh(I am not sure if this is possible though)
  • Merging each face on each block into one mesh = 1mesh/Block
  • Somehow creating only one block and then just making copies of it(Like a VBO object)
  • If you have a better suggestion than any of these then I'd gladly like to hear it

I'm pretty new to the shader concept and I have yet to understand it completely, but I kinda know how they work.
And as I understand using shaders to do some of the work would increase the performance alot.

Comments

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - as I recall from my own experiments, performance is linked quite closely to the number of vertices.

    If all your blocks are the same size, you should only need to build one of them initially, then you can draw it as many times you need with different textures.

    I guess you know you should sort any overlapping blocks or other objects, to draw the furthest ones first, to avoid flicker and other artifacts.

    OpenGL is supposed to do its own face culling, but I'm not an expert on that.

    I have written an ebook on shaders, and another on lighting, based on my own explorations, which you can find here.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - reading your post again, I'll explain a little further that you only need to create one mesh cube, centred on 0,0,0. Then you can set the texture, translate to any position, draw the cube, and repeat as required to draw everything. (As I recall, you should specify your mesh triangles for the cube in an anti clockwise direction around the vertices, as Open GL uses this to determine which are front faces and which are back faces, when it does its own culling - but it's been a little while since I did this).

    Shaders aren't going to solve the basic speed problem in drawing and culling cubes, but you may find a use for them.

    In one of my posts, I show how to create a hemispherical sky which would look very nice in your world.

  • IgnatzIgnatz Mod
    edited July 2014 Posts: 5,396

    @Muffincoder - a couple more thoughts. Narrowing the field of view from the default of 45 to 30 degrees shouldn't hurt the look of your app, but will cut down on the drawing.

    You can also restrict the depth that can be viewed, again to cut down the amount you draw. I think I may have covered this an another ebook I wrote on 3D in Codea (same link as above).

    Alternatively, you could use mist to hide the faraway stuff, cutting back the drawing required. This is where a shader should help you (and I explain mist shaders in my ebook), although there is a balance between the amount of work required to check whether each pixel is visible, and the time saved in not drawing it.

  • @Ignatz Your thoughts seem quite interesting though some of it (like fov and mist) wouldn't help performance in the current state where I have a small chunk which is completely visible. But I'll keep them in mind for the future :)

    About the face culling already being in opengl is right, I actually have used it once or twice before in other projects, though it is not activated by default and since I cannot access the opengl functions in Codea, I have to make my own culling (If there is a way I am not aware of then please tell me)

    Your thoughts about creating one cube mesh and redrawing it would make the faces between two neighbouring cubes be drawn, Though not reinstantiating a class for each polygon could be the better solution.

    And yes, performance is vertex dependant if you got optimized code on each draw call. I tested by making a new project with one mesh and add rectangles to it the size of 2 by 2 pixels, It got smoothly up to about 200 000 rectangles and about 28fps indicating that the amount of total meshes should be kept to a minimum. Though I am not sure if Codea handles rectangles this way with vertex points which 3D meshes do.

  • IgnatzIgnatz Mod
    edited July 2014 Posts: 5,396

    @Muffincoder - rectangles are just meshes behind the scenes.

    I don't think you can access OpenGL commands from Codea. Wrt what you are able to do, I was advised that the last two pages of this document contain what you can do in Codea.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - rather than culling parts of blocks, have you tried simply drawing complete blocks on the outside layer and culling complete blocks within (ie only culling completely invisible blocks)? While this involves drawing more faces, you avoid all the work required to cull vertices, because you are basically just drawing one pre-made block mesh over and over, using a 3d table in memory to guide you as to what needs to be drawn or not.

  • @Ignatz Now after some coding I have reduced the total meshes to 6(1 per face) and the cube then calls the class containing these meshes and translates and draws the necessary faces in the position of the current block.

    What's surprising is that the same 10 by 10 by 10 with 3600 verts is still giving that 20 fps as before, no improvement nor any decrease in performance.(Note that this is without backface culling though)

    I also found that not drawing any textures gave the same scene about 40 fps meaning the texture part in the drawing is quite heavy...

    As for the culling I'm currently just going through all blocks and check if any of the neighbouring 'slots' is occupied by a block, so that part should not be any problem since it is ran once)

  • Jmv38Jmv38 Mod
    Posts: 3,295

    @muffincoder. Maybe you should just remove the faces that touch one another? They will never show up, so you are drawing them for nothing, but still they cost on the GPU.

  • I have no idea why but after some testing I concluded that increasing the block count decreases the 'fpsdrop'. If I have about 100 blocks(10x10x10) and add about 21 blocks the fps drops by about 10 fps, but if I have a 20x20x20 I run at ~5fps and at 20x30x20 at ~4fps which is a increase in about 200 blocks(though alot get culled away but still) and a fpsdrop in about 1fps.

    This seems quite strange to me...

  • @Jmv38 Yea, that's the only type of culling I'm using atm.

    Before my latest optimization which reduced all meshes to a total of 6 I also used my own backface culling which I haven't adapted to the new type of face rendering yet.

  • Jmv38Jmv38 Mod
    Posts: 3,295

    @muffincoder the processing time is not proportionnal to fps but to 1/fps. So 1 fps drop from 30 fps is about 1/30s, while 1 fps dtop from 10 fps is about 1/10s.. 3x times longer! This is what gets you confused.

  • Posts: 425

    But also the cps is affected by the total surface area of the blocks, not the amount.

  • @Jmv38 Oh, right! Thanks, I didn't notice that :)

    I'm getting quite frustrated on my project not delivering the performance I expect it to, it's not my first time that happens, in fact every project does just that :/

    I can't understand how some games/programs can run alot more complicated geometry and things happening at the same time on the same hardware I'm struggling on getting something alot simpler to run smoothly on.

    Though I think alot of it is in writing optimal code for the platform you're developing on..

  • @Coder That is not true, I tested with reducing the size of the blocks to one tenth of the original size and got the same fps as with the original.

  • Jmv38Jmv38 Mod
    Posts: 3,295

    @muffincoder.
    try to run this program: it defines a fullscreen mesh with 1 rectangle. The fps is displayed on top of that.
    tap the right part of the screen to add 1 fullscreen rectangle. Tap the left to decrease.

    On my ipad air i can have 14 fullscreen rects until the fps drops at 50 fps. On ipad1 you would be 5x slower...

    So you cannot, in any case, display more than 14 layers on top of another, and keep the fps good. 14 is not so bad.

    So if you manage your core program to run at 50 hz, and can do the trick with 14 layers or less, then you have it!


    --# Main -- speedtest -- Use this function to perform your initial setup local floor = math.floor local m function setup() displayMode(FULLSCREEN) N=1 meshUpdate(N) fps = 60 fontSize(70) fill(0) end function meshUpdate(N) m = mesh() m.texture = readImage("Cargo Bot:Codea Icon") for i=1,N do dx,dy = math.random()*100-50 , math.random()*100-50 m:addRect(WIDTH/2 + dx,HEIGHT/2+dy ,WIDTH,HEIGHT) end end -- This function gets called once every frame function draw() -- This sets a dark background color background(40, 40, 50) fps = 1/DeltaTime*0.02 + fps*0.98 m:draw() text(tostring(N).." screens = "..tostring(floor(fps+0.5)).."fps",WIDTH/2,HEIGHT/2) end function touched(t) if t.state~=BEGAN then return end if t.x > WIDTH/2 then N = N + 1 else N = N - 1 end if N<1 then N=1 end meshUpdate(N) end
  • Posts: 1,976

    In my experience, multiple meshes is laggy. I would recommend have one mesh per chunk, or one giant mesh for each type of block's faces. I would also try and generate the meshes so the vertices are only made for visible faces, which would require some math but shouldn't be too hard. Also, OpenGL doesn't automatically cull backfaces, so you should have this line at the top of your main() in your fragment shader:

    if (!gl_FrontFacing) discard;
    
  • @Jmv38 Hmm, quite interesting.. Well to start with I managed to get 16 screens at 50 fps on my ipad 4(not too sure if 3).

    I tested with reducing the size of the screens which greatly increased the amount of rects I could get, which proves your point @Coder , I did not expect this I must say...

    Well, this complicates everything..
    But how in the world can the iPad run minecraft if each of the blocks there have their own faces? There has to be some very complicated reductions made there.

  • So in other words, if you need to have a large mesh on a iPad you will have to do some real aggressive reductions and only draw the bare minimum to be able to draw at good framerates, am I right?

    Well, I haven't done too much work on reducing mesh vertices and optimizations regarding that so I really need some advice where to start.

    I do have an idea how I'd enable backface culling in the shader (Thanks @SkyTheCoder) but when it comes to having a cube with random holes in it, I have no idea how I'd reduce the vertices in that.

  • Jmv38Jmv38 Mod
    edited July 2014 Posts: 3,295

    minecraft only draws a few layers (the good ones) i am sure. And if your objects are smaller on the screen, they will take less time to draw, so you can have more.
    My program above gives you an estimate of your (best case) total screen budget: width x height x 16 is all the pixel you can draw at 50Hz.
    You should keep it simple: no transparent cubes, or cubes with holes. When one builds something in minecraft, you mainly see 1 layer...
    You could use the trick ignatz used for 3D touch functions: he draw the scene into an image A, the color of each face is the n° of the face. Then you know exactly which faces are visible. These ones, and only these ones (and their neighbours), should be drawn to the screen. the image A doesnt have to be full screen size. You must check regularly (every s?) which faces are visible.
    This is just a suggestion, i am no expert in this field.

  • Jmv38Jmv38 Mod
    edited July 2014 Posts: 3,295

    one more thing: your choice of having 6 meshes, one for each face, wont work: 3d is correctly managed within a mesh, but not between meshes. once drawn, each mesh is a 2d image with not depth...

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - you can poke holes in faces using a shader (by simply not drawing certain pixels), either by passing through bounding coordinates or else by using a particular colour as a signal not to draw. However, if you want those holes to have a 3D appearance, there may be no alternative to constructing the block out of smaller pieces which fit together seamlessly and leave a hole. But that is a big hit on performance.

    The trick Jmv38 refers to, is about reacting to touches, which is a problem you may not have considered yet. If you want to be able to touch a specific object, which may be partly hidden behind another object, it is hard, because Codea can't tell you what you are touching, as what is on the screen is just pixels, the objects cannot be distinguished. But you have at least two choices.

    1. @Andrew_Stacey (our mathematician in residence) has code that does this for regular objects (that are reasonably well behaved mathematically).

    2. I also have a kludge that involves trapping the touch position, then drawing the next frame to an image in memory, replacing all the touchable objects by coded colours, then looking at the colour of the pixel in the touched position to get the colour and thereby the object. It means the drawing skips a beat but if your FPS is fast enough you won't notice. This works best for irregular or holey objects, eg scanned tree images where you want to touch something showing behind the trunk, and the math would be pretty impossible.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - I might also try @SkyTheCoder's suggestion of setting the shader to do back face culling. If it does this well, you could end up creating just one mesh block, attached to a shader that does nothing except cull back faces (ie one extra line of code), and draw this block all over the place using different textures.

    I think quite a bit of experimentation is required....

  • Posts: 426

    @Jmv38

    one more thing: your choice of having 6 meshes, one for each face, wont work: 3d is correctly managed within a mesh, but not between meshes. once drawn, each mesh is a 2d image with not depth...

    Not true. OpenGl remembers the depth of each rendered pixel and only draws on top if the new pixel is closer to the eye than what is there. This is why if you don't have transparent pixels it is best to draw from front to back.

  • @Ignatz I got the backface culling in a shader to work now so thats one less problem to solve but when you say that I should make one mesh and just redraw it everywhere it is needed, that is in fact what I've done, but with 6 meshes instead of 1.

    And joining them to 1 mesh would result in hidden faces being drawn all over the place.

    I was thinking about how I could make one mesh for each chunk and it could be done as long as I somehow can identify which vertices belong to which block, which is the tricky part. I'm assuming that the vertices you give a mesh do not have to be connected triangles, just 3 connected vertices per triangle.

  • And one more thing, the performance problem I'm having is probably the draw calls bunched up from calling the draw method for each face visible.

    This could be avoided with batching, a functionality I do not know how to implement code-vise and would like some input on this matter.

  • edited July 2014 Posts: 454

    One thing I notice from a comment earlier, re one mesh for a cube and draw it with different textures, this may be inefficient. If you change the texture on the mesh it probably needs to pass the new texture to the shader. You could do multiple textures by putting all the textures in a bigger image and passing a parameter to the shader to select the texture (this would inside the shader set an offset to the right part of the texture image).

    Basically sending stuff to the shader requires memory transfers to the GPU, and these are expensive, it's why modifying vertices each draw is bad because it has to pass the vertex array to the GPU each frame, rather than once when the mesh is created and never again...

  • @spacemonkey You're right, I tested with only one preset texture and the 10x10x10 got a great performance boost to about 36fps, Thanks!

    I'm going to try and create one mesh of the whole chunk now and see what results I'll get, I'll keep you updated as I go.

  • Jmv38Jmv38 Mod
    Posts: 3,295

    @loopSpace thank you for the remark. I was really unaware of this.

  • edited July 2014 Posts: 43

    I'm getting there!
    When I'm building the whole chunk as one mesh I get around 55-60fps with a 10x10x10 chunk that has 30% of the blocks removed(which creates more faces than a solid chunk)

    EDIT: Which is a total of about 9200 faces(about half of them is culled though)

  • About sending one texture and setting an offset in the shader for cubes, is it possible to have separate textures and merge them into one at runtime?

  • Jmv38Jmv38 Mod
    Posts: 3,295

    yes, use setContext(A) to draw them in image A, then use A for texture

  • @Jmv38 Right, I had forgot about that functionality :P

    After some more testing I've recorded 30fps on a 16x16x30 which is far more than I could have ever come close to before :)

    Now I just have to figure out why the textures adds a fpsdrop when viewing the chunk from negative x coordinates...

  • edited July 2014 Posts: 43

    My findings show that when the camera moves to the negative x side of the blocks, all other faces become visible which should be culled away in the shader.

    Do I need to update the gl_FrontFacing variable or some other variable in openGl? As this seems to be the issue here...

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - what may be happening is that because you are drawing each face as a separate mesh, you are drawing them in a fixed order, presumably from back to front - assuming you are at zero x.

    If you are at the back of the blocks, looking back the other way, the order of the faces will now be in the wrong order, front to back, and the culling and overlapping may be faulty. You need to ensure you always draw from furthest to nearest (relative to the camera). What I did was sort all my meshes at each draw, based on distance from the camera. I would try doing this as a first step just to confirm that is the problem.

    PS You said you didn't want to use complete blocks because the back faces would be drawn, but isn't that what back face culling will deal with for you?

  • @Ignatz As I mentioned earlier I have now merged all meshes to one big mesh for the whole chunk which means that the order of drawing shouldn't matter afaik.

    And the matter with complete blocks wasn't the backfaces but the faces which are in between blocks. There would always be two faces in the same position right in between the blocks and one of then would be drawn as it is not a backface.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - fair enough

  • edited July 2014 Posts: 43

    @Ignatz I even tested with adding normals to each face without getting rid of this nasty effect...

    I'm running out of options here, any ideas?

    Edit: Here are some screenshots of the scene, the yellow box mark ~0,0,0
    http://imgur.com/RWMSbJW
    http://imgur.com/USKKCFL
    http://imgur.com/1wIP4wU

  • IgnatzIgnatz Mod
    edited July 2014 Posts: 5,396

    @Muffincoder - can you give us a clearer idea of what is happening, eg record a short video or post a screencap?

    I think you are saying that OpenGL is not culling correctly when you go behind your mesh.

    But I'm not sure what you're doing when you merge all your faces. Are you saying that at each draw, you figure out which faces will be visible to the camera, and including only those faces in the mesh?

    So a bit more detail would be helpful.... :)

  • edited July 2014 Posts: 43

    @Ignatz Oh, sorry if I'm a bit unclear.

    At start of the app I create the chunk and the blocks as array objects.

    Then I check which faces should be removed(ie. theres a neighbour block obscuring a face) and keep an array of numbers to keep record of which should be drawn and which shouldn't.

    After this I go through all block objects(still only classes with data, no mesh data yet) in the chunk and their array of which faces to be drawn and add 2triangles(one face) to the mesh in the proper position. (I add texture coordinates and normals too for each face)

    Lastly I generate the mesh from the vertexdata that I generated and add textures to it.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - do you add the faces to the mesh from furthest to nearest (the camera) or do you always add them in the same order? Sorting them by distance from the camera may make a difference.

    Also, are your triangle vertices defined in an anti clockwise direction?

  • @Ignatz The faces are added as the blocks are arranged in the array hence it would be hard to control which faces to draw first. AND the mesh is built only once, and it's very large, so rebuilding it every draw would not be an option here from a performance aspect.

    And yes one face is made up of two CCW triangles

  • edited July 2014 Posts: 43

    And as I have studied the look of the blocks beign drawn at negative x values, I've come to the conclusion that it has to be something about the draworder(which I know nothing about).

    So is it possible to control the draworder in a static mesh? If so how would I do that?

  • IgnatzIgnatz Mod
    Posts: 5,396

    @Muffincoder - OpenGL draws the triangles in the order you provide them, and (when they overlap) the optimum result comes from drawing them from furthest to nearest the camera, because OpenGL keeps track of the z position of each screen pixel as it processes each triangle. The worst case is when you have images with transparent pixels and draw something behind them, because OpenGL ignores the transparency and won't draw anything behind!

    One other thing to watch for is not to draw faces too close to each other (relative to camera position), eg within 0.5 pixels, because you are likely to get flickering as OpenGL gets confused about which one to show.

    As to controlling the draw order, you ultimately need to end up with a table of vertices in far-to-near distance order each time you draw. The question is how to do that without killing FPS.

    You don't want to have to rebuild the vertex (and normal and texture) tables at each draw.

    One possible option, if the mesh is only built once, is to build (say) 8 separate copies of it to start with, with vertices for each one sorted far-to-near for camera positions 45 degrees apart, all the way round the mesh. Then you choose the closest one to whatever camera position you are in. That would cost you in memory and setup time, but would not cost you anything in drawing speed.

  • @Ignatz That could work, I think 4 meshes would suffice in my case though.

    But isn't there any way I could somehow reverse draw a mesh in the shader? This could reduce the meshcount to two, or possibly one mesh for each chunk.

  • Posts: 454

    @Ignatz not sure about efficiency of drawing furthest to nearest... drawing furthest to nearest resolves the transparency issue, because furthest is drawn first, then drawing something "in front" which is transparent will give you working transparency.

    For efficiency I would expect "theoretically" that the opposite is true, because if you draw the nearest one first, then when it gets to drawing the one behind it'll realise it's occluded and not draw it. This would of course wreck any chance at transparency working.

  • Posts: 426

    I agree with spacemonkey. Unless you have transparency issues, draw from front to back.

  • edited July 2014 Posts: 43

    I'll just have to keep my opaque blocks in one mesh and the transparent ones in a different mesh.

    The problem with having 4meshes for one chunk is that atm my generation time is about 3-4sec for one chunk, and the drawdistance will have to be at least 5-8 chunks, which means about 25-64 chunks, and that means about 1-2minutes of generation delay. Unacceptable...

    Preferably 20-30sec for generation on startup, and a minimum of 5 chunk drawdistance means maximum 1sec for each chunk...

    So, this means I'll have to try to reduce the overhead for generating the mesh, by alot.

    Now I'm using a 3 layer array for keeping my blocks position in the chunk easily accessible, But is it faster to use a one layer array and just have an index for each block?

    Edit: Answering myself here. :P After testing I found no real difference in generation time if I used a 3 layer or 1 layer array for the block array. So I'll keep the 3 layer one for convenience. (If you have evidence showing different results in this matter then I'd like to see it, this is just how I concluded this)

Sign In or Register to comment.