While Metal has come to Mac. The desktop GPUs have generally support OpenGL 4.1 which supports geometry shader and tessellation-relative shaders.
Will Metal introduce these two shaders?
While Metal has come to Mac. The desktop GPUs have generally support OpenGL 4.1 which supports geometry shader and tessellation-relative shaders.
Will Metal introduce these two shaders?
I sure hope so - we have a number of games ported from D3D11 that are using these now.
To give a concrete example, Shadow of Mordor requires both tess and geometry shaders to render the world properly, so their absence in Metal is a show-stopper for that title.
Metal does support layered rendering on OS X, if that's what you were going to use geometry shaders for. The 'What's new in Metal, Part 1' WWDC video talks about it a bit (starting at 24 minutes and 20 seconds in the video): https://developer.apple.com/videos/wwdc/2015/?id=603
The usage I've seen varies on the port, but:
* 2 games that use geometry shaders do so to render particle effects. One game also uses layered outputs (and multiple viewports). I haven't investigated yet how the specific algorithm works, other than to note that it seems to be working as expected with the support in GL 4.1.
* The tessellation shader use-cases so far have been: cloth tessellation (flags in a few racing games, parts of Batman in the Batman Arkham games), water surface tessellation (seen in a few racing games and Tomb Raider), Hitman's bald head in Hitman Absolution, Talion's body in Shadow of Mordor, and higher-detail terrain in Mordor.
A few of our upcoming titles also use these to varying degrees. We have a radar open with more details.
Thank.
Thanks. The video helps me a lot.
Thanks for your reply.
I believe the best way to emmulate geometry shaders currently is to write per vertex data directly into an attached output buffer from within the vertex function. There is a good example of this setup on the Metal Programming Guide Page under the section Vertex Function Example using Attribute Qualifiers.
Below is a snippet of the vertex function shown in the programming guide:
render_vertex(const device VertexInput* v_in [[ buffer(0) ]],
constant float4x4& mvp_matrix [[ buffer(1) ]],
constant LightDesc& light_desc [[ buffer(2) ]],
device VertexOutput* xform_output [[ buffer(3) ]],
uint v_id [[ vertex_id ]] )
{
VertexOutput v_out;
v_out.position = v_in[v_id].position * mvp_matrix;
v_out.color = do_lighting(v_in[v_id].position,
v_in[v_id].normal,
light_desc);
v_out.texcoord = v_in[v_id].texcoord;
// output position to a buffer
xform_output[v_id] = v_out.position;
}You could issue a drawPrimitives command using Point primitives. The render_vertex function takes as input both the vertex ID (v_id) and the vertex buffer, buffer(0), containing the vertex position. For every v_id you could write 3 positions into buffer(3), the output buffer. Then on a second rendering pass you could render buffer(3) as the attached vertex buffer with primitives to set to Triangles, thus emulating the point-to-triangle conversion of a typical geometry shader.
Thank you for your detailed reply. It really works.😉
It is very much possible that Metal does not need geometry shaders at all. Common applications of geometry shaders are easy enough to implement via vertex/compute shaders (dustinb illustrates this very nicely), you don't need much more than buffer writes + indirect draw commands for that. Of course, it woudl be interesting to know how the performance compares (i.e whether there is some dedicated hardware in the GPU to support geometry shaders).
However, tesseslation is very different. Tesselation is part of fixed function hardware in the GPU, hardware which Metal simply doesn't expose. I would like to see it in Metal 2.0 or so this year 🙂 Along with more explicit synronisation primitives, batch ressource binding (aka Vulkan's descriptor sets), virtual ressources and other things that are lacking from the API currently.
Concerning the fact that Metal does not need geometry shaders, I think this is a wrong assertion... the geometry shaders allow to do some things that is not possible to do with such a trick. For example, It allows to compute the normal of a triangle or other per primitive components. And I cannot imagine any way to do such a computation with this 2 passes vertex shader method.
If so, I would really appreciate to know your approach !
True, that is a good point. I think there is a way to do it without a GS stage, provided the vertices are not indexed. You can use vertex_id to compute the id of the primitive, e.g. something like this in the compute shader/first vertex pass:
normal[v0id/3] = compute_normal(pos[v0id], pos[v1id], pos[v2id])
and then in the vertex shader for rendering:
normal = normals[vertex_id/3]
However, this won't work for indexed primitives. For that, we'd need a primitive_id attribute. It is also unclear what the impact on the performance vs. a GS approach would be. An explicit GS stage should be able to manage scenarios liek these more efficiently, e.g. by keeping all GS output in the cache, processing small batches of primitives at a time and avoiding unnessesary copies/allocations.
So I guess you are right and a GS stage can be quite useful after all. Alternatively, I can imagine an API that offers the ability to create custom pipelines, where shader (or just call them processing) stages are set up as components connected by pipes and otehr data flow connectors, with explicit invocations of fixed function hardware in between.
"For that, we'd need a primitive_id attribute"
It would be a nonsense in a vertex pipeline, because a vertex can be shared between many primitives.
"Alternatively, I can imagine an API that offers the ability to create custom pipelines, where shader (or just call them processing) stages are set up as components connected by pipes and otehr data flow connectors, with explicit invocations of fixed function hardware in between."
Yep, this could be a great feature for sure ! I'm pretty sure that it could be simulated via compute kernels. But performances could decrease dramatically, and a fixed pipeline is able to handle this in a more efficient way.
I'm relatively new to GPU programming, Metal being the first time I've done more than copy/paste someone else's GLSL code. Certainly not in a position to argue for/against tessellation or geometry shaders, I did however do some experiments a while ago using Metal compute shaders to update vertex and normal data.
The nice thing about the compute shader approach it that you only need to recalculate the vertex normals when the mesh is deformed, instead of every frame.
Source code is over on GitHub, but it's fairly straight forward. Two compute kernels; one updates the vertex positions, the next updates the normals (using jcookie's approach). The default SceneKit renderer is then used for the rendering.
Geometry shaders can also be used for removing things. For example, you can create simple particle system (particles with finite "life") where geometry shader cancels particles that come to the end of life. Or inserts new particle in between existing ones (for example in adaptive contours). This is pretty simple in OpenGL with gs and vertex feedback. In Metal, you have to write compute kernels for re-arranging particles in buffer so that "dead" ones get discarted, and space gets allocated for "new" ones. Can be solved using parallel-prefix-scan algorithm, but is nontrivial.