Repository: godot
Issue: resource_format_binary slow at saving scenes with many meshes.
Labels: enhancement,topic:core,topic:import,performance

### Tested versions

Reproducible in latest.

### System information

Godot v4.3.dev.mono (3ce73e5d4) - Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4090 (NVIDIA; 31.0.15.4592) - AMD Ryzen 9 5950X 16-Core Processor (32 Threads)

### Issue description

I'm working with external tools that produces GLTF files that has quite a large number of small meshes (around 1000+, most being basic cuboid meshes), and a number of external textures (around 80 in my tests). When importing these GLTFs, the actual import time is superb, but then the process halts when "Saving..." the resource.

I got curious to why this is, since in my experience, the parsing and processing of the meshes, and potentially loading textures are the most expensive part, not saving.

I ran VS (basic) sampling profiler, and found some interesting things:

![ResourceSaver_profiling_hotpath](https://github.com/godotengine/godot/assets/18470258/8cedd8b7-2311-4946-8039-060b4bc337d1)

Looking at the hot path, we can see that the time spent saving basically boils down to two things:

1. Finding properties that are resource types
2. Getting property data

So I dug a bit deeper.

![ResourceSaver_profiling_find_resources](https://github.com/godotengine/godot/assets/18470258/f7554871-4e8c-446c-b934-57df31cb34ed)

Looking at the `_find_resources` function, there's not that much that can be said about it. Almost all of the time is spent looping over every property that exists in every node, recursively. The code is not really doing that much, but since there's a lot of properties, and a lot of individual meshes, all which have external resources, this just takes a lot of time. In my case, around a second (0.8s).

![ResourceSaver_profiling_Object_get](https://github.com/godotengine/godot/assets/18470258/f436aab8-7793-4db8-95b0-54848e4f62d5)

This is where it gets interesting.

`Get` sounded like an innocent little function to me, but looking further, it seems to be quite a lot more to it than meets the eye.

![ResourceSaver_profiling_Object_get_expanded](https://github.com/godotengine/godot/assets/18470258/6c07441f-cd6d-4213-80ec-26576c9ce1d6)

This is the "worst path" that gets taken by the `get` function. It seems to literally stall the GPU, multiple times per file.

![ResourceSaver_profiling_Object_get_code](https://github.com/godotengine/godot/assets/18470258/8c34d9c7-e0cd-422f-a525-f030b6eaa059)

Looking at the code, it seems like the idea here is to flush the GPU, map the GPU memory and then get the actual content for every mesh individually from the GPU. Now I haven't actually looked into this in detail and might misunderstand this fully.

![ResourceSaver_profiling_Object_get_vulkan_map_samples](https://github.com/godotengine/godot/assets/18470258/7c57e59f-bf5c-4825-a110-e528e7e14b07)

Out of 2524 samples taken (15 seconds of looping the save function) on the `get` function, _1979_ are in the "worst path", which I interpret as either being called a lot of times, or just taking a ridiculous amount of time.

A total of around 0.7 seconds are spent "getting" all the properties.

So how do we fix this?

The `_find_resources` function is looping over a gastronomical amount of properties per file, out of which almost none of them are used in the end. If this can be reduced somehow, maybe by sorting so that resources are always the first properties to be read, we might be able to early out and cut the time by quite a bit. Not sure if this is even possible though (just spitballing here).

The `get` actually calling vk flush seems like insanity to me, but there might be a good reason for it?

### Steps to reproduce

Import/reimport a GLTF with 1000+ meshes, and 80+ external textures.

### Minimal reproduction project (MRP)

[ResourceImporterSavingSlow.zip](https://github.com/godotengine/godot/files/13757145/ResourceImporterSavingSlow.zip)