2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* editor_resource_preview.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2015-05-31 06:59:42 +02:00
# include "editor_resource_preview.h"
2017-01-16 08:04:19 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
# include "core/io/resource_saver.h"
2020-11-07 23:33:38 +01:00
# include "core/object/message_queue.h"
2022-08-19 18:14:57 +02:00
# include "core/variant/variant_utility.cpp"
2022-02-12 02:46:22 +01:00
# include "editor/editor_node.h"
2022-02-14 14:00:03 +01:00
# include "editor/editor_paths.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_scale.h"
# include "editor/editor_settings.h"
2015-05-31 06:59:42 +02:00
2017-03-05 16:44:50 +01:00
bool EditorResourcePreviewGenerator : : handles ( const String & p_type ) const {
2022-09-29 11:53:28 +02:00
bool success = false ;
2021-08-22 03:52:44 +02:00
if ( GDVIRTUAL_CALL ( _handles , p_type , success ) ) {
return success ;
2016-09-10 20:32:17 +02:00
}
2021-05-15 23:48:59 +02:00
ERR_FAIL_V_MSG ( false , " EditorResourcePreviewGenerator::_handles needs to be overridden. " ) ;
2016-09-10 20:32:17 +02:00
}
2018-09-12 13:10:49 +02:00
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorResourcePreviewGenerator : : generate ( const Ref < Resource > & p_from , const Size2 & p_size , Dictionary & p_metadata ) const {
2021-08-22 03:52:44 +02:00
Ref < Texture2D > preview ;
2022-08-19 18:14:57 +02:00
if ( GDVIRTUAL_CALL ( _generate , p_from , p_size , p_metadata , preview ) ) {
2021-08-22 03:52:44 +02:00
return preview ;
2016-09-10 20:32:17 +02:00
}
2021-05-15 23:48:59 +02:00
ERR_FAIL_V_MSG ( Ref < Texture2D > ( ) , " EditorResourcePreviewGenerator::_generate needs to be overridden. " ) ;
2016-09-10 20:32:17 +02:00
}
2022-08-19 18:14:57 +02:00
Ref < Texture2D > EditorResourcePreviewGenerator : : generate_from_path ( const String & p_path , const Size2 & p_size , Dictionary & p_metadata ) const {
2021-08-22 03:52:44 +02:00
Ref < Texture2D > preview ;
2022-08-19 18:14:57 +02:00
if ( GDVIRTUAL_CALL ( _generate_from_path , p_path , p_size , p_metadata , preview ) ) {
2021-08-22 03:52:44 +02:00
return preview ;
2016-09-10 20:32:17 +02:00
}
2022-05-03 01:43:50 +02:00
Ref < Resource > res = ResourceLoader : : load ( p_path ) ;
2020-05-14 16:41:43 +02:00
if ( ! res . is_valid ( ) ) {
2015-05-31 06:59:42 +02:00
return res ;
2020-05-14 16:41:43 +02:00
}
2022-08-19 18:14:57 +02:00
return generate ( res , p_size , p_metadata ) ;
2018-09-12 13:10:49 +02:00
}
2019-05-20 10:45:12 +02:00
bool EditorResourcePreviewGenerator : : generate_small_preview_automatically ( ) const {
2022-09-29 11:53:28 +02:00
bool success = false ;
2022-10-18 18:47:44 +02:00
GDVIRTUAL_CALL ( _generate_small_preview_automatically , success ) ;
return success ;
2019-05-20 10:45:12 +02:00
}
bool EditorResourcePreviewGenerator : : can_generate_small_preview ( ) const {
2022-09-29 11:53:28 +02:00
bool success = false ;
2022-10-18 18:47:44 +02:00
GDVIRTUAL_CALL ( _can_generate_small_preview , success ) ;
return success ;
2015-05-31 06:59:42 +02:00
}
2016-09-10 20:32:17 +02:00
void EditorResourcePreviewGenerator : : _bind_methods ( ) {
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _handles , " type " ) ;
2022-08-19 18:14:57 +02:00
GDVIRTUAL_BIND ( _generate , " resource " , " size " , " metadata " ) ;
GDVIRTUAL_BIND ( _generate_from_path , " path " , " size " , " metadata " ) ;
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _generate_small_preview_automatically ) ;
GDVIRTUAL_BIND ( _can_generate_small_preview ) ;
2016-09-10 20:32:17 +02:00
}
2015-05-31 06:59:42 +02:00
EditorResourcePreviewGenerator : : EditorResourcePreviewGenerator ( ) {
}
2020-04-02 01:20:12 +02:00
EditorResourcePreview * EditorResourcePreview : : singleton = nullptr ;
2015-05-31 06:59:42 +02:00
void EditorResourcePreview : : _thread_func ( void * ud ) {
2017-03-05 16:44:50 +01:00
EditorResourcePreview * erp = ( EditorResourcePreview * ) ud ;
2015-05-31 06:59:42 +02:00
erp - > _thread ( ) ;
}
2022-08-19 18:14:57 +02:00
void EditorResourcePreview : : _preview_ready ( const String & p_path , int p_hash , const Ref < Texture2D > & p_texture , const Ref < Texture2D > & p_small_texture , ObjectID id , const StringName & p_func , const Variant & p_ud , const Dictionary & p_metadata ) {
2020-02-26 11:28:13 +01:00
{
2020-03-13 11:13:58 +01:00
MutexLock lock ( preview_mutex ) ;
2020-02-26 11:28:13 +01:00
uint64_t modified_time = 0 ;
2016-05-27 19:18:40 +02:00
2022-08-19 18:14:57 +02:00
if ( ! p_path . begins_with ( " ID: " ) ) {
modified_time = FileAccess : : get_modified_time ( p_path ) ;
2020-02-26 11:28:13 +01:00
}
2016-05-27 19:18:40 +02:00
2020-02-26 11:28:13 +01:00
Item item ;
item . order = order + + ;
item . preview = p_texture ;
item . small_preview = p_small_texture ;
2022-08-19 18:14:57 +02:00
item . last_hash = p_hash ;
2020-02-26 11:28:13 +01:00
item . modified_time = modified_time ;
2022-08-19 18:14:57 +02:00
item . preview_metadata = p_metadata ;
2015-05-31 06:59:42 +02:00
2022-08-19 18:14:57 +02:00
cache [ p_path ] = item ;
2020-02-26 11:28:13 +01:00
}
2016-07-09 16:19:49 +02:00
2022-08-19 18:14:57 +02:00
MessageQueue : : get_singleton ( ) - > push_call ( id , p_func , p_path , p_texture , p_small_texture , p_ud ) ;
2015-05-31 06:59:42 +02:00
}
2022-08-19 18:14:57 +02:00
void EditorResourcePreview : : _generate_preview ( Ref < ImageTexture > & r_texture , Ref < ImageTexture > & r_small_texture , const QueueItem & p_item , const String & cache_base , Dictionary & p_metadata ) {
2016-05-27 19:18:40 +02:00
String type ;
2020-05-14 16:41:43 +02:00
if ( p_item . resource . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
type = p_item . resource - > get_class ( ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
type = ResourceLoader : : get_resource_type ( p_item . path ) ;
2020-05-14 16:41:43 +02:00
}
2015-05-31 06:59:42 +02:00
2021-12-09 10:42:46 +01:00
if ( type . is_empty ( ) ) {
2018-09-12 13:10:49 +02:00
r_texture = Ref < ImageTexture > ( ) ;
r_small_texture = Ref < ImageTexture > ( ) ;
return ; //could not guess type
}
2015-05-31 06:59:42 +02:00
2022-10-18 16:43:37 +02:00
int thumbnail_size = EDITOR_GET ( " filesystem/file_dialog/thumbnail_size " ) ;
2018-09-12 13:10:49 +02:00
thumbnail_size * = EDSCALE ;
2015-05-31 06:59:42 +02:00
2018-09-12 13:10:49 +02:00
r_texture = Ref < ImageTexture > ( ) ;
r_small_texture = Ref < ImageTexture > ( ) ;
2016-07-09 16:19:49 +02:00
2018-09-12 13:10:49 +02:00
for ( int i = 0 ; i < preview_generators . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( ! preview_generators [ i ] - > handles ( type ) ) {
2015-05-31 06:59:42 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-09-12 13:10:49 +02:00
2019-06-11 20:43:37 +02:00
Ref < Texture2D > generated ;
2016-05-27 19:18:40 +02:00
if ( p_item . resource . is_valid ( ) ) {
2022-08-19 18:14:57 +02:00
generated = preview_generators . write [ i ] - > generate ( p_item . resource , Vector2 ( thumbnail_size , thumbnail_size ) , p_metadata ) ;
2016-05-27 19:18:40 +02:00
} else {
2022-08-19 18:14:57 +02:00
generated = preview_generators . write [ i ] - > generate_from_path ( p_item . path , Vector2 ( thumbnail_size , thumbnail_size ) , p_metadata ) ;
2016-05-27 19:18:40 +02:00
}
2018-09-12 13:10:49 +02:00
r_texture = generated ;
2015-05-31 06:59:42 +02:00
2021-07-17 23:22:52 +02:00
int small_thumbnail_size = EditorNode : : get_singleton ( ) - > get_theme_base ( ) - > get_theme_icon ( SNAME ( " Object " ) , SNAME ( " EditorIcons " ) ) - > get_width ( ) ; // Kind of a workaround to retrieve the default icon size
2019-05-20 10:45:12 +02:00
if ( preview_generators [ i ] - > can_generate_small_preview ( ) ) {
2019-06-11 20:43:37 +02:00
Ref < Texture2D > generated_small ;
2022-08-19 18:14:57 +02:00
Dictionary d ;
2019-05-20 10:45:12 +02:00
if ( p_item . resource . is_valid ( ) ) {
2022-08-19 18:14:57 +02:00
generated_small = preview_generators . write [ i ] - > generate ( p_item . resource , Vector2 ( small_thumbnail_size , small_thumbnail_size ) , d ) ;
2019-05-20 10:45:12 +02:00
} else {
2022-08-19 18:14:57 +02:00
generated_small = preview_generators . write [ i ] - > generate_from_path ( p_item . path , Vector2 ( small_thumbnail_size , small_thumbnail_size ) , d ) ;
2019-05-20 10:45:12 +02:00
}
r_small_texture = generated_small ;
}
2018-09-12 13:10:49 +02:00
2019-05-20 10:45:12 +02:00
if ( ! r_small_texture . is_valid ( ) & & r_texture . is_valid ( ) & & preview_generators [ i ] - > generate_small_preview_automatically ( ) ) {
2021-03-28 13:32:17 +02:00
Ref < Image > small_image = r_texture - > get_image ( ) ;
2019-01-27 17:39:16 +01:00
small_image = small_image - > duplicate ( ) ;
2018-09-12 13:10:49 +02:00
small_image - > resize ( small_thumbnail_size , small_thumbnail_size , Image : : INTERPOLATE_CUBIC ) ;
2021-06-18 00:03:09 +02:00
r_small_texture . instantiate ( ) ;
2022-05-04 01:49:20 +02:00
r_small_texture - > set_image ( small_image ) ;
2018-09-12 13:10:49 +02:00
}
2019-05-20 10:45:12 +02:00
2015-05-31 06:59:42 +02:00
break ;
}
2016-05-27 19:18:40 +02:00
if ( ! p_item . resource . is_valid ( ) ) {
2022-08-19 18:14:57 +02:00
// Cache the preview in case it's a resource on disk.
2018-09-12 13:10:49 +02:00
if ( r_texture . is_valid ( ) ) {
2022-08-19 18:14:57 +02:00
// Wow it generated a preview... save cache.
2018-09-12 13:10:49 +02:00
bool has_small_texture = r_small_texture . is_valid ( ) ;
2022-06-03 01:33:42 +02:00
ResourceSaver : : save ( r_texture , cache_base + " .png " ) ;
2018-09-12 13:10:49 +02:00
if ( has_small_texture ) {
2022-06-03 01:33:42 +02:00
ResourceSaver : : save ( r_small_texture , cache_base + " _small.png " ) ;
2018-09-12 13:10:49 +02:00
}
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( cache_base + " .txt " , FileAccess : : WRITE ) ;
ERR_FAIL_COND_MSG ( f . is_null ( ) , " Cannot create file ' " + cache_base + " .txt'. Check user write permissions. " ) ;
2022-08-19 18:14:57 +02:00
_write_preview_cache ( f , thumbnail_size , has_small_texture , FileAccess : : get_modified_time ( p_item . path ) , FileAccess : : get_md5 ( p_item . path ) , p_metadata ) ;
2016-05-27 19:18:40 +02:00
}
2015-05-31 06:59:42 +02:00
}
}
2022-08-19 18:14:57 +02:00
Variant EditorResourcePreview : : get_preview_metadata ( const String & p_path , const String & p_meta ) const {
ERR_FAIL_COND_V ( ! cache . has ( p_path ) , Variant ( ) ) ;
return cache [ p_path ] . preview_metadata . get ( p_meta , Variant ( ) ) ;
}
2020-11-18 19:11:30 +01:00
void EditorResourcePreview : : _iterate ( ) {
preview_mutex . lock ( ) ;
if ( queue . size ( ) ) {
QueueItem item = queue . front ( ) - > get ( ) ;
queue . pop_front ( ) ;
if ( cache . has ( item . path ) ) {
2022-08-19 18:14:57 +02:00
// Already has it because someone loaded it, just let it know it's ready.
_preview_ready ( item . path , cache [ item . path ] . last_hash , cache [ item . path ] . preview , cache [ item . path ] . small_preview , item . id , item . function , item . userdata , cache [ item . path ] . preview_metadata ) ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
preview_mutex . unlock ( ) ;
} else {
preview_mutex . unlock ( ) ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
Ref < ImageTexture > texture ;
Ref < ImageTexture > small_texture ;
2018-09-12 13:10:49 +02:00
2022-10-18 16:43:37 +02:00
int thumbnail_size = EDITOR_GET ( " filesystem/file_dialog/thumbnail_size " ) ;
2020-11-18 19:11:30 +01:00
thumbnail_size * = EDSCALE ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
if ( item . resource . is_valid ( ) ) {
2022-08-19 18:14:57 +02:00
Dictionary preview_metadata ;
_generate_preview ( texture , small_texture , item , String ( ) , preview_metadata ) ;
2016-07-09 16:19:49 +02:00
2022-08-19 18:14:57 +02:00
_preview_ready ( item . path , item . resource - > hash_edited_version ( ) , texture , small_texture , item . id , item . function , item . userdata , preview_metadata ) ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
} else {
2022-08-19 18:14:57 +02:00
Dictionary preview_metadata ;
2020-11-18 19:11:30 +01:00
String temp_path = EditorPaths : : get_singleton ( ) - > get_cache_dir ( ) ;
String cache_base = ProjectSettings : : get_singleton ( ) - > globalize_path ( item . path ) . md5_text ( ) ;
2022-08-30 02:34:01 +02:00
cache_base = temp_path . path_join ( " resthumb- " + cache_base ) ;
2015-05-31 06:59:42 +02:00
2022-08-19 18:14:57 +02:00
// Does not have it, try to load a cached thumbnail.
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
String file = cache_base + " .txt " ;
2022-03-23 10:08:58 +01:00
Ref < FileAccess > f = FileAccess : : open ( file , FileAccess : : READ ) ;
if ( f . is_null ( ) ) {
2022-08-19 18:14:57 +02:00
// No cache found, generate.
_generate_preview ( texture , small_texture , item , cache_base , preview_metadata ) ;
2020-11-18 19:11:30 +01:00
} else {
uint64_t modtime = FileAccess : : get_modified_time ( item . path ) ;
2022-08-19 18:14:57 +02:00
int tsize ;
bool has_small_texture ;
uint64_t last_modtime ;
String hash ;
_read_preview_cache ( f , & tsize , & has_small_texture , & last_modtime , & hash , & preview_metadata ) ;
2020-11-18 19:11:30 +01:00
bool cache_valid = true ;
if ( tsize ! = thumbnail_size ) {
cache_valid = false ;
2022-04-12 09:12:40 +02:00
f . unref ( ) ;
2020-11-18 19:11:30 +01:00
} else if ( last_modtime ! = modtime ) {
String last_md5 = f - > get_line ( ) ;
String md5 = FileAccess : : get_md5 ( item . path ) ;
2022-04-12 09:12:40 +02:00
f . unref ( ) ;
2020-11-18 19:11:30 +01:00
if ( last_md5 ! = md5 ) {
2017-03-05 16:44:50 +01:00
cache_valid = false ;
2020-11-18 19:11:30 +01:00
} else {
2022-08-19 18:14:57 +02:00
// Update modified time.
2017-06-09 05:23:50 +02:00
2022-04-12 09:12:40 +02:00
Ref < FileAccess > f2 = FileAccess : : open ( file , FileAccess : : WRITE ) ;
if ( f2 . is_null ( ) ) {
2020-11-18 19:11:30 +01:00
// Not returning as this would leave the thread hanging and would require
// some proper cleanup/disabling of resource preview generation.
ERR_PRINT ( " Cannot create file ' " + file + " '. Check user write permissions. " ) ;
2016-07-09 16:19:49 +02:00
} else {
2022-08-19 18:14:57 +02:00
_write_preview_cache ( f2 , thumbnail_size , has_small_texture , modtime , md5 , preview_metadata ) ;
2016-07-09 16:19:49 +02:00
}
2015-05-31 06:59:42 +02:00
}
2022-04-12 09:12:40 +02:00
} else {
f . unref ( ) ;
2020-11-18 19:11:30 +01:00
}
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
if ( cache_valid ) {
Ref < Image > img ;
img . instantiate ( ) ;
Ref < Image > small_img ;
small_img . instantiate ( ) ;
2017-06-09 05:23:50 +02:00
2020-11-18 19:11:30 +01:00
if ( img - > load ( cache_base + " .png " ) ! = OK ) {
cache_valid = false ;
} else {
texture . instantiate ( ) ;
2022-05-04 01:49:20 +02:00
texture - > set_image ( img ) ;
2020-11-18 19:11:30 +01:00
if ( has_small_texture ) {
if ( small_img - > load ( cache_base + " _small.png " ) ! = OK ) {
cache_valid = false ;
} else {
small_texture . instantiate ( ) ;
2022-05-04 01:49:20 +02:00
small_texture - > set_image ( small_img ) ;
2018-09-12 13:10:49 +02:00
}
2016-07-09 16:19:49 +02:00
}
2015-05-31 06:59:42 +02:00
}
2020-11-18 19:11:30 +01:00
}
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
if ( ! cache_valid ) {
2022-08-19 18:14:57 +02:00
_generate_preview ( texture , small_texture , item , cache_base , preview_metadata ) ;
2016-07-09 16:19:49 +02:00
}
}
2022-08-19 18:14:57 +02:00
_preview_ready ( item . path , 0 , texture , small_texture , item . id , item . function , item . userdata , preview_metadata ) ;
2015-05-31 06:59:42 +02:00
}
}
2020-11-18 19:11:30 +01:00
} else {
preview_mutex . unlock ( ) ;
}
}
2022-08-19 18:14:57 +02:00
void EditorResourcePreview : : _write_preview_cache ( Ref < FileAccess > p_file , int p_thumbnail_size , bool p_has_small_texture , uint64_t p_modified_time , String p_hash , const Dictionary & p_metadata ) {
p_file - > store_line ( itos ( p_thumbnail_size ) ) ;
p_file - > store_line ( itos ( p_has_small_texture ) ) ;
p_file - > store_line ( itos ( p_modified_time ) ) ;
p_file - > store_line ( p_hash ) ;
p_file - > store_line ( VariantUtilityFunctions : : var_to_str ( p_metadata ) . replace ( " \n " , " " ) ) ;
}
void EditorResourcePreview : : _read_preview_cache ( Ref < FileAccess > p_file , int * r_thumbnail_size , bool * r_has_small_texture , uint64_t * r_modified_time , String * r_hash , Dictionary * r_metadata ) {
* r_thumbnail_size = p_file - > get_line ( ) . to_int ( ) ;
* r_has_small_texture = p_file - > get_line ( ) . to_int ( ) ;
* r_modified_time = p_file - > get_line ( ) . to_int ( ) ;
* r_hash = p_file - > get_line ( ) ;
* r_metadata = VariantUtilityFunctions : : str_to_var ( p_file - > get_line ( ) ) ;
}
2020-11-18 19:11:30 +01:00
void EditorResourcePreview : : _thread ( ) {
exited . clear ( ) ;
while ( ! exit . is_set ( ) ) {
preview_sem . wait ( ) ;
_iterate ( ) ;
2015-05-31 06:59:42 +02:00
}
2021-02-10 19:22:13 +01:00
exited . set ( ) ;
2015-05-31 06:59:42 +02:00
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : queue_edited_resource_preview ( const Ref < Resource > & p_res , Object * p_receiver , const StringName & p_receiver_func , const Variant & p_userdata ) {
2016-05-27 19:18:40 +02:00
ERR_FAIL_NULL ( p_receiver ) ;
ERR_FAIL_COND ( ! p_res . is_valid ( ) ) ;
2020-02-26 11:28:13 +01:00
{
MutexLock lock ( preview_mutex ) ;
2016-05-27 19:18:40 +02:00
2020-02-26 11:28:13 +01:00
String path_id = " ID: " + itos ( p_res - > get_instance_id ( ) ) ;
2016-07-03 18:15:15 +02:00
2020-02-26 11:28:13 +01:00
if ( cache . has ( path_id ) & & cache [ path_id ] . last_hash = = p_res - > hash_edited_version ( ) ) {
cache [ path_id ] . order = order + + ;
p_receiver - > call ( p_receiver_func , path_id , cache [ path_id ] . preview , cache [ path_id ] . small_preview , p_userdata ) ;
return ;
}
2016-05-27 19:18:40 +02:00
2020-02-26 11:28:13 +01:00
cache . erase ( path_id ) ; //erase if exists, since it will be regen
2016-07-03 18:15:15 +02:00
2020-02-26 11:28:13 +01:00
QueueItem item ;
item . function = p_receiver_func ;
item . id = p_receiver - > get_instance_id ( ) ;
item . resource = p_res ;
item . path = path_id ;
item . userdata = p_userdata ;
2016-05-27 19:18:40 +02:00
2020-02-26 11:28:13 +01:00
queue . push_back ( item ) ;
}
2020-03-03 09:26:42 +01:00
preview_sem . post ( ) ;
2016-05-27 19:18:40 +02:00
}
2015-05-31 06:59:42 +02:00
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : queue_resource_preview ( const String & p_path , Object * p_receiver , const StringName & p_receiver_func , const Variant & p_userdata ) {
2015-05-31 06:59:42 +02:00
ERR_FAIL_NULL ( p_receiver ) ;
2020-02-26 11:28:13 +01:00
{
MutexLock lock ( preview_mutex ) ;
if ( cache . has ( p_path ) ) {
cache [ p_path ] . order = order + + ;
p_receiver - > call ( p_receiver_func , p_path , cache [ p_path ] . preview , cache [ p_path ] . small_preview , p_userdata ) ;
return ;
}
2015-05-31 06:59:42 +02:00
2020-02-26 11:28:13 +01:00
QueueItem item ;
item . function = p_receiver_func ;
item . id = p_receiver - > get_instance_id ( ) ;
item . path = p_path ;
item . userdata = p_userdata ;
2015-05-31 06:59:42 +02:00
2020-02-26 11:28:13 +01:00
queue . push_back ( item ) ;
}
2020-03-03 09:26:42 +01:00
preview_sem . post ( ) ;
2015-05-31 06:59:42 +02:00
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : add_preview_generator ( const Ref < EditorResourcePreviewGenerator > & p_generator ) {
2015-05-31 06:59:42 +02:00
preview_generators . push_back ( p_generator ) ;
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : remove_preview_generator ( const Ref < EditorResourcePreviewGenerator > & p_generator ) {
2016-09-10 20:32:17 +02:00
preview_generators . erase ( p_generator ) ;
}
2017-03-05 16:44:50 +01:00
EditorResourcePreview * EditorResourcePreview : : get_singleton ( ) {
2015-05-31 06:59:42 +02:00
return singleton ;
}
void EditorResourcePreview : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( " _preview_ready " , & EditorResourcePreview : : _preview_ready ) ;
2016-07-03 18:15:15 +02:00
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " queue_resource_preview " , " path " , " receiver " , " receiver_func " , " userdata " ) , & EditorResourcePreview : : queue_resource_preview ) ;
ClassDB : : bind_method ( D_METHOD ( " queue_edited_resource_preview " , " resource " , " receiver " , " receiver_func " , " userdata " ) , & EditorResourcePreview : : queue_edited_resource_preview ) ;
ClassDB : : bind_method ( D_METHOD ( " add_preview_generator " , " generator " ) , & EditorResourcePreview : : add_preview_generator ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_preview_generator " , " generator " ) , & EditorResourcePreview : : remove_preview_generator ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " check_for_invalidation " , " path " ) , & EditorResourcePreview : : check_for_invalidation ) ;
2016-07-03 18:15:15 +02:00
2017-03-05 16:44:50 +01:00
ADD_SIGNAL ( MethodInfo ( " preview_invalidated " , PropertyInfo ( Variant : : STRING , " path " ) ) ) ;
2016-07-03 18:15:15 +02:00
}
2017-03-05 16:44:50 +01:00
void EditorResourcePreview : : check_for_invalidation ( const String & p_path ) {
bool call_invalidated = false ;
2020-02-26 11:28:13 +01:00
{
2020-03-13 11:13:58 +01:00
MutexLock lock ( preview_mutex ) ;
2020-02-26 11:28:13 +01:00
if ( cache . has ( p_path ) ) {
uint64_t modified_time = FileAccess : : get_modified_time ( p_path ) ;
if ( modified_time ! = cache [ p_path ] . modified_time ) {
cache . erase ( p_path ) ;
call_invalidated = true ;
}
2016-07-03 18:15:15 +02:00
}
}
2017-03-05 16:44:50 +01:00
if ( call_invalidated ) { //do outside mutex
2021-07-17 23:22:52 +02:00
call_deferred ( SNAME ( " emit_signal " ) , " preview_invalidated " , p_path ) ;
2016-07-03 18:15:15 +02:00
}
2015-05-31 06:59:42 +02:00
}
2019-02-27 17:31:11 +01:00
void EditorResourcePreview : : start ( ) {
2023-02-23 21:45:41 +01:00
if ( DisplayServer : : get_singleton ( ) - > get_name ( ) ! = " headless " ) {
ERR_FAIL_COND_MSG ( thread . is_started ( ) , " Thread already started. " ) ;
thread . start ( _thread_func , this ) ;
}
2019-02-27 17:31:11 +01:00
}
2019-05-31 08:05:44 +02:00
2019-01-17 13:09:01 +01:00
void EditorResourcePreview : : stop ( ) {
2021-01-19 13:29:41 +01:00
if ( thread . is_started ( ) ) {
2021-02-10 19:22:13 +01:00
exit . set ( ) ;
2020-03-03 09:26:42 +01:00
preview_sem . post ( ) ;
2021-02-10 19:22:13 +01:00
while ( ! exited . is_set ( ) ) {
2019-03-01 20:32:49 +01:00
OS : : get_singleton ( ) - > delay_usec ( 10000 ) ;
2021-10-07 15:46:55 +02:00
RenderingServer : : get_singleton ( ) - > sync ( ) ; //sync pending stuff, as thread may be blocked on rendering server
2019-03-01 20:32:49 +01:00
}
2021-01-19 13:29:41 +01:00
thread . wait_to_finish ( ) ;
2019-01-17 13:09:01 +01:00
}
}
2015-05-31 06:59:42 +02:00
EditorResourcePreview : : EditorResourcePreview ( ) {
2017-03-05 16:44:50 +01:00
singleton = this ;
order = 0 ;
2015-05-31 06:59:42 +02:00
}
2017-03-05 16:44:50 +01:00
EditorResourcePreview : : ~ EditorResourcePreview ( ) {
2019-01-17 13:09:01 +01:00
stop ( ) ;
2015-05-31 06:59:42 +02:00
}