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