2016-06-18 14:46:12 +02:00
/*************************************************************************/
/* editor_resource_preview.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2016-06-18 14:46:12 +02:00
/*************************************************************************/
2022-01-03 21:27:34 +01:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2016-06-18 14:46:12 +02:00
/* */
/* 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-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 {
2021-08-22 03:52:44 +02:00
bool success ;
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-05-03 01:43:50 +02:00
Ref < Texture2D > EditorResourcePreviewGenerator : : generate ( const Ref < Resource > & p_from , const Size2 & p_size ) const {
2021-08-22 03:52:44 +02:00
Ref < Texture2D > preview ;
if ( GDVIRTUAL_CALL ( _generate , p_from , p_size , preview ) ) {
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
}
2019-06-11 20:43:37 +02:00
Ref < Texture2D > EditorResourcePreviewGenerator : : generate_from_path ( const String & p_path , const Size2 & p_size ) const {
2021-08-22 03:52:44 +02:00
Ref < Texture2D > preview ;
if ( GDVIRTUAL_CALL ( _generate_from_path , p_path , p_size , preview ) ) {
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
}
2018-09-12 13:10:49 +02:00
return generate ( res , p_size ) ;
}
2019-05-20 10:45:12 +02:00
bool EditorResourcePreviewGenerator : : generate_small_preview_automatically ( ) const {
2021-08-22 03:52:44 +02:00
bool success ;
if ( GDVIRTUAL_CALL ( _generate_small_preview_automatically , success ) ) {
return success ;
2019-05-20 10:45:12 +02:00
}
return false ;
}
bool EditorResourcePreviewGenerator : : can_generate_small_preview ( ) const {
2021-08-22 03:52:44 +02:00
bool success ;
if ( GDVIRTUAL_CALL ( _can_generate_small_preview , success ) ) {
return success ;
2019-05-20 10:45:12 +02:00
}
2018-09-12 13:10:49 +02:00
return false ;
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 " ) ;
GDVIRTUAL_BIND ( _generate , " resource " , " size " ) ;
GDVIRTUAL_BIND ( _generate_from_path , " path " , " size " ) ;
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 ( ) ;
}
2019-06-11 20:43:37 +02:00
void EditorResourcePreview : : _preview_ready ( const String & p_str , const Ref < Texture2D > & p_texture , const Ref < Texture2D > & p_small_texture , ObjectID id , const StringName & p_func , const Variant & p_ud ) {
2016-05-27 19:18:40 +02:00
String path = p_str ;
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
uint32_t hash = 0 ;
uint64_t modified_time = 0 ;
2016-05-27 19:18:40 +02:00
2020-02-26 11:28:13 +01:00
if ( p_str . begins_with ( " ID: " ) ) {
2020-05-13 11:31:51 +02:00
hash = uint32_t ( p_str . get_slicec ( ' : ' , 2 ) . to_int ( ) ) ;
2020-02-26 11:28:13 +01:00
path = " ID: " + p_str . get_slicec ( ' : ' , 1 ) ;
} else {
modified_time = FileAccess : : get_modified_time ( path ) ;
}
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 ;
item . last_hash = hash ;
item . modified_time = modified_time ;
2015-05-31 06:59:42 +02:00
2020-02-26 11:28:13 +01:00
cache [ path ] = item ;
}
2016-07-09 16:19:49 +02:00
2018-09-12 13:10:49 +02:00
MessageQueue : : get_singleton ( ) - > push_call ( id , p_func , path , p_texture , p_small_texture , p_ud ) ;
2015-05-31 06:59:42 +02:00
}
2018-09-12 13:10:49 +02:00
void EditorResourcePreview : : _generate_preview ( Ref < ImageTexture > & r_texture , Ref < ImageTexture > & r_small_texture , const QueueItem & p_item , const String & cache_base ) {
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
2018-09-12 13:10:49 +02:00
int thumbnail_size = EditorSettings : : get_singleton ( ) - > get ( " filesystem/file_dialog/thumbnail_size " ) ;
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 ( ) ) {
2018-09-12 13:10:49 +02:00
generated = preview_generators [ i ] - > generate ( p_item . resource , Vector2 ( thumbnail_size , thumbnail_size ) ) ;
2016-05-27 19:18:40 +02:00
} else {
2018-09-12 13:10:49 +02:00
generated = preview_generators [ i ] - > generate_from_path ( p_item . path , Vector2 ( thumbnail_size , thumbnail_size ) ) ;
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 ;
2019-05-20 10:45:12 +02:00
if ( p_item . resource . is_valid ( ) ) {
generated_small = preview_generators [ i ] - > generate ( p_item . resource , Vector2 ( small_thumbnail_size , small_thumbnail_size ) ) ;
} else {
generated_small = preview_generators [ i ] - > generate_from_path ( p_item . path , Vector2 ( small_thumbnail_size , small_thumbnail_size ) ) ;
}
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 ( ) ;
2018-09-12 13:10:49 +02:00
r_small_texture - > create_from_image ( small_image ) ;
}
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 ( ) ) {
// cache the preview in case it's a resource on disk
2018-09-12 13:10:49 +02:00
if ( r_texture . is_valid ( ) ) {
2016-05-27 19:18:40 +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 ( ) ;
ResourceSaver : : save ( cache_base + " .png " , r_texture ) ;
if ( has_small_texture ) {
ResourceSaver : : save ( cache_base + " _small.png " , r_small_texture ) ;
}
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. " ) ;
2016-05-27 19:18:40 +02:00
f - > store_line ( itos ( thumbnail_size ) ) ;
2018-09-12 13:10:49 +02:00
f - > store_line ( itos ( has_small_texture ) ) ;
2016-05-27 19:18:40 +02:00
f - > store_line ( itos ( FileAccess : : get_modified_time ( p_item . path ) ) ) ;
f - > store_line ( FileAccess : : get_md5 ( p_item . path ) ) ;
}
2015-05-31 06:59:42 +02:00
}
}
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 ) ) {
//already has it because someone loaded it, just let it know it's ready
String path = item . path ;
if ( item . resource . is_valid ( ) ) {
path + = " : " + itos ( cache [ item . path ] . last_hash ) ; //keep last hash (see description of what this is in condition below)
}
2016-07-09 16:19:49 +02:00
2020-11-18 19:11:30 +01:00
_preview_ready ( path , cache [ item . path ] . preview , cache [ item . path ] . small_preview , item . id , item . function , item . userdata ) ;
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
2020-11-18 19:11:30 +01:00
int thumbnail_size = EditorSettings : : get_singleton ( ) - > get ( " filesystem/file_dialog/thumbnail_size " ) ;
thumbnail_size * = EDSCALE ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
if ( item . resource . is_valid ( ) ) {
_generate_preview ( texture , small_texture , item , String ( ) ) ;
2016-07-09 16:19:49 +02:00
2020-11-18 19:11:30 +01:00
//adding hash to the end of path (should be ID:<objid>:<hash>) because of 5 argument limit to call_deferred
_preview_ready ( item . path + " : " + itos ( item . resource - > hash_edited_version ( ) ) , texture , small_texture , item . id , item . function , item . userdata ) ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01:00
} else {
String temp_path = EditorPaths : : get_singleton ( ) - > get_cache_dir ( ) ;
String cache_base = ProjectSettings : : get_singleton ( ) - > globalize_path ( item . path ) . md5_text ( ) ;
cache_base = temp_path . plus_file ( " resthumb- " + cache_base ) ;
2015-05-31 06:59:42 +02:00
2020-11-18 19:11:30 +01: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 ( ) ) {
2020-11-18 19:11:30 +01:00
// No cache found, generate
_generate_preview ( texture , small_texture , item , cache_base ) ;
} else {
uint64_t modtime = FileAccess : : get_modified_time ( item . path ) ;
int tsize = f - > get_line ( ) . to_int ( ) ;
bool has_small_texture = f - > get_line ( ) . to_int ( ) ;
uint64_t last_modtime = f - > get_line ( ) . to_int ( ) ;
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 {
//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-04-12 09:12:40 +02:00
f2 - > store_line ( itos ( thumbnail_size ) ) ;
f2 - > store_line ( itos ( has_small_texture ) ) ;
f2 - > store_line ( itos ( modtime ) ) ;
f2 - > store_line ( md5 ) ;
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 ( ) ;
texture - > create_from_image ( img ) ;
if ( has_small_texture ) {
if ( small_img - > load ( cache_base + " _small.png " ) ! = OK ) {
cache_valid = false ;
} else {
small_texture . instantiate ( ) ;
small_texture - > create_from_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 ) {
_generate_preview ( texture , small_texture , item , cache_base ) ;
2016-07-09 16:19:49 +02:00
}
}
2020-11-18 19:11:30 +01:00
_preview_ready ( item . path , texture , small_texture , item . id , item . function , item . userdata ) ;
2015-05-31 06:59:42 +02:00
}
}
2020-11-18 19:11:30 +01:00
} else {
preview_mutex . unlock ( ) ;
}
}
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 ( ) {
2020-11-18 19:11:30 +01:00
if ( OS : : get_singleton ( ) - > get_render_main_thread_mode ( ) = = OS : : RENDER_ANY_THREAD ) {
ERR_FAIL_COND_MSG ( thread . is_started ( ) , " Thread already started. " ) ;
thread . start ( _thread_func , this ) ;
} else {
_mainthread_only = true ;
}
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
}
2020-11-18 19:11:30 +01:00
void EditorResourcePreview : : update ( ) {
if ( ! _mainthread_only ) {
return ;
}
if ( ! exit . is_set ( ) ) {
// no need to even lock the mutex if the size is zero
// there is no problem if queue.size() is wrong, even if
// there was a race condition.
if ( queue . size ( ) ) {
_iterate ( ) ;
}
}
}