update: I didn't read the question carefully: the indirect call target is still inside the executable, and is itself an indirect jmp, according to the OP.
The answer below is discussing making the call rel32 go directly into the DLL.
That would require modifying the machine code at every call instruction to put in the right offset during dynamic loading of a DLL. (You don't know what address the DLL will be loaded at, so you don't know the rel32 distance between the executable and the DLL at link time of the executable.)
Using a table of function pointers puts all the relocation stuff in one place where it can be written efficiently while dynamic linking.
It also couldn't reach far enough in 64-bit code if a DLL was loaded more than 2GB away from code that needed to call it.
IIRC, Windows does support the scheme you describe for DLLs that you link normally (with the linker at compile time, not with a runtime dll import).
Every DLL has a "preferred" load address, and code that calls it optimistically uses call rel32 instructions so every call site needs fixups if that address isn't available. These fixups happen while the process is being loaded. With ASLR enabled, DLLs won't load at the same address every time.
Once the process is already running, its code pages will be read-only, so if these fixups are needed, it's a problem. This is probably why dynamic DLL imports don't use this mechanism. (The implementation could use VirtualProtect to make code pages writeable for these fixups, but it wouldn't be thread-safe to have two different threads importing DLLs at the same time. One thread might make a page read-only after it was done, but while another thread was still writing fixups to that page, resulting in a fault.)
Also, cross-modifying code isn't in general safe. Other threads could be running instructions in the same function where you were applying a fixup. You could atomically store the new rel32 using an xchg or something. This might be safe.
BTW, on Linux, even "normal" libraries like libc are called through a level of indirection like this (function-pointer from the Global Offset Table). See Sorry state of dynamic libraries on Linux.
It's partly a tradeoff between overhead of runtime dynamic linking (load times) vs. performance after it's loaded.