Fixing resource management properly (includes tagging)
Open, HighPublic

Description

Resource Management

Purpose of the rewrite:

  • The current implementation is very buggy
  • The current implementation does not scale
  • The current implementation loads all resources on start-up
    • this is slow.
    • this takes a lot of memory: default pack is 120 mb of memory
    • even if the brushes get dropped after loading, the least amount of memory management is better because it takes less time.
    • some people have 20K amount of resources. These people don't want to have all of those loaded all the time. We wish to offer people grouping.

Grouping and Tagging

Resources should be groupable. Users need to be able to select which resources are visible in the user interface, group resources related to tasks together. Groups can be heterogenous. We have the following options to group resources:

  • Bundles
  • Library (Adobe brush library)
  • Tags:
    • The most flexible.
    • Usability is a bit buggier (adding/removing) Four types of tags:
      • Krita tags
      • Bundle tags. We have both bundles and tags, for each bundle an automatic tag is generated. Sometimes when the bundle is removed. Automatic Bundle Tag is troublesome because they have an ugly name.
        • We probably should stop automatic generating of tags for bundles. Bundles can be enabled and disabled in the resource manager.
        • We should allow search by bundle name in the resource browser search bar
        • We should recommend that bundle authors provide useful tags inside the bundle
      • Tags inside the bundle.
      • User tags
  • Resources can refer to each other. (tips, patterns)

We will consider bundles and adobe resource libraries as resource storage and not as user-visible grouping mechanisms. All grouping in Krita will be done with tags. Bundles and adobe resource libraries are read-only. (We will provide a way to generate and regenerate bundles.

Tags are considered to be a separate resource.

Translating tags

The tags Krita provides by default should be translatable. Tags contained in a bundle should also be translatable. We will provide a default set of useful tags that can be translated by the i18n community. Bundle authors can use these tags, or provide tags with translations. The bundle will provide a domain for translations: if a bundle contains a translated tag that is a duplicate of a tag provided by Krita or by another bundle, krita will use the translation only for the resources in the bundle.

Resource locations

Right now we have 2 places of resources that get loaded.

  • resources contained in the Krita installation: these are read-only
  • User resources.

There is a third place, namely .kra files, which can contain gamut masks and palettes. Maybe .kra files should be treated as temporary bundle storages.

These two folders are tricky to keep in sync and this gives a lot of trouble. We will package the default resources in the application binary through Qt's resource system (http://doc.qt.io/qt-5/resources.html). The bundled resources will be treated as a "template". On first run, the resources will be placed in the user's resource location. The user will be able to select one location where Krita will store resources.

  • There will be a button for restoring resource(s) to factory?
  • How to handle modified resource overriding.
    • Only copying files over when the brushes are new.
    • UUID?
    • Store database in bundle?
    • How to handle duplicated files with different UUID?
      • Scan function?
      • GUI duplicates?
      • Force duplicate brushes to be named differently?
  • update:
    • user didn't change anything
    • user changed stuff
    • User deleted stuff
    • Krita changed
    • Krita Deleted
    • Krita added stuff.

The user's local resource storage system will contain a database with metadata about installed resources (name, icon, history, location of the resource). The database should be sufficient to populate the resource selection models. Only when a resource is accessed should it be loaded.

It must be possible to locate the resource storage in a synchronized folder, like Dropbox, onedrive, icloud, google drive or nextcloud. The system should not have absolute paths to resources.

Note: should it be a goal to support network drives? If so, the database should not be in the storage location, because that would give us all the headaches of a single-user database masking as a multi-user database.

Synchronizing the database and the folder: this should be light-weight. We will check for:

  • filename: if we cannot find a file, or a new file appears, the database must be updated
  • creation/modification date. If the date differs, we need to run a checksum to determine whether there's been a real change.

This takes care of the situation where resources are changed with outside applications.

It should be possible to migrate a resource storage to a new system. Either through zipping up everything or an export/import mechanism.

What happens with resources on updating Krita

User made no changeUser changedUser Removed
Krita made no changeNo change
Krita changedCopy to userKeep user, no historyKeep remembered
Krita remove simpleRemoveKeep
Krita force removeRemove even when used a lotKeep
Krita addAdd resource

Simple Remove: Brush has been removed from the default set in a newer version of Krita.
Forced Remove: Brush has been determined to be buggy, and we want to be sure it or any derived brush gets removed.

We will implement a mechanism that checks how often and how recent a resource has been used: if Krita removes a brush from the default install but the user has used this resource often and recently, it will be kept and become a user-defined resource.

This means that a Krita update needs to have a list of resources removed since we implemented the new resource handling.

We might want to allow the user to review changed resources on udpating Krita. This should not be shown by default on updating, but as a flag in the resource history view. See next section.

What happens if the user adds an old compatibility bundle that contains resources that have the same "identity" as a new resource? The latest added resource will be the current version. Add a bundle with 2.9 resources that have the same uuid as current resources, and the current resources will be replaced, the old ones will become history states.

History of resources

We will implement a history management dialog that will show the history of resources: creating/installation, modification, removal, restore.

  • Make it possible to allow users to step backwards: recover earlier states of a resource
  • This needs restore to system or just clearing the history.
    • Add a collapse history state function which will delete everything but original and last state.
    • Handling states: a history slider
    • Locking states: users can flag a state for keeping, even if history will be cleared or collapsed
      • Which doesn't get removed.
      • Add favourite/bookmarked states
    • History can be collapsed automatically if there are more than X states: locked, favorite or bookmarked states do not count towards X.
    • Make default: a certain state that will be used as a default state to restore to.
  • Restart from a history?
  • Hide the resource and history cache from the user.

The history view should also have a resource inspector to view:

  • Metadata inside kpp files
    • GIMP and others delete the metadata, so that is awkward.
    • Users should be able to see the md5sum for resources. In fact, all the boring information should be visible to the user in some kind of distant window.

The resource inspector should also be available from the bundle editor.

Resources that contain resources

We will not consider bundles or libraries to be resources in their own right but as a storage mechanism.

Examples:

  1. Adobe brush libraries:
    • Treat the abr library as a bundle? Yes.
  2. Bundles
  3. Folders as a sort of bundle

Vector libraries are one resource per library, swatches are one resource per swatch file.

What is a resource, and what is not a resource

  • ASL (Adobe Style Library)
    • Is a resource without resourceserver.
  • Templates: though they should be
  • Workspaces and Sessions are technically resources.
  • Text/Paragraph/Shape styles: are not implemented yet. There is already a design for this.

Translating Resources

  • We currently translate brush presets and brush tips and other Gimp-based resources. We will stop doing this, because it makes it very difficult to support tutorials. However, we will show the translations as tooltips.
  • Translating templates (https://phabricator.kde.org/T8759).
    • Use keywords/vocabulary which can be translated.
    • And then when we create an image from the template it translates the keywords into proper language.
    • We can also do this when creating a new image

Resources that have no extensions

Not all resources have no sensible extension
Dmitry: Solve by database.

Dynamic Resources

These get created by Krita

  • They cannot be edited
  • They cannot be deleted.
  • Only gimp gradients can have foreground/background as part of the gradient definition.
  • Clipboard brush?
  • Clipboard pattern -- custom brush.

No resource state

If there's no resources at all, what should Krita do?

  • Create a new default brush?
    • If this situation happens, krita informs user that resource folder could not be accessed.
  • Crash?
  • No resources.

Answer: Just stick all resources into a qrc and let noone near them? Dmitry suggest using the paintopfactory. Decision made affects unit test.

Editing Bundles

If the user edits a resource that originates from a bundle we will not automatically save the new version in the bundle. This is hellish when user changes a bundle, and then shares it again, it may seem that the original bundle with copyright attached is kinda awful.

We _will_ make it possible for bundle authors to regenerate bundles from edited resources.

  • Make changes to brushes that are in bundles, having some kind of updating of the brushes inside the bundle with the changes brushes outside.
  • Using history to check if brushes have been changed.
  • Also works for removing and adding.

The current bundle creator/editor is sufficient gui-wise, though bugs need to be fixed.

  • Create a wizard that creates a bundle from a set of tags.
    • Awkward if there’s patterns + gradients.
    • Bundle creation is pretty easy due to automatic pattern and brush-tip resolution.

If the bundle format changes, it will be by extending the current format. No automatic conversion of older bundles is foreseen.

  • People really really want to get rid of their bundles: just disabling is not enough.

Get Hot New Stuff/share.krita.org

  • Clemens is still putting money in it, two fte are working on the server side
  • The protocol is pretty hard though. The work done in 2017 was not useful.
  • We still have no moderation: the design has not been adapted, so all share sites are one big database.

RSS feeds for bundles as an alternative? → Just researching what is possible in terms of registery. We could have an upload queue that we could moderate, and then publish as an rss feed and page on the website. The rss feed could be shown in the welcome page or the resoruce manager.

Related Objects

There are a very large number of changes, so older changes are hidden. Show Older Changes
dkazakov added a comment.EditedAug 5 2018, 1:20 PM

Review of the current state of the resources branch

  1. I do really like the idea of having KisResourceStorage with plugins that use iterator-based interface for accessing resources. It looks really nice.
  2. It might be a good idea to state explicitly in KisStoragePlugin's documentation that KisStoragePlugin is not limited by one type of a resource. When I first saw KisAbrStorage class I first thought these plugins are resource type specific, but the were not :)
  3. I don't see any new code for UUID and md5 fixes. The current code still uses (incorrect) md5 sums for distinguishing resources and even saves it into the database. We should either implement stable and correct calculation of md5 sums for all the types of the resources (almost impossible) or just go into direction of UUID and use md5 only for checking validity of the database cache. Right now the implementation is incorrect (md5 calculates bogus values, basing of the filename or just random numbers in the resource content, so the cache will always be in a dirty state).
  4. (possibly a bug) KisResourceCacheDb::storageTypes stores i18n'ed values, and stores them in the database. If these values are not used as keys, then it might be okay. But it also means that the database becomes locale-aware. Perhaps we should also store the associated locale in the database version tag/table?
  5. The thing that worries me the most is how we will maintain the versions of this database... Right now it is created manually in initDB function, so it will be almost impossible to convert older versions of the user's database during updates. There are at least two opensource projects that provide automated schema migrations: flywaydb and liquibase. Perhaps, we could reuse one of them? The only trouble is they are both apache2-licensed, which is compatible with gpl3, but incompatible with gpl2. I think it is the main problem of the resources system design. Just adding UUID field to the existing database will be a problem now :(
  6. The database-related code also uses a lot of error-checking code without proper user feedback (qWarning is not a proper feedback :) ). I guess these checks should either be changed into KIS_SAFE_ASSERT_RECOVER... (if they are considered as a bug) or to be ported to exceptions with a proper user feedback (if the problem is considered and sane for the user environment).

For example, this check is clearly an assert, if the sql file is not packed into our resources or sql text is not valid, then it is our (developers') bug:

QFile f(":/select_resource_id.sql");
f.open(QFile::ReadOnly);
QSqlQuery q;
if (!q.prepare(f.readAll())) {
    qWarning() << "Could not read and prepare resourceIdForResource" << q.lastError();
    return -1;
}
...
rempt added a comment.Aug 6 2018, 10:18 AM
  1. It might be a good idea to state explicitly in KisStoragePlugin's documentation that KisStoragePlugin is not limited by one type of a resource. When I first saw KisAbrStorage class I first thought these plugins are resource type specific, but the were not :)

Yes... And an abr or asl file isn't necessarily a resource anymore, though
right now that hasn't changed yet.

  1. I don't see any new code for UUID and md5 fixes.

No, that's not there yet, and I'm still wondering how to work with uuid for
resources that don't support that, like gbr files. I'm still working on
actually loading everything in the cache database for the first time and
synchronizing on startup if there are new resources in the folder. This
project turns out to be much work than I imagined...

. 4. (possibly a bug)
KisResourceCacheDb::storageTypes stores i18n'ed values, and stores them in
the database. If these values are not used as keys, then it might be okay.
But it also means that the database becomes locale-aware. Perhaps we should
also store the associated locale in the database version tag/table?

The ID is the key, so the text can be safely translated. We probably would
have to store the language in the version information so we can update the
translated fields if the user switches their language.

  1. The thing that worries me the most is how we will maintain the versions of this database... Right now it is created manually in initDB function, so it will be almost impossible to convert older versions of the user's database during updates. There are at least two opensource projects that provide automated schema migrations: flywaydb https://flywaydb.org and liquibase https://liquibase.org. Perhaps, we could reuse one of them? The only trouble is they are both apache2-licensed, which is compatible with gpl3, but incompatible with gpl2. I think it is the main problem of the resources system design. Just adding UUID field to the existing database will be a problem now :(

Well... This bit means that database migration hasn't been implemented yet:

if (schemaVersion != KisResourceCacheDb::databaseVersion) {
// XXX: Implement migration

warnResources << "Database schema is outdated, migration is needed";

schemaIsOutDated = true;
}

So that's not done yhet. flywaydb does look interesting, and krita is
effectively gplv3 in any case, and I'm fine with that. We'd still need to
create an initial schema in any case, like we do now.

  1. The database-related code also uses a lot of error-checking code without proper user feedback (qWarning is not a proper feedback :) ). I guess these checks should either be changed into KIS_SAFE_ASSERT_RECOVER... (if they are considered as a bug) or to be ported to exceptions with a proper user feedback (if the problem is considered and sane for the user environment).

    For example, this check is clearly an assert, if the sql file is not packed into our resources or sql text is not valid, then it is our (developers') bug:

    QFile f(":/select_resource_id.sql"); f.open(QFile::ReadOnly); QSqlQuery q; if (!q.prepare(f.readAll())) { qWarning() << "Could not read and prepare resourceIdForResource" << q.lastError(); return -1; } ...

Yes, that probably needs to be changed.

In T379#153645, @rempt wrote:

Yes... And an abr or asl file isn't necessarily a resource anymore, though
right now that hasn't changed yet.

Good idea. It would make our life a bit easier if abs and asl would not be a resource :)

  1. I don't see any new code for UUID and md5 fixes.

No, that's not there yet, and I'm still wondering how to work with uuid for
resources that don't support that, like gbr files.

Well, the problem is that our database, strictly speaking, is not just a "cache". It is a complete "data model", because it stores some information that we cannot recover if the database is lost (blacklisted/removed resources, history information and so on). So, I guess, it is perfectly okay to also store UUID in it without syncing it with the actual resource (if the resource doesn't support that). And every time the database will be regenerated (which we should be avoided as much as possible) UUIDs will be regenerated as well (for resources that do not support them).

So that's not done yhet. flywaydb does look interesting, and krita is
effectively gplv3 in any case, and I'm fine with that. We'd still need to
create an initial schema in any case, like we do now.

That is good that we can use it :)

Note to self: make sure that we don't store absolute paths in the database

rempt added a comment.Oct 31 2018, 1:21 PM

Note: we should also handle resources stored in kra files!

rempt updated the task description. (Show Details)Oct 31 2018, 1:34 PM
rempt added a comment.Jan 6 2019, 11:45 AM

Note to self: it is not necessary to write a gridwidget, we can use a proxy model to translate the the fake rows + columns index to rows. Also, we should use a filtering proxy model for the tags.

pilee added a subscriber: pilee.Feb 27 2019, 5:39 PM
bcooksley changed the visibility from "All Users" to "Public (No Login Required)".Aug 12 2019, 10:13 AM