PSA: I'm not super familiar with any of this, so there might be incorrect assumptions and mistakes, if so, sorry, I'll correct them as I learn more!
I've been playing around with DrawMeshInstancedIndirect, which is like the souped-up version of DrawMeshInstance, in that it's not limited to 1023 instances per draw. It's a bit more complex and confusing, but I think it's worth the effort to get working, there's a lot you can do with a whole lot of instancing.
Using it requires the use of StructuredBuffers in the shader, which until recently I didn't know how to do inside Amplify Shader Editor. But thankfully I was educated to the possibility by this post from GAND over on the Amplify Discord.
Amplify also recently updated their InstanceID node to be able to use SV_InstanceID via a toggle in the node properties. Which you'll need in Vert/frag shaders to access the necessary buffer index.

SHADERS:
These ASE shaders were made in the latest version, currently v1.8.9 rev 17, which is not yet available on the Unity Asset Store. You'll want to grab it from http://amplify.pt/download/
I've included both Surface and Vert/Frag shader versions with different setups.
The shaders require a bit more setup, to be compatible with the indirect instancing. Specifically, check in the Additional Directives section for the important inclusions.

Vert/Frag Additional Directives

Surface Additional Directives
Both versions need the StructuredBuffer<type> myBuffer; buffers defined there. While the surface shader versions also seem to require
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
#endif
surrounding the buffers, as well as a pragma instancing_options procedural:setup that sets up the unity_ObjectToWorld and unity_WorldToObject matrices. You can see in those shaders a little floating Custom Expression called "setup" which includes the necessary code.

Don't delete this even though it's not connected to anything. I'd like to find a way to avoid this and just offset the vertices via the positionBuffer, the way it's done in the vert/frag shader. I'll look into that more.
Once all that is setup, the rest of the shader seems to functions as you'd expect.
You can pass in multiple buffers - in the hundred thousand quads covering the ground in the included example, I've passed in not just the position, but the surface normal and vertex colours, so the quads are shaded to match the ground. I'm using three <float4> buffers in that example, but I expect it would be better to package them all up into one struct and just send that.
At the moment the buffer is populated once, and it stays where it is. I think I can get the positions to update in the shader via the object matrix though, so at least they can move when the mesh is repositioned. Another thing to look into.
SCRIPTS:
I've included two scripts to generate points. One just spreads them out in a grid (the balls floating in the air), and the other generates them on the surface of a mesh, which I based on https://gist.github.com/v21/5378391
The mesh surface version can be pretty slow when generating 100k points on a mesh with tens of thousands of triangles (it can take a minute or more). You only need to do it once though, offline, and once they're generated updating the buffer is quick.
I hope you find this interesting, please let me know on Discord or in the comments if you have any questions or clarifications, and we can figure it out together.
Thanks again for your continued support.