Blender V5.0
image_gpu.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "BLI_boxpack_2d.h"
12#include "BLI_linklist.h"
13#include "BLI_listbase.h"
14#include "BLI_math_base.hh"
15#include "BLI_rect.h"
16#include "BLI_threads.h"
17#include "BLI_time.h"
18
19#include "DNA_image_types.h"
20#include "DNA_userdef_types.h"
21
23#include "IMB_imbuf.hh"
24#include "IMB_imbuf_types.hh"
25
26#include "BKE_global.hh"
27#include "BKE_image.hh"
29#include "BKE_main.hh"
30
31#include "GPU_capabilities.hh"
32#include "GPU_state.hh"
33#include "GPU_texture.hh"
34
35#include "CLG_log.h"
36
37static CLG_LogRef LOG = {"gpu.texture"};
38
40
41/* Prototypes. */
42static void gpu_free_unused_buffers();
43static void image_free_gpu(Image *ima, const bool immediate);
45 Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h);
46
48{
49 if (image) {
50 /* Render result and compositor output are always premultiplied */
52 return true;
53 }
54 /* Generated images use pre multiplied float buffer, but straight alpha for byte buffers. */
55 if (image->type == IMA_TYPE_UV_TEST && ibuf) {
56 return ibuf->float_buffer.data != nullptr;
57 }
58 }
59 if (ibuf) {
60 if (ibuf->float_buffer.data) {
61 return image ? (image->alpha_mode != IMA_ALPHA_STRAIGHT) : false;
62 }
63
64 return image ? (image->alpha_mode == IMA_ALPHA_PREMUL) : true;
65 }
66 return false;
67}
68
69/* -------------------------------------------------------------------- */
72
73static bool is_over_resolution_limit(int w, int h)
74{
76}
77
82
83static blender::gpu::Texture *gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye)
84{
85 blender::gpu::Texture *tilearray = ima->gputexture[TEXTARGET_2D_ARRAY][multiview_eye];
86
87 if (tilearray == nullptr) {
88 return nullptr;
89 }
90
91 float array_w = GPU_texture_width(tilearray);
92 float array_h = GPU_texture_height(tilearray);
93
94 /* Determine maximum tile number. */
96 ImageTile *last_tile = (ImageTile *)ima->tiles.last;
97 int max_tile = last_tile->tile_number - 1001;
98
99 /* create image */
100 int width = max_tile + 1;
101 float *data = MEM_calloc_arrayN<float>(size_t(width) * 8, __func__);
102 for (int i = 0; i < width; i++) {
103 data[4 * i] = -1.0f;
104 }
106 int i = tile->tile_number - 1001;
107 ImageTile_Runtime *tile_runtime = &tile->runtime;
108 data[4 * i] = tile_runtime->tilearray_layer;
109
110 float *tile_info = &data[4 * width + 4 * i];
111 tile_info[0] = tile_runtime->tilearray_offset[0] / array_w;
112 tile_info[1] = tile_runtime->tilearray_offset[1] / array_h;
113 tile_info[2] = tile_runtime->tilearray_size[0] / array_w;
114 tile_info[3] = tile_runtime->tilearray_size[1] / array_h;
115 }
116
118 ima->id.name + 2,
119 width,
120 2,
121 1,
122 blender::gpu::TextureFormat::SFLOAT_32_32_32_32,
124 data);
125 GPU_texture_mipmap_mode(tex, false, false);
126
128
129 return tex;
130}
131
137
138static int compare_packtile(const void *a, const void *b)
139{
140 const PackTile *tile_a = (const PackTile *)a;
141 const PackTile *tile_b = (const PackTile *)b;
142
143 return tile_a->pack_score < tile_b->pack_score;
144}
145
147{
148 int arraywidth = 0, arrayheight = 0;
149 ListBase boxes = {nullptr};
150
151 int planes = 0;
152
154 ImageUser iuser;
155 BKE_imageuser_default(&iuser);
156 iuser.tile = tile->tile_number;
157 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
158
159 if (ibuf) {
160 PackTile *packtile = MEM_callocN<PackTile>(__func__);
161 packtile->tile = tile;
162 packtile->boxpack.w = ibuf->x;
163 packtile->boxpack.h = ibuf->y;
164
165 if (is_over_resolution_limit(packtile->boxpack.w, packtile->boxpack.h)) {
166 packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w);
167 packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h);
168 }
169 arraywidth = max_ii(arraywidth, packtile->boxpack.w);
170 arrayheight = max_ii(arrayheight, packtile->boxpack.h);
171
172 /* We sort the tiles by decreasing size, with an additional penalty term
173 * for high aspect ratios. This improves packing efficiency. */
174 float w = packtile->boxpack.w, h = packtile->boxpack.h;
175 packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h;
176
177 BKE_image_release_ibuf(ima, ibuf, nullptr);
178 BLI_addtail(&boxes, packtile);
179 planes = max_ii(planes, ibuf->planes);
180 }
181 }
182
183 BLI_assert(arraywidth > 0 && arrayheight > 0);
184
186 int arraylayers = 0;
187 /* Keep adding layers until all tiles are packed. */
188 while (boxes.first != nullptr) {
189 ListBase packed = {nullptr};
190 BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed);
191 BLI_assert(packed.first != nullptr);
192
193 LISTBASE_FOREACH (PackTile *, packtile, &packed) {
194 ImageTile *tile = packtile->tile;
195 ImageTile_Runtime *tile_runtime = &tile->runtime;
196 int *tileoffset = tile_runtime->tilearray_offset;
197 int *tilesize = tile_runtime->tilearray_size;
198
199 tileoffset[0] = packtile->boxpack.x;
200 tileoffset[1] = packtile->boxpack.y;
201 tilesize[0] = packtile->boxpack.w;
202 tilesize[1] = packtile->boxpack.h;
203 tile_runtime->tilearray_layer = arraylayers;
204 }
205
207 arraylayers++;
208 }
209
210 const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH);
211 const bool use_grayscale = planes <= 8;
212 /* Create Texture without content. */
214 main_ibuf,
215 arraywidth,
216 arrayheight,
217 arraylayers,
218 use_high_bitdepth,
219 use_grayscale);
220
221 /* Upload each tile one by one. */
223 const ImageTile_Runtime *tile_runtime = &tile->runtime;
224 const int tilelayer = tile_runtime->tilearray_layer;
225 const int *tileoffset = tile_runtime->tilearray_offset;
226 const int *tilesize = tile_runtime->tilearray_size;
227
228 if (tilesize[0] == 0 || tilesize[1] == 0) {
229 continue;
230 }
231
232 ImageUser iuser;
233 BKE_imageuser_default(&iuser);
234 iuser.tile = tile->tile_number;
235 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
236
237 if (ibuf) {
238 const bool store_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(ima, ibuf);
240 ibuf,
241 UNPACK2(tileoffset),
242 tilelayer,
243 UNPACK2(tilesize),
244 use_high_bitdepth,
245 use_grayscale,
246 store_premultiplied);
247 }
248
249 BKE_image_release_ibuf(ima, ibuf, nullptr);
250 }
251
252 if (GPU_mipmap_enabled()) {
254 GPU_texture_mipmap_mode(tex, true, true);
255 if (ima) {
257 }
258 }
259 else {
260 GPU_texture_mipmap_mode(tex, false, true);
261 }
262
263 return tex;
264}
265
267
268/* -------------------------------------------------------------------- */
271
273 eGPUTextureTarget textarget,
274 const int multiview_eye)
275{
276 const bool in_range = (int(textarget) >= 0) && (textarget < TEXTARGET_COUNT);
277 BLI_assert(in_range);
278 BLI_assert(ELEM(multiview_eye, 0, 1));
279
280 if (in_range) {
281 return &(ima->gputexture[textarget][multiview_eye]);
282 }
283 return nullptr;
284}
285
287{
288 CLOG_ERROR(&LOG, "Failed to create GPU texture from Blender image");
289 switch (textarget) {
291 return GPU_texture_create_error(2, true);
293 return GPU_texture_create_error(1, true);
294 case TEXTARGET_2D:
295 default:
296 return GPU_texture_create_error(2, false);
297 }
298}
299
302{
304 /* Calculate the clipping region with the tile buffer.
305 * TODO(jbakker): should become part of ImageTileData to deduplicate with image engine. */
306 rcti buffer_rect;
308 &buffer_rect, 0, changes.tile_data.tile_buffer->x, 0, changes.tile_data.tile_buffer->y);
309 rcti clipped_update_region;
310 const bool has_overlap = BLI_rcti_isect(
311 &buffer_rect, &changes.changed_region.region, &clipped_update_region);
312 if (!has_overlap) {
313 continue;
314 }
315
317 changes.tile_data.tile,
318 changes.tile_data.tile_buffer,
319 clipped_update_region.xmin,
320 clipped_update_region.ymin,
321 BLI_rcti_size_x(&clipped_update_region),
322 BLI_rcti_size_y(&clipped_update_region));
323 }
324}
325
327{
328 PartialUpdateChecker<ImageTileData> checker(image, iuser, image->runtime->partial_update_user);
330 switch (changes.get_result_code()) {
332 image_free_gpu(image, true);
333 break;
334 }
335
338 break;
339 }
340
342 /* GPUTextures are up to date. */
343 break;
344 }
345 }
346}
347
349{
350 if (!image) {
351 return;
352 }
353
354 /* Note that the image can cache both stereo views, so we only invalidate the cache if the view
355 * index is more than 2. */
356 if (!ELEM(image->gpu_pass, IMAGE_GPU_PASS_NONE, iuser->pass) ||
357 !ELEM(image->gpu_layer, IMAGE_GPU_LAYER_NONE, iuser->layer) ||
358 (!ELEM(image->gpu_view, IMAGE_GPU_VIEW_NONE, iuser->multi_index) && iuser->multi_index >= 2))
359 {
361 }
362}
363
365 ImageUser *iuser,
366 const bool use_viewers,
367 const bool use_tile_mapping,
368 bool try_only)
369{
371
372 if (ima == nullptr) {
373 return result;
374 }
375
376 /* Free any unused GPU textures, since we know we are in a thread with OpenGL
377 * context and might as well ensure we have as much space free as possible. */
379
380 /* Free GPU textures when requesting a different render pass/layer.
381 * When `iuser` isn't set (texture painting single image mode) we assume that
382 * the current `pass` and `layer` should be 0. */
383 short requested_pass = iuser ? iuser->pass : 0;
384 short requested_layer = iuser ? iuser->layer : 0;
385 short requested_view = iuser ? iuser->multi_index : 0;
386 /* There is room for 2 multiview textures. When a higher number is requested we should always
387 * target the first view slot. This is fine as multi view images aren't used together. */
388 if (requested_view < 2) {
389 requested_view = 0;
390 }
391 if (ima->gpu_pass != requested_pass || ima->gpu_layer != requested_layer ||
392 ima->gpu_view != requested_view)
393 {
394 ima->gpu_pass = requested_pass;
395 ima->gpu_layer = requested_layer;
396 ima->gpu_view = requested_view;
397 /* The cache should be invalidated here, but it is intentionally isn't due to possible
398 * performance implications, see the BKE_image_ensure_gpu_texture function for more
399 * information. */
400 }
401#undef GPU_FLAGS_TO_CHECK
402
403 if (ima->runtime->partial_update_user == nullptr) {
404 ima->runtime->partial_update_user = BKE_image_partial_update_create(ima);
405 }
406
408
409 /* Tag as in active use for garbage collector. */
411
412 /* Test if we need to get a tiled array texture. */
413 eGPUTextureTarget textarget = (use_tile_mapping && ima->source == IMA_SRC_TILED) ?
416
417 /* Test if we already have a texture. */
418 int current_view = iuser ? iuser->multi_index : 0;
419 if (current_view >= 2) {
420 current_view = 0;
421 }
422
423 result.texture = get_image_gpu_texture_ptr(ima, textarget, current_view);
424 if (textarget == TEXTARGET_2D_ARRAY) {
425 result.tile_mapping = get_image_gpu_texture_ptr(ima, TEXTARGET_TILE_MAPPING, current_view);
426 }
427
428 if (*result.texture) {
429 return result;
430 }
431
432 if (try_only) {
433 /* If we got this far, it means the texture is not loaded. */
434 return result;
435 }
436
437 /* Check if we have a valid image. If not, we return a dummy
438 * texture with zero bind-code so we don't keep trying. */
440 if (tile == nullptr) {
441 *result.texture = image_gpu_texture_error_create(textarget);
442 if (textarget == TEXTARGET_2D_ARRAY) {
444 }
445 return result;
446 }
447
448 /* check if we have a valid image buffer */
449 void *lock;
450 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, (use_viewers) ? &lock : nullptr);
451 if (ibuf == nullptr) {
452 BKE_image_release_ibuf(ima, ibuf, (use_viewers) ? lock : nullptr);
453 *result.texture = image_gpu_texture_error_create(textarget);
454 if (textarget == TEXTARGET_2D_ARRAY) {
456 }
457 return result;
458 }
459
460 if (textarget == TEXTARGET_2D_ARRAY) {
461 /* For materials, array and tile mapping in case there are UDIM tiles. */
462 *result.texture = gpu_texture_create_tile_array(ima, ibuf);
463 *result.tile_mapping = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0);
464 }
465 else {
466 /* Single image texture. */
467 const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH);
468 const bool store_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(ima, ibuf);
469
471 ima->id.name + 2, ibuf, use_high_bitdepth, store_premultiplied);
472
473 if (*result.texture) {
475
476 if (GPU_mipmap_enabled()) {
479 GPU_texture_mipmap_mode(*result.texture, true, true);
480 }
481 else {
482 GPU_texture_mipmap_mode(*result.texture, false, true);
483 }
484 }
485 }
486
487 if (*result.texture) {
488 GPU_texture_original_size_set(*result.texture, ibuf->x, ibuf->y);
489 }
490
491 BKE_image_release_ibuf(ima, ibuf, (use_viewers) ? lock : nullptr);
492
493 return result;
494}
495
497{
498 return *image_get_gpu_texture(image, iuser, false, false, false).texture;
499}
500
505
507 ImageUser *iuser,
508 const bool use_tile_mapping)
509{
510 return image_get_gpu_texture(image, iuser, false, use_tile_mapping, false);
511}
512
514 ImageUser *iuser,
515 const bool use_tile_mapping)
516{
517 return image_get_gpu_texture(image, iuser, false, use_tile_mapping, true);
518}
519
521
522/* -------------------------------------------------------------------- */
528
531
533{
534 if (gpu_texture_free_queue == nullptr) {
535 return;
536 }
537
538 std::scoped_lock lock(gpu_texture_queue_mutex);
539
540 while (gpu_texture_free_queue != nullptr) {
541 blender::gpu::Texture *tex = static_cast<blender::gpu::Texture *>(
543 GPU_texture_free(tex);
544 }
545}
546
553
555
556/* -------------------------------------------------------------------- */
559
560static void image_free_gpu(Image *ima, const bool immediate)
561{
562 for (int eye = 0; eye < 2; eye++) {
563 for (int i = 0; i < TEXTARGET_COUNT; i++) {
564 if (ima->gputexture[i][eye] != nullptr) {
565 if (immediate) {
566 GPU_texture_free(ima->gputexture[i][eye]);
567 }
568 else {
569 std::scoped_lock lock(gpu_texture_queue_mutex);
571 }
572
573 ima->gputexture[i][eye] = nullptr;
574 }
575 }
576 }
577
579}
580
585
587{
588 if (bmain) {
589 LISTBASE_FOREACH (Image *, ima, &bmain->images) {
591 }
592 }
593}
594
596{
597 if (bmain) {
598 LISTBASE_FOREACH (Image *, ima, &bmain->images) {
599 if (BKE_image_is_animated(ima)) {
601 }
602 }
603 }
604}
605
607{
608 static int lasttime = 0;
609 int ctime = int(BLI_time_now_seconds());
610
611 /*
612 * Run garbage collector once for every collecting period of time
613 * if textimeout is 0, that's the option to NOT run the collector
614 */
615 if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) {
616 return;
617 }
618
619 /* of course not! */
620 if (G.is_rendering) {
621 return;
622 }
623
624 lasttime = ctime;
625
626 LISTBASE_FOREACH (Image *, ima, &bmain->images) {
627 if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) {
628 /* If it's in GL memory, deallocate and set time tag to current time
629 * This gives textures a "second chance" to be used before dying. */
632 ima->lastused = ctime;
633 }
634 /* Otherwise, just kill the buffers */
635 else {
637 }
638 }
639 }
640}
641
643
644/* -------------------------------------------------------------------- */
647
648static ImBuf *update_do_scale(const uchar *rect,
649 const float *rect_float,
650 int *x,
651 int *y,
652 int *w,
653 int *h,
654 int limit_w,
655 int limit_h,
656 int full_w,
657 int full_h)
658{
659 /* Partial update with scaling. */
660 float xratio = limit_w / float(full_w);
661 float yratio = limit_h / float(full_h);
662
663 int part_w = *w, part_h = *h;
664
665 /* Find sub coordinates in scaled image. Take ceiling because we will be
666 * losing 1 pixel due to rounding errors in x,y. */
667 *x *= xratio;
668 *y *= yratio;
669 *w = int(ceil(xratio * (*w)));
670 *h = int(ceil(yratio * (*h)));
671
672 /* ...but take back if we are over the limit! */
673 if (*x + *w > limit_w) {
674 (*w)--;
675 }
676 if (*y + *h > limit_h) {
677 (*h)--;
678 }
679
680 /* Scale pixels. */
681 ImBuf *ibuf = IMB_allocFromBuffer(rect, rect_float, part_w, part_h, 4);
682 IMB_scale(ibuf, *w, *h, IMBScaleFilter::Box, false);
683
684 return ibuf;
685}
686
688 const uchar *rect,
689 const float *rect_float,
690 int full_w,
691 int full_h,
692 int x,
693 int y,
694 int layer,
695 const int *tile_offset,
696 const int *tile_size,
697 int w,
698 int h)
699{
700 ImBuf *ibuf;
701 if (layer > -1) {
702 ibuf = update_do_scale(
703 rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h);
704
705 /* Shift to account for tile packing. */
706 x += tile_offset[0];
707 y += tile_offset[1];
708 }
709 else {
710 /* Partial update with scaling. */
711 int limit_w = GPU_texture_width(tex);
712 int limit_h = GPU_texture_height(tex);
713
714 ibuf = update_do_scale(rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h);
715 }
716
717 void *data = (ibuf->float_buffer.data) ? (void *)(ibuf->float_buffer.data) :
718 (void *)(ibuf->byte_buffer.data);
720
721 GPU_texture_update_sub(tex, data_format, data, x, y, blender::math::max(layer, 0), w, h, 1);
722
723 IMB_freeImBuf(ibuf);
724}
725
727 uchar *rect,
728 float *rect_float,
729 int x,
730 int y,
731 int layer,
732 const int tile_offset[2],
733 int w,
734 int h,
735 int tex_stride,
736 int tex_offset)
737{
738 if (layer > -1) {
739 /* Shift to account for tile packing. */
740 x += tile_offset[0];
741 y += tile_offset[1];
742 }
743
744 void *data = (rect_float) ? (void *)(rect_float + tex_offset) : (void *)(rect + tex_offset);
745 eGPUDataFormat data_format = (rect_float) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
746
747 /* Partial update without scaling. Stride and offset are used to copy only a
748 * subset of a possible larger buffer than what we are updating. */
749 GPU_unpack_row_length_set(tex_stride);
750
751 GPU_texture_update_sub(tex, data_format, data, x, y, blender::math::max(layer, 0), w, h, 1);
752 /* Restore default. */
754}
755
757 Image *ima,
758 ImBuf *ibuf,
760 int x,
761 int y,
762 int w,
763 int h)
764{
765 bool scaled;
766 if (tile != nullptr) {
767 ImageTile_Runtime *tile_runtime = &tile->runtime;
768 int *tilesize = tile_runtime->tilearray_size;
769 scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]);
770 }
771 else {
772 scaled = (GPU_texture_width(tex) != ibuf->x) || (GPU_texture_height(tex) != ibuf->y);
773 }
774
775 if (scaled) {
776 /* Extra padding to account for bleed from neighboring pixels. */
777 const int padding = 4;
778 const int xmax = min_ii(x + w + padding, ibuf->x);
779 const int ymax = min_ii(y + h + padding, ibuf->y);
780 x = max_ii(x - padding, 0);
781 y = max_ii(y - padding, 0);
782 w = xmax - x;
783 h = ymax - y;
784 }
785
786 /* Get texture data pointers. */
787 float *rect_float = ibuf->float_buffer.data;
788 uchar *rect = ibuf->byte_buffer.data;
789 int tex_stride = ibuf->x;
790 int tex_offset = ibuf->channels * (y * ibuf->x + x);
791
792 const bool store_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(ima, ibuf);
793 if (rect_float) {
794 /* Float image is already in scene linear colorspace or non-color data by
795 * convention, no colorspace conversion needed. But we do require 4 channels
796 * currently. */
797 if (ibuf->channels != 4 || scaled || !store_premultiplied) {
798 rect_float = MEM_malloc_arrayN<float>(4 * size_t(w) * size_t(h), __func__);
799 if (rect_float == nullptr) {
800 return;
801 }
802
803 tex_stride = w;
804 tex_offset = 0;
805
807 rect_float, x, y, w, h, ibuf, store_premultiplied);
808 }
809 }
810 else {
811 /* Byte image is in original colorspace from the file, and may need conversion. */
813 /* Not scaled Non-color data, just store buffer as is. */
814 }
818 {
819 /* sRGB or scene linear or scaled down non-color data, store as byte texture that the GPU
820 * can decode directly. */
821 rect = MEM_malloc_arrayN<uchar>(4 * size_t(w) * size_t(h), __func__);
822 if (rect == nullptr) {
823 return;
824 }
825
826 tex_stride = w;
827 tex_offset = 0;
828
829 /* Convert to scene linear with sRGB compression, and premultiplied for
830 * correct texture interpolation. */
831 IMB_colormanagement_imbuf_to_byte_texture(rect, x, y, w, h, ibuf, store_premultiplied);
832 }
833 else {
834 /* Other colorspace, store as float texture to avoid precision loss. */
835 rect_float = MEM_malloc_arrayN<float>(4 * size_t(w) * size_t(h), __func__);
836 if (rect_float == nullptr) {
837 return;
838 }
839
840 tex_stride = w;
841 tex_offset = 0;
842
844 rect_float, x, y, w, h, ibuf, store_premultiplied);
845 }
846 }
847
848 if (scaled) {
849 /* Slower update where we first have to scale the input pixels. */
850 if (tile != nullptr) {
851 ImageTile_Runtime *tile_runtime = &tile->runtime;
852 int *tileoffset = tile_runtime->tilearray_offset;
853 int *tilesize = tile_runtime->tilearray_size;
854 int tilelayer = tile_runtime->tilearray_layer;
856 tex, rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h);
857 }
858 else {
860 tex, rect, rect_float, ibuf->x, ibuf->y, x, y, -1, nullptr, nullptr, w, h);
861 }
862 }
863 else {
864 /* Fast update at same resolution. */
865 if (tile != nullptr) {
866 ImageTile_Runtime *tile_runtime = &tile->runtime;
867 int *tileoffset = tile_runtime->tilearray_offset;
868 int tilelayer = tile_runtime->tilearray_layer;
870 tex, rect, rect_float, x, y, tilelayer, tileoffset, w, h, tex_stride, tex_offset);
871 }
872 else {
874 tex, rect, rect_float, x, y, -1, nullptr, w, h, tex_stride, tex_offset);
875 }
876 }
877
878 /* Free buffers if needed. */
879 if (rect && rect != ibuf->byte_buffer.data) {
880 MEM_freeN(rect);
881 }
882 if (rect_float && rect_float != ibuf->float_buffer.data) {
883 MEM_freeN(rect_float);
884 }
885
886 if (GPU_mipmap_enabled()) {
888 }
889 else {
891 }
892
894}
895
897 Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h)
898{
899 const int eye = 0;
901 /* Check if we need to update the main gputexture. */
902 if (tex != nullptr && tile == ima->tiles.first) {
903 gpu_texture_update_from_ibuf(tex, ima, ibuf, nullptr, x, y, w, h);
904 }
905
906 /* Check if we need to update the array gputexture. */
907 tex = ima->gputexture[TEXTARGET_2D_ARRAY][eye];
908 if (tex != nullptr) {
909 gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h);
910 }
911}
912
913void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
914{
915 ImageTile *image_tile = BKE_image_get_tile_from_iuser(ima, iuser);
916 ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr);
917 BKE_image_update_gputexture_delayed(ima, image_tile, ibuf, x, y, w, h);
918 BKE_image_release_ibuf(ima, ibuf, nullptr);
919}
920
922 Image *ima, ImageTile *image_tile, ImBuf *ibuf, int x, int y, int w, int h)
923{
924 /* Check for full refresh. */
925 if (ibuf != nullptr && ima->source != IMA_SRC_TILED && x == 0 && y == 0 && w == ibuf->x &&
926 h == ibuf->y)
927 {
929 }
930 else {
931 rcti dirty_region;
932 BLI_rcti_init(&dirty_region, x, x + w, y, y + h);
933 BKE_image_partial_update_mark_region(ima, image_tile, ibuf, &dirty_region);
934 }
935}
936
937void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap)
938{
939 LISTBASE_FOREACH (Image *, ima, &bmain->images) {
941 if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) {
942 for (int a = 0; a < TEXTARGET_COUNT; a++) {
944 for (int eye = 0; eye < 2; eye++) {
945 blender::gpu::Texture *tex = ima->gputexture[a][eye];
946 if (tex != nullptr) {
947 GPU_texture_mipmap_mode(tex, mipmap, true);
948 }
949 }
950 }
951 }
952 }
953 else {
955 }
956 }
957 else {
958 ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE;
959 }
960 }
961}
962
ImBuf * BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
void BKE_image_partial_update_mark_region(Image *image, const ImageTile *image_tile, const ImBuf *image_buffer, const rcti *updated_region)
Mark a region of the image to update.
void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock)
void BKE_image_tag_time(Image *ima)
void BKE_image_sort_tiles(Image *ima)
bool BKE_image_has_opengl_texture(Image *ima)
void BKE_imageuser_default(ImageUser *iuser)
PartialUpdateUser * BKE_image_partial_update_create(const Image *image)
Create a new PartialUpdateUser. An Object that contains data to use partial updates.
bool BKE_image_is_animated(Image *image)
void BKE_image_partial_update_mark_full_update(Image *image)
Mark the whole image to be updated.
#define BLI_assert(a)
Definition BLI_assert.h:46
void BLI_box_pack_2d_fixedarea(struct ListBase *boxes, int width, int height, struct ListBase *packed)
#define LISTBASE_FOREACH(type, var, list)
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void void BLI_listbase_sort(ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
MINLINE int power_of_2_min_i(int n)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
ATTR_WARN_UNUSED_RESULT const size_t num
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition rct.cc:414
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
unsigned char uchar
int BLI_thread_is_main(void)
Definition threads.cc:179
Platform independent time functions.
double BLI_time_now_seconds(void)
Definition time.cc:113
#define UNPACK2(a)
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
@ IMA_GPU_MIPMAP_COMPLETE
#define IMAGE_GPU_PASS_NONE
#define IMAGE_GPU_LAYER_NONE
@ IMA_SRC_TILED
@ IMA_NOCOLLECT
@ IMA_HIGH_BITDEPTH
@ IMA_ALPHA_STRAIGHT
@ IMA_ALPHA_PREMUL
@ IMA_TYPE_UV_TEST
@ IMA_TYPE_R_RESULT
@ IMA_TYPE_COMPOSITE
#define IMAGE_GPU_VIEW_NONE
eGPUTextureTarget
@ TEXTARGET_2D
@ TEXTARGET_2D_ARRAY
@ TEXTARGET_COUNT
@ TEXTARGET_TILE_MAPPING
int GPU_texture_size_with_limit(int res)
bool GPU_mipmap_enabled()
Definition gpu_state.cc:293
void GPU_texture_update_sub(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *pixels, int offset_x, int offset_y, int offset_z, int width, int height, int depth)
int GPU_texture_height(const blender::gpu::Texture *texture)
blender::gpu::Texture * GPU_texture_create_1d_array(const char *name, int width, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_update_mipmap_chain(blender::gpu::Texture *texture)
void GPU_texture_unbind(blender::gpu::Texture *texture)
int GPU_texture_width(const blender::gpu::Texture *texture)
eGPUDataFormat
@ GPU_DATA_UBYTE
@ GPU_DATA_FLOAT
void GPU_texture_original_size_set(blender::gpu::Texture *texture, int width, int height)
void GPU_texture_extend_mode(blender::gpu::Texture *texture, GPUSamplerExtendMode extend_mode)
void GPU_texture_mipmap_mode(blender::gpu::Texture *texture, bool use_mipmap, bool use_filter)
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_SAMPLER_EXTEND_MODE_REPEAT
void GPU_unpack_row_length_set(uint len)
blender::gpu::Texture * GPU_texture_create_error(int dimension, bool array)
void GPU_texture_free(blender::gpu::Texture *texture)
void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer, int offset_x, int offset_y, int width, int height, const ImBuf *ibuf, bool store_premultiplied)
bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace)
bool IMB_colormanagement_space_is_data(const ColorSpace *colorspace)
void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer, int offset_x, int offset_y, int width, int height, const ImBuf *ibuf, bool store_premultiplied)
bool IMB_colormanagement_space_is_srgb(const ColorSpace *colorspace)
blender::gpu::Texture * IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, bool use_premult)
Definition util_gpu.cc:327
ImBuf * IMB_allocFromBuffer(const uint8_t *byte_buffer, const float *float_buffer, unsigned int w, unsigned int h, unsigned int channels)
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_update_gpu_texture_sub(blender::gpu::Texture *tex, ImBuf *ibuf, int x, int y, int z, int w, int h, bool use_high_bitdepth, bool use_grayscale, bool use_premult)
Definition util_gpu.cc:296
blender::gpu::Texture * IMB_touch_gpu_texture(const char *name, ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth, bool use_grayscale)
Definition util_gpu.cc:270
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:465
Read Guarded memory(de)allocation.
volatile int lock
#define U
BMesh const char void * data
btMatrix3x3 scaled(const btVector3 &s) const
Create a scaled copy of the matrix.
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
nullptr float
#define packed
#define ceil
uint padding(uint offset, uint alignment)
static ImageGPUTextures image_get_gpu_texture(Image *ima, ImageUser *iuser, const bool use_viewers, const bool use_tile_mapping, bool try_only)
Definition image_gpu.cc:364
static void gpu_texture_update_unscaled(blender::gpu::Texture *tex, uchar *rect, float *rect_float, int x, int y, int layer, const int tile_offset[2], int w, int h, int tex_stride, int tex_offset)
Definition image_gpu.cc:726
static void gpu_texture_update_scaled(blender::gpu::Texture *tex, const uchar *rect, const float *rect_float, int full_w, int full_h, int x, int y, int layer, const int *tile_offset, const int *tile_size, int w, int h)
Definition image_gpu.cc:687
static int compare_packtile(const void *a, const void *b)
Definition image_gpu.cc:138
static void gpu_free_unused_buffers()
Definition image_gpu.cc:532
static void image_gpu_texture_partial_update_changes_available(Image *image, PartialUpdateChecker< ImageTileData >::CollectResult &changes)
Definition image_gpu.cc:300
void BKE_image_update_gputexture(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
Definition image_gpu.cc:913
ImageGPUTextures BKE_image_get_gpu_material_texture_try(Image *image, ImageUser *iuser, const bool use_tile_mapping)
Definition image_gpu.cc:513
void BKE_image_free_anim_gputextures(Main *bmain)
Definition image_gpu.cc:595
bool BKE_image_has_gpu_texture_premultiplied_alpha(Image *image, ImBuf *ibuf)
Definition image_gpu.cc:47
ImageGPUTextures BKE_image_get_gpu_material_texture(Image *image, ImageUser *iuser, const bool use_tile_mapping)
Definition image_gpu.cc:506
static void image_gpu_texture_try_partial_update(Image *image, ImageUser *iuser)
Definition image_gpu.cc:326
void BKE_image_update_gputexture_delayed(Image *ima, ImageTile *image_tile, ImBuf *ibuf, int x, int y, int w, int h)
Definition image_gpu.cc:921
blender::gpu::Texture * BKE_image_get_gpu_viewer_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:501
void BKE_image_ensure_gpu_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:348
static void image_free_gpu(Image *ima, const bool immediate)
Definition image_gpu.cc:560
void BKE_image_free_gputextures(Image *ima)
Definition image_gpu.cc:581
static blender::gpu::Texture * gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye)
Definition image_gpu.cc:83
static blender::gpu::Texture ** get_image_gpu_texture_ptr(Image *ima, eGPUTextureTarget textarget, const int multiview_eye)
Definition image_gpu.cc:272
static bool is_over_resolution_limit(int w, int h)
Definition image_gpu.cc:73
void BKE_image_paint_set_mipmap(Main *bmain, bool mipmap)
Definition image_gpu.cc:937
static int smaller_power_of_2_limit(int num)
Definition image_gpu.cc:78
static void image_update_gputexture_ex(Image *ima, ImageTile *tile, ImBuf *ibuf, int x, int y, int w, int h)
Definition image_gpu.cc:896
static ImBuf * update_do_scale(const uchar *rect, const float *rect_float, int *x, int *y, int *w, int *h, int limit_w, int limit_h, int full_w, int full_h)
Definition image_gpu.cc:648
blender::gpu::Texture * BKE_image_get_gpu_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:496
static LinkNode * gpu_texture_free_queue
Definition image_gpu.cc:529
void BKE_image_free_all_gputextures(Main *bmain)
Definition image_gpu.cc:586
void BKE_image_free_unused_gpu_textures()
Definition image_gpu.cc:547
static blender::Mutex gpu_texture_queue_mutex
Definition image_gpu.cc:530
static blender::gpu::Texture * image_gpu_texture_error_create(eGPUTextureTarget textarget)
Definition image_gpu.cc:286
void BKE_image_free_old_gputextures(Main *bmain)
Definition image_gpu.cc:606
static blender::gpu::Texture * gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
Definition image_gpu.cc:146
static void gpu_texture_update_from_ibuf(blender::gpu::Texture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h)
Definition image_gpu.cc:756
const ccl_global KernelWorkTile * tile
#define LOG(level)
Definition log.h:97
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
@ PartialChangesDetected
Changes detected since the last time requested.
@ FullUpdateNeeded
Unable to construct partial updates. Caller should perform a full update.
@ NoChangesDetected
No changes detected since the last time requested.
T max(const T &a, const T &b)
std::mutex Mutex
Definition BLI_mutex.hh:47
char name[258]
Definition DNA_ID.h:432
const ColorSpace * colorspace
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
unsigned char planes
blender::gpu::Texture ** texture
Definition BKE_image.hh:614
short multi_index
short gpuflag
GPUTexture * gputexture[3][2]
short gpu_pass
short gpu_layer
short gpu_view
ListBase tiles
short source
ImageRuntimeHandle * runtime
void * last
void * first
ListBase images
Definition BKE_main.hh:286
ImageTile * tile
Definition image_gpu.cc:134
float pack_score
Definition image_gpu.cc:135
FixedSizeBoxPack boxpack
Definition image_gpu.cc:133
ePartialUpdateIterResult get_next_change()
Load the next changed region.
CollectResult collect_changes()
Check for new changes since the last time this method was invoked for this user.
rcti region
region of the image that has been updated. Region can be bigger than actual changes.
int ymin
int xmin
void * BKE_image_free_buffers
Definition stubs.c:35
void * BKE_image_get_tile_from_iuser
Definition stubs.c:37
void * BKE_image_get_tile
Definition stubs.c:36
i
Definition text_draw.cc:230