Blender V4.5
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_fpn";
54 return "nanovdb_fp16";
56 assert(!"System enumerator type, should never be used");
57 return "";
58 }
59 assert(!"Unhandled image data type");
60 return "";
61}
62
63} // namespace
64
65/* Image Handle */
66
68
70 : slots(other.slots), is_tiled(other.is_tiled), manager(other.manager)
71{
72 /* Increase image user count. */
73 for (const size_t slot : slots) {
74 manager->add_image_user(slot);
75 }
76}
77
79{
80 clear();
81 manager = other.manager;
82 is_tiled = other.is_tiled;
83 slots = other.slots;
84
85 for (const size_t slot : slots) {
86 manager->add_image_user(slot);
87 }
88
89 return *this;
90}
91
96
98{
99 for (const size_t slot : slots) {
100 manager->remove_image_user(slot);
101 }
102
103 slots.clear();
104 manager = nullptr;
105}
106
108{
109 return slots.empty();
110}
111
113{
114 return (is_tiled) ? slots.size() : 0;
115}
116
118{
119 return slots.size();
120}
121
123{
124 if (slots.empty()) {
125 return ImageMetaData();
126 }
127
128 ImageManager::Image *img = manager->get_image_slot(slots.front());
129 manager->load_image_metadata(img);
130 return img->metadata;
131}
132
133int ImageHandle::svm_slot(const int slot_index) const
134{
135 if (slot_index >= slots.size()) {
136 return -1;
137 }
138
139 if (manager->osl_texture_system) {
140 ImageManager::Image *img = manager->get_image_slot(slots[slot_index]);
141 if (!img->loader->osl_filepath().empty()) {
142 return -1;
143 }
144 }
145
146 return slots[slot_index];
147}
148
150{
151 const size_t num_nodes = divide_up(slots.size(), 2);
152
153 vector<int4> svm_slots;
154 svm_slots.reserve(num_nodes);
155 for (size_t i = 0; i < num_nodes; i++) {
156 int4 node;
157
158 size_t slot = slots[2 * i];
159 node.x = manager->get_image_slot(slot)->loader->get_tile_number();
160 node.y = slot;
161
162 if ((2 * i + 1) < slots.size()) {
163 slot = slots[2 * i + 1];
164 node.z = manager->get_image_slot(slot)->loader->get_tile_number();
165 node.w = slot;
166 }
167 else {
168 node.z = -1;
169 node.w = -1;
170 }
171
172 svm_slots.push_back(node);
173 }
174
175 return svm_slots;
176}
177
179{
180 if (slots.empty()) {
181 return nullptr;
182 }
183
184 ImageManager::Image *img = manager->get_image_slot(slots[0]);
185 return img ? img->mem.get() : nullptr;
186}
187
189{
190 if (slots.empty()) {
191 return nullptr;
192 }
193
194 ImageManager::Image *img = manager->get_image_slot(slots[0]);
195
196 if (img == nullptr) {
197 return nullptr;
198 }
199
200 ImageLoader *loader = img->loader.get();
201
202 if (loader == nullptr) {
203 return nullptr;
204 }
205
206 if (loader->is_vdb_loader()) {
207 return dynamic_cast<VDBImageLoader *>(loader);
208 }
209
210 return nullptr;
211}
212
214{
215 return manager;
216}
217
218bool ImageHandle::operator==(const ImageHandle &other) const
219{
220 return manager == other.manager && is_tiled == other.is_tiled && slots == other.slots;
221}
222
223/* Image MetaData */
224
238
240{
241 return channels == other.channels && width == other.width && height == other.height &&
242 depth == other.depth && use_transform_3d == other.use_transform_3d &&
243 (!use_transform_3d || transform_3d == other.transform_3d) && type == other.type &&
245}
246
252
254{
255 /* Convert used specified color spaces to one we know how to handle. */
258
260 /* Nothing to do. */
261 }
262 else if (colorspace == u_colorspace_srgb) {
263 /* Keep sRGB colorspace stored as sRGB, to save memory and/or loading time
264 * for the common case of 8bit sRGB images like PNG. */
265 compress_as_srgb = true;
266 }
267 else {
268 /* If colorspace conversion needed, use half instead of short so we can
269 * represent HDR values that might result from conversion. */
272 }
275 }
276 }
277}
278
279/* Image Loader */
280
281ImageLoader::ImageLoader() = default;
282
284{
285 return ustring();
286}
287
289{
290 return 0;
291}
292
294{
295 if (a == nullptr && b == nullptr) {
296 return true;
297 }
298 return (a && b && typeid(*a) == typeid(*b) && a->equals(*b));
299}
300
302{
303 return false;
304}
305
306/* Image Manager */
307
309{
310 need_update_ = true;
311 osl_texture_system = nullptr;
312 animation_frame = 0;
313
314 /* Set image limits */
315 features.has_nanovdb = info.has_nanovdb;
316}
317
319{
320 for (size_t slot = 0; slot < images.size(); slot++) {
321 assert(!images[slot]);
322 }
323}
324
325void ImageManager::set_osl_texture_system(void *texture_system)
326{
327 osl_texture_system = texture_system;
328}
329
331{
332 if (frame != animation_frame) {
333 const thread_scoped_lock device_lock(images_mutex);
334 animation_frame = frame;
335
336 for (size_t slot = 0; slot < images.size(); slot++) {
337 if (images[slot] && images[slot]->params.animated) {
338 return true;
339 }
340 }
341 }
342
343 return false;
344}
345
346void ImageManager::load_image_metadata(Image *img)
347{
348 if (!img->need_metadata) {
349 return;
350 }
351
352 const thread_scoped_lock image_lock(img->mutex);
353 if (!img->need_metadata) {
354 return;
355 }
356
357 ImageMetaData &metadata = img->metadata;
358 metadata = ImageMetaData();
359 metadata.colorspace = img->params.colorspace;
360
361 if (img->loader->load_metadata(features, metadata)) {
362 assert(metadata.type != IMAGE_DATA_NUM_TYPES);
363 }
364 else {
365 metadata.type = IMAGE_DATA_TYPE_BYTE4;
366 }
367
368 metadata.detect_colorspace();
369
370 assert(features.has_nanovdb || (metadata.type != IMAGE_DATA_TYPE_NANOVDB_FLOAT ||
372 metadata.type != IMAGE_DATA_TYPE_NANOVDB_FPN ||
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 depth = img->metadata.depth;
548 const int components = img->metadata.channels;
549
550 /* Read pixels. */
551 vector<StorageType> pixels_storage;
552 StorageType *pixels;
553 const size_t max_size = max(max(width, height), depth);
554 if (max_size == 0) {
555 /* Don't bother with empty images. */
556 return false;
557 }
558
559 /* Allocate memory as needed, may be smaller to resize down. */
560 if (texture_limit > 0 && max_size > texture_limit) {
561 pixels_storage.resize(((size_t)width) * height * depth * 4);
562 pixels = &pixels_storage[0];
563 }
564 else {
565 const thread_scoped_lock device_lock(device_mutex);
566 pixels = (StorageType *)img->mem->alloc(width, height, depth);
567 }
568
569 if (pixels == nullptr) {
570 /* Could be that we've run out of memory. */
571 return false;
572 }
573
574 const size_t num_pixels = ((size_t)width) * height * depth;
575 img->loader->load_pixels(
576 img->metadata, pixels, num_pixels * components, image_associate_alpha(img));
577
578 /* The kernel can handle 1 and 4 channel images. Anything that is not a single
579 * channel image is converted to RGBA format. */
580 const bool is_rgba = (img->metadata.type == IMAGE_DATA_TYPE_FLOAT4 ||
581 img->metadata.type == IMAGE_DATA_TYPE_HALF4 ||
582 img->metadata.type == IMAGE_DATA_TYPE_BYTE4 ||
583 img->metadata.type == IMAGE_DATA_TYPE_USHORT4);
584
585 if (is_rgba) {
586 const StorageType one = util_image_cast_from_float<StorageType>(1.0f);
587
588 if (components == 2) {
589 /* Grayscale + alpha to RGBA. */
590 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
591 pixels[i * 4 + 3] = pixels[i * 2 + 1];
592 pixels[i * 4 + 2] = pixels[i * 2 + 0];
593 pixels[i * 4 + 1] = pixels[i * 2 + 0];
594 pixels[i * 4 + 0] = pixels[i * 2 + 0];
595 }
596 }
597 else if (components == 3) {
598 /* RGB to RGBA. */
599 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
600 pixels[i * 4 + 3] = one;
601 pixels[i * 4 + 2] = pixels[i * 3 + 2];
602 pixels[i * 4 + 1] = pixels[i * 3 + 1];
603 pixels[i * 4 + 0] = pixels[i * 3 + 0];
604 }
605 }
606 else if (components == 1) {
607 /* Grayscale to RGBA. */
608 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
609 pixels[i * 4 + 3] = one;
610 pixels[i * 4 + 2] = pixels[i];
611 pixels[i * 4 + 1] = pixels[i];
612 pixels[i * 4 + 0] = pixels[i];
613 }
614 }
615
616 /* Disable alpha if requested by the user. */
617 if (img->params.alpha_type == IMAGE_ALPHA_IGNORE) {
618 for (size_t i = num_pixels - 1, pixel = 0; pixel < num_pixels; pixel++, i--) {
619 pixels[i * 4 + 3] = one;
620 }
621 }
622 }
623
624 if (img->metadata.colorspace != u_colorspace_raw &&
625 img->metadata.colorspace != u_colorspace_srgb)
626 {
627 /* Convert to scene linear. */
629 img->metadata.colorspace, pixels, num_pixels, is_rgba, img->metadata.compress_as_srgb);
630 }
631
632 /* Make sure we don't have buggy values. */
633 if constexpr (FileFormat == TypeDesc::FLOAT) {
634 /* For RGBA buffers we put all channels to 0 if either of them is not
635 * finite. This way we avoid possible artifacts caused by fully changed
636 * hue. */
637 if (is_rgba) {
638 for (size_t i = 0; i < num_pixels; i += 4) {
639 StorageType *pixel = &pixels[i * 4];
640 if (!isfinite(pixel[0]) || !isfinite(pixel[1]) || !isfinite(pixel[2]) ||
641 !isfinite(pixel[3]))
642 {
643 pixel[0] = 0;
644 pixel[1] = 0;
645 pixel[2] = 0;
646 pixel[3] = 0;
647 }
648 }
649 }
650 else {
651 for (size_t i = 0; i < num_pixels; ++i) {
652 StorageType *pixel = &pixels[i];
653 if (!isfinite(pixel[0])) {
654 pixel[0] = 0;
655 }
656 }
657 }
658 }
659
660 /* Scale image down if needed. */
661 if (!pixels_storage.empty()) {
662 float scale_factor = 1.0f;
663 while (max_size * scale_factor > texture_limit) {
664 scale_factor *= 0.5f;
665 }
666 VLOG_WORK << "Scaling image " << img->loader->name() << " by a factor of " << scale_factor
667 << ".";
668 vector<StorageType> scaled_pixels;
669 size_t scaled_width;
670 size_t scaled_height;
671 size_t scaled_depth;
672 util_image_resize_pixels(pixels_storage,
673 width,
674 height,
675 depth,
676 is_rgba ? 4 : 1,
677 scale_factor,
678 &scaled_pixels,
679 &scaled_width,
680 &scaled_height,
681 &scaled_depth);
682
683 StorageType *texture_pixels;
684
685 {
686 const thread_scoped_lock device_lock(device_mutex);
687 texture_pixels = (StorageType *)img->mem->alloc(scaled_width, scaled_height, scaled_depth);
688 }
689
690 memcpy(texture_pixels, &scaled_pixels[0], scaled_pixels.size() * sizeof(StorageType));
691 }
692
693 return true;
694}
695
696void ImageManager::device_load_image(Device *device,
697 Scene *scene,
698 const size_t slot,
700{
701 if (progress.get_cancel()) {
702 return;
703 }
704
705 Image *img = images[slot].get();
706
707 progress.set_status("Updating Images", "Loading " + img->loader->name());
708
709 const int texture_limit = scene->params.texture_limit;
710
711 load_image_metadata(img);
712 const ImageDataType type = img->metadata.type;
713
714 /* Name for debugging. */
715 img->mem_name = string_printf("tex_image_%s_%03d", name_from_type(type), (int)slot);
716
717 /* Free previous texture in slot. */
718 if (img->mem) {
719 const thread_scoped_lock device_lock(device_mutex);
720 img->mem.reset();
721 }
722
723 img->mem = make_unique<device_texture>(
724 device, img->mem_name.c_str(), slot, type, img->params.interpolation, img->params.extension);
725 img->mem->info.use_transform_3d = img->metadata.use_transform_3d;
726 img->mem->info.transform_3d = img->metadata.transform_3d;
727
728 /* Create new texture. */
729 if (type == IMAGE_DATA_TYPE_FLOAT4) {
730 if (!file_load_image<TypeDesc::FLOAT, float>(img, texture_limit)) {
731 /* on failure to load, we set a 1x1 pixels pink image */
732 const thread_scoped_lock device_lock(device_mutex);
733 float *pixels = (float *)img->mem->alloc(1, 1);
734
735 pixels[0] = TEX_IMAGE_MISSING_R;
736 pixels[1] = TEX_IMAGE_MISSING_G;
737 pixels[2] = TEX_IMAGE_MISSING_B;
738 pixels[3] = TEX_IMAGE_MISSING_A;
739 }
740 }
741 else if (type == IMAGE_DATA_TYPE_FLOAT) {
742 if (!file_load_image<TypeDesc::FLOAT, float>(img, texture_limit)) {
743 /* on failure to load, we set a 1x1 pixels pink image */
744 const thread_scoped_lock device_lock(device_mutex);
745 float *pixels = (float *)img->mem->alloc(1, 1);
746
747 pixels[0] = TEX_IMAGE_MISSING_R;
748 }
749 }
750 else if (type == IMAGE_DATA_TYPE_BYTE4) {
751 if (!file_load_image<TypeDesc::UINT8, uchar>(img, texture_limit)) {
752 /* on failure to load, we set a 1x1 pixels pink image */
753 const thread_scoped_lock device_lock(device_mutex);
754 uchar *pixels = (uchar *)img->mem->alloc(1, 1);
755
756 pixels[0] = (TEX_IMAGE_MISSING_R * 255);
757 pixels[1] = (TEX_IMAGE_MISSING_G * 255);
758 pixels[2] = (TEX_IMAGE_MISSING_B * 255);
759 pixels[3] = (TEX_IMAGE_MISSING_A * 255);
760 }
761 }
762 else if (type == IMAGE_DATA_TYPE_BYTE) {
763 if (!file_load_image<TypeDesc::UINT8, uchar>(img, texture_limit)) {
764 /* on failure to load, we set a 1x1 pixels pink image */
765 const thread_scoped_lock device_lock(device_mutex);
766 uchar *pixels = (uchar *)img->mem->alloc(1, 1);
767
768 pixels[0] = (TEX_IMAGE_MISSING_R * 255);
769 }
770 }
771 else if (type == IMAGE_DATA_TYPE_HALF4) {
772 if (!file_load_image<TypeDesc::HALF, half>(img, texture_limit)) {
773 /* on failure to load, we set a 1x1 pixels pink image */
774 const thread_scoped_lock device_lock(device_mutex);
775 half *pixels = (half *)img->mem->alloc(1, 1);
776
777 pixels[0] = TEX_IMAGE_MISSING_R;
778 pixels[1] = TEX_IMAGE_MISSING_G;
779 pixels[2] = TEX_IMAGE_MISSING_B;
780 pixels[3] = TEX_IMAGE_MISSING_A;
781 }
782 }
783 else if (type == IMAGE_DATA_TYPE_USHORT) {
784 if (!file_load_image<TypeDesc::USHORT, uint16_t>(img, texture_limit)) {
785 /* on failure to load, we set a 1x1 pixels pink image */
786 const thread_scoped_lock device_lock(device_mutex);
787 uint16_t *pixels = (uint16_t *)img->mem->alloc(1, 1);
788
789 pixels[0] = (TEX_IMAGE_MISSING_R * 65535);
790 }
791 }
792 else if (type == IMAGE_DATA_TYPE_USHORT4) {
793 if (!file_load_image<TypeDesc::USHORT, uint16_t>(img, texture_limit)) {
794 /* on failure to load, we set a 1x1 pixels pink image */
795 const thread_scoped_lock device_lock(device_mutex);
796 uint16_t *pixels = (uint16_t *)img->mem->alloc(1, 1);
797
798 pixels[0] = (TEX_IMAGE_MISSING_R * 65535);
799 pixels[1] = (TEX_IMAGE_MISSING_G * 65535);
800 pixels[2] = (TEX_IMAGE_MISSING_B * 65535);
801 pixels[3] = (TEX_IMAGE_MISSING_A * 65535);
802 }
803 }
804 else if (type == IMAGE_DATA_TYPE_HALF) {
805 if (!file_load_image<TypeDesc::HALF, half>(img, texture_limit)) {
806 /* on failure to load, we set a 1x1 pixels pink image */
807 const thread_scoped_lock device_lock(device_mutex);
808 half *pixels = (half *)img->mem->alloc(1, 1);
809
810 pixels[0] = TEX_IMAGE_MISSING_R;
811 }
812 }
813#ifdef WITH_NANOVDB
816 {
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
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,
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
float progress
Definition WM_types.hh:1019
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)
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
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 input_depth, const size_t components, vector< T > *output_pixels, size_t *output_width, size_t *output_height, size_t *output_depth)
#define CCL_NAMESPACE_END
#define assert(assertion)
float half
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global KernelWorkTile * tiles
const ccl_global KernelWorkTile * tile
#define VLOG_WORK
Definition log.h:74
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:167
unique_ptr< SceneUpdateStats > update_stats
Definition scene.h:174
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
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
ImageDataType
@ IMAGE_DATA_NUM_TYPES
@ IMAGE_DATA_TYPE_BYTE
@ IMAGE_DATA_TYPE_FLOAT
@ IMAGE_DATA_TYPE_NANOVDB_FP16
@ IMAGE_DATA_TYPE_FLOAT4
@ IMAGE_DATA_TYPE_USHORT4
@ IMAGE_DATA_TYPE_USHORT
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT
@ IMAGE_DATA_TYPE_NANOVDB_FLOAT3
@ IMAGE_DATA_TYPE_HALF
@ IMAGE_DATA_TYPE_BYTE4
@ IMAGE_DATA_TYPE_HALF4
@ IMAGE_DATA_TYPE_NANOVDB_FPN
@ IMAGE_ALPHA_CHANNEL_PACKED
@ IMAGE_ALPHA_IGNORE
@ TEX_IMAGE_MISSING_G
@ TEX_IMAGE_MISSING_A
@ TEX_IMAGE_MISSING_R
@ TEX_IMAGE_MISSING_B
wmTimer * timer