The BatchRendererGroup might be what you are looking for. It can allow you to store most data as GPU buffers and only upload an index list of visible instances for that frame. It also works the same way as Graphics.RenderMeshX as it can insert draws into all cull results and passes of the rendering, but you will have to do a lot of work your self per cull result.
It’s not as simple though (actually quite far from it) and requires URP/HDRP (or custom code in shaders). You will have to do one draw per material/mesh combination of course but can probably get away with a lot less data uploads