It seems likely that the mechanism by which the
gl_PrimitiveID is generated is, in some way, based on the hardware that makes geometry shaders possible.
OpenGL ES 3.1 contains compute shaders but not geometry shaders (or tessellation). But it doesn't have
gl_PrimitiveID. By contrast, ES 3.2 contains GS's and
gl_PrimitiveID. The same goes for desktop OpenGL 3.1 vs. 3.2 and its GS functionality. Having a GS and having the ability to identify primitives in a sequence seem to be part of the same general functionality.
In my pipeline I used gl_PrimitiveID to fetch per-face data from global ssbo and to render face ids to attachment for later reuse.
Without the hardware functionality, there is no general way to compute a count of primitives. There are only specific solutions for specific circumstances.
For example, if you are using non-index rendering of triangle lists, then you can use
gl_VertexID / 3 to compute a primitive ID. Obviously that's a pretty tight restriction, but it would work in that circumstance.
You could also provide each vertex with a per-vertex face ID. The VS would output this value using
flat interpolation. By carefully matching the face ID with the provoking vertex for the primitive topology, you can effectively assign a face ID to a face. This works best with indexed triangle lists, as you can play around with the order of indices in a triangle more than you can with strips. You basically have to decide which vertex maps to which face, and make sure that that face uses that vertex as the first one.