Blender V5.0
scene/image.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "scene/image.h"
6#include "device/device.h"
7#include "scene/colorspace.h"
8#include "scene/image_oiio.h"
9#include "scene/image_vdb.h"
10#include "scene/scene.h"
11#include "scene/stats.h"
12
13#include "util/image.h"
14#include "util/image_impl.h"
15#include "util/log.h"
16#include "util/progress.h"
17#include "util/task.h"
18#include "util/texture.h"
19
20#ifdef WITH_OSL
21# include <OSL/oslexec.h>
22#endif
23
25
26namespace {
27
29{
30 switch (type) {
32 return "float4";
34 return "byte4";
36 return "half4";
38 return "float";
40 return "byte";
42 return "half";
44 return "ushort4";
46 return "ushort";
48 return "nanovdb_float";
50 return "nanovdb_float3";
52 return "nanovdb_float4";
54 return "nanovdb_fpn";
56 return "nanovdb_fp16";
58 return "nanovdb_empty";
60 assert(!"System enumerator type, should never be used");
61 return "";
62 }
63 assert(!"Unhandled image data type");
64 return "";
65}
66
67} // namespace
68
69/* Image Handle */
70
72
74 : slots(other.slots), is_tiled(other.is_tiled), manager(other.manager)
75{
76 /* Increase image user count. */
77 for (const size_t slot : slots) {
78 manager->add_image_user(slot);
79 }
80}
81
83{
84 clear();
85 manager = other.manager;
86 is_tiled = other.is_tiled;
87 slots = other.slots;
88
89 for (const size_t slot : slots) {
90 manager->add_image_user(slot);
91 }
92
93 return *this;
94}
95
100
102{
103 for (const size_t slot : slots) {
104 manager->remove_image_user(slot);
105 }
106
107 slots.clear();
108 manager = nullptr;
109}
110
112{
113 return slots.empty();
114}
115
117{
118 return (is_tiled) ? slots.size() : 0;
119}
120
122{
123 return slots.size();
124}
125
127{
128 if (slots.empty()) {
129 return ImageMetaData();
130 }
131
132 ImageManager::Image *img = manager->get_image_slot(slots.front());
133 manager->load_image_metadata(img);
134 return img->metadata;
135}
136
137int ImageHandle::svm_slot(const int slot_index) const
138{
139 if (slot_index >= slots.size()) {
140 return -1;
141 }
142
143 if (manager->osl_texture_system) {
144 ImageManager::Image *img = manager->get_image_slot(slots[slot_index]);
145 if (!img->loader->osl_filepath().empty()) {
146 return -1;
147 }
148 }
149
150 return slots[slot_index];
151}
152
154{
155 const size_t num_nodes = divide_up(slots.size(), 2);
156
157 vector<int4> svm_slots;
158 svm_slots.reserve(num_nodes);
159 for (size_t i = 0; i < num_nodes; i++) {
160 int4 node;
161
162 size_t slot = slots[2 * i];
163 node.x = manager->get_image_slot(slot)->loader->get_tile_number();
164 node.y = slot;
165
166 if ((2 * i + 1) < slots.size()) {
167 slot = slots[2 * i + 1];
168 node.z = manager->get_image_slot(slot)->loader->get_tile_number();
169 node.w = slot;
170 }
171 else {
172 node.z = -1;
173 node.w = -1;
174 }
175
176 svm_slots.push_back(node);
177 }
178
179 return svm_slots;
180}
181
183{
184 if (slots.empty()) {
185 return nullptr;
186 }
187
188 ImageManager::Image *img = manager->get_image_slot(slots[0]);
189 return img ? img->mem.get() : nullptr;
190}
191
193{
194 if (slots.empty()) {
195 return nullptr;
196 }
197
198 ImageManager::Image *img = manager->get_image_slot(slots[0]);
199
200 if (img == nullptr) {
201 return nullptr;
202 }
203
204 ImageLoader *loader = img->loader.get();
205
206 if (loader == nullptr) {
207 return nullptr;
208 }
209
210 if (loader->is_vdb_loader()) {
211 return dynamic_cast<VDBImageLoader *>(loader);
212 }
213
214 return nullptr;
215}
216
218{
219 return manager;
220}
221
222bool ImageHandle::operator==(const ImageHandle &other) const
223{
224 return manager == other.manager && is_tiled == other.is_tiled && slots == other.slots;
225}
226
227/* Image MetaData */
228
241
243{
244 return channels == other.channels && width == other.width && height == other.height &&
246 (!use_transform_3d || transform_3d == other.transform_3d) && type == other.type &&
248}
249
255
257{
258 /* Convert used specified color spaces to one we know how to handle. */
261
263 /* Nothing to do. */
264 }
265 else if (colorspace == u_colorspace_srgb) {
266 /* Keep sRGB colorspace stored as sRGB, to save memory and/or loading time
267 * for the common case of 8bit sRGB images like PNG. */
268 compress_as_srgb = true;
269 }
270 else {
271 /* If colorspace conversion needed, use half instead of short so we can
272 * represent HDR values that might result from conversion. */
275 }
278 }
279 }
280}
281
282/* Image Loader */
283
284ImageLoader::ImageLoader() = default;
285
287{
288 return ustring();
289}
290
292{
293 return 0;
294}
295
297{
298 if (a == nullptr && b == nullptr) {
299 return true;
300 }
301 return (a && b && typeid(*a) == typeid(*b) && a->equals(*b));
302}
303
305{
306 return false;
307}
308
309/* Image Manager */
310
312{
313 need_update_ = true;
314 osl_texture_system = nullptr;
315 animation_frame = 0;
316
317 /* Set image limits */
318 features.has_nanovdb = info.has_nanovdb;
319}
320
322{
323 for (size_t slot = 0; slot < images.size(); slot++) {
324 assert(!images[slot]);
325 }
326}
327
328void ImageManager::set_osl_texture_system(void *texture_system)
329{
330 osl_texture_system = texture_system;
331}
332
334{
335 if (frame != animation_frame) {
336 const thread_scoped_lock device_lock(images_mutex);
337 animation_frame = frame;
338
339 for (size_t slot = 0; slot < images.size(); slot++) {
340 if (images[slot] && images[slot]->params.animated) {
341 return true;
342 }
343 }
344 }
345
346 return false;
347}
348
349void ImageManager::load_image_metadata(Image *img)
350{
351 if (!img->need_metadata) {
352 return;
353 }
354
355 const thread_scoped_lock image_lock(img->mutex);
356 if (!img->need_metadata) {
357 return;
358 }
359
360 ImageMetaData &metadata = img->metadata;
361 metadata = ImageMetaData();
362 metadata.colorspace = img->params.colorspace;
363
364 if (img->loader->load_metadata(features, metadata)) {
365 assert(metadata.type != IMAGE_DATA_NUM_TYPES);
366 }
367 else {
368 metadata.type = IMAGE_DATA_TYPE_BYTE4;
369 }
370
371 metadata.detect_colorspace();
372
373 assert(features.has_nanovdb || !is_nanovdb_type(metadata.type));
374
375 img->need_metadata = false;
376}
377
379{
380 const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(filename), params, false);
381
382 ImageHandle handle;
383 handle.slots.push_back(slot);
384 handle.manager = this;
385 return handle;
386}
387
389 const ImageParams &params,
390 const array<int> &tiles)
391{
392 ImageHandle handle;
393 handle.manager = this;
394 handle.is_tiled = !tiles.empty();
395
396 if (!handle.is_tiled) {
397 const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(filename), params, false);
398 handle.slots.push_back(slot);
399 return handle;
400 }
401
402 for (const int tile : tiles) {
403 string tile_filename = filename;
404
405 /* Since we don't have information about the exact tile format used in this code location,
406 * just attempt all replacement patterns that Blender supports. */
407 string_replace(tile_filename, "<UDIM>", string_printf("%04d", tile));
408
409 const int u = ((tile - 1001) % 10);
410 const int v = ((tile - 1001) / 10);
411 string_replace(tile_filename, "<UVTILE>", string_printf("u%d_v%d", u + 1, v + 1));
412
413 const size_t slot = add_image_slot(make_unique<OIIOImageLoader>(tile_filename), params, false);
414 handle.slots.push_back(slot);
415 }
416
417 return handle;
418}
419
421 const ImageParams &params,
422 const bool builtin)
423{
424 const size_t slot = add_image_slot(std::move(loader), params, builtin);
425
426 ImageHandle handle;
427 handle.slots.push_back(slot);
428 handle.manager = this;
429 return handle;
430}
431
433 const ImageParams &params)
434{
435 ImageHandle handle;
436 handle.is_tiled = true;
437
438 for (unique_ptr<ImageLoader> &loader : loaders) {
439 unique_ptr<ImageLoader> local_loader;
440 std::swap(loader, local_loader);
441 const size_t slot = add_image_slot(std::move(local_loader), params, true);
442 handle.slots.push_back(slot);
443 }
444
445 handle.manager = this;
446 return handle;
447}
448
449size_t ImageManager::add_image_slot(unique_ptr<ImageLoader> &&loader,
450 const ImageParams &params,
451 const bool builtin)
452{
453 size_t slot;
454
455 const thread_scoped_lock device_lock(images_mutex);
456
457 /* Find existing image. */
458 for (slot = 0; slot < images.size(); slot++) {
459 Image *img = images[slot].get();
460 if (img && ImageLoader::equals(img->loader.get(), loader.get()) && img->params == params) {
461 img->users++;
462 return slot;
463 }
464 }
465
466 /* Find free slot. */
467 for (slot = 0; slot < images.size(); slot++) {
468 if (!images[slot]) {
469 break;
470 }
471 }
472
473 if (slot == images.size()) {
474 images.resize(images.size() + 1);
475 }
476
477 /* Add new image. */
478 unique_ptr<Image> img = make_unique<Image>();
479 img->params = params;
480 img->loader = std::move(loader);
481 img->need_metadata = true;
482 img->need_load = !(osl_texture_system && !img->loader->osl_filepath().empty());
483 img->builtin = builtin;
484 img->users = 1;
485 img->mem = nullptr;
486
487 images[slot] = std::move(img);
488
489 need_update_ = true;
490
491 return slot;
492}
493
494void ImageManager::add_image_user(const size_t slot)
495{
496 const thread_scoped_lock device_lock(images_mutex);
497 Image *image = images[slot].get();
498 assert(image && image->users >= 1);
499
500 image->users++;
501}
502
503void ImageManager::remove_image_user(const size_t slot)
504{
505 const thread_scoped_lock device_lock(images_mutex);
506 Image *image = images[slot].get();
507 assert(image && image->users >= 1);
508
509 /* decrement user count */
510 image->users--;
511
512 /* don't remove immediately, rather do it all together later on. one of
513 * the reasons for this is that on shader changes we add and remove nodes
514 * that use them, but we do not want to reload the image all the time. */
515 if (image->users == 0) {
516 need_update_ = true;
517 }
518}
519
520ImageManager::Image *ImageManager::get_image_slot(const size_t slot)
521{
522 /* Need mutex lock, images vector might get resized by another thread. */
523 const thread_scoped_lock device_lock(images_mutex);
524 return images[slot].get();
525}
526
528{
529 /* For typical RGBA images we let OIIO convert to associated alpha,
530 * but some types we want to leave the RGB channels untouched. */
534}
535
536template<TypeDesc::BASETYPE FileFormat, typename StorageType>
537bool ImageManager::file_load_image(Image *img, const int texture_limit)
538{
539 /* Ignore empty images. */
540 if (!(img->metadata.channels > 0)) {
541 return false;
542 }
543
544 /* Get metadata. */
545 const int width = img->metadata.width;
546 const int height = img->metadata.height;
547 const int components = img->metadata.channels;
548
549 /* Read pixels. */
550 vector<StorageType> pixels_storage;
551 StorageType *pixels;
552 const size_t max_size = max(width, height);
553 if (max_size == 0) {
554 /* Don't bother with empty images. */
555 return false;
556 }
557
558 /* Allocate memory as needed, may be smaller to resize down. */
559 if (texture_limit > 0 && max_size > texture_limit) {
560 pixels_storage.resize(((size_t)width) * height * 4);
561 pixels = &pixels_storage[0];
562 }
563 else {
564 const thread_scoped_lock device_lock(device_mutex);
565 pixels = (StorageType *)img->mem->alloc(width, height);
566 }
567
568 if (pixels == nullptr) {
569 /* Could be that we've run out of memory. */
570 return false;
571 }
572
573 const size_t num_pixels = ((size_t)width) * height;
574 img->loader->load_pixels(
575 img->metadata, pixels, num_pixels * components, image_associate_alpha(img));
576
577 /* The kernel can handle 1 and 4 channel images. Anything that is not a single
578 * channel image is converted to RGBA format. */
579 const bool is_rgba = (img->metadata.type == IMAGE_DATA_TYPE_FLOAT4 ||
580 img->metadata.type == IMAGE_DATA_TYPE_HALF4 ||
581 img->metadata.type == IMAGE_DATA_TYPE_BYTE4 ||
582 img->metadata.type == IMAGE_DATA_TYPE_USHORT4);
583
584 if (is_rgba) {
585 const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
586
587 if (components == 2) {
588 /* Grayscale + alpha to RGBA. */
589 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
590 pixels[i * 4 + 3] = pixels[i * 2 + 1];
591 pixels[i * 4 + 2] = pixels[i * 2 + 0];
592 pixels[i * 4 + 1] = pixels[i * 2 + 0];
593 pixels[i * 4 + 0] = pixels[i * 2 + 0];
594 }
595 }
596 else if (components == 3) {
597 /* RGB to RGBA. */
598 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
599 pixels[i * 4 + 3] = one;
600 pixels[i * 4 + 2] = pixels[i * 3 + 2];
601 pixels[i * 4 + 1] = pixels[i * 3 + 1];
602 pixels[i * 4 + 0] = pixels[i * 3 + 0];
603 }
604 }
605 else if (components == 1) {
606 /* Grayscale to RGBA. */
607 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
608 pixels[i * 4 + 3] = one;
609 pixels[i * 4 + 2] = pixels[i];
610 pixels[i * 4 + 1] = pixels[i];
611 pixels[i * 4 + 0] = pixels[i];
612 }
613 }
614
615 /* Disable alpha if requested by the user. */
616 if (img->params.alpha_type == IMAGE_ALPHA_IGNORE) {
617 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
618 pixels[i * 4 + 3] = one;
619 }
620 }
621 }
622
623 if (img->metadata.colorspace != u_colorspace_raw &&
624 img->metadata.colorspace != u_colorspace_srgb)
625 {
626 /* Convert to scene linear. */
627 const bool ignore_alpha = img->params.alpha_type == IMAGE_ALPHA_IGNORE ||
628 img->params.alpha_type == IMAGE_ALPHA_CHANNEL_PACKED;
629 ColorSpaceManager::to_scene_linear(img->metadata.colorspace,
630 pixels,
631 num_pixels,
632 is_rgba,
633 img->metadata.compress_as_srgb,
634 ignore_alpha);
635 }
636
637 /* Make sure we don't have buggy values. */
638 if constexpr (FileFormat == TypeDesc::FLOAT) {
639 /* For RGBA buffers we put all channels to 0 if either of them is not
640 * finite. This way we avoid possible artifacts caused by fully changed
641 * hue. */
642 if (is_rgba) {
643 for (size_t i = 0; i < num_pixels; i += 4) {
644 StorageType *pixel = &pixels[i * 4];
645 if (!isfinite(pixel[0]) || !isfinite(pixel[1]) || !isfinite(pixel[2]) ||
646 !isfinite(pixel[3]))
647 {
648 pixel[0] = 0;
649 pixel[1] = 0;
650 pixel[2] = 0;
651 pixel[3] = 0;
652 }
653 }
654 }
655 else {
656 for (size_t i = 0; i < num_pixels; ++i) {
657 StorageType *pixel = &pixels[i];
658 if (!isfinite(pixel[0])) {
659 pixel[0] = 0;
660 }
661 }
662 }
663 }
664
665 /* Scale image down if needed. */
666 if (!pixels_storage.empty()) {
667 float scale_factor = 1.0f;
668 while (max_size * scale_factor > texture_limit) {
669 scale_factor *= 0.5f;
670 }
671 LOG_DEBUG << "Scaling image " << img->loader->name() << " by a factor of " << scale_factor
672 << ".";
673 vector<StorageType> scaled_pixels;
674 size_t scaled_width;
675 size_t scaled_height;
676 util_image_resize_pixels(pixels_storage,
677 width,
678 height,
679 is_rgba ? 4 : 1,
680 scale_factor,
681 &scaled_pixels,
682 &scaled_width,
683 &scaled_height);
684
685 StorageType *texture_pixels;
686
687 {
688 const thread_scoped_lock device_lock(device_mutex);
689 texture_pixels = (StorageType *)img->mem->alloc(scaled_width, scaled_height);
690 }
691
692 memcpy(texture_pixels, &scaled_pixels[0], scaled_pixels.size() * sizeof(StorageType));
693 }
694
695 return true;
696}
697
698void ImageManager::device_load_image(Device *device,
699 Scene *scene,
700 const size_t slot,
701 Progress &progress)
702{
703 if (progress.get_cancel()) {
704 return;
705 }
706
707 Image *img = images[slot].get();
708
709 progress.set_status("Updating Images", "Loading " + img->loader->name());
710
711 const int texture_limit = scene->params.texture_limit;
712
713 load_image_metadata(img);
714 const ImageDataType type = img->metadata.type;
715
716 /* Name for debugging. */
717 img->mem_name = string_printf("tex_image_%s_%03d", name_from_type(type), (int)slot);
718
719 /* Free previous texture in slot. */
720 if (img->mem) {
721 const thread_scoped_lock device_lock(device_mutex);
722 img->mem.reset();
723 }
724
725 img->mem = make_unique<device_texture>(
726 device, img->mem_name.c_str(), slot, type, img->params.interpolation, img->params.extension);
727 img->mem->info.use_transform_3d = img->metadata.use_transform_3d;
728 img->mem->info.transform_3d = img->metadata.transform_3d;
729
730 /* Create new texture. */
731 if (type == IMAGE_DATA_TYPE_FLOAT4) {
732 if (!file_load_image<TypeDesc::FLOAT, float>(img, texture_limit)) {
733 /* on failure to load, we set a 1x1 pixels pink image */
734 const thread_scoped_lock device_lock(device_mutex);
735 float *pixels = (float *)img->mem->alloc(1, 1);
736
737 pixels[0] = TEX_IMAGE_MISSING_R;
738 pixels[1] = TEX_IMAGE_MISSING_G;
739 pixels[2] = TEX_IMAGE_MISSING_B;
740 pixels[3] = TEX_IMAGE_MISSING_A;
741 }
742 }
743 else if (type == IMAGE_DATA_TYPE_FLOAT) {
744 if (!file_load_image<TypeDesc::FLOAT, float>(img, texture_limit)) {
745 /* on failure to load, we set a 1x1 pixels pink image */
746 const thread_scoped_lock device_lock(device_mutex);
747 float *pixels = (float *)img->mem->alloc(1, 1);
748
749 pixels[0] = TEX_IMAGE_MISSING_R;
750 }
751 }
752 else if (type == IMAGE_DATA_TYPE_BYTE4) {
753 if (!file_load_image<TypeDesc::UINT8, uchar>(img, texture_limit)) {
754 /* on failure to load, we set a 1x1 pixels pink image */
755 const thread_scoped_lock device_lock(device_mutex);
756 uchar *pixels = (uchar *)img->mem->alloc(1, 1);
757
758 pixels[0] = (TEX_IMAGE_MISSING_R * 255);
759 pixels[1] = (TEX_IMAGE_MISSING_G * 255);
760 pixels[2] = (TEX_IMAGE_MISSING_B * 255);
761 pixels[3] = (TEX_IMAGE_MISSING_A * 255);
762 }
763 }
764 else if (type == IMAGE_DATA_TYPE_BYTE) {
765 if (!file_load_image<TypeDesc::UINT8, uchar>(img, texture_limit)) {
766 /* on failure to load, we set a 1x1 pixels pink image */
767 const thread_scoped_lock device_lock(device_mutex);
768 uchar *pixels = (uchar *)img->mem->alloc(1, 1);
769
770 pixels[0] = (TEX_IMAGE_MISSING_R * 255);
771 }
772 }
773 else if (type == IMAGE_DATA_TYPE_HALF4) {
774 if (!file_load_image<TypeDesc::HALF, half>(img, texture_limit)) {
775 /* on failure to load, we set a 1x1 pixels pink image */
776 const thread_scoped_lock device_lock(device_mutex);
777 half *pixels = (half *)img->mem->alloc(1, 1);
778
779 pixels[0] = TEX_IMAGE_MISSING_R;
780 pixels[1] = TEX_IMAGE_MISSING_G;
781 pixels[2] = TEX_IMAGE_MISSING_B;
782 pixels[3] = TEX_IMAGE_MISSING_A;
783 }
784 }
785 else if (type == IMAGE_DATA_TYPE_USHORT) {
786 if (!file_load_image<TypeDesc::USHORT, uint16_t>(img, texture_limit)) {
787 /* on failure to load, we set a 1x1 pixels pink image */
788 const thread_scoped_lock device_lock(device_mutex);
789 uint16_t *pixels = (uint16_t *)img->mem->alloc(1, 1);
790
791 pixels[0] = (TEX_IMAGE_MISSING_R * 65535);
792 }
793 }
794 else if (type == IMAGE_DATA_TYPE_USHORT4) {
795 if (!file_load_image<TypeDesc::USHORT, uint16_t>(img, texture_limit)) {
796 /* on failure to load, we set a 1x1 pixels pink image */
797 const thread_scoped_lock device_lock(device_mutex);
798 uint16_t *pixels = (uint16_t *)img->mem->alloc(1, 1);
799
800 pixels[0] = (TEX_IMAGE_MISSING_R * 65535);
801 pixels[1] = (TEX_IMAGE_MISSING_G * 65535);
802 pixels[2] = (TEX_IMAGE_MISSING_B * 65535);
803 pixels[3] = (TEX_IMAGE_MISSING_A * 65535);
804 }
805 }
806 else if (type == IMAGE_DATA_TYPE_HALF) {
807 if (!file_load_image<TypeDesc::HALF, half>(img, texture_limit)) {
808 /* on failure to load, we set a 1x1 pixels pink image */
809 const thread_scoped_lock device_lock(device_mutex);
810 half *pixels = (half *)img->mem->alloc(1, 1);
811
812 pixels[0] = TEX_IMAGE_MISSING_R;
813 }
814 }
815#ifdef WITH_NANOVDB
816 else if (is_nanovdb_type(type)) {
817 const thread_scoped_lock device_lock(device_mutex);
818 void *pixels = img->mem->alloc(img->metadata.byte_size, 0);
819
820 if (pixels != nullptr) {
821 img->loader->load_pixels(img->metadata, pixels, img->metadata.byte_size, false);
822 }
823 }
824#endif
825
826 {
827 const thread_scoped_lock device_lock(device_mutex);
828 img->mem->copy_to_device();
829 }
830
831 /* Cleanup memory in image loader. */
832 img->loader->cleanup();
833 img->need_load = false;
834}
835
836void ImageManager::device_free_image(Device * /*unused*/, size_t slot)
837{
838 Image *img = images[slot].get();
839 if (img == nullptr) {
840 return;
841 }
842
843 if (osl_texture_system) {
844#ifdef WITH_OSL
845 const ustring filepath = img->loader->osl_filepath();
846 if (!filepath.empty()) {
847 ((OSL::TextureSystem *)osl_texture_system)->invalidate(filepath);
848 }
849#endif
850 }
851
852 if (img->mem) {
853 const thread_scoped_lock device_lock(device_mutex);
854 img->mem.reset();
855 }
856
857 images[slot].reset();
858}
859
860void ImageManager::device_update(Device *device, Scene *scene, Progress &progress)
861{
862 if (!need_update()) {
863 return;
864 }
865
866 const scoped_callback_timer timer([scene](double time) {
867 if (scene->update_stats) {
868 scene->update_stats->image.times.add_entry({"device_update", time});
869 }
870 });
871
872 TaskPool pool;
873 for (size_t slot = 0; slot < images.size(); slot++) {
874 Image *img = images[slot].get();
875 if (img && img->users == 0) {
876 device_free_image(device, slot);
877 }
878 else if (img && img->need_load) {
879 pool.push([this, device, scene, slot, &progress] {
880 device_load_image(device, scene, slot, progress);
881 });
882 }
883 }
884
885 pool.wait_work();
886
887 need_update_ = false;
888}
889
891 Scene *scene,
892 const size_t slot,
893 Progress &progress)
894{
895 Image *img = images[slot].get();
896 assert(img != nullptr);
897
898 if (img->users == 0) {
899 device_free_image(device, slot);
900 }
901 else if (img->need_load) {
902 device_load_image(device, scene, slot, progress);
903 }
904}
905
907{
908 /* Load only builtin images, Blender needs this to load evaluated
909 * scene data from depsgraph before it is freed. */
910 if (!need_update()) {
911 return;
912 }
913
914 TaskPool pool;
915 for (size_t slot = 0; slot < images.size(); slot++) {
916 Image *img = images[slot].get();
917 if (img && img->need_load && img->builtin) {
918 pool.push([this, device, scene, slot, &progress] {
919 device_load_image(device, scene, slot, progress);
920 });
921 }
922 }
923
924 pool.wait_work();
925}
926
928{
929 for (size_t slot = 0; slot < images.size(); slot++) {
930 Image *img = images[slot].get();
931 if (img && img->builtin) {
932 device_free_image(device, slot);
933 }
934 }
935}
936
938{
939 for (size_t slot = 0; slot < images.size(); slot++) {
940 device_free_image(device, slot);
941 }
942 images.clear();
943}
944
946{
947 for (const unique_ptr<Image> &image : images) {
948 if (!image) {
949 /* Image may have been freed due to lack of users. */
950 continue;
951 }
952 stats->image.textures.add_entry(
953 NamedSizeEntry(image->loader->name(), image->mem->memory_size()));
954 }
955}
956
958{
959 need_update_ = true;
960}
961
963{
964 return need_update_;
965}
966
unsigned char uchar
ATTR_WARN_UNUSED_RESULT const BMVert * v
static bool colorspace_is_data(ustring colorspace)
static ustring detect_known_colorspace(ustring colorspace, const char *file_colorspace, const char *file_format, bool is_float)
static void to_scene_linear(ustring colorspace, T *pixels, const size_t num_pixels, bool is_rgba, bool compress_as_srgb, bool ignore_alpha)
int num_tiles() const
vector< size_t > slots
ImageManager * get_manager() const
bool operator==(const ImageHandle &other) const
friend class ImageManager
bool empty() const
ImageManager * manager
ImageHandle & operator=(const ImageHandle &other)
ImageMetaData metadata()
VDBImageLoader * vdb_loader() const
device_texture * image_memory() const
int num_svm_slots() const
vector< int4 > get_svm_slots() const
int svm_slot(const int slot_index=0) const
virtual bool equals(const ImageLoader &other) const =0
virtual ustring osl_filepath() const
virtual int get_tile_number() const
virtual bool is_vdb_loader() const
ImageManager(const DeviceInfo &info)
void device_update(Device *device, Scene *scene, Progress &progress)
bool need_update() const
void device_free(Device *device)
void device_load_builtin(Device *device, Scene *scene, Progress &progress)
bool set_animation_frame_update(const int frame)
void device_free_builtin(Device *device)
ImageHandle add_image(const string &filename, const ImageParams &params)
void collect_statistics(RenderStats *stats)
friend class ImageHandle
void device_update_slot(Device *device, Scene *scene, const size_t slot, Progress &progress)
void set_osl_texture_system(void *texture_system)
ImageDataType type
bool is_float() const
void detect_colorspace()
bool operator==(const ImageMetaData &other) const
const char * colorspace_file_format
ImageAlphaType alpha_type
NamedSizeStats textures
void add_entry(const NamedSizeEntry &entry)
Definition stats.cpp:56
bool get_cancel() const
Definition progress.h:77
void set_status(const string &status_, const string &substatus_="")
Definition progress.h:248
int texture_limit
Definition scene.h:74
T util_image_cast_from_float(const float value)
void util_image_resize_pixels(const vector< T > &input_pixels, const size_t input_width, const size_t input_height, const size_t components, vector< T > *output_pixels, size_t *output_width, size_t *output_height)
#define CCL_NAMESPACE_END
#define assert(assertion)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
const ccl_global KernelWorkTile * tile
#define LOG_DEBUG
Definition log.h:107
const char * name_from_type(ImageDataType type)
ustring u_colorspace_raw
ustring u_colorspace_srgb
static bool image_associate_alpha(ImageManager::Image *img)
CCL_NAMESPACE_BEGIN string string_printf(const char *format,...)
Definition string.cpp:23
void string_replace(string &haystack, const string &needle, const string &other)
Definition string.cpp:145
unique_ptr< device_texture > mem
unique_ptr< ImageLoader > loader
ImageStats image
SceneParams params
Definition scene.h:168
unique_ptr< SceneUpdateStats > update_stats
Definition scene.h:175
void push(TaskRunFunction &&task)
Definition task.cpp:21
void wait_work(Summary *stats=nullptr)
Definition task.cpp:27
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
ImageDataType
Definition texture.h:32
@ IMAGE_DATA_NUM_TYPES
Definition texture.h:48
@ IMAGE_DATA_TYPE_BYTE
Definition texture.h:37
@ IMAGE_DATA_TYPE_FLOAT
Definition texture.h:36
@ IMAGE_DATA_TYPE_NANOVDB_FP16
Definition texture.h:45
@ IMAGE_DATA_TYPE_FLOAT4
Definition texture.h:33
@ IMAGE_DATA_TYPE_USHORT4
Definition texture.h:39
@ IMAGE_DATA_TYPE_USHORT
Definition texture.h:40
@ IMAGE_DATA_TYPE_NANOVDB_EMPTY
Definition texture.h:46
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT
Definition texture.h:41
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT3
Definition texture.h:42
@ IMAGE_DATA_TYPE_HALF
Definition texture.h:38
@ IMAGE_DATA_TYPE_BYTE4
Definition texture.h:34
@ IMAGE_DATA_TYPE_HALF4
Definition texture.h:35
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT4
Definition texture.h:43
@ IMAGE_DATA_TYPE_NANOVDB_FPN
Definition texture.h:44
@ IMAGE_ALPHA_CHANNEL_PACKED
Definition texture.h:61
@ IMAGE_ALPHA_IGNORE
Definition texture.h:62
@ TEX_IMAGE_MISSING_G
Definition texture.h:15
@ TEX_IMAGE_MISSING_A
Definition texture.h:17
@ TEX_IMAGE_MISSING_R
Definition texture.h:14
@ TEX_IMAGE_MISSING_B
Definition texture.h:16
ccl_device_inline bool is_nanovdb_type(int type)
Definition texture.h:51
std::unique_lock< std::mutex > thread_scoped_lock
Definition thread.h:28
ccl_device_inline size_t divide_up(const size_t x, const size_t y)
Definition types_base.h:52
wmTimer * timer