What Kevin did was basically reading that density map (on the cpu) when generating the grass patch meshes and stored the value in the vertices (the same value in all the vertices of 1 blade). Then when the patch was rendered, in the pixel shader, if this value was less than some threshold, he discarded the pixel. The system is a little bit different patches made of billboards, density is stored in a texture instead. So basically you render grass everywhere but the grass becomes invisible on the road.
It's explained in his thesis, page 67-71.
But what he describes doesn't work with instancing because each patch is a different mesh or uses different textures so you can't draw them with instancing.
A solution would be to store this density value per instance of grass blade in a uniform buffer instead. So that each grass patch is the same mesh and can be instanced.
For the patches made of billboards it's more complicated, you would need to have "1 texture" per billboard. You could make a big texture (or a texture array) that contains the equivalent of several billboards and store a texture coordinate offset per billboard in the constant buffer. Be sure to reuse the same part of the texture for billboards that look identical in this case.