Skip to main content

News

Topic: neoSphere 5.9.2 (Read 537323 times) previous topic - next topic

0 Members and 26 Guests are viewing this topic.
  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1125
Speaking of Galileo, I found a bug in GetDefaultShaderProgram() which caused the default shaders to fail to compile.  3.0.3 fixes the issue.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1126
Initial experiments with Galileo-based map rendering is promising--running at 1920x1080, filling the screen with tiles uses around 2.5% CPU on my i7-6700HQ at 60fps when drawing tile-by-tile (= 4 vertices and 1 draw per tile), while rendering with Galileo (~5 vertices per tile, 1 draw per layer) dropped that to around 1.8%.  Now I'm curious what the results will be on a slower machine.

Now I just have to fix it so it supports repeating maps...

edit: Yay! Repeating maps work!  Hm... in theory I could add the persons on a layer to the layer's Group too... although then I'd be recalculating the entire map every frame, which defeats the purpose.
  • Last Edit: April 06, 2016, 03:04:57 am by Lord English
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.2
Reply #1127

As far as the texture coordinates of the burned vertex, I think it does not matter since the triangle that the burned vertex appears in is totally flat across the bottom of the tile. The next triangle is formed up the right-hand side of the triangle (again with zero width), and then finally the first triangle of the new tile is draw. I think (it's been a while since I looked at this code) that the only non-zero-width triangle this burned vertex appears in is overdrawn, and it only appears in a single non-zero-width triangle (which spans the bottom left of the new tile, the top left, and then the top right, and is immediately overdrawn by issuing the third vertex of the new tile).


It turns out that it actually does matter!  For solid tiles there's no issue, however in case of transparency you want to use (u1,v1) instead of (u2,v2) for that extra vertex.  Compare the screenshots below.  The (u1,v1) version still has a small artifact on the flame that I haven't figured out the cause of, but is otherwise perfect.  The (u2,v2) version has a very striking artifact on tiles with transparency due to "jumping" from one corner of the tile texture to the other during rendering.  That triangle isn't fully overdrawn because the tile is transparent, so the illusion is broken.

As for reducing the number of vertices, it might actually be possible.  Rather than rendering exclusively left-to-right like in the Turbo runtime, I suspect that if you construct the strip in a zigzag pattern, alternating between left-to-right and right-to-left for each line and burning a vertex for each tile to reset the texture coordinates, you could get it down to 3 vertices per tile.  This would make the rendering calculations more complicated, of course.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1128
Hm, ignore that 3-vertices-per-tile idea.  I just realized the issue: Once you issue the burner vertex the next triangle is zero-width by definition since two of its vertices will share the same position.  A more elegant way to do this would probably be to use a triangle list, but that bumps us up to 6 vertices per tile which might be a performance issue unless you also use an index buffer.

Who said trigonometry was useless? ;)
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: minisphere 3.0.3
Reply #1129
Well, the "solution" is to use a primitive reset object, or so most OpenGL folk have decided. But in this case, and in fact I'm fairly certain in all 2D cases, it's less overhead and is a simpler API to just burn a vertex (whereas you may need to burn up to three vertices per triangle in 3D).

I think the other solution could be to use the UV of the next vertex. This would still have overdrawn areas, but they'd be the right texturing at least.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1130
I will say that experimenting with Galileo I find it to be less complicated to use than I originally thought.

There are things that bug me about the JS API for Galileo though, I want to look into maybe redesigning it a bit in the next release.  I'm not sure exactly what it is but it feels awkward to use at times.  Shapes and Groups being immutable after construction is a bit annoying, for example.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: minisphere 3.0.3
Reply #1131
Groups and Shapes are not immutable...or at least they aren't in TurboSphere. Groups expose a `shapes` member which holds the array of shapes. Shapes have mutable images, although vertices are immutable simply because Shapes are relatively cheap to create, and due to how OpenGL is tooled it is the same cost (or even cheaper) to just create new Shapes than to modify an existing Shape's vertices.

I probably never updated any of the specs since actually updating a Group's array of Shapes required fully setting the shapes member, since I never quite figured out how to set data accessors on pure arrays in SM.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1132
Hm, so fixing all the glitches with transparent tiles ended up needing to burn two vertices:

Code: (c) [Select]

s_layer_groups[i] = new_group(get_default_shader());
layer_shape = new_shape(SHAPE_TRIANGLE_STRIP, get_atlas_image(tile_atlas));
for (y = 0; y < s_map->height; ++y) for (x = 0; x < s_map->width; ++x) {
tile = s_map->layers[i].tilemap[x + y * s_map->layers[i].width];
uv = get_atlas_uv(tile_atlas, tile.tile_index);
x1 = x * tile_width; x2 = x1 + tile_width;
y1 = y * tile_height; y2 = y1 + tile_height;
add_shape_vertex(layer_shape, vertex(x1, y1, uv.x1, uv.y1, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x2, y1, uv.x2, uv.y1, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x1, y2, uv.x1, uv.y2, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x2, y2, uv.x2, uv.y2, rgba(255, 255, 255, 255)));
if (x < s_map->layers[i].width - 1) {
tile = s_map->layers[i].tilemap[(x + 1) + y * s_map->layers[i].width];
uv = get_atlas_uv(tile_atlas, tile.tile_index);
add_shape_vertex(layer_shape, vertex(x2, y2, uv.x1, uv.y1, rgba(255, 255, 255, 255)));
add_shape_vertex(layer_shape, vertex(x2, y1, uv.x1, uv.y1, rgba(255, 255, 255, 255)));
}
}
upload_shape(layer_shape);
add_group_shape(s_layer_groups[i], layer_shape);
free_shape(layer_shape);


At this point I may as well just use a list instead of a strip, I think.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: minisphere 3.0.3
Reply #1133
Could you not just use the u[3], v[3] of the new tile on the burned vertex?

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1134
I think there was a reason I didn't do that, I don't remember what it was now.  I'll experiment some more later.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1135

Could you not just use the u[3], v[3] of the new tile on the burned vertex?


Okay, you were right.  I'm assuming by uv[3] you were referring to the bottom-left corner of the next tile, since that's the corner I used:
Code: (c) [Select]
add_shape_vertex(layer_shape, vertex(x2, y2, uv.x1, uv.y2, rgba(255, 255, 255, 255)));


And all the rendering artifacts seem to have gone away.

Sometimes it's a bit tough to visualize how the triangle strip will go.  I'm used to thinking in terms of quads, but graphics cards only render triangles. :)
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1136
Anyway, refactoring the map engine to use Galileo turned out to be a good idea.  I discovered a huge memory leak in the process that was not found due to lack of testing--when shapes were freed, their list of vertices was being leaked.  Kefka's Revenge ended up leaking over 3GB worth of vertices within seconds!

By the way, in case of animated maps, the map needs to be recalculated every time a tile changes.  You say Shapes are cheap to create.  Are they cheap enough that recalculating and uploading all the vertices for the entire map potentially every frame won't be an issue?  I assume that the vastly lower number of draw calls required will more than make up the difference, but I want to make sure.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: minisphere 3.0.3
Reply #1137
If Allegro is implementing its primitives similar to Sapphire, then it's as cheap if not cheaper to recreate Shapes than it is to modify texture coordinates.

In OpenGL, it's the same cost or more expensive to modify a buffer than as it is to upload the same data to a new buffer.

I would, however, expect that it's cheaper to play with texturing (possibly in shader, maybe swapping textures) than to create a new map of Shapes.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: minisphere 3.0.3
Reply #1138
I believe that al_create_vertex_buffer() is just a thin wrapper over OpenGL VAOs, so yeah, it should be similar in performance characteristics to Sapphire.  Although for older OpenGL versions where that function will fail I fall back to storing the vertices in a malloc'd buffer.  In that case I'm uploading more vertices per draw than will be visible, but I assume I'm unlikely to encounter such hardware that often these days, so I won't worry about it.

I do have to refactor a few things now, though, to maintain perfect emulation.  SetTileImage() is currently implemented by swapping out the image for a tile with the specified one.  Normally a tileset is a list of subimages of the tile atlas, any one of which can easily be replaced by an independent image (by the way: making an image which is a slice of another image is an awesome Allegro feature and greatly simplifies atlas management).  But that won't work now--instead the function will need to modify the atlas directly.

I guess the next thing I should do is refactor text rendering.  That's currently one of minisphere's weak points as discovered by Radnen way back in the early versions.  Rendering whole strings as a single primitive should improve it greatly.  It might even fix the performance issues I'm seeing in Specs' battle engine!
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: minisphere 3.0.3
Reply #1139

I believe that al_create_vertex_buffer() is just a thin wrapper over OpenGL VAOs, so yeah, it should be similar in performance characteristics to Sapphire.  Although for older OpenGL versions where that function will fail I fall back to storing the vertices in a malloc'd buffer.  In that case I'm uploading more vertices per draw than will be visible, but I assume I'm unlikely to encounter such hardware that often these days, so I won't worry about it.


That would be really old OpenGL. I would be quite surprised if any machines that can even work with Allegro didn't have VBOs (which are still used as the underlying storage API for VAOs, and have similar performance characteristics in this regard).