Asset System: Technical Design #87235

Open
opened 2021-04-06 14:59:39 +02:00 by Julian Eisel · 4 comments
Member

Asset System: Technical Design

Collaborative design work by @dr.sybren and @Severin.

NOTE: Also see #88184 (Asset System: Data Storage, Reading & UI Access).

Asset related concepts need to be integrated natively into Blender. This includes things like:

  • A representation of an asset (an abstraction for arbitrary entities: data-blocks, files, data-base entries, ...)
  • Asset libraries (entry points to access a possibly large collection of assets)
  • Asset catalogs (structure to organize assets for efficient browsing)
  • Asset metadata
  • Understanding of asset types and type characteristics
  • Mapping of type characteristics to behavior (e.g. visual feedback while dragging assets)
  • Loading of asset libraries, asset catalogs and asset
  • Asset library indexing for fast loading

The asset system needs to interact well with asset related UIs. Because of this, they are designed together, with a well-defined interface between them.

The current system in master (experimental feature) is mostly limited to the Asset Browser, so all that can be done by the File Browser editor (which the Asset Browser is using), much of it was already there for managing files.
However there is currently an asset view UI template in the works, that supports displaying assets anywhere in the UI, so it has to work outside the File/Asset Browser context. Plus, in future we want to allow Add-ons to register custom libraries, dynamically populate asset libraries, provide non-Blender data-block assets, etc.

Given the importance of asset management, we want to have a well thought out design in place, with well defined interfaces for general Blender code and Python access.


Here's a basic visual presentation of how components could work together (BPY is to be designed still, may just mirror the asset system):
Architecture Design - Planned Design (WIP).jpg

Initial design of the main interfaces and how that can be translated to the Python API.

We mocked up the main design units as code here, which for us (being coders) is a simple way to get a good grip of the design.

C++ Mockup

The highest level entity is the Asset System and provides basic functionality with global effect.

using AssetListReceiver =
    void (*)(AssetRepresentation asset,
             // For progress report, like "this is number 5 of the 10 assets
             // available". Note that BOTH can increase over time, for example
             // when more results are found, `total_asset_count` can increase.
             size_t total_asset_count,
             size_t asset_index);

/** AssetSystem manages multiple asset libraries. */
class AssetSystem {
public:
  void register_library(AssetLibrary library);
  void set_active_library(AssetLibrary library);
  AssetLibrary get_active_library();

  // Query the available assets of the active library that match the given filter.
  void available_assets(AssetFilter filter, AssetListReceiver receiver);
};

The AssetListReceiver and available_assets() part may be superseded by the asset storage design (see {T88184}). In there the storage is managed through AssetList and AssetListStorage (better separation of concerns). AssetSystem can be a mere interface.

/**
 * AssetIdentifier contains enough information to find the AssetLibrary that
 * contains it, and for that library to load the asset.
 */
class AssetIdentifier {
public:
  // URIs are one example of suitable identifiers, for example:
  //  cloud://cloud.blender.org/project/asset/file.jpg
  //  file:///path/to/project/asset/file.blend/Actions/MyPose
  //  smb://nas.mystudio/assets/file.blend/Objects/SuperSintel
  static AssetIdentifier from_uri(std::string_view uri);
  std::string as_uri() const;
};

It's unclear still if we need such full URIs in practice. It may also make sense to let asset management add-ons opt-out of URIs and use custom identifiers. The identifier could be just an abstract class with a purely virtual comparison operator.

using AssetReceiver = void (*)(AssetRepresentation asset);
using AssetErrorCallback = void (*)();  // TBD signature

/**
 * AssetLibrary manages a single library. This is just for information about
 * the library. The actual storage of assets is handled by #AssetList, and the
 * global #AssetListStorage.
 */
class AssetLibrary {
  // Expose the 'protocol:*host' part supported by this library, so that
  // AssetIdentifiers can be matched to a library that supports it.
  std::string uri_protocol() const;
  std::string uri_host() const;  // can be empty to indicate "any host"

  // Unsure whether this should be per AssetLibrary or on the AssetSystem (or
  // both).
  void register_error_callback(AssetErrorCallback callback);

  // Fast, as it only needs metadata and not the asset itself.
  void load_asset_repr(AssetIdentifier identifier, AssetReceiver callback);

  // Loads the actual asset, so could take more time.
  enum class LoadType {
    LINK,
    APPEND,
    NOMAIN,
  };
  void load_asset(AssetIdentifier identifier,
                  LoadType load_type,
                  AssetReceiver callback);
};
/**
 * AssetRepresentation represents the asset, and contains enough information to
 * show it in the UI (name, description, tags, preview, etc.).
 *
 * Information that takes longer to load, like previews, could be loaded
 * asynchronously and become available later.
 *
 * There could be several singletons with their own semantics, for example one
 * for 'data is still loading' or 'this could get expanded into several assets'.
 *
 * When the asset is loaded & available in memory, this also provides access to
 * it via the `as_id()` function. Not sure about this design choice, though.
 */
class AssetRepresentation {
  AssetIdentifier identifier;

  ID *as_id_datablock();  // Valid once actually loaded.
  
  /**
   * The preview-image for this asset, if available (local or )
   */
  PreviewImage *preview();
  /**
   * Path relative to the containing asset library. The asset-library and 
   * asset-list provide helpers to get the full path from this
   * #AssetRepresentation.
   */
  string relative_path();
};

We may want to differentiate between local and external assets here. AssetRepresentation could also be an (abstract) base class with LocalAssetRepresentation and ExternalAssetRepresentation as implementations.

This class needs a handle for C and RNA. E.g. so that operators can get asset information from context.

/** A handle as C alias. */
typedef struct AssetRepresentation * AssetHandle;

Python API

Most (but not all!) asset information is independent of the current file loaded. Exceptions are the Current File and the planned Current Project library. The Python API needs to work independent of bpy.data, but can do some interactions on it (e.g. after getting the ID of a local asset).

Therefore, a new module might make sense.

import asset_system

def register():
  asset_system.register_library(...)

# Example usage
def some_func():
  from asset_system.types import AssetLibrary
  
  library: AssetLibrary = asset_system.find_library(some_identifier)
  library.do_stuff()

Although maybe it shoudn't be a standalone module, it could be bpy.asset_system instead.

The API would then simply wrap the internal C/C++ API.

# Asset System: Technical Design Collaborative design work by @dr.sybren and @Severin. NOTE: Also see #88184 (Asset System: Data Storage, Reading & UI Access). Asset related concepts need to be integrated natively into Blender. This includes things like: - A representation of an asset (an abstraction for arbitrary entities: data-blocks, files, data-base entries, ...) - Asset libraries (entry points to access a possibly large collection of assets) - Asset catalogs (structure to organize assets for efficient browsing) - Asset metadata - Understanding of asset types and type characteristics - Mapping of type characteristics to behavior (e.g. visual feedback while dragging assets) - Loading of asset libraries, asset catalogs and asset - Asset library indexing for fast loading The asset system needs to interact well with asset related UIs. Because of this, they are designed together, with a well-defined interface between them. The current system in master (experimental feature) is mostly limited to the Asset Browser, so all that can be done by the File Browser editor (which the Asset Browser is using), much of it was already there for managing files. However there is currently an asset view UI template in the works, that supports displaying assets anywhere in the UI, so it has to work outside the File/Asset Browser context. Plus, in future we want to allow Add-ons to register custom libraries, dynamically populate asset libraries, provide non-Blender data-block assets, etc. Given the importance of asset management, we want to have a well thought out design in place, with well defined interfaces for general Blender code and Python access. ---- Here's a basic visual presentation of how components could work together (BPY is to be designed still, may just mirror the asset system): ![Architecture Design - Planned Design (WIP).jpg](https://archive.blender.org/developer/F13487581/Architecture_Design_-_Planned_Design__WIP_.jpg) Initial design of the main interfaces and how that can be translated to the Python API. We mocked up the main design units as code here, which for us (being coders) is a simple way to get a good grip of the design. ### C++ Mockup The highest level entity is the Asset System and provides basic functionality with global effect. ```cpp using AssetListReceiver = void (*)(AssetRepresentation asset, // For progress report, like "this is number 5 of the 10 assets // available". Note that BOTH can increase over time, for example // when more results are found, `total_asset_count` can increase. size_t total_asset_count, size_t asset_index); /** AssetSystem manages multiple asset libraries. */ class AssetSystem { public: void register_library(AssetLibrary library); void set_active_library(AssetLibrary library); AssetLibrary get_active_library(); // Query the available assets of the active library that match the given filter. void available_assets(AssetFilter filter, AssetListReceiver receiver); }; ``` The `AssetListReceiver` and `available_assets()` part may be superseded by the asset storage design (see {T88184}). In there the storage is managed through `AssetList` and `AssetListStorage` (better separation of concerns). `AssetSystem` can be a mere interface. ```cpp /** * AssetIdentifier contains enough information to find the AssetLibrary that * contains it, and for that library to load the asset. */ class AssetIdentifier { public: // URIs are one example of suitable identifiers, for example: // cloud://cloud.blender.org/project/asset/file.jpg // file:///path/to/project/asset/file.blend/Actions/MyPose // smb://nas.mystudio/assets/file.blend/Objects/SuperSintel static AssetIdentifier from_uri(std::string_view uri); std::string as_uri() const; }; ``` It's unclear still if we need such full URIs in practice. It may also make sense to let asset management add-ons opt-out of URIs and use custom identifiers. The identifier could be just an abstract class with a purely virtual comparison operator. ```cpp using AssetReceiver = void (*)(AssetRepresentation asset); using AssetErrorCallback = void (*)(); // TBD signature /** * AssetLibrary manages a single library. This is just for information about * the library. The actual storage of assets is handled by #AssetList, and the * global #AssetListStorage. */ class AssetLibrary { // Expose the 'protocol:*host' part supported by this library, so that // AssetIdentifiers can be matched to a library that supports it. std::string uri_protocol() const; std::string uri_host() const; // can be empty to indicate "any host" // Unsure whether this should be per AssetLibrary or on the AssetSystem (or // both). void register_error_callback(AssetErrorCallback callback); // Fast, as it only needs metadata and not the asset itself. void load_asset_repr(AssetIdentifier identifier, AssetReceiver callback); // Loads the actual asset, so could take more time. enum class LoadType { LINK, APPEND, NOMAIN, }; void load_asset(AssetIdentifier identifier, LoadType load_type, AssetReceiver callback); }; ``` ```cpp /** * AssetRepresentation represents the asset, and contains enough information to * show it in the UI (name, description, tags, preview, etc.). * * Information that takes longer to load, like previews, could be loaded * asynchronously and become available later. * * There could be several singletons with their own semantics, for example one * for 'data is still loading' or 'this could get expanded into several assets'. * * When the asset is loaded & available in memory, this also provides access to * it via the `as_id()` function. Not sure about this design choice, though. */ class AssetRepresentation { AssetIdentifier identifier; ID *as_id_datablock(); // Valid once actually loaded. /** * The preview-image for this asset, if available (local or ) */ PreviewImage *preview(); /** * Path relative to the containing asset library. The asset-library and * asset-list provide helpers to get the full path from this * #AssetRepresentation. */ string relative_path(); }; ``` We may want to differentiate between local and external assets here. `AssetRepresentation` could also be an (abstract) base class with `LocalAssetRepresentation` and `ExternalAssetRepresentation` as implementations. This class needs a handle for C and RNA. E.g. so that operators can get asset information from context. ```c /** A handle as C alias. */ typedef struct AssetRepresentation * AssetHandle; ``` ### Python API Most (but not all!) asset information is independent of the current file loaded. Exceptions are the *Current File* and the planned *Current Project* library. The Python API needs to work independent of `bpy.data`, but can do some interactions on it (e.g. after getting the ID of a local asset). Therefore, a new module might make sense. ```py import asset_system def register(): asset_system.register_library(...) # Example usage def some_func(): from asset_system.types import AssetLibrary library: AssetLibrary = asset_system.find_library(some_identifier) library.do_stuff() ``` Although maybe it shoudn't be a standalone module, it could be `bpy.asset_system` instead. The API would then simply wrap the internal C/C++ API.
Author
Member

Changed status from 'Needs Triage' to: 'Confirmed'

Changed status from 'Needs Triage' to: 'Confirmed'
Author
Member

Added subscriber: @JulianEisel

Added subscriber: @JulianEisel
Julian Eisel changed title from Asset System Technical Design to Asset System: Technical Design 2021-07-14 11:59:44 +02:00
Author
Member

Added subscriber: @dr.sybren

Added subscriber: @dr.sybren

Added subscriber: @AlexeyAdamitsky

Added subscriber: @AlexeyAdamitsky
Julian Eisel added
Interest
Asset System
and removed
Legacy
Asset Browser Project
labels 2024-05-29 15:06:20 +02:00
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset System
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Viewport & EEVEE
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Asset Browser Project
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Module
Viewport & EEVEE
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Severity
High
Severity
Low
Severity
Normal
Severity
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#87235
No description provided.