Bindless Texturing, Virtual Texture and Array Texture
I'm not sure which method to start first, there got be some missing details. So I'd like to know more options on those methods.
You're right, there's one missing thing - you forgot bindless textures. Bindless textures are a concept different from sparse textures - while sparse/virtual textures have potentially large textures that is only partly allocated, bindless textures will give you the same advantage as array textures. Basically, you have a texture and generate a global handle that you can use everywhere in your shaders without having to bind the texture. Before that, the texture is made resident. In your case you could generate a texture handle, save it in your global material buffer and afterwards referencing it via your material attributes, or write it to a texture of your gbuffer (not recommended). Instead of indexing into an array texture, where you can only have textures with the same size and format, you can use the handle directly and sample with the given uvs from the texture in a deferred way.
EDIT: To be clear, you could use sparse textures in combination with this approach. Even it doesn't make that much sense at first sight, you could make every single texture a sparse one. Virtual textures are nowadays mostly used for very large textures...for example you could combine all surface textures of a scene into a large one and make it sparse, with loading/unloading small pieces of it at runtime. But this brings other problems you don't want to have. I would recommend you to use bindless textures at first, I used it, it's simple to integrate if your hardware supports it and it gets the job done 100%. Performance overhead wouldnt matter that much, as I experienced similar experience as with array textures.
I'm not sure which method to start first, there got be some missing details. So I'd like to know more options on those methods.
You're right, there's one missing thing - you forgot bindless textures. Bindless textures are a concept different from sparse textures - while sparse/virtual textures have potentially large textures that is only partly allocated, bindless textures will give you the same advantage as array textures. Basically, you have a texture and generate a global handle that you can use everywhere in your shaders without having to bind the texture. Before that, the texture is made resident. In your case you could generate a texture handle, save it in your global material buffer and afterwards referencing it via your material attributes, or write it to a texture of your gbuffer (not recommended). Instead of indexing into an array texture, where you can only have textures with the same size and format, you can use the handle directly and sample with the given uvs from the texture in a deferred way.
EDIT: To be clear, you could use sparse textures in combination with this approach. Even it doesn't make that much sense at first sight, you could make every single texture a sparse one. Virtual textures are nowadays mostly used for very large textures...for example you could combine all surface textures of a scene into a large one and make it sparse, with loading/unloading small pieces of it at runtime. But this brings other problems you don't want to have. I would recommend you to use bindless textures at first, I used it, it's simple to integrate if your hardware supports it and it gets the job done 100%. Performance overhead wouldnt matter that much, as I experienced similar experience as with array textures.
Thanks for your advice. I could use bindless texture extension for experiment, but my target API is opengl ES 3.0/3.1. As far as I know, ARB_bindless_texture is not supported on most mobile devices, so in the end I got to implement either Array Texture or Virtual Texture anyway.
Hm.... and it would be okay to use sparse textures, but not bindless textures? I don't know for sure, but sparse texture extensions came after bindless textures. And if you ask me, it could be problematic to use ARB_sparse_texture without EXT_sparse_texture2... because without the second extension, you have no chance to figure out whether a texture region is resident or not in your shader....effectively making the sampling cause "undefined behaviour" or returning black color or something. Please correct me if I'm wrong. If I didn't overlook something, sparse_texture2 wasn't even available on my GTX 770 desktop card...so you would rather end up using array textures solely.
Hm.... and it would be okay to use sparse textures, but not bindless textures? I don't know for sure, but sparse texture extensions came after bindless textures. And if you ask me, it could be problematic to use ARB_sparse_texture without EXT_sparse_texture2... because without the second extension, you have no chance to figure out whether a texture region is resident or not in your shader....effectively making the sampling cause "undefined behaviour" or returning black color or something. Please correct me if I'm wrong. If I didn't overlook something, sparse_texture2 wasn't even available on my GTX 770 desktop card...so you would rather end up using array textures solely.
Well, virtual texture can be achieved without hardware extension, use an extra texture as indirect table, just like they did it in Rage.
Also according to this article, it seems Doom still uses software virtual texture.
http://www.adriancourreges.com/blog/2016/09/09/doom-2016-graphics-study/