POST-MORTEM

if you're looking for useful caching information, there's something in this xyz answer.
if you're looking for my real problem, it's unrelated to caching. a @preload variable cached a scene when a certain node was readied, causing the bizarre caching behavior. i was unaware of this variable.
test in a dedicated, minimal environment to reduce variables.
share knowledge, don't be greedy
science 101

past this point is a quickly modified rendition of the question. it's been edited for education's sake.
the unedited post was written in a haze of caffeine withdrawal and sinus headaches, so you're welcome.

REASON I'M POSTING HERE

none of the results below makes sense to me, nor is any of it expected, which means i clearly don't have a clue how caching works, and that bothers me.
i'm asking smart people to shed light, or offer advice to debug this better if "debug" is the right word. feel free to rip into this formatting too. i promise i never aced an essay in my life for a good reason.

SCENE TREE DETAILS HERE

There is a 'Mother' node that's handling the loading of child nodes.
It loads child nodes into the SubViewport node pictured below using ResourceLoader.load_threaded_request().
ResourceLoader.load_threaded_get_status() is used to make sure it doesn't break while loading. It's also used to draw a loading animation in the Label under the LoadScreeen node, and then to call $SubViewport.add_child(ChildScene) once it returns that the loading has finished.

note: Control nodes and Node3Ds pictured above are absent during testing. they are shown here to make very clear where nodes are being added during runtime.

PROCEDURE HERE

Trial A: Scene1 loads in automatically
On launch, it loads Scene1 featuring a very vertex heavy icosphere mesh imported as a .glb from Blender. It's instanced to Scene1 and made local.
Result:
Scene1 takes a while to load in.

Trial B: Scene1 loses mesh, Scene2 is introduced with the same(different instance) mesh
The mesh is removed from Scene1, and added to Scene2, made local, and MainMenu is introduced by Scene1 that will signal to Mother that it should Scene1.queue_free() and load Scene2 in its place, only when the player(me) hits the right(directional) arrow key.
Result:
Scene1 takes just as long to load, but Scene2 is instant. The mesh is totally absent from Scene1.

Trial C: Scene1 and Scene2 do not have the mesh, Scene3 gains mesh but never appears during runtime
I made a Scene3 that never enters the scenetree at any point. It exists inaccessible to the player during runtime. The mesh is removed from Scene2, added to Scene3 and made local.
Result:
same as Trial B.

ResourceLoader.load_threaded_request() called with ResourceLoader.CACHE_MODE_IGNORE results in all scenes taking a long time to load. because nothing is cached. i think.

  • xyz replied to this.
  • People are generally confused by resources. So let me try to salvage the thread by discussing the cache mode some more.

    Cache mode can be specified when calling load() and, according to docs, can have one of 3 values: ignore, reuse and replace. Docs do not really explain how each works precisely, so I'll give my (un)educated speculation here. If someone knows better, feel free to correct it.

    Ignore - resource is loaded but it is not cached by the loader. A subsequent attempt to load the same resource will load the data from the disk again.

    Reuse (default) - if not loaded before, the resource is loaded and cached. Otherwise the cache is returned.

    Replace - resource is forced to re-load and re-cache regardless of existing cache.

    It's worthy of note that resource's cache will be gone once its ref count goes to zero. For example:

    var r = ResourceLoader.load("res://icon.svg", "", ResourceLoader.CACHE_MODE_REUSE)
    print(ResourceLoader.has_cached("res://icon.svg"))
    r.unreference()
    print(ResourceLoader.has_cached("res://icon.svg"))

    Will print:
    true
    false

    packrat All scenes refer to the same mesh resource. When caching is enabled, this resource is loaded from disk to ram only once, the first time someone calls a function to load it. From that point on whoever loads that resource again just gets a reference to the resource object that's already in the ram, i.e. gets the cached resource.

      xyz interesting.
      so, a heavy scene with non-reused resources elsewhere in the project that the player never sees is potentially a gigabyte of RAM eaten up for essentially nothing if a hypothetical developer really sucked at optimizing?
      for example, Trial C described above in an exported project.

      • xyz replied to this.

        packrat If you don't need a resource, don't explicitly load it. Calling load() will, as the name suggests, do exactly that 🙂

        ResourceLoader doesn't care if a resource will be "used" or not. If you tell it to load a resource, it will do so. If you tell it to load it again, it will instead give you a reference to previously loaded data. Unless you tell it to ignore cache. In that case it will play dumb and load the same thing again.

        Developers who don't know what they're doing better stay away from ResourceLoader altogether. They should just assign their resources in the editor. References to these resources will be saved in tscn files and such resources will be loaded only if/when needed by the scenes.

          xyz
          oh no.
          i just found Scene2 was being preloaded in the script I wrote for the prompt to load it 2 weeks ago.
          the preload variable wasn't being used for anything, but it was placing that scene in the cache this whole time. i've done the tests again and everything looks what i would have expected from the start: things not being cached "seemingly" at random.
          that entire essay could have been avoided.

          i am not joking when i say i have spent 9 hours investigating this.

          I'm politely asking @MikeCL to delete this discussion before the masses find out i am terminally silly, or rename this discussion "Why should I replicate bugs inside a minimal reproduction?".

          i've had a sinus infection the past week. i havent been thinking clearly. i do want to give myself credit and say i already knew everything you just explained about caching, thus why i resorted to here after 9 hours of not noticing the @preload var oops at the top of a script i forgot to debug.

          • xyz replied to this.

            packrat before the masses find out i am terminally silly

            We already know you're terminally silly 😉

            People are generally confused by resources. So let me try to salvage the thread by discussing the cache mode some more.

            Cache mode can be specified when calling load() and, according to docs, can have one of 3 values: ignore, reuse and replace. Docs do not really explain how each works precisely, so I'll give my (un)educated speculation here. If someone knows better, feel free to correct it.

            Ignore - resource is loaded but it is not cached by the loader. A subsequent attempt to load the same resource will load the data from the disk again.

            Reuse (default) - if not loaded before, the resource is loaded and cached. Otherwise the cache is returned.

            Replace - resource is forced to re-load and re-cache regardless of existing cache.

            It's worthy of note that resource's cache will be gone once its ref count goes to zero. For example:

            var r = ResourceLoader.load("res://icon.svg", "", ResourceLoader.CACHE_MODE_REUSE)
            print(ResourceLoader.has_cached("res://icon.svg"))
            r.unreference()
            print(ResourceLoader.has_cached("res://icon.svg"))

            Will print:
            true
            false