Blender V5.0
util_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 "BLI_utildefines.h"
10
11#include "MEM_guardedalloc.h"
12
13#include "CLG_log.h"
14
15#include "GPU_capabilities.hh"
16#include "GPU_texture.hh"
17
19#include "IMB_imbuf.hh"
20#include "IMB_imbuf_types.hh"
21
22static CLG_LogRef LOG = {"image.gpu"};
23
24/* gpu ibuf utils */
25
27{
28 if (ibuf->planes > 8) {
29 return false;
30 }
31
32 if (ibuf->byte_buffer.data && !ibuf->float_buffer.data) {
33
36 {
37 /* Grey-scale byte buffers with these color transforms utilize float buffers under the hood
38 * and can therefore be optimized. */
39 return true;
40 }
41 /* TODO: Support gray-scale byte buffers.
42 * The challenge is that Blender always stores byte images as RGBA. */
43 return false;
44 }
45
46 /* Only #IMBuf's with color-space that do not modify the chrominance of the texture data relative
47 * to the scene color space can be uploaded as single channel textures. */
51 {
52 return true;
53 }
54 return false;
55}
56
57static void imb_gpu_get_format(const ImBuf *ibuf,
58 bool high_bitdepth,
59 bool use_grayscale,
60 blender::gpu::TextureFormat *r_texture_format)
61{
62 const bool float_rect = (ibuf->float_buffer.data != nullptr);
63 const bool is_grayscale = use_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
64
65 if (float_rect) {
66 /* Float. */
67 const bool use_high_bitdepth = (!(ibuf->foptions.flag & OPENEXR_HALF) && high_bitdepth);
68 *r_texture_format = is_grayscale ?
69 (use_high_bitdepth ? blender::gpu::TextureFormat::SFLOAT_32 :
70 blender::gpu::TextureFormat::SFLOAT_16) :
71 (use_high_bitdepth ? blender::gpu::TextureFormat::SFLOAT_32_32_32_32 :
72 blender::gpu::TextureFormat::SFLOAT_16_16_16_16);
73 }
74 else {
77 {
78 /* Non-color data or scene linear, just store buffer as is. */
79 *r_texture_format = (is_grayscale) ? blender::gpu::TextureFormat::UNORM_8 :
80 blender::gpu::TextureFormat::UNORM_8_8_8_8;
81 }
83 /* sRGB, store as byte texture that the GPU can decode directly. */
84 *r_texture_format = (is_grayscale) ? blender::gpu::TextureFormat::SFLOAT_16 :
85 blender::gpu::TextureFormat::SRGBA_8_8_8_8;
86 }
87 else {
88 /* Other colorspace, store as half float texture to avoid precision loss. */
89 *r_texture_format = (is_grayscale) ? blender::gpu::TextureFormat::SFLOAT_16 :
90 blender::gpu::TextureFormat::SFLOAT_16_16_16_16;
91 }
92 }
93}
94
95static const char *imb_gpu_get_swizzle(const ImBuf *ibuf)
96{
97 return imb_is_grayscale_texture_format_compatible(ibuf) ? "rrra" : "rgba";
98}
99
100/* Return false if no suitable format was found. */
102 blender::gpu::TextureFormat *r_texture_format)
103{
104 /* For DDS we only support data, scene linear and sRGB. Converting to
105 * different colorspace would break the compression. */
106 const bool use_srgb = (!IMB_colormanagement_space_is_data(ibuf->byte_buffer.colorspace) &&
108
109 if (ibuf->dds_data.fourcc == FOURCC_DXT1) {
110 *r_texture_format = (use_srgb) ? blender::gpu::TextureFormat::SRGB_DXT1 :
111 blender::gpu::TextureFormat::SNORM_DXT1;
112 }
113 else if (ibuf->dds_data.fourcc == FOURCC_DXT3) {
114 *r_texture_format = (use_srgb) ? blender::gpu::TextureFormat::SRGB_DXT3 :
115 blender::gpu::TextureFormat::SNORM_DXT3;
116 }
117 else if (ibuf->dds_data.fourcc == FOURCC_DXT5) {
118 *r_texture_format = (use_srgb) ? blender::gpu::TextureFormat::SRGB_DXT5 :
119 blender::gpu::TextureFormat::SNORM_DXT5;
120 }
121 else {
122 return false;
123 }
124 return true;
125}
126
131static void *imb_gpu_get_data(const ImBuf *ibuf,
132 const bool do_rescale,
133 const int rescale_size[2],
134 const bool store_premultiplied,
135 const bool allow_grayscale,
136 bool *r_freedata,
137 eGPUDataFormat *r_data_format)
138{
139 bool is_float_rect = (ibuf->float_buffer.data != nullptr);
140 const bool is_grayscale = allow_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
141 void *data_rect = (is_float_rect) ? (void *)ibuf->float_buffer.data :
142 (void *)ibuf->byte_buffer.data;
143 bool freedata = false;
144
145 if (is_float_rect) {
146 /* Float image is already in scene linear colorspace or non-color data by
147 * convention, no colorspace conversion needed. But we do require 4 channels
148 * currently. */
149 if (ibuf->channels != 4 || !store_premultiplied) {
150 data_rect = MEM_malloc_arrayN<float>(4 * size_t(ibuf->x) * size_t(ibuf->y), __func__);
151 *r_freedata = freedata = true;
152
153 if (data_rect == nullptr) {
154 return nullptr;
155 }
156
158 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
159 }
160 }
161 else {
162 /* Byte image is in original colorspace from the file, and may need conversion.
163 *
164 * We must also convert to premultiplied for correct texture interpolation
165 * and consistency with float images. */
167 /* Non-color data, just store buffer as is. */
168 }
171 {
172 /* sRGB or scene linear, store as byte texture that the GPU can decode directly. */
173 data_rect = MEM_mallocN((is_grayscale ? sizeof(float[4]) : sizeof(uchar[4])) *
175 __func__);
176 *r_freedata = freedata = true;
177
178 if (data_rect == nullptr) {
179 return nullptr;
180 }
181
182 /* Texture storage of images is defined by the alpha mode of the image. The
183 * downside of this is that there can be artifacts near alpha edges. However,
184 * this allows us to use sRGB texture formats and preserves color values in
185 * zero alpha areas, and appears generally closer to what game engines that we
186 * want to be compatible with do. */
187 if (is_grayscale) {
188 /* Convert to byte buffer to then pack as half floats reducing the buffer size by half. */
190 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
191 is_float_rect = true;
192 }
193 else {
195 (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
196 }
197 }
198 else {
199 /* Other colorspace, store as float texture to avoid precision loss. */
200 data_rect = MEM_malloc_arrayN<float>(4 * size_t(ibuf->x) * size_t(ibuf->y), __func__);
201 *r_freedata = freedata = true;
202 is_float_rect = true;
203
204 if (data_rect == nullptr) {
205 return nullptr;
206 }
207
208 /* Texture storage of images is defined by the alpha mode of the image. The
209 * downside of this is that there can be artifacts near alpha edges. However,
210 * this allows us to use sRGB texture formats and preserves color values in
211 * zero alpha areas, and appears generally closer to what game engines that we
212 * want to be compatible with do. */
214 (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
215 }
216 }
217
218 if (do_rescale) {
219 const uint8_t *rect = (is_float_rect) ? nullptr : (uint8_t *)data_rect;
220 const float *rect_float = (is_float_rect) ? (float *)data_rect : nullptr;
221
222 ImBuf *scale_ibuf = IMB_allocFromBuffer(rect, rect_float, ibuf->x, ibuf->y, 4);
223 IMB_scale(scale_ibuf, UNPACK2(rescale_size), IMBScaleFilter::Box, false);
224
225 if (freedata) {
226 MEM_freeN(data_rect);
227 }
228
229 data_rect = (is_float_rect) ? (void *)scale_ibuf->float_buffer.data :
230 (void *)scale_ibuf->byte_buffer.data;
231 *r_freedata = freedata = true;
232 /* Steal the rescaled buffer to avoid double free. */
233 (void)IMB_steal_byte_buffer(scale_ibuf);
234 (void)IMB_steal_float_buffer(scale_ibuf);
235 IMB_freeImBuf(scale_ibuf);
236 }
237
238 /* Pack first channel data manually at the start of the buffer. */
239 if (is_grayscale) {
240 void *src_rect = data_rect;
241
242 if (freedata == false) {
243 data_rect = MEM_mallocN(
244 (is_float_rect ? sizeof(float) : sizeof(uchar)) * IMB_get_pixel_count(ibuf), __func__);
245 *r_freedata = freedata = true;
246 }
247
248 if (data_rect == nullptr) {
249 return nullptr;
250 }
251
252 size_t buffer_size = do_rescale ? size_t(rescale_size[0]) * size_t(rescale_size[1]) :
253 size_t(ibuf->x) * size_t(ibuf->y);
254 if (is_float_rect) {
255 for (size_t i = 0; i < buffer_size; i++) {
256 ((float *)data_rect)[i] = ((float *)src_rect)[i * 4];
257 }
258 }
259 else {
260 for (size_t i = 0; i < buffer_size; i++) {
261 ((uchar *)data_rect)[i] = ((uchar *)src_rect)[i * 4];
262 }
263 }
264 }
265
266 *r_data_format = (is_float_rect) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
267 return data_rect;
268}
269
271 ImBuf *ibuf,
272 int w,
273 int h,
274 int layers,
275 bool use_high_bitdepth,
276 bool use_grayscale)
277{
279 imb_gpu_get_format(ibuf, use_high_bitdepth, use_grayscale, &tex_format);
280
282 if (layers > 0) {
284 name, w, h, layers, 9999, tex_format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
285 }
286 else {
288 name, w, h, 9999, tex_format, GPU_TEXTURE_USAGE_SHADER_READ, nullptr);
289 }
290
293 return tex;
294}
295
297 ImBuf *ibuf,
298 int x,
299 int y,
300 int z,
301 int w,
302 int h,
303 bool use_high_bitdepth,
304 bool use_grayscale,
305 bool use_premult)
306{
307 const bool do_rescale = (ibuf->x != w || ibuf->y != h);
308 const int size[2] = {w, h};
309
311 imb_gpu_get_format(ibuf, use_high_bitdepth, use_grayscale, &tex_format);
312
313 bool freebuf = false;
314
315 eGPUDataFormat data_format;
316 void *data = imb_gpu_get_data(
317 ibuf, do_rescale, size, use_premult, use_grayscale, &freebuf, &data_format);
318
319 /* Update Texture. */
320 GPU_texture_update_sub(tex, data_format, data, x, y, z, w, h, 1);
321
322 if (freebuf) {
324 }
325}
326
328 ImBuf *ibuf,
329 bool use_high_bitdepth,
330 bool use_premult)
331{
332 blender::gpu::Texture *tex = nullptr;
334 bool do_rescale = (ibuf->x != size[0]) || (ibuf->y != size[1]);
335
336 /* Correct the smaller size to maintain the original aspect ratio of the image. */
337 if (do_rescale && ibuf->x != ibuf->y) {
338 if (size[0] > size[1]) {
339 size[1] = int(ibuf->y * (float(size[0]) / ibuf->x));
340 }
341 else {
342 size[0] = int(ibuf->x * (float(size[1]) / ibuf->y));
343 }
344 }
345
346 if (ibuf->ftype == IMB_FTYPE_DDS) {
347 blender::gpu::TextureFormat compressed_format;
348 if (!IMB_gpu_get_compressed_format(ibuf, &compressed_format)) {
349 CLOG_WARN(&LOG, "Unable to find a suitable DXT compression");
350 }
351 else if (do_rescale) {
352 CLOG_WARN(&LOG, "Unable to load DXT image resolution");
353 }
354 else if (!is_power_of_2_i(ibuf->x) || !is_power_of_2_i(ibuf->y)) {
355 /* We require POT DXT/S3TC texture sizes not because something in there
356 * intrinsically needs it, but because we flip them upside down at
357 * load time, and that (when mipmaps are involved) is only possible
358 * with POT height. */
359 CLOG_WARN(&LOG, "Unable to load non-power-of-two DXT image resolution");
360 }
361 else {
363 ibuf->x,
364 ibuf->y,
365 ibuf->dds_data.nummipmaps,
366 compressed_format,
368 ibuf->dds_data.data);
369
370 if (tex != nullptr) {
371 return tex;
372 }
373
374 CLOG_WARN(&LOG, "ST3C support not found");
375 }
376 /* Fall back to uncompressed texture. */
377 CLOG_WARN(&LOG, "Falling back to uncompressed (%s, %ix%i).", name, ibuf->x, ibuf->y);
378 }
379
381 imb_gpu_get_format(ibuf, use_high_bitdepth, true, &tex_format);
382
383 bool freebuf = false;
384
385 /* Create Texture. Specify read usage to allow both shader and host reads, the latter is needed
386 * by the GPU compositor. */
388 tex = GPU_texture_create_2d(name, UNPACK2(size), 9999, tex_format, usage, nullptr);
389 if (tex == nullptr) {
390 size[0] = max_ii(1, size[0] / 2);
391 size[1] = max_ii(1, size[1] / 2);
392 tex = GPU_texture_create_2d(name, UNPACK2(size), 9999, tex_format, usage, nullptr);
393 do_rescale = true;
394 }
395 BLI_assert(tex != nullptr);
396 eGPUDataFormat data_format;
397 void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, true, &freebuf, &data_format);
398 GPU_texture_update(tex, data_format, data);
399
402
403 if (freebuf) {
405 }
406
407 return tex;
408}
409
411 bool high_bitdepth,
412 bool use_grayscale)
413{
414 blender::gpu::TextureFormat gpu_texture_format;
415 imb_gpu_get_format(ibuf, high_bitdepth, use_grayscale, &gpu_texture_format);
416 return gpu_texture_format;
417}
418
420{
421 const float half_min = -65504;
422 const float half_max = 65504;
423 if (!image_buffer->float_buffer.data) {
424 return;
425 }
426
427 float *rect_float = image_buffer->float_buffer.data;
428
429 int rect_float_len = image_buffer->x * image_buffer->y *
430 (image_buffer->channels == 0 ? 4 : image_buffer->channels);
431
432 for (int i = 0; i < rect_float_len; i++) {
433 rect_float[i] = clamp_f(rect_float[i], half_min, half_max);
434 }
435}
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float clamp_f(float value, float min, float max)
MINLINE int max_ii(int a, int b)
MINLINE int is_power_of_2_i(int n)
unsigned char uchar
#define UNPACK2(a)
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
int GPU_texture_size_with_limit(int res)
void GPU_texture_swizzle_set(blender::gpu::Texture *texture, const char swizzle[4])
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)
blender::gpu::Texture * GPU_texture_create_2d_array(const char *name, int width, int height, int layer_len, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_anisotropic_filter(blender::gpu::Texture *texture, bool use_aniso)
blender::gpu::Texture * GPU_texture_create_compressed_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const void *data)
eGPUDataFormat
@ GPU_DATA_UBYTE
@ GPU_DATA_FLOAT
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_HOST_READ
@ GPU_TEXTURE_USAGE_GENERAL
blender::gpu::Texture * GPU_texture_create_2d(const char *name, int width, int height, int mip_len, blender::gpu::TextureFormat format, eGPUTextureUsage usage, const float *data)
void GPU_texture_update(blender::gpu::Texture *texture, eGPUDataFormat data_format, const void *data)
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)
float * IMB_steal_float_buffer(ImBuf *ibuf)
uint8_t * IMB_steal_byte_buffer(ImBuf *ibuf)
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)
size_t IMB_get_pixel_count(const ImBuf *ibuf)
Get the length of the data of the given image buffer in pixels.
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:465
@ IMB_FTYPE_DDS
#define FOURCC_DXT5
#define FOURCC_DXT1
#define FOURCC_DXT3
Read Guarded memory(de)allocation.
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
#define OPENEXR_HALF
#define LOG(level)
Definition log.h:97
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
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
const char * name
unsigned int nummipmaps
unsigned char * data
unsigned int fourcc
const ColorSpace * colorspace
const ColorSpace * colorspace
DDSData dds_data
ImBufFloatBuffer float_buffer
ImbFormatOptions foptions
ImBufByteBuffer byte_buffer
unsigned char planes
enum eImbFileType ftype
i
Definition text_draw.cc:230
static void imb_gpu_get_format(const ImBuf *ibuf, bool high_bitdepth, bool use_grayscale, blender::gpu::TextureFormat *r_texture_format)
Definition util_gpu.cc:57
blender::gpu::Texture * IMB_create_gpu_texture(const char *name, ImBuf *ibuf, bool use_high_bitdepth, bool use_premult)
Definition util_gpu.cc:327
static bool imb_is_grayscale_texture_format_compatible(const ImBuf *ibuf)
Definition util_gpu.cc:26
static const char * imb_gpu_get_swizzle(const ImBuf *ibuf)
Definition util_gpu.cc:95
static void * imb_gpu_get_data(const ImBuf *ibuf, const bool do_rescale, const int rescale_size[2], const bool store_premultiplied, const bool allow_grayscale, bool *r_freedata, eGPUDataFormat *r_data_format)
Definition util_gpu.cc:131
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
blender::gpu::TextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf, bool high_bitdepth, bool use_grayscale)
Definition util_gpu.cc:410
bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, blender::gpu::TextureFormat *r_texture_format)
Definition util_gpu.cc:101
void IMB_gpu_clamp_half_float(ImBuf *image_buffer)
Definition util_gpu.cc:419