Repository: godotengine/godot
File: editor/inspector/editor_resource_preview.cpp (C++)

Code before:

bool EditorResourcePreviewGenerator::generate_small_preview_automatically() const {
	bool success = false;
	GDVIRTUAL_CALL(_generate_small_preview_automatically, success);
	return success;
}

bool EditorResourcePreviewGenerator::can_generate_small_preview() const {
	bool success = false;
	GDVIRTUAL_CALL(_can_generate_small_preview, success);
	return success;
}

void EditorResourcePreviewGenerator::_bind_methods() {
	GDVIRTUAL_BIND(_handles, "type");
	GDVIRTUAL_BIND(_generate, "resource", "size", "metadata");
	GDVIRTUAL_BIND(_generate_from_path, "path", "size", "metadata");
	GDVIRTUAL_BIND(_generate_small_preview_automatically);
	GDVIRTUAL_BIND(_can_generate_small_preview);
}

void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewport) {
	Callable request_vp_update_once = callable_mp(RS::get_singleton(), &RS::viewport_set_update_mode).bind(p_viewport, RS::VIEWPORT_UPDATE_ONCE);

	if (EditorResourcePreview::get_singleton()->is_threaded()) {
		RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(this, &EditorResourcePreviewGenerator::DrawRequester::_prepare_draw).bind(p_viewport), Object::CONNECT_ONE_SHOT);
		semaphore.wait();
	} else {
		// Avoid the main viewport and children being redrawn.
		SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
		ERR_FAIL_NULL_MSG(st, "Editor's MainLoop is not a SceneTree. This is a bug.");
		RID root_vp = st->get_root()->get_viewport_rid();
		RenderingServer::get_singleton()->viewport_set_active(root_vp, false);

		request_vp_update_once.call();
		RS::get_singleton()->draw(false);

		// Let main viewport and children be drawn again.
		RenderingServer::get_singleton()->viewport_set_active(root_vp, true);
	}
}

void EditorResourcePreviewGenerator::DrawRequester::abort() {
	if (EditorResourcePreview::get_singleton()->is_threaded()) {
		semaphore.post();
	}
}

void EditorResourcePreviewGenerator::DrawRequester::_prepare_draw(RID p_viewport) {
	RS::get_singleton()->viewport_set_update_mode(p_viewport, RS::VIEWPORT_UPDATE_ONCE);
	RS::get_singleton()->request_frame_drawn_callback(callable_mp(this, &EditorResourcePreviewGenerator::DrawRequester::_post_semaphore));

Review comment (suggestion):
Maybe, instead of passing the viewport id, you could pass the `request_vp_update_once` callable, which is already bound to such id. Alternatively, let's get rid of the `request_vp_update_once` callable and do the call directly in the `else`. The callable doesn't have a real point anymore after this PR.

Code after:

bool EditorResourcePreviewGenerator::generate_small_preview_automatically() const {
	bool success = false;
	GDVIRTUAL_CALL(_generate_small_preview_automatically, success);
	return success;
}

bool EditorResourcePreviewGenerator::can_generate_small_preview() const {
	bool success = false;
	GDVIRTUAL_CALL(_can_generate_small_preview, success);
	return success;
}

void EditorResourcePreviewGenerator::_bind_methods() {
	GDVIRTUAL_BIND(_handles, "type");
	GDVIRTUAL_BIND(_generate, "resource", "size", "metadata");
	GDVIRTUAL_BIND(_generate_from_path, "path", "size", "metadata");
	GDVIRTUAL_BIND(_generate_small_preview_automatically);
	GDVIRTUAL_BIND(_can_generate_small_preview);

	ClassDB::bind_method(D_METHOD("request_draw_and_wait", "viewport"), &EditorResourcePreviewGenerator::request_draw_and_wait);
}

void EditorResourcePreviewGenerator::DrawRequester::request_and_wait(RID p_viewport) {
	if (EditorResourcePreview::get_singleton()->is_threaded()) {
		RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(this, &EditorResourcePreviewGenerator::DrawRequester::_prepare_draw).bind(p_viewport), Object::CONNECT_ONE_SHOT);
		semaphore.wait();
	} else {
		// Avoid the main viewport and children being redrawn.
		SceneTree *st = Object::cast_to<SceneTree>(OS::get_singleton()->get_main_loop());
		ERR_FAIL_NULL_MSG(st, "Editor's MainLoop is not a SceneTree. This is a bug.");
		RID root_vp = st->get_root()->get_viewport_rid();
		RenderingServer::get_singleton()->viewport_set_active(root_vp, false);

		RS::get_singleton()->viewport_set_update_mode(p_viewport, RS::VIEWPORT_UPDATE_ONCE);
		RS::get_singleton()->draw(false);

		// Let main viewport and children be drawn again.
		RenderingServer::get_singleton()->viewport_set_active(root_vp, true);
	}
}

void EditorResourcePreviewGenerator::DrawRequester::abort() {
	if (EditorResourcePreview::get_singleton()->is_threaded()) {
		semaphore.post();
	}
}

void EditorResourcePreviewGenerator::request_draw_and_wait(RID viewport) const {
	DrawRequester draw_requester;
	draw_requester.request_and_wait(viewport);