Mesh shaders seem to be a really good replacement for instance rendering! In a single draw call it can draw multiple different meshes, choose level of detail, and even perform frustum culling to skip vertex transformations for particles that aren't visible. Meshlets go from 256 triangles at highest resolution to 32 at lowest. 425984 particles (weird number, but this is what would run pretty stably at 60FPS on my system).